PDA

View Full Version : QScriptEngine



coderbob
22nd January 2010, 21:01
Is it possible to use QScriptEngine::pushContext to grab a QScriptContext out of a script file to then use QScriptContext::thisObject as a argument to another script so it could run with all the "saved" information associated with 'this' from the first script?

Bob

wysota
23rd January 2010, 16:01
What purpose would it serve?

coderbob
24th January 2010, 09:55
Two fold; One it would let me eval the heavy load portions of a script in a thread and then pass the "this" to the main gui thread so it could do the light processing of displaying the results. Only other way would be to store them in an object and pass it around but the GUI is .ui defined and can change at any time as can the script. FYI the script could be processed from any number of separate threads depending on circumstances. All this works fine for console output the problem is communication with the main gui thread.

Two it would let me have a persistent state across script evals since some processing depends on how many time X, Y etc have occurred. Like before this processing could take place in an undetermined thread.

Bob

wysota
24th January 2010, 10:07
Only other way would be to store them in an object and pass it around but the GUI is .ui defined and can change at any time as can the script.
I don't see what the definition of a GUI has to do with passing objects between script engines. Your script returns something (it can even be a JSON formatted string) and you can pass this something as input of another script on another script engine in another thread.


Two it would let me have a persistent state across script evals since some processing depends on how many time X, Y etc have occurred. Like before this processing could take place in an undetermined thread.

You can have the same by doing it properly, i.e. by using the "Memento" (aka Hibernate) design pattern.

coderbob
24th January 2010, 10:38
I don't see what the definition of a GUI has to do with passing objects between script engines..

GUI being the main thread the the UI was created in and cannot be referenced from the script in a separate thread. It is referenced because I can use stdout with no problems, it just when I need to display the result in the main thread (gui) that I run into problems.


Your script returns something (it can even be a JSON formatted string) and you can pass this something as input of another script on another script engine in another thread

This is probably the only way to do it at this time but it is going to create more script code to encode and decode the information passed rather then just passing "this" which comes already defined.


You can have the same by doing it properly, i.e. by using the "Memento" (aka Hibernate) design pattern.

This would require storing a return from the script as you suggested above. Formatted string being the only viable options I can see since it would have many values to track.

Not totally what I wanted to here since just moving around the context appears to be the most elegant and easy solution but your suggestions will get the end result and thats would ultimately matters.

Bob

wysota
24th January 2010, 11:17
GUI being the main thread the the UI was created in and cannot be referenced from the script in a separate thread.
But why do you want to "reference" the UI from the script? Something runs the script, right? So it can retrieve the result from the script and display it on the UI.


This is probably the only way to do it at this time but it is going to create more script code to encode and decode the information passed rather then just passing "this" which comes already defined.
You don't have to do it this way, there are other ways, especially that you have access to the global object of the script.


This would require storing a return from the script as you suggested above. Formatted string being the only viable options I can see since it would have many values to track.
Wrong, you can use objects.


Not totally what I wanted to here since just moving around the context appears to be the most elegant and easy solution
Not really. A context sits inside another context. You'd have to move all contexts which effectively would mean moving the entire machine. So it's better to move the entire machine which is possible or just don't do anything which would be the same...

coderbob
24th January 2010, 19:16
But why do you want to "reference" the UI from the script? Something runs the script, right? So it can retrieve the result from the script and display it on the UI.

The script itself is updating the ui, similar to the Calculator example.


Wrong, you can use objects.

My understanding of scripting is limited, so you are saying I can create an object inside the script pass it back to my c++ as a QScriptValue and then pass that back to the other script as an argument?

If that is true that would be exactly what I needed, will just need to figure out the creation syntax for an object inside a script and setting its variables and returning it.

Bob

Edit:

Looks like I could do something like


function foo() {

myObject = new Object();

myObject.a = 1;
myObject.b = 2;

return myObject;
}


That should return the myObject as a QScriptValue that I can pass as an argument to the other script?

coderbob
24th January 2010, 20:53
In the Calculator example the argument is passed the script like



QScriptValue ctor = engine.evaluate("Calculator");
QScriptValue scriptUi = engine.newQObject(ui, QScriptEngine::ScriptOwnership);
QScriptValue calc = ctor.construct(QScriptValueList() << scriptUi);


This doesn't work


QScriptValue ctor = engine.evaluate("foo");
QScriptValue scriptObj = engine.newQObject(myScriptValueObject.toObject(), QScriptEngine::ScriptOwnership);
QScriptValue calc = ctor.construct(QScriptValueList() << scriptObj);

I cannot find the syntax and playing with the code I am unable to figure out how to pass a QScriptValue as an argument to a script.

EDIT: and past that how to pass both the ui and the script object.

Bob

wysota
25th January 2010, 00:37
The script itself is updating the ui, similar to the Calculator example.
That obviously can't be done from a worker thread.


My understanding of scripting is limited, so you are saying I can create an object inside the script pass it back to my c++ as a QScriptValue and then pass that back to the other script as an argument?
Yes, more or less. You can't pass the same object but you can send a copy of the data.


If that is true that would be exactly what I needed, will just need to figure out the creation syntax for an object inside a script and setting its variables and returning it.
There are many ways to do it but you can start with:
var x = new Object;
x.something = somethingelse;
x.xyz = abc;
return x;


Looks like I could do something like


function foo() {

myObject = new Object();

myObject.a = 1;
myObject.b = 2;

return myObject;
}


That should return the myObject as a QScriptValue that I can pass as an argument to the other script?
Yes, that's one possibility. But you can't pass the same value to another engine, you have to copy the data from it to a new object that is bound to the other engine.


In the Calculator example the argument is passed the script like



QScriptValue ctor = engine.evaluate("Calculator");
QScriptValue scriptUi = engine.newQObject(ui, QScriptEngine::ScriptOwnership);
QScriptValue calc = ctor.construct(QScriptValueList() << scriptUi);


This doesn't work


QScriptValue ctor = engine.evaluate("foo");
QScriptValue scriptObj = engine.newQObject(myScriptValueObject.toObject(), QScriptEngine::ScriptOwnership);
QScriptValue calc = ctor.construct(QScriptValueList() << scriptObj);

I cannot find the syntax and playing with the code I am unable to figure out how to pass a QScriptValue as an argument to a script.
JavaScript Object and QObject are two different things. QObjects are modelled as Objects but you can't convert freely between the two (i.e. you can't "cast" Object to QObject).


EDIT: and past that how to pass both the ui and the script object.
You can't reference GUI from non-GUI threads. As for making objects visible to the script, consult the reference manual, there are at least three ways to do it.

coderbob
25th January 2010, 14:00
Yes, that's one possibility. But you can't pass the same value to another engine, you have to copy the data from it to a new object that is bound to the other engine.

In the QScriptValue docs in reference to copy it says:


Note that a QScriptValue for which isObject() is true only carries a reference to an actual object; copying the QScriptValue will only copy the object reference, not the object itself. If you want to clone an object (i.e. copy an object's properties to another object), you can do so with the help of a for-in statement in script code, or QScriptValueIterator in C++.

Doing it inside the script is irrelevant in this case so the QScriptValueIterator was used.



QScriptValue newObject;

QScriptValueIterator it(oldObject);
while (it.hasNext()) {
it.next();
newObject.setProperty(it.name(),it.value());
}

I still run into the problem of "QScriptValue::setProperty(newObject) failed: cannot set value created in a different engine". I have read, reread, and rerereread the docs on this and not sure what I need to reference.

it.value().data() does not produce the error but also does not give me anything either.

Bob

wysota
25th January 2010, 17:12
You are not making a copy :) QScriptValueIterator::value() returns a QScriptValue so you can't assign it to a different engine. You have to convert the value to appropriate C++ type (which makes a copy of the data) and then set it on the other object.