How to get Javascript in a QWebView to create new

2020-02-12 07:09发布

问题:

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?

回答1:

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!



回答2:

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.



回答3:

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



回答4:

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.



回答5:

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