I am going through the documentation of Qt Scripting and came up to it is totally confusing and full if mis guiding text. Could some please explain in simple English how to wrap a function and access it in script code after wrapping. I have included my example below.
Wrapper function. This is a simple wrapper which I need to return the string that is passed as the parameter. following is the code.
#include <QApplication>
#include <QtScript/QScriptEngine>
#include <QtScript/QScriptContext>
#include <QDebug>
QScriptValue returnProperty(QScriptContext *context , QScriptEngine *engine)
{
qDebug() << "running returnValues Function "<< context->argument(0).toString();
return context->thisObject().property("returnValue");
}
int main(int argc, char *argv[])
{
QApplication app(argc,argv);
QScriptEngine engine;
//Evaluating a simple expression
qDebug() << engine.evaluate("1+2").toNumber();
QScriptValue func = engine.globalObject();
func.setProperty("foo",engine.newFunction(returnProperty),QScriptValue::PropertyGetter);
engine.evaluate("var v= foo('name') ; print( 'This prints values from addValues function :',v) ;");
}
And the output is as follows
3
Running returnValues Function "undefined"
If I am understanding this correctly this is what I should do and if I call engine.newObject() as it is mentioned in the doc function does not even get called.
what I do not get here is that in what is the property I am assigning in func.setproperty line and what can I do with the property foo once I set it. How can I set a value in the function.
I appreciate if someone explain what I am doing wrong here.
You are already on the right track. QScriptEngine::newFunction() brings the function into the engine. Now, you need a way to access this function from the script. A "function" is just a property of the global object and you can add a new property with setProperty(). The code
QScriptValue globalObject = engine.globalObject();
QScriptValue func = engine.newFunction(returnProperty);
globalObject.setProperty("foo", func);
produces the output
3
running returnValues Function "name"
This prints values from addValues function : name
The flags QScriptValue::PropertyGetter and QScriptValue::PropertySetter are only needed, when you want to create a property, which has to call a function upon access. It is similar to the properties of QObject. Consider this example:
class MyObject : public QObject
{
Q_PROPERTY(QString name READ getName WRITE setName)
};
MyObject* obj = new MyObject;
When you do a obj->setProperty("name", "Sam"); you call MyObject::setName("Sam") in the background and obj->getProperty("name") is a wrapper for MyObject::getName(). A small example:
QScriptValue getName(QScriptContext* ctx, QScriptEngine* eng)
{
// Return the value of the internal '_name_' property.
qDebug() << "Getter 'getName' called";
return ctx->thisObject().property("_name_");
}
QScriptValue setName(QScriptContext* ctx, QScriptEngine* eng)
{
// Do some processing and store the name in an internal '_name_' property.
qDebug() << "Setter 'setName' called";
ctx->thisObject().setProperty("_name_",
ctx->argument(0).toString().toUpper());
return QScriptValue::UndefinedValue;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QScriptEngine engine;
QScriptValue globalObject = engine.globalObject();
// Create a new object.
QScriptValue obj = engine.newObject();
// Bring the functions into the engine.
QScriptValue getNameFunc = engine.newFunction(getName);
QScriptValue setNameFunc = engine.newFunction(setName);
// Create a 'name' property, which calls the getter and setter from above.
obj.setProperty("name", getNameFunc, QScriptValue::PropertyGetter);
obj.setProperty("name", setNameFunc, QScriptValue::PropertySetter);
// Make the new object known as 'person'.
globalObject.setProperty("person", obj);
// Test our construct.
engine.evaluate("print('Set the name to fitzgerald');");
engine.evaluate("person.name = 'fitzgerald';");
engine.evaluate("print('And the name is... ' + person.name)");
}
Finally the output:
Set the name to fitzgerald
Setter 'setName' called
Getter 'getName' called
And the name is... FITZGERALD
Related
For my serialization method i need to store a QList<T> where T is my custom Type, in a QVariantList.
QList<T> l;
l.append(T());
QVariant var = QVariant::fromValue(l);
var.canConvert(QVariant::List); // returns true
//So i can easily iterate over the variant with sth like this:
QVariantList list;
QSequentialIterable it = var.value<QSequentialIterable>();
for (const QVariant &v : it)
list << v;
/* deserialization side */
var = list;
var.value<QList<T>>(); //returns an empty list which is not my serialized list;
My problem is that i cannot convert back the variant list into QList<T>
EDIT:
#define PROPERTY(type, name) \
Q_PROPERTY(type name MEMBER name) \
type name;
class Measurement
{
Q_GADGET
public:
PROPERTY(int, index)
PROPERTY(QString, name)
PROPERTY(QString, unit)
PROPERTY(double, factor)
PROPERTY(bool, isVisible)
PROPERTY(quint8, decimal)
bool operator ==(const Measurement &other)
{
return (this->index == other.index);
}
};
you can consider this class as my custom type (T). i also save the class name (here "Measurement") along with serialized data for furthur uses, because as you know we can get the registered type with QMetaType::type(char*) but with that type i can only construct a QVariant with QVariant(int typeId, const void *copy) but here i want to construct the QList<Measurement> itself.
You will need to deserialize the QVariant list one item at a time. I am also not sure that this line:
var = list;
is performing what you intended. It will take your QVariantList list and wrap it inside another QVariant called var, which is of type QVariant(QVariantList, (QVariant(MyType, ), QVariant(MyType, ))). There doesn't seem to be much benefit to doing this.
Nonetheless, the example below shows a way to recover the list from var.
#include <QCoreApplication>
#include <QVariant>
class MyType {
public:
MyType() {}
MyType(QString value) { m_value = value; }
QString m_value;
};
Q_DECLARE_METATYPE(MyType)
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<MyType> l;
l.append(MyType("foo"));
l.append(MyType("bar"));
QVariant var = QVariant::fromValue(l);
var.canConvert(QVariant::List); // returns true
//So i can easily iterate over the variant with sth like this:
QVariantList list;
QSequentialIterable it = var.value<QSequentialIterable>();
for (const QVariant &v : it)
list << v;
/* deserialization side */
var = list;
QList<MyType> deserializedList;
foreach(QVariant v, var.value<QVariantList>()) {
deserializedList << v.value<MyType>();
}
return a.exec();
}
i'm using QT for the first time and got some problems with refreshing the GUI while adding elements.
The Code looks like:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
PObj obj;
MainWindow mw;
qRegisterMetaType<std::string>();
QObject::connect(&obj, SIGNAL(setText(std::string const&)),
&mw, SLOT(appendText(std::string const&)));
QFuture<void> f1 = QtConcurrent::run(&obj, &PObj::process);
mw.show();
f1.waitForFinished();
return a.exec();
}
With the PObj::process definition:
void PObj::process()
{
for(; ;)
{
sleep(1);
//do work and set text
std::string text = "bla";
emit setText( text );
}
}
And the MainWindow::appendText slot:
void MainWindow::appendText(std::string const& str )
{
ui->listWidget->addItem(QString::fromStdString(str));
}
I've tried placing qApp->processEvents() ,QCoreApplication::processEvents(); ... running wit future in the ThreadPool.
I thought running them with Concurrent::run is enough ?
UPDATE:
The question is, why the GUI isnt refreshed every second a new item is added ?
The f1.waitForFinished(); calls blocks until f1 is finished, as the name implies. This will never happen because you have the infinite loop. So your code will never get to main loop. You can't block the main thread like that! In general, avoid any WaitForXxxx() methods, especially the GUI thread.
Also, you have no way of stopping the process(); anyway, so waiting for it to finish doesn't make any sense... You might want to add a way to tell it to stop (such as atomic variable) but anyway, to fix your problem, simply remove the f1.waitForFinished(); line.
To terminate the task nicely, try adding QAtomicInt flag (not volatile boolean, it won't do), and then change the code like this:
Add member variable to PObj (should make it private and add setter):
QAtomicInt termianteFlag;
Change main like this:
int main(int argc, char *argv[])
{
///snip
QFuture<void> f1 = QtConcurrent::run(&obj, &PObj::process);
mw.show();
int ret = a.exec();
f1.terminateFlag = 1; // change this to setter method
f1.waitForFinished(); // this is not ideal, will wait for up to a second before exit
}
and
void PObj::process()
{
while(!terminateFlag)
{
sleep(1);
//do work and set text
std::string text = "bla";
emit setText( text );
}
}
In some Qt examples, I see they use
QTimer::singleShot(0, this , SLOT(funcA())), why not to call the slot funcA directly? also the same question for using QMetaMethod::invoke to call function with parameters.
The following lines are all functionally equivalent:
QTimer::singleShot(0, object, &Class::funcA); // Qt 5
QTimer::singleShot(0, object, SLOT(funcA())); // Qt 4
QMetaObject::invokeMethod(object, "funcA", Qt::QueuedConnection);
As is now apparent, the intent is to execute the call within the event loop. The queued call results in the posting of an QMetaCallEvent to the object. This event is handled by QObject::event and results in the call of the desired method. Thus, the following are exactly equivalent, even if the latter is a private implementation detail - letting me skip the details of instantiating the event:
QMetaObject::invokeMethod(object, "funcA", Qt::QueuedConnection);
QCoreApplication::postEvent(object, new QMetaCallEvent{...});
This comes handy in various situations. For example:
To execute some code after all hitherto posted events have been handled.
To execute only after the event loop has started.
To call an invokable method that's not accessible due to C++ access modifiers. The invokable methods are: signals, slot, and methods declared Q_INVOKABLE.
The direct call is unsafe (read: an error!) when a QObject resides in another thread, unless you're explicitly calling a method documented as thread-safe.
The queued call is a necessity if you wish to ensure that an event loop quits immediately: a direct quit() call is a no-op if the loop is not running yet.
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
app.quit(); // this is a no-op since the event loop isn't running yet
return app.exec(); // will not quit as desired
}
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
return app.exec(); // will return immediately
}
Ideally, you'd use postToThread from this answer, it offers the lowest-cost way of calling methods in other threads:
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
postToThread([]{ qApp->quit(); });
}
An alternative way of doing it is using a QObject as a signal source:
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
{
QObject src;
src.connect(&src, &QObject::destroyed, &app, &QCoreApplication::quit,
Qt::QueuedConnection);
}
return app.exec(); // will return immediately
}
Yet another way would be to use a custom event and act in its destructor:
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
struct QuitEvent : QEvent {
QuitEvent() : QEvent(QEvent::None) {}
~QuitEvent() { qApp->quit(); }
};
QCoreApplication::postEvent(&app, new QuitEvent);
return app.exec(); // will return immediately
}
Every system has a event loop where events are processed. Say like
application::processEvents()
{
// process event list..
}
Now the place where you write QTimer::singleShot(0, this, SLOT(doSomething())); might be inside some processing event.
When this loop is done, processEvents will be called again and in that the doSomething() will be executed.
So this is like calling doSomething in the next event loop, rather than calling it immediately.
Hope you get the idea.
These methods can also be used to invoke protected and private members of a class (if they are defined as slots) from a scope that would otherwise require public access.
I know that I can use QMetaType to create an object without parameters.
Another possible option is to use QMetaObject and call newInstance. But I need to get QMetaObject from something.
I tried to use QMetaType::metaObjectForType, but it always returns null pointer (but QMetaType is able to create the object).
QMetaObject const* metaObject = QMetaType::metaObjectForType(id); // return null pointer
QObject* object = (QObject*)QMetaType::create(id); // create the object
QMetaObject const* metaObject = object->metaObject(); // return not-null pointer
UPDATE:
I think the question is why metaObjectForType does not work for me.
The class is registered with qRegisterMetaType, also Q_DECLARE_METATYPE and Q_OBJECT are applied.
First of all, to pass parameters to methods, you needs some kind of reflection framework beyond plain C++. With Qt, the obivous choice is Qt Meta Object system with it's QMetaObject, though then you must derive your classes for QObject. After that, you need to do two things:
1. make constructor invokable
Signals and slots are invokable by default, but any other method you want to invoke through the meta object system needs to be explicitly marked as such. Example myqobject.h:
#ifndef MYQOBJECT_H
#define MYQOBJECT_H
#include <QObject>
class MyQObject : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE MyQObject(QObject *parent = 0); // tested with empty constructor in .cpp
};
#endif // MYQOBJECT_H
2. create your own mapping from class name string to the QMetaObject
QMetaType doc says: "any class or struct that has a public default constructor, a public copy constructor, and a public destructor can be registered." This rules out QObject, because they can't have copy constructors. You need to create your own mapping from name to the meta object. An example shown in this main.cpp:
#include <QCoreApplication>
#include <QtCore>
#include "myqobject.h"
// a global map for mapping strings to QMetaObjects,
// you need header file like this if you want to access it from other .cpp files:
//
// #include <QHash>
// #include <QString>
// class QMetaObject; // forward declaration, enough when only pointer is needed
// extern QHash<QString, const QMetaObject*> metaObjs;
//
QHash<QString, const QMetaObject*> metaObjs;
// optional: create a macro to avoid typing class name twice,
// #c surrounds macro argument with double quotes converting it to string
#define METAOBJS_INSERT(c) (metaObjs.insert(#c, &c::staticMetaObject))
int main()
{
METAOBJS_INSERT(MyQObject);
const QMetaObject *meta = metaObjs["MyQObject"];
qDebug() << "Class name from staticMetaObject: " << meta->className();
QObject *o = meta->newInstance(); // add constructor arguments as needed
MyQObject *mo = qobject_cast<MyQObject*>(o);
if (mo) qDebug() << "Class name from object instance: " << mo->metaObject()->className();
else qDebug() << "Instance creation failed or created wrong class!";
return 0;
}
If you do not want to use QObject, then you need to come up with some similar (probably lighter-weight and without separate compiler step) mechanism of your own.
I had the same problem.
The solution is in two steps:
call qRegisterMetaType() with a pointer type:
qRegisterMetaType<MyClass*>(). this will give you a valid
QMetaObject with call of QMetaType::metaObjectForType(id);
make your constructor Q_INVOKABLE. this will enable a valid call to
QMetaObject::newInstance
And also be sure that your class is derived from QObject and have a Q_OBJECT macro in it.
class A : public QObject
{
Q_OBJECT
public:
A(const A&) {}; // dummy copy contructor that do nothing to disable error message
Q_INVOKABLE A(int test_value = 99999) : TestValue(test_value) {};
int TestValue;
};
Q_DECLARE_METATYPE(A);
int main(int argc, char *argv[])
{
qRegisterMetaType<A>(); //you do not need this
qRegisterMetaType<A*>();//the real registration
int type_id_for_A = QMetaType::type("A"); // valid type id (in my case = 403)
const QMetaObject *meta_object_for_A = QMetaType::metaObjectForType(type_id_for_A); // returns NULL
int type_id_for_A_ptr = QMetaType::type("A*"); // valid type id (in my case = 404)
const QMetaObject *meta_object_for_A_tr = QMetaType::metaObjectForType(type_id_for_A_ptr); // returns NOT NULL
A* A_obj= dynamic_cast<A*>(meta_object_for_A_tr->newInstance(Q_ARG(int, 12345)));
int test_value = A_obj->TestValue; // returns 12345, not 99999
}
I want do a math editor using qtscript.
It will support array calculating in script. Such as array1 + array2 = array3.({1,2,3}+{3,4,5} = {4,6,8});
Maybe I need override operator+,
I consult the example of QByteArray, and I override operator+,but when I execute in Script,it can't be invoke,anyone coule give me some suggestions?
bytearray.h
class ByteArrayClass : public QObject, public QScriptClass
{
public:
QByteArray &operator+(int n);
}
main.cpp
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QScriptEngine eng;
ByteArrayClass *baClass = new ByteArrayClass(&eng);
eng.globalObject().setProperty("ByteArray", baClass->constructor());
eng.evaluate("ba = new ByteArray(4))"
eng.evaluate("ba+2;"); //this will not invoke override operator+.
ByteArrayClass *ba = new ByteArrayClass(&eng);
int n = 3;
*ba + n; //but this can invoke the override operator+
}
If this couldn't be realised,maybe the one way is to replace all the operator to the custom function.
As far as I know operators can not be overloaded in QtScript, because it is not allowed in Javascript in general (e.g. see ECMA Script 4 - Progress and this Article).
Now for your case you have the choice to go with Add, Mult, ... functions or leave for some less constrained scripting language.