PDA

View Full Version : Access QScriptEngine from QML executed javascript



coderbob
22nd October 2010, 10:10
If I am using import "myscript.js" as MyScript in a QML file.

inside of myscript.js referencing an object that has been sub classed as scriptable the engine() variable will be null.

Anyone know a way to get access to the QScriptEngine used to call the script file from the QML code?

wysota
22nd October 2010, 19:23
QDeclarativeView::engine()

Note QML is not QtScript, there is no QScriptEngine for it. If you need a QScriptEngine you have to bind it with the declarative context yourself but it will not be related to the engine/context used by QML itself.

coderbob
23rd October 2010, 01:05
MyScript.js


function doSomethingFromScriptFile()
{
for (var i=0; i < grabQObject.maxQObjects; i++)
{
var myQObject = grabQObject.objectAt(i);

myQObject.doMore();
}
}

This code is using a known QObject passed to the QML file by view.rootContext()->setContextProperty("grabQObject",grabQObject); to grab and return a new QObject each time through the loop so the javascript can process it.

MyQML.qml


import Qt 4.7

import "MyScript.js" as MyScript

Rectangle {
id: container
width: 600; height: 480
color: "lightgray"

Button {
text: "RunScript"
onClicked: {
MyScript.doSomethingFromScriptFile();
}
}
}

QML code with a simple button and mouse click area to run doSomethingFromScriptFile() from MyScript.js

main.cpp


#include <qdeclarative.h>
#include <QDeclarativeView>
#include <qdeclarativecontext.h>

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

QDeclarativeView view;

view.rootContext()->setContextProperty("grabQObject",grabQObject);

view.setSource(QUrl::fromLocalFile("MyQML.qml"));
view.show();
}


This is just enough c++ code to get us to execute the QML file and pass it our QObject grabQObject.


Now when I was using QtScript before I could just subclass QScriptable and do somethinglike this.

grabQObject.h


class GrabQObject : public QObject, protected QScriptable
{
Q_OBJECT

public:
GrabQObject(QObject *parent = 0);

public slots:
QScriptValue objectAt(int index);
int maxQObjects() { return m_count; }

private:
QList <QObject *> availableObjects;
int m_count;
}


grabQObject.cpp


QScriptValue grabQObject::objectAt(int index)
{
if (index > availableObjects.size())
return engine()->nullValue();
else
return engine()->newQObject(availableObjects.at(index));
}


This would use the calling QScriptEngine to make a returnable QObject formated as a QScriptValue and all was good.


My problem now is being able to access each new QObject as I loop through them in the script. When I call myQObject.doMore(); I get.


TypeError: Result of expression 'myQObject' [undefined] is not an object.

Not sure what is going wrong here and still learning QML and how it executes javascript, any help would be greatly appreciated.

Bob

wysota
23rd October 2010, 01:25
I'm not sure what you need QScriptable here for. If you return a QObject from a custom type, QtScript should wrap it into a QScriptValue on its own so your objectAt() method might look like this:

QObject* grabQObject::objectAt(int index) const {
if(index > availableObject.size())
return 0;
return availableObjects.at(index);
}

Anyway... back to QtQuick...

In general I think that returning an object like in the method above should be ok too (just don't try to return QScriptValue, it won't work). Of course remember to make the objectAt() method invokable from QML using Q_INVOKABLE or by declaring it as a slot!

coderbob
23rd October 2010, 01:35
Thank you got the quick response.

I was wrapping QScriptValue that way because it just didn't seem to want to work otherwise. Most likely something else I was doing wrong, that portion of the code is fairly old now.

I just noticed something in your reply that I missed in the code earlier. When I was returning the value it was not as a generic QObject but rather as the specific type.

I was using qRegisterMetaType<CustomQObject *>("CustomQObject"); but it was not being recognized in the script.

It seems I am not clearly defining my types somehow.

Thanks again for the help

wysota
23rd October 2010, 01:52
Usually you use qRegisterMetaType (or qScriptRegisterMetaType) only for value-based types. Subclasses of QObject already have a meta-type. And if you want a meta-type registered first you have to have it, so to be able to perform qRegisterMetaType<X> you need to do Q_DECLARE_METATYPE(x) first.

If you want a custom QObject to be convertible to/from QtScript, you'd need to use qScriptRegisterMetaType passing it two functions to convert to and from a QScriptValue. See the docs for details. For QML you might need qmlRegisterType() although I don't have any practical experience with QML so I'm just guessing.