PDA

View Full Version : How to really really use custom QtScript classes



axed
18th July 2014, 15:15
Hello everybody :)

i'm aiming for the following ecma scenario:


var whatever = new myClass();
whatever.whatreallyever();


So i want to use a custom c++, from QObject inherited class from within QtScript.

... after hours of consulting the documentation, forum threads and tried tons of experiments by myself - i still wasn't able to make any object available to a QScriptEngine like shown in the case above.

So my question is, how can you implement some newable c++ object for QtScript and/or is there any minimal example for that case?

What i've tried so far:
1) Qt 4.8's customClass Example - doesnt compile under Qt5.3, mocking that

ByteArrayClass *cls = qscriptvalue_cast<ByteArrayClass*>(ctx->callee().data());
results in:

In file included from /usr/include/qt5/QtCore/qnamespace.h:45:0,
from /usr/include/qt5/QtCore/qobjectdefs.h:45,
from /usr/include/qt5/QtCore/qobject.h:48,
from /usr/include/qt5/QtCore/QObject:1,
from ../customClass/bytearrayclass.h:4,
from ../customClass/bytearrayclass.cpp:2:
/usr/include/qt5/QtCore/qobject.h: In instantiation of 'T qobject_cast(QObject*) [with T = ByteArrayClass*]':
/usr/include/qt5/QtCore/qvariant.h:695:51: required from 'static T QtPrivate::QVariantValueHelper<T>::object(const QVariant&) [with T = ByteArrayClass*]'
/usr/include/qt5/QtCore/qvariant.h:101:37: required from 'static ReturnType QtPrivate::ObjectInvoker<Derived, Argument, ReturnType>::invoke(Argument) [with Derived = QtPrivate::QVariantValueHelper<ByteArrayClass*>; Argument = const QVariant&; ReturnType = ByteArrayClass*]'
/usr/include/qt5/QtCore/qvariant.h:810:64: required from 'T qvariant_cast(const QVariant&) [with T = ByteArrayClass*]'
/usr/include/qt5/QtScript/qscriptengine.h:349:50: required from 'T qscriptvalue_cast(const QScriptValue&) [with T = ByteArrayClass*]'
../customClass/bytearrayclass.cpp:157:82: required from here
/usr/include/qt5/QtCore/qglobal.h:679:85: error: invalid application of 'sizeof' to incomplete type 'QStaticAssertFailure<false>'
enum {Q_STATIC_ASSERT_PRIVATE_JOIN(q_static_assert_resu lt, __COUNTER__) = sizeof(QStaticAssertFailure<!!(Condition)>)}
^
/usr/include/qt5/QtCore/qglobal.h:684:47: note: in expansion of macro 'Q_STATIC_ASSERT'
#define Q_STATIC_ASSERT_X(Condition, Message) Q_STATIC_ASSERT(Condition)
^
/usr/include/qt5/QtCore/qobject.h:520:5: note: in expansion of macro 'Q_STATIC_ASSERT_X'
Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
^
make: *** [bytearrayclass.o] Error 1

2) Experiment 1:


qScriptRegisterQObjectMetaType<myObject*>(&engine);
QScriptValue myObjectClass = engine.scriptValueFromQMetaObject<myObject>();
engine.globalObject().setProperty("myObject", myObjectClass);

QScriptValue result = engine.evaluate("var obj = new myObject();");
// (program crashes according to GDB at (??))


3) Experiment 2:


QScriptEngine engine;
myCLassPrototype myProto;
engine.setDefaultPrototype(qMetaTypeId<myClassMaster*>(),
engine.newQObject(&myProto));
engine.evaluate("var x = new myClassProto()");
// or engine.evaluate("var x = new myClassMaster()");
// (resulting in "ReferenceError: Can't find variable: shitCLassPrototype")


4) Experiment 3:


class Wrapper_QMessageBox: public QMessageBox, protected QScriptable
{
Q_OBJECT
public:

Wrapper_QMessageBox(QWidget *parent =0)
: QMessageBox(parent) {}

Wrapper_QMessageBox(Icon icon, const QString & title, const QString & text, StandardButtons buttons = NoButton, QWidget * parent = 0, Qt::WindowFlags f = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint)
: QMessageBox(icon,title,text,buttons,parent,f) {}

public slots:
QScriptValue qscript_call(QWidget *parent = 0)
{
QMessageBox * const iface = new Wrapper_QMessageBox(parent);
return engine()->newQObject(iface, QScriptEngine::AutoOwnership);
}
QScriptValue qscript_call( Icon icon, const QString & title, const QString & text, StandardButtons buttons = NoButton, QWidget * parent = 0, Qt::WindowFlags f = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint )
{
QMessageBox * const iface = new Wrapper_QMessageBox(icon,title,text,buttons,parent ,f);
return engine()->newQObject(iface, QScriptEngine::AutoOwnership);
}
void setWindowTitle ( const QString & title )
{
QMessageBox::setWindowTitle(title);
}
};

engine.globalObject().setProperty("QMessageBox", engine.newQObject(new Wrapper_QMessageBox, QScriptEngine::AutoOwnership));
QScriptValue result = engine.evaluate("var x = new QMessageBox();");
// (resulting in "error: TypeError: Result of expression 'QMessageBox' [Wrapper_QMessageBox(name = "")] is not a constructor.<global>() at 1")


Also, whenever i've tried to use:


Q_SCRIPT_DECLARE_QMETAOBJECT(myObject, QObject*);

or


Q_DECLARE_METATYPE(myObject);


gcc yells at me that:


In file included from ../../Qt5.3.1/5.3/gcc_64/include/QtScript/QScriptEngine:1:0,
from ../scriptedObjects/main.cpp:2:
../scriptedObjects/main.cpp: In function 'void testsuite3::startTest()':
../../Qt5.3.1/5.3/gcc_64/include/QtScript/qscriptengine.h:281:1: error: a template declaration cannot appear at block scope
template<> inline QScriptValue qscriptQMetaObjectConstructor<T>(QScriptContext *ctx, QScriptEngine *eng, T *) \
^
../scriptedObjects/main.cpp:93:9: note: in expansion of macro 'Q_SCRIPT_DECLARE_QMETAOBJECT'
Q_SCRIPT_DECLARE_QMETAOBJECT(myObject, QObject*);
^
make: *** [main.o] Error 1


Besides: where are all the QtScript examples in the Qt 5.3 SDK (QtCreator doesn't show any - only QML examples when searching for "script")?

anda_skoa
18th July 2014, 17:05
I would go for the method of providing a constructor function
http://qt-project.org/doc/qt-5/qscriptengine.html#newQMetaObject

Basically you have a stand-alone C++ function that can create an object of your type and then registers that instance with the engine using newQObject().

This function, wrapped in a QScriptValue by newFunction(), is then registered as a constructor for you class's meta object.



QScriptValue myClassConstructor(QScriptContext *context, QScriptEngine *engine)
{
QObject *object = new MyClass();
return engine->newQObject(object, QScriptEngine::ScriptOwnership);
}

...

QScriptValue ctor = engine.newFunction(myClassConstructor);
QScriptValue metaObject = engine.newQMetaObject(&MyClass::staticMetaObject, ctor);
engine.globalObject().setProperty("MyClass", metaObject);

QScriptValue result = engine.evaluate("new MyClass()");


Cheers,
_

axed
18th July 2014, 19:07
AWESOME - THANK YOU VERY MUCH, anda_skoa! :D

Here is a working (~minimal) example, for whom ever googles about this.

Have a nice weekend! :)

main.cpp


#include <QCoreApplication>
#include <QScriptEngine>
#include <QScriptContext>
#include <QScriptValue>
#include <QDebug>

#include "myClass.h"

QScriptValue myClassConstructor(QScriptContext* context, QScriptEngine* engine) {
Q_UNUSED(context)
QObject *object = new myClass();
return engine->newQObject(object, QScriptEngine::ScriptOwnership);
}

QScriptProgram evaluateMe("var x = new myClass();"
"var y = new myClass();"
""
"x.mySignal.connect(y.mySlot);"
""
"x.setScriptValue('wooohoooo');"
""
"y.setScriptValue(x.scriptValue());"
""
// RESULTING:
"y.scriptValue()",
"main-script");

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

QScriptEngine engine;
QScriptValue constructor = engine.newFunction(myClassConstructor);
QScriptValue metaObject = engine.newQMetaObject(&myClass::staticMetaObject, constructor);
engine.globalObject().setProperty("myClass", metaObject);

QScriptValue result = "result: " + engine.evaluate(evaluateMe).toString();

if (engine.hasUncaughtException()) {
result = "error: " + engine.uncaughtException().toString() + ", " + engine.uncaughtExceptionBacktrace().join("\n");
}

qDebug() << result.toString();

return a.exec();
}


myclass.h


#ifndef MYCLASS_H
#define MYCLASS_H

#include <QObject>
#include <QString>
#include <QVariant>
#include <QScriptValue>
#include <QDebug>

class myClass : public QObject {
Q_OBJECT

QScriptValue _secretScriptValue;
public:
myClass() {
_secretScriptValue = QScriptValue("fresh and hot");
}

public slots:

void setScriptValue(QScriptValue newValue) {
_secretScriptValue = newValue;
emit this->mySignal();
}

QScriptValue scriptValue() {
return _secretScriptValue;
}

void mySlot() {
qDebug() << "mySlot has been called! :)";
}

signals:
void mySignal();
};

#endif // MYCLASS_H