Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. General talk
  3. Qt 6
  4. QML exposed objects across shared library boundaries
Forum Updated to NodeBB v4.3 + New Features

QML exposed objects across shared library boundaries

Scheduled Pinned Locked Moved Qt 6
2 Posts 1 Posters 310 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • kshegunovK Offline
    kshegunovK Offline
    kshegunov
    Moderators
    wrote on last edited by
    #1

    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 the QML_ELEMENT and QML_SINGLETON macros to the MySingletonClass, or if I don't use a QML module in the library I can skip this?

    Read and abide by the Qt Code of Conduct

    kshegunovK 1 Reply Last reply
    0
    • kshegunovK kshegunov

      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 the QML_ELEMENT and QML_SINGLETON macros to the MySingletonClass, or if I don't use a QML module in the library I can skip this?

      kshegunovK Offline
      kshegunovK Offline
      kshegunov
      Moderators
      wrote on last edited by
      #2

      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 a QObject and a registered property (Q_PROPERTY) of the Tsc::Engine instance, which hasn't been wrapped nor does it appear necessary for this class to be exposed explicitly to the QML engine.

      Read and abide by the Qt Code of Conduct

      1 Reply Last reply
      0

      • Login

      • Login or register to search.
      • First post
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups
      • Search
      • Get Qt Extensions
      • Unsolved