Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Registering an Instantiable Object Type (Official Example)
Qt 6.11 is out! See what's new in the release blog

Registering an Instantiable Object Type (Official Example)

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
7 Posts 4 Posters 3.0k Views 2 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.
  • G Offline
    G Offline
    Gamma_Sigma
    wrote on last edited by Gamma_Sigma
    #1

    I'm following the official example from here, but either I'm getting something wrong, or there is something missing in the guide.

    I'm trying to register a C++ type into QML, to be able to instantiate it in a QML document. I'm following the guide as well as the worked example, but I'm always getting the

    QQmlApplicationEngine failed to load component
    qrc:/exampleDir/Main.qml:3:1: module "myclass" is not installed

    error.

    I've created a new, empty Qt Quick project, and added a minimalist C++ class, almost identical to the example, just replacing the identifier Message with MyClass:

    #ifndef MYCLASS_H
    #define MYCLASS_H
    
    #include <QObject>
    #include <qqmlregistration.h>
    
    class MyClass : public QObject
    {
    	Q_OBJECT
    	Q_PROPERTY(QString author READ getAuthor WRITE setAuthor NOTIFY authorChanged)
    	QML_ELEMENT
    
    public:
    	explicit MyClass(QObject *parent = nullptr);
    
    public slots:
      QString getAuthor(void);
      void    setAuthor(const QString&);
    
    signals:
      void authorChanged(void);
    
    private:
      QString m_author = "Author's Name";
    };
    
    #endif // MYCLASS_H
    

    Then, by following the guide, I added this snippet of code in CMakeLists.txt, just after the already existing qt_add_qml_module invokation for the main project:

    qt_add_qml_module(myClass
        URI myclass
        VERSION 1.0
        SOURCES
        myclass.cpp myclass.h
    )
    

    This is Main.qml:

    import QtQuick
    import QtQuick.Window
    import myclass
    
    Window
    {
    	width: 640
    	height: 480
    	visible: true
    	title: qsTr("Hello World")
    
    	MyClass
    	{}
    }
    

    When I try to compile, I'm always getting the module "myClass" is not installed error.

    NOTE: if I add these lines to main.cpp:

      const char *packageName = "myclass";
      const char *QMLTypeName = "MyClass";
      const int   majorVerNum = 1;
      const int   minorVerNum = 0;
      qmlRegisterType<MyClass>(packageName, majorVerNum, minorVerNum, QMLTypeName);
    

    The project compiles and works correctly. However, the usage of qmlRegisterType isn't mentioned anywhere in the documentation, which makes me think that it should not be necessary to add the call to qmlRegisterType in main.cpp, because the call to qt_add_qml_module in CMakeLists.txt should take care of everything, together with the QML_ELEMENT macro in the class header file. In fact, after having added the the call to qmlRegisterType in main.cpp, the qt_add_qml_module snippet can be safely removed from CMakeLists.txt.

    What am I missing here?

    • Qt version: 6.5.1
    • Qt Creator version: 10.0.2
    • OS: Windows 10
    • Filesystem:
      6fcf88f9-aea0-407b-83cd-d5d4c55216c0-image.png
    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi and welcome to devnet,

      I haven't done that yet but one thing that is important with QML is the casing. You QML classes should always start with an uppercased letter and I see that you have myClass in your declaration rather than MyClass.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      G 1 Reply Last reply
      1
      • SGaistS SGaist

        Hi and welcome to devnet,

        I haven't done that yet but one thing that is important with QML is the casing. You QML classes should always start with an uppercased letter and I see that you have myClass in your declaration rather than MyClass.

        G Offline
        G Offline
        Gamma_Sigma
        wrote on last edited by
        #3

        @SGaist Hello, thank you for your reply.

        Later I will start this example from scratch, and I will try to use unique identifiers instead of repeating variations of "MyClass", so that it will be easier to track them, and identify where the case usage might go wrong. If the problem does not disappear, I will post the whole source files, together with the CMakeLists.txt file.

        G 1 Reply Last reply
        0
        • G Gamma_Sigma

          @SGaist Hello, thank you for your reply.

          Later I will start this example from scratch, and I will try to use unique identifiers instead of repeating variations of "MyClass", so that it will be easier to track them, and identify where the case usage might go wrong. If the problem does not disappear, I will post the whole source files, together with the CMakeLists.txt file.

          G Offline
          G Offline
          Gamma_Sigma
          wrote on last edited by Gamma_Sigma
          #4

          I gave another unsuccessful try. This time I tried to be even more minimalist than before, to no avail.

          This is what I did:

          • Create a new "Qt Quick Application" project named testIntegration
          • Select CMake as build system
          • Select "Desktop Qt 6.5.1 MinGW 64-bit" as kit
          • Add new C++ class named Message (source files message.cpp and message.h), derived from QObject and adding the Q_OBJECT macro
          • Adding source files message.h and message.cpp to qt_add_executable in CMakeLists.txt
          • Add #include <QtQml/qqmlregistration.h> to message.h to be able to use the QML_ELEMENT macro in class Message
          • Add qt_add_qml_module statement to CMakeLists.txt
          • Add import statement and instantiate Message class in Main.qml inside Window

          Now, two error messages are possible, depending on the case of the URI (lowercase messagingUri or uppercase MessagingUri):

          1. If i add URI messagingUri in qt_add_qml_module (in CMakeLists.txt) and import messagingUri (in Main.qml), i get this error message: "QQmlApplicationEngine failed to load component qrc:/testIntegration/Main.qml:3:1: module "messagingUri" is not installed"
          2. If i add URI MessagingUri in qt_add_qml_module (in CMakeLists.txt) and import MessagingUri (in Main.qml), i get this error message: ":-1: error: ninja: build stopped: subcommand failed."

          In both cases, i get the warning message "D:\Qt\6.5.1\mingw_64\lib\cmake\Qt6Qml\Qt6QmlMacros.cmake:794: warning: The messagingModule target is a QML module with target path MessagingUri. It uses an OUTPUT_DIRECTORY of D:/Qt_6/build-testIntegration-Desktop_Qt_6_5_1_MinGW_64_bit-Debug, which should end in the same target path, but doesn't. Tooling such as qmllint may not work correctly. D:/Qt/6.5.1/mingw_64/lib/cmake/Qt6Qml/Qt6QmlMacros.cmake:1840 (_qt_internal_target_enable_qmllint) D:/Qt/6.5.1/mingw_64/lib/cmake/Qt6Qml/Qt6QmlMacros.cmake:625 (qt6_target_qml_sources) D:/Qt/6.5.1/mingw_64/lib/cmake/Qt6Qml/Qt6QmlMacros.cmake:684 (qt6_add_qml_module) CMakeLists.txt:24 (qt_add_qml_module)"

          Again, if i add these lines to main.cpp (as well as #include "message.h"):

          const char *packageName = "messagingUri";
          const char *QMLTypeName = "Message";
          const int   majorVerNum = 1;
          const int   minorVerNum = 0;
          qmlRegisterType<Message>(packageName, majorVerNum, minorVerNum, QMLTypeName);
          

          Everything works perfectly: no error messages, no warnings, no nothing.

          At this point, I'm really stumped. The example from Qt documentation never mentions the usage of qmlRegisterType, but it looks like it is inevitable.

          Here are the full source files:

          ***** CMakeLists.txt *****
          (mostly auto-generated. I only added the qt_policy command to suppress a warning, the source files for Message class to qt_add_executable, and the full qt_add_qml_module command, copied from the example, only changing the package's name and the URI)

          cmake_minimum_required(VERSION 3.16)
          
          project(testIntegration VERSION 0.1 LANGUAGES CXX)
          
          set(CMAKE_CXX_STANDARD_REQUIRED ON)
          
          find_package(Qt6 6.4 REQUIRED COMPONENTS Quick)
          
          qt_standard_project_setup()
          
          qt_policy(SET QTP0001 OLD) # Suppress warning https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html
          
          qt_add_executable(apptestIntegration
              main.cpp
              message.h message.cpp
          )
          
          qt_add_qml_module(apptestIntegration
              URI testIntegration
              VERSION 1.0
              QML_FILES Main.qml
          )
          
          qt_add_qml_module(messagingModule
              URI MessagingUri
              VERSION 1.0
              SOURCES
              message.cpp message.h
          )
          
          set_target_properties(apptestIntegration PROPERTIES
              MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
              MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
              MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
              MACOSX_BUNDLE TRUE
              WIN32_EXECUTABLE TRUE
          )
          
          target_link_libraries(apptestIntegration
              PRIVATE Qt6::Quick
          )
          
          install(TARGETS apptestIntegration
              BUNDLE DESTINATION .
              LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
              RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
          )
          

          ***** main.cpp *****
          (fully auto-generated. Nothing was added or modified by me here)

          #include <QGuiApplication>
          #include <QQmlApplicationEngine>
          
          int main(int argc, char *argv[])
          {
            QGuiApplication app(argc, argv);
          
            QQmlApplicationEngine engine;
            const QUrl url(u"qrc:/testIntegration/Main.qml"_qs);
            QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
              &app, []() { QCoreApplication::exit(-1); },
              Qt::QueuedConnection);
            engine.load(url);
          
            return app.exec();
          }
          

          ***** message.h *****

          #ifndef MESSAGE_H
          #define MESSAGE_H
          
          #include <QObject>
          #include <QtQml/qqmlregistration.h>
          
          class Message : public QObject
          {
              Q_OBJECT
              QML_ELEMENT
          
          public:
              explicit Message(QObject *parent = nullptr);
          
          signals:
          };
          
          #endif // MESSAGE_H
          

          ***** message.cpp *****

          #include "message.h"
          
          Message::Message(QObject *parent)
              : QObject{parent}
          {;}
          

          ***** Main.qml *****

          import QtQuick
          import QtQuick.Window
          import messagingUri
          
          Window
          {
            width: 640
            height: 480
            visible: true
            title: qsTr("Hello World")
          
            Message
            {}
          }
          
          G 1 Reply Last reply
          0
          • G Gamma_Sigma

            I gave another unsuccessful try. This time I tried to be even more minimalist than before, to no avail.

            This is what I did:

            • Create a new "Qt Quick Application" project named testIntegration
            • Select CMake as build system
            • Select "Desktop Qt 6.5.1 MinGW 64-bit" as kit
            • Add new C++ class named Message (source files message.cpp and message.h), derived from QObject and adding the Q_OBJECT macro
            • Adding source files message.h and message.cpp to qt_add_executable in CMakeLists.txt
            • Add #include <QtQml/qqmlregistration.h> to message.h to be able to use the QML_ELEMENT macro in class Message
            • Add qt_add_qml_module statement to CMakeLists.txt
            • Add import statement and instantiate Message class in Main.qml inside Window

            Now, two error messages are possible, depending on the case of the URI (lowercase messagingUri or uppercase MessagingUri):

            1. If i add URI messagingUri in qt_add_qml_module (in CMakeLists.txt) and import messagingUri (in Main.qml), i get this error message: "QQmlApplicationEngine failed to load component qrc:/testIntegration/Main.qml:3:1: module "messagingUri" is not installed"
            2. If i add URI MessagingUri in qt_add_qml_module (in CMakeLists.txt) and import MessagingUri (in Main.qml), i get this error message: ":-1: error: ninja: build stopped: subcommand failed."

            In both cases, i get the warning message "D:\Qt\6.5.1\mingw_64\lib\cmake\Qt6Qml\Qt6QmlMacros.cmake:794: warning: The messagingModule target is a QML module with target path MessagingUri. It uses an OUTPUT_DIRECTORY of D:/Qt_6/build-testIntegration-Desktop_Qt_6_5_1_MinGW_64_bit-Debug, which should end in the same target path, but doesn't. Tooling such as qmllint may not work correctly. D:/Qt/6.5.1/mingw_64/lib/cmake/Qt6Qml/Qt6QmlMacros.cmake:1840 (_qt_internal_target_enable_qmllint) D:/Qt/6.5.1/mingw_64/lib/cmake/Qt6Qml/Qt6QmlMacros.cmake:625 (qt6_target_qml_sources) D:/Qt/6.5.1/mingw_64/lib/cmake/Qt6Qml/Qt6QmlMacros.cmake:684 (qt6_add_qml_module) CMakeLists.txt:24 (qt_add_qml_module)"

            Again, if i add these lines to main.cpp (as well as #include "message.h"):

            const char *packageName = "messagingUri";
            const char *QMLTypeName = "Message";
            const int   majorVerNum = 1;
            const int   minorVerNum = 0;
            qmlRegisterType<Message>(packageName, majorVerNum, minorVerNum, QMLTypeName);
            

            Everything works perfectly: no error messages, no warnings, no nothing.

            At this point, I'm really stumped. The example from Qt documentation never mentions the usage of qmlRegisterType, but it looks like it is inevitable.

            Here are the full source files:

            ***** CMakeLists.txt *****
            (mostly auto-generated. I only added the qt_policy command to suppress a warning, the source files for Message class to qt_add_executable, and the full qt_add_qml_module command, copied from the example, only changing the package's name and the URI)

            cmake_minimum_required(VERSION 3.16)
            
            project(testIntegration VERSION 0.1 LANGUAGES CXX)
            
            set(CMAKE_CXX_STANDARD_REQUIRED ON)
            
            find_package(Qt6 6.4 REQUIRED COMPONENTS Quick)
            
            qt_standard_project_setup()
            
            qt_policy(SET QTP0001 OLD) # Suppress warning https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html
            
            qt_add_executable(apptestIntegration
                main.cpp
                message.h message.cpp
            )
            
            qt_add_qml_module(apptestIntegration
                URI testIntegration
                VERSION 1.0
                QML_FILES Main.qml
            )
            
            qt_add_qml_module(messagingModule
                URI MessagingUri
                VERSION 1.0
                SOURCES
                message.cpp message.h
            )
            
            set_target_properties(apptestIntegration PROPERTIES
                MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
                MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
                MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
                MACOSX_BUNDLE TRUE
                WIN32_EXECUTABLE TRUE
            )
            
            target_link_libraries(apptestIntegration
                PRIVATE Qt6::Quick
            )
            
            install(TARGETS apptestIntegration
                BUNDLE DESTINATION .
                LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
            )
            

            ***** main.cpp *****
            (fully auto-generated. Nothing was added or modified by me here)

            #include <QGuiApplication>
            #include <QQmlApplicationEngine>
            
            int main(int argc, char *argv[])
            {
              QGuiApplication app(argc, argv);
            
              QQmlApplicationEngine engine;
              const QUrl url(u"qrc:/testIntegration/Main.qml"_qs);
              QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
                &app, []() { QCoreApplication::exit(-1); },
                Qt::QueuedConnection);
              engine.load(url);
            
              return app.exec();
            }
            

            ***** message.h *****

            #ifndef MESSAGE_H
            #define MESSAGE_H
            
            #include <QObject>
            #include <QtQml/qqmlregistration.h>
            
            class Message : public QObject
            {
                Q_OBJECT
                QML_ELEMENT
            
            public:
                explicit Message(QObject *parent = nullptr);
            
            signals:
            };
            
            #endif // MESSAGE_H
            

            ***** message.cpp *****

            #include "message.h"
            
            Message::Message(QObject *parent)
                : QObject{parent}
            {;}
            

            ***** Main.qml *****

            import QtQuick
            import QtQuick.Window
            import messagingUri
            
            Window
            {
              width: 640
              height: 480
              visible: true
              title: qsTr("Hello World")
            
              Message
              {}
            }
            
            G Offline
            G Offline
            Gamma_Sigma
            wrote on last edited by Gamma_Sigma
            #5

            I think I have found a workaround to the problem. Unfortunately, I cannot explain much of what I've done, because I am quite confused myself about how Qt manages resources through the Resource System, and the recent policy modification makes things even worse.

            Start by creating a new Qt Quick project with Qt Creator's wizard, selecting CMake as the build system; I called mine testIntegration. Qt Creator's wizard will auto-generate CMakeLists.txt, main.cpp, and Main.qml for you.

            The first problem that needs tackling is the Qt policy QTP0001 is not set warning message. To fix this, either add the qt_policy(SET QTP0001 NEW) command in CMakeLists.txt (before qt_add_executable), or add a RESOURCE_PREFIX argument to the qt_add_qml_module command in CMakeLists.txt. I did both, just to be safe (I used RESOURCE_PREFIX /).

            Then add a new C++ class, selecting QObject as base class (use Qt Creator's wizard to save yourself some effort). Call it whatever you like: I called mine MyClass.

            Now CMakeLists.txt needs to be edited again. Find the qt_add_qml_module command, and add the source files for your class, by passing the SOURCES myclass.h myclass.cpp argument.

            Edit your myclass.h header file, and add #include <qqmlregistration.h>. This inclusion will allow you to use the QML_ELEMENT macro. I put QML_ELEMENT immediately after the Q_OBJECT macro.

            I also added a qDebug statement in the constructor of MyClass (qDebug() << "Class instantiated";), to ascertain that the class has actually been instantiated. You will need to #include <QDebug> to use qDebug().

            Now edit Main.qml and add this import statement: import testIntegration. You will notice that it is the same identifier as the URI testIntegration argument used in the qt_add_qml_module command in CMakeLists.txt. You can now declare a MyClass object in your QML code. In my case, I simply created an empty MyClass{} inside the main Window.

            This way I managed to compile and run the project without issues, getting a nice "Class instantiated" debug message in Application Output.

            NOTE: As you can see, no qmlRegisterType was used in main.cpp. However, it looks like Qt / QML / CMake / whatever is (are?) extremely finicky for what concerns the identifiers you use. You can choose whatever URI you like in qt_add_qml_module, but you have to make sure that it is the same both in the QML document and in main.cpp. For example, if in CMakeLists.txt you use the URI MyBeautifulURI argument for qt_add_qml_module, then you'll have to update Main.qml and main.cpp accordingly:

            • import MyBeautifulURI in Main.qml
            • const QUrl url(u"qrc:/MyBeautifulURI/Main.qml"_qs); in main.cpp

            If you are interested, here are the full source files:

            ***** CMakeLists.txt *****

            cmake_minimum_required(VERSION 3.16)
            
            project(testIntegration VERSION 0.1 LANGUAGES CXX)
            
            set(CMAKE_CXX_STANDARD_REQUIRED ON)
            
            find_package(Qt6 6.4 REQUIRED COMPONENTS Quick)
            
            qt_policy(SET QTP0001 NEW) # Suppress warning https://doc.qt.io/qt-6/qt-cmake-policy-qtp0001.html
            
            qt_standard_project_setup()
            
            qt_add_executable(apptestIntegration
                main.cpp
            )
            
            qt_add_qml_module(apptestIntegration
                URI testIntegration
                RESOURCE_PREFIX /
                VERSION 1.0
                QML_FILES Main.qml
                SOURCES myclass.h myclass.cpp
            )
            
            set_target_properties(apptestIntegration PROPERTIES
                MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
                MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
                MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
                MACOSX_BUNDLE TRUE
                WIN32_EXECUTABLE TRUE
            )
            
            target_link_libraries(apptestIntegration
                PRIVATE Qt6::Quick
            )
            
            install(TARGETS apptestIntegration
                BUNDLE DESTINATION .
                LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
            )
            

            ***** main.cpp *****

            #include <QGuiApplication>
            #include <QQmlApplicationEngine>
            
            int main(int argc, char *argv[])
            {
              QGuiApplication app(argc, argv);
            
              QQmlApplicationEngine engine;
              const QUrl url(u"qrc:/testIntegration/Main.qml"_qs);
              QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
                &app, []() { QCoreApplication::exit(-1); },
                Qt::QueuedConnection);
              engine.load(url);
            
              return app.exec();
            }
            

            ***** Main.qml *****

            import QtQuick
            import QtQuick.Window
            import testIntegration
            
            Window
            {
              width: 640
              height: 480
              visible: true
              title: qsTr("Hello World")
            
              MyClass{}
            }
            

            ***** myclass.h *****

            #ifndef MYCLASS_H
            #define MYCLASS_H
            
            #include <QObject>
            #include <qqmlregistration.h>
            
            class MyClass : public QObject
            {
              Q_OBJECT
              QML_ELEMENT
            
            public:
              explicit MyClass(QObject *parent = nullptr);
            
            signals:
            };
            
            #endif // MYCLASS_H
            

            ***** myclass.cpp *****

            #include <QDebug>
            #include "myclass.h"
            
            MyClass::MyClass(QObject *parent)
              : QObject{parent}
            {
              qDebug() << "Class instantiated";
            }
            
            1 Reply Last reply
            1
            • R Offline
              R Offline
              RangerQT
              wrote on last edited by
              #6

              I know this is an old topic but thank you very much, Gamma Sigma, for summarizing this. I want to use the current practices for adding modules without registration but nothing worked despite following Qt's blogs and documentation. I got a test case to work. Now
              I need to figure out how to get C++ Classes that are in subdirectories working.

              1 Reply Last reply
              0
              • R RangerQT referenced this topic on
              • T Offline
                T Offline
                TonyNguyen147
                wrote on last edited by
                #7

                image.png

                image.png

                I found this mismatch. Maybe you should try to capitalize the first character exactly the URI in your CMakeLists

                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