QML exposed objects across shared library boundaries
-
Hello,
Say I have an application where I add some QML files to a module (through the generated template):
qt_add_qml_module(ApplicationName URI MyNamespace VERSION MyVersion QML_FILES SomeQMLFile NO_LINT NO_CACHEGEN )
Is it possible for me to inject into this namespace a (QML) singleton object from a shared library?
What I have currently in the application code for testing is:
int main(int argc, char ** argv) { MySingletonClass singleton; QQmlApplicationEngine engine; // ... init code for engine qmlRegisterSingletonInstance("MyNamespace", 0, 1, "MySingletonClassName", &singleton); // ... engine.load(url); return app.exec(); }
Is this the right way to do it?
Must I also add theQML_ELEMENT
andQML_SINGLETON
macros to theMySingletonClass
, or if I don't use a QML module in the library I can skip this? -
Well, I forgot about this a little bit, but I'm adding the solution anyway for future reference.
After some reading through the documentation and experimenting, it appears that dynamically registering a foreign class to a qml module declared as above isn't possible (nor encouraged, nor supported, as per the docs). The "old" way of plugin whatever you want into the registered QML namespace doesn't work anymore.
The proper solution is to wrap the class as described in the docs and use that wrapper to expose it to the QML engine. Like follows (
Tsc::Engine
is what I'm attempting to inject):Header:
#include <QQmlEngine> #include <QJSEngine> struct EngineForeign { Q_GADGET QML_FOREIGN(Tsc::Engine) QML_SINGLETON QML_NAMED_ELEMENT(Engine) public: static Tsc::Engine * create(QQmlEngine *, QJSEngine *); static QJSEngine * currentJsEngine; };
Source:
#include "TscQml.h" QJSEngine * EngineForeign::currentJsEngine = nullptr; Tsc::Engine * EngineForeign::create(QQmlEngine *, QJSEngine * jsEngine) { Tsc::Engine * engine = Tsc::Engine::instance(); Q_ASSERT(engine->thread() == jsEngine->thread()); Q_ASSERT(!currentJsEngine || currentJsEngine == jsEngine); currentJsEngine = jsEngine; QJSEngine::setObjectOwnership(engine, QJSEngine::CppOwnership); return engine; } #include "moc_TscQml.cpp"
After that everything works as expected and I can see and use it in QML:
import QtQuick import QtQuick.Controls import Tsc ApplicationWindow { width: 640 height: 480 visible: true title: qsTr("Hello World") Rectangle { anchors.fill: parent color: 'red' Connections { target: Engine.simulation function onAdvanced() { console.log('advanced') } } } }
Notably, this must be done for the singleton itself only.
simulation
is aQObject
and a registered property (Q_PROPERTY
) of theTsc::Engine
instance, which hasn't been wrapped nor does it appear necessary for this class to be exposed explicitly to the QML engine.