Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to build an API with QJSEngine?

I am starting with Qt and one of my projects is using QJSEngine to evaluate javascript and I want to provide an entire API to the script, with classes and global functions.

Right now my program provides only the ECMAScript default stuff (eval, encodeURI, parseInt, etc...), but I need to expose some custom classes to the code, like the browsers API (WebSocket class, Image class, document object). For example:

var obj = new CustomClass("", 0);
var ret = obj.customClassMethod("[...]!");
customFunction(ret);

I need to define the behavior of the classes in C++, it wouldn't help evaluate the classes definition and let the user code run.

like image 462
Guerreiro Avatar asked Oct 23 '25 18:10

Guerreiro


1 Answers

In contrast to QScriptEngine, where you can add custom classes if they inherit from QObject by using the Q_SCRIPT_DECLARE_QMETAOBJECT macro, the QJSEngine does not directly provide this functionality.

You can still use the Qt Meta-object system to provide interfaces for Javascript, but you have to instantiate the object in C++ and add it to the Javascript context. Then its slots, methods defined with Q_INVOKABLE, and properties defined with Q_PROPERTY are all accessible from within the Javascript runtime.

Now you can create a factory which creates instances of your custom class CustomClass for a given QJSEngine wrapped as Javascript objects:

class CustomClassFactory : public QObject
{
    Q_OBJECT
public:
  CustomClassFactory(QJSEngine* engine) : m_engine(engine) {}
  Q_INVOKABLE QJSValue createInstance() {
      // The engine takes ownership and destroys the object if no longer required.
      return m_engine->newQObject(new CustomClass());
  }
private:
    QJSEngine* m_engine;
}

A factory instance needs to be constructed and added to the global object of the Javascript runtime:

QJSEngine engine;
QJSValue factoryObj = engine.newQObject(new CustomClassFactory());
engine.globalObject().setProperty("_customClassFactory", factoryObj);

Now we can construct objects in Javascript with:

var obj = _customClassFactory.createInstance()

As we've come this far, lets additionally inject a constructor for the custom class into the Javascript runtime:

QJSEngine engine;
// Again, the QJSEngine will take ownership of the created object.
QJSValue factoryObj = engine.newQObject(new CustomClassFactory());
engine.globalObject().setProperty("_customClassFactory", factoryObj);
engine.evaluate(
    "function CustomClass() {"
    "    return _customClassFactory.createInstance()"
    "}");

Et voilà, now you can construct C++ object in Javascript, like you would custom Javascript classes:

var obj = new CustomClass()

For the mentioned WebSocket API you could wrap QtWebSocket for that purpose – that was exactly what I required when I came up with the proposed approach.

Note that for the sake of simplicity I omitted parameters for the constructor, but they can simply be added as well.

PS: I would have added more links to the official documentation, but due to the lack of reputation I'm not allowed to.

like image 160
Max Avatar answered Oct 26 '25 09:10

Max



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!