I've successfully added an C++ object to a QWebFrame with addToJavaScriptWindowObject,
and can call a slot on that object from javascript.
But what I really want to do is have one of those slots return a new object. For example, I have a slot like this, which returns a QObject derived class instance:
MyObject* MyApp::helloWorld()
{
//MyObject is dervied from QObject
return new MyObject();
}
I can call this slot from javascript successfully like this
var foo=myapp.helloWorld();
But foo appears to be empty, I can't call any slots or access any
properties on it from Javascript.
Any ideas on how I can achieve this?
One rather ugly hack I've considered is to use addToJavaScriptWindowObject to drop the object I want to return into the window object with a random name, then have my slot return the name of the object instance instead:
QString MyApp::helloWorld()
{
//general a unique name for the js variable
QString name=getRandomVariableName();
//here's the object we want to expose to js
MyObject* pReturn=new MyObject();
//we make attach our object to the js window object
getWebFrame()->addToJavaScriptWindowObject(name, pReturn,
QScriptEngine::ScriptOwnership);
//tell js the name we used
return name;
}
The JS can be written to check if the return value is a string, and if it is, grab the object from the window.:
var foo=myapp.helloWorld();
if (typeof foo == "string")
{
foo=window[foo];
}
A little ugly, but will get me by until a better method comes along. Future Qt versions are going unify the scripting support so that it's all based on the JavaScriptCore in WebKit, so hopefully this will improve then!
You can assign your Object pointer to a QObject * and return that.
QObject * obj = new MyObject();
return obj;
That is working for me on Qt Webkit port on Linux.
QtScript has the notion of prototypes - which allows you to create a C++ prototype for a script value. We are investigating wether we can bridge QtScript with JavaScriptCore - which should result in the possibility of using prototypes from WebKit's JavaScript environment as well; http://doc.trolltech.com/4.5/qtscript.html#making-use-of-prototype-based-inheritance
Try returning your new object as a QObject* rather than a MyObject*. If you're just working with QtScript, then you can call qScriptRegisterMetaType to define some code for handling conversion of MyObject*s into QScriptValues (or QVariants), but there doesn't seem to be an equivalent for the JavaScript engine used in QtWebKit.
Annoyingly, this means that exposing your internal object model to WebKit will involve either having a separate set of proxy functions that convert your object pointers to QObject*s, or using adapter classes of some kind to do the same thing.
This answer is based on Paul's answer, but a bit simplified. Tested and working for Qt 5.3. You need a factory that instantiates an object, and then returns a QObject
pointer to this object. The object's class has to inherit from QObject
so that it works properly in JavaScript:
QObject * MyApp::createInstance(QString objname) {
MyClass * obj = new MyClass(this);
m_mainWindow->webView->page()->mainFrame()->addToJavaScriptWindowObject(objname, obj, QWebFrame::ScriptOwnership);
return obj;
}
With this, you can do the following from Javascript:
var myobject = MyApp.createInstance("obj1");
At this point, you have obj1
as well as myobject
in the global JavaScript namespace (they are just pointers, so doing both of the following works:
myobject.testmethod();
obj1.testmethod();
At this point, you can use connect
in JavaScript to connect C++ signals to JavaScript slots. More information on this technique here: http://doc.qt.io/qt-5/qtwebkit-bridge.html