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. using C++ classes and structs in QML
Forum Updated to NodeBB v4.3 + New Features

using C++ classes and structs in QML

Scheduled Pinned Locked Moved Solved QML and Qt Quick
30 Posts 9 Posters 5.4k Views 3 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.
  • M mzimmers
    10 Oct 2023, 13:17

    @J-Hilk sure - I'll post from the top to the point of errors (I'm skipping the Labels that I use for telltales):

    import QtQuick
    import QtQuick.Controls
    import QtQuick.Layouts
    import QtQuick.Window
    
    import MyStruct
    
    Window {
        id: mainWindow
        width: 640
        height: 480
        visible: true
    
        property MyStruct myStruct1: MyStruct {
            myInt: 100
        }
    
        MyStruct {
            id: myStruct2
            myInt: 200
        }
    

    While I'm at it, here's my main.cpp ("struct" is the project name):

    #include <QGuiApplication>
    #include <QQmlContext>
    #include <QQmlApplicationEngine>
    
    #include "mystruct.h"
    #include "myclass.h"
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
        QQmlApplicationEngine engine;
    
        qmlRegisterType<MyStruct>("MyStruct", 1, 0, "MyStruct"); // tried "myStruct" too
        qmlRegisterType<MyClass>("MyClass", 1, 0, "MyClass");
    
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
            &app, []() { QCoreApplication::exit(-1); },
            Qt::QueuedConnection);
    
        engine.loadFromModule("struct", "Main");
    
        return app.exec();
    }
    
    J Offline
    J Offline
    J.Hilk
    Moderators
    wrote on 10 Oct 2023, 13:27 last edited by
    #15

    don't know if that changed with Qt6 but shouldn't this

    @mzimmers said in using C++ classes and structs in QML:

    import MyStruct

    be

    import MyStruct 1.0

    ?


    Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


    Q: What's that?
    A: It's blue light.
    Q: What does it do?
    A: It turns blue.

    M 1 Reply Last reply 10 Oct 2023, 13:29
    0
    • J J.Hilk
      10 Oct 2023, 13:27

      don't know if that changed with Qt6 but shouldn't this

      @mzimmers said in using C++ classes and structs in QML:

      import MyStruct

      be

      import MyStruct 1.0

      ?

      M Offline
      M Offline
      mzimmers
      wrote on 10 Oct 2023, 13:29 last edited by mzimmers 10 Oct 2023, 13:43
      #16

      @J-Hilk that doesn't seem to matter any more. If you try to import a version number that's higher than what you registered, you'll get a runtime error, but that's about it.

      This is just nuts, though. In main.cpp, this line:

      qmlRegisterType<MyStruct>("myStruct", 1, 0, "myStruct");
      

      Produces a warning from the editor/code model/whatever that QML types must begin with uppercase. Plus it won't build, giving an error at my QML declaration "error: Expected type name".

      But if I modify that line in main.cpp to:

      qmlRegisterType<MyStruct>("myStruct", 1, 0, "MyStruct"); // tried "myStruct" too
      

      and then I have to change my Main.qml to:

          property MyStruct myStruct1: MyStruct {
      

      I get a runtime error: "qt.qml.typeregistration: Invalid QML element name "MyStruct"; value type names should begin with a lowercase letter"

      It's almost as though I shouldn't be registering my struct, though I don't know what the alternative might be.

      L 1 Reply Last reply 10 Oct 2023, 14:22
      1
      • M mzimmers
        10 Oct 2023, 13:29

        @J-Hilk that doesn't seem to matter any more. If you try to import a version number that's higher than what you registered, you'll get a runtime error, but that's about it.

        This is just nuts, though. In main.cpp, this line:

        qmlRegisterType<MyStruct>("myStruct", 1, 0, "myStruct");
        

        Produces a warning from the editor/code model/whatever that QML types must begin with uppercase. Plus it won't build, giving an error at my QML declaration "error: Expected type name".

        But if I modify that line in main.cpp to:

        qmlRegisterType<MyStruct>("myStruct", 1, 0, "MyStruct"); // tried "myStruct" too
        

        and then I have to change my Main.qml to:

            property MyStruct myStruct1: MyStruct {
        

        I get a runtime error: "qt.qml.typeregistration: Invalid QML element name "MyStruct"; value type names should begin with a lowercase letter"

        It's almost as though I shouldn't be registering my struct, though I don't know what the alternative might be.

        L Offline
        L Offline
        lemons
        wrote on 10 Oct 2023, 14:22 last edited by
        #17

        @mzimmers
        I thought the Q_GADGET macro is used for meta-types only, which can't be instantiated from QML.
        To be used e.g. if the struct is a property of an QObject derived class, which is somehow accessible in QML (either passed through C++ or created in QML).
        Allows you to use e.g. Q_PROPERTY macros, without the QObject overhead.

        To be creatable from QML you need a derived class from QObject, so the Q_GADGET macro is not sufficient, as it is missing e.g. the signals and slots of the QObject class.

        Note: I can be totally wrong / outdated, but this is how I was thinking and using it all the time :D

        J 1 Reply Last reply 10 Oct 2023, 14:36
        1
        • L lemons
          10 Oct 2023, 14:22

          @mzimmers
          I thought the Q_GADGET macro is used for meta-types only, which can't be instantiated from QML.
          To be used e.g. if the struct is a property of an QObject derived class, which is somehow accessible in QML (either passed through C++ or created in QML).
          Allows you to use e.g. Q_PROPERTY macros, without the QObject overhead.

          To be creatable from QML you need a derived class from QObject, so the Q_GADGET macro is not sufficient, as it is missing e.g. the signals and slots of the QObject class.

          Note: I can be totally wrong / outdated, but this is how I was thinking and using it all the time :D

          J Offline
          J Offline
          JoeCFD
          wrote on 10 Oct 2023, 14:36 last edited by JoeCFD 10 Oct 2023, 14:39
          #18

          @lemons said in using C++ classes and structs in QML:

          Q_GADGET macro

          Lemons seems right.

          In QML (Qt Meta-Object Language), the Q_GADGET macro is typically used with C++ classes to create non-instantiable classes that can be registered with the Qt meta-object system. These classes are similar to Q_OBJECT classes but cannot have signals, slots, or properties. They are often used for data-only structures that need to be exposed to QML.

          I use upper case for my class registration in qmlRegisterType without issues. I guess lower case is needed in app_engine->rootContext()->setContextProperty( ... );

          1 Reply Last reply
          0
          • G Offline
            G Offline
            GrecKo
            Qt Champions 2018
            wrote on 10 Oct 2023, 14:45 last edited by GrecKo 10 Oct 2023, 14:58
            #19

            You can now instantiate Gadgets from QML with undocumented macros (so maybe don't use it).

            Use QML_VALUE_TYPE(typeName) with a lower case name like you currently do.
            Then you can add QML_CONSTRUCTIBLE_VALUE or QML_STRUCTURED_VALUE (even both).

            QML_CONSTRUCTIBLE_VALUE will call a user defined constructor

            if you have Q_INVOKABLE MyStruct(int foo) : m_myInt{foo} {}
            then doing property myStruct myStruct1: 42 will call the constructor with 42 as the foo param

            QML_STRUCTURED_VALUE will assign properties depending on the js object you passed in QML.

            property myStruct myStruct1: ({myInt: 42}) will create a default constructed MyStruct and assign its myInt property to 42.

            Note that the type should always be default constructible.

            EDIT: Note that you don't have to use QML_ELEMENT, Q_DECLARE_METATYPE or qmlRegisterType if you use Q_GADGET and QML_VALUE_TYPE.

            M L 2 Replies Last reply 10 Oct 2023, 15:23
            3
            • G GrecKo
              10 Oct 2023, 14:45

              You can now instantiate Gadgets from QML with undocumented macros (so maybe don't use it).

              Use QML_VALUE_TYPE(typeName) with a lower case name like you currently do.
              Then you can add QML_CONSTRUCTIBLE_VALUE or QML_STRUCTURED_VALUE (even both).

              QML_CONSTRUCTIBLE_VALUE will call a user defined constructor

              if you have Q_INVOKABLE MyStruct(int foo) : m_myInt{foo} {}
              then doing property myStruct myStruct1: 42 will call the constructor with 42 as the foo param

              QML_STRUCTURED_VALUE will assign properties depending on the js object you passed in QML.

              property myStruct myStruct1: ({myInt: 42}) will create a default constructed MyStruct and assign its myInt property to 42.

              Note that the type should always be default constructible.

              EDIT: Note that you don't have to use QML_ELEMENT, Q_DECLARE_METATYPE or qmlRegisterType if you use Q_GADGET and QML_VALUE_TYPE.

              M Offline
              M Offline
              mzimmers
              wrote on 10 Oct 2023, 15:23 last edited by
              #20

              @GrecKo curiouser and curiouser.

              My modified struct:

              struct MyStruct {
                  QML_STRUCTURED_VALUE
                  QML_VALUE_TYPE(myStruct)
                  Q_PROPERTY(int myInt MEMBER m_myInt)
                  int m_myInt = 55;
                  MyStruct() {}
              };
              

              my registration (in main.cpp):

                  qmlRegisterType<MyStruct>("myStruct", 1, 0, "MyStruct");
              

              and my QML:

              property myStruct myStruct1: myStruct ({myInt: 100})
              

              produces a runtime error: "myStruct is not a type."

              Isn't using QML_VALUE_TYPE intended to allow me to use "myStruct" in the QML?

              G 1 Reply Last reply 10 Oct 2023, 16:14
              0
              • M mzimmers
                10 Oct 2023, 15:23

                @GrecKo curiouser and curiouser.

                My modified struct:

                struct MyStruct {
                    QML_STRUCTURED_VALUE
                    QML_VALUE_TYPE(myStruct)
                    Q_PROPERTY(int myInt MEMBER m_myInt)
                    int m_myInt = 55;
                    MyStruct() {}
                };
                

                my registration (in main.cpp):

                    qmlRegisterType<MyStruct>("myStruct", 1, 0, "MyStruct");
                

                and my QML:

                property myStruct myStruct1: myStruct ({myInt: 100})
                

                produces a runtime error: "myStruct is not a type."

                Isn't using QML_VALUE_TYPE intended to allow me to use "myStruct" in the QML?

                G Offline
                G Offline
                GrecKo
                Qt Champions 2018
                wrote on 10 Oct 2023, 16:14 last edited by
                #21

                As I said : you don't have to use qmlRegisterType. You do have to use Q_GADGET though.

                And Q_GADGET adds a private: so you need to add back public:

                struct MyStruct {
                  Q_GADGET
                  QML_VALUE_TYPE(myStruct)
                  QML_STRUCTURED_VALUE
                
                  Q_PROPERTY(int myInt MEMBER m_myInt)
                
                public:
                  int m_myInt = 55;
                  MyStruct() {};
                };
                

                and my QML:
                property myStruct myStruct1: myStruct ({myInt: 100})

                Did I write that? nope. The syntax for structured values is :

                property myStruct myStruct1: ({myInt: 100})
                

                Ditch the myStruct on the right-hand side.

                M 1 Reply Last reply 10 Oct 2023, 16:24
                1
                • G GrecKo
                  10 Oct 2023, 16:14

                  As I said : you don't have to use qmlRegisterType. You do have to use Q_GADGET though.

                  And Q_GADGET adds a private: so you need to add back public:

                  struct MyStruct {
                    Q_GADGET
                    QML_VALUE_TYPE(myStruct)
                    QML_STRUCTURED_VALUE
                  
                    Q_PROPERTY(int myInt MEMBER m_myInt)
                  
                  public:
                    int m_myInt = 55;
                    MyStruct() {};
                  };
                  

                  and my QML:
                  property myStruct myStruct1: myStruct ({myInt: 100})

                  Did I write that? nope. The syntax for structured values is :

                  property myStruct myStruct1: ({myInt: 100})
                  

                  Ditch the myStruct on the right-hand side.

                  M Offline
                  M Offline
                  mzimmers
                  wrote on 10 Oct 2023, 16:24 last edited by mzimmers 10 Oct 2023, 18:52
                  #22

                  @GrecKo oh, that is a thing of beauty.

                  Now...about QML_STRUCTURED_VALUE being an undocumented macro, as you said...is it safe to use? I did notice mention of it in a bug report, so maybe it's OK.

                  Thanks for the help.

                  EDIT: everything above works, but if possible, I'd like to directly access enums defined in the struct (actually, this was the original point behind this entire thread).

                  struct MyStruct {
                      Q_GADGET
                      QML_STRUCTURED_VALUE
                      QML_VALUE_TYPE(myStruct)
                      Q_PROPERTY(int myInt MEMBER m_myInt)
                  public:
                      enum MyEnums {
                          Enum0,
                          Enum1,
                          Enum2,
                          Enum3
                      } m_myEnums;
                      Q_ENUM(MyEnums)
                      int m_myInt = 55;
                      MyStruct() {}
                  };
                  

                  This attempt doesn't work:

                  Label { text: "myStruct.Enum3: " + myStruct.Enum3 }
                  

                  (it doesn't work with "MyStruct" either.)

                  Can this be made to work? Thanks...

                  EDIT 2:

                  I have a workaround, which is to define my enums in a separate class and expose them to QML using the guidelines here, but if possible I'd prefer to avoid this level of indirection, and keep the enums in the struct.

                  G 1 Reply Last reply 11 Oct 2023, 07:55
                  0
                  • M mzimmers
                    10 Oct 2023, 16:24

                    @GrecKo oh, that is a thing of beauty.

                    Now...about QML_STRUCTURED_VALUE being an undocumented macro, as you said...is it safe to use? I did notice mention of it in a bug report, so maybe it's OK.

                    Thanks for the help.

                    EDIT: everything above works, but if possible, I'd like to directly access enums defined in the struct (actually, this was the original point behind this entire thread).

                    struct MyStruct {
                        Q_GADGET
                        QML_STRUCTURED_VALUE
                        QML_VALUE_TYPE(myStruct)
                        Q_PROPERTY(int myInt MEMBER m_myInt)
                    public:
                        enum MyEnums {
                            Enum0,
                            Enum1,
                            Enum2,
                            Enum3
                        } m_myEnums;
                        Q_ENUM(MyEnums)
                        int m_myInt = 55;
                        MyStruct() {}
                    };
                    

                    This attempt doesn't work:

                    Label { text: "myStruct.Enum3: " + myStruct.Enum3 }
                    

                    (it doesn't work with "MyStruct" either.)

                    Can this be made to work? Thanks...

                    EDIT 2:

                    I have a workaround, which is to define my enums in a separate class and expose them to QML using the guidelines here, but if possible I'd prefer to avoid this level of indirection, and keep the enums in the struct.

                    G Offline
                    G Offline
                    GrecKo
                    Qt Champions 2018
                    wrote on 11 Oct 2023, 07:55 last edited by
                    #23

                    @mzimmers I don't believe it is possible no. Instead of defining your enums in a separate class I would do it in a namespace with Q_ENUM_NS instead.

                    G M 2 Replies Last reply 11 Oct 2023, 13:45
                    0
                    • G GrecKo
                      10 Oct 2023, 14:45

                      You can now instantiate Gadgets from QML with undocumented macros (so maybe don't use it).

                      Use QML_VALUE_TYPE(typeName) with a lower case name like you currently do.
                      Then you can add QML_CONSTRUCTIBLE_VALUE or QML_STRUCTURED_VALUE (even both).

                      QML_CONSTRUCTIBLE_VALUE will call a user defined constructor

                      if you have Q_INVOKABLE MyStruct(int foo) : m_myInt{foo} {}
                      then doing property myStruct myStruct1: 42 will call the constructor with 42 as the foo param

                      QML_STRUCTURED_VALUE will assign properties depending on the js object you passed in QML.

                      property myStruct myStruct1: ({myInt: 42}) will create a default constructed MyStruct and assign its myInt property to 42.

                      Note that the type should always be default constructible.

                      EDIT: Note that you don't have to use QML_ELEMENT, Q_DECLARE_METATYPE or qmlRegisterType if you use Q_GADGET and QML_VALUE_TYPE.

                      L Offline
                      L Offline
                      lemons
                      wrote on 11 Oct 2023, 13:18 last edited by lemons 10 Nov 2023, 13:19
                      #24

                      @GrecKo said in using C++ classes and structs in QML:

                      You can now instantiate Gadgets from QML with undocumented macros (so maybe don't use it).

                      Use QML_VALUE_TYPE(typeName) with a lower case name like you currently do.
                      Then you can add QML_CONSTRUCTIBLE_VALUE or QML_STRUCTURED_VALUE (even both).

                      QML_CONSTRUCTIBLE_VALUE will call a user defined constructor

                      if you have Q_INVOKABLE MyStruct(int foo) : m_myInt{foo} {}
                      then doing property myStruct myStruct1: 42 will call the constructor with 42 as the foo param

                      QML_STRUCTURED_VALUE will assign properties depending on the js object you passed in QML.

                      property myStruct myStruct1: ({myInt: 42}) will create a default constructed MyStruct and assign its myInt property to 42.

                      Note that the type should always be default constructible.

                      EDIT: Note that you don't have to use QML_ELEMENT, Q_DECLARE_METATYPE or qmlRegisterType if you use Q_GADGET and QML_VALUE_TYPE.

                      This is amazing !!
                      Gonna have to explore the possibilities...

                      1 Reply Last reply
                      0
                      • G GrecKo
                        11 Oct 2023, 07:55

                        @mzimmers I don't believe it is possible no. Instead of defining your enums in a separate class I would do it in a namespace with Q_ENUM_NS instead.

                        G Offline
                        G Offline
                        GrecKo
                        Qt Champions 2018
                        wrote on 11 Oct 2023, 13:45 last edited by
                        #25

                        Coming back on what I said:

                        It is possible to expose an enum from a Q_GADGET.

                        If you add pragma ValueTypeBehavior: Addressable at the top of your QML file you can use the lower case gadget type name in QML and then do property int enumValue: myStruct.TestEnum.D

                        Alternatively you could do it very verbosely with QML_FOREIGN_NAMESPACE like explained here : https://codereview.qt-project.org/c/qt/qtdeclarative/+/510832 (fresh out of the oven)

                        1 Reply Last reply
                        0
                        • G GrecKo
                          11 Oct 2023, 07:55

                          @mzimmers I don't believe it is possible no. Instead of defining your enums in a separate class I would do it in a namespace with Q_ENUM_NS instead.

                          M Offline
                          M Offline
                          mzimmers
                          wrote on 11 Oct 2023, 16:22 last edited by
                          #26

                          @GrecKo said in using C++ classes and structs in QML:

                          Instead of defining your enums in a separate class I would do it in a namespace with Q_ENUM_NS instead.

                          namespace scheduleNS {
                          Q_NAMESPACE
                          enum StartAction {
                              START_ACTION_TURN_ON,
                              START_ACTION_TURN_OFF,
                              START_ACTION_BE_READY,
                              START_ACTION_SUSPEND
                          };
                          Q_ENUM_NS(StartAction)
                          } // namespace
                          

                          but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?

                          M K G 3 Replies Last reply 12 Oct 2023, 15:39
                          0
                          • M mzimmers
                            11 Oct 2023, 16:22

                            @GrecKo said in using C++ classes and structs in QML:

                            Instead of defining your enums in a separate class I would do it in a namespace with Q_ENUM_NS instead.

                            namespace scheduleNS {
                            Q_NAMESPACE
                            enum StartAction {
                                START_ACTION_TURN_ON,
                                START_ACTION_TURN_OFF,
                                START_ACTION_BE_READY,
                                START_ACTION_SUSPEND
                            };
                            Q_ENUM_NS(StartAction)
                            } // namespace
                            

                            but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?

                            M Offline
                            M Offline
                            mzimmers
                            wrote on 12 Oct 2023, 15:39 last edited by
                            #27

                            @mzimmers said in using C++ classes and structs in QML:

                            How do I export a C++ namespace to QML?

                            I should have asked, is there a newer alternative to the old method of manually registering it like this:

                               qmlRegisterUncreatableMetaObject(scheduleNS::staticMetaObject, // static meta object
                                                                "schedule.enums",          // import statement
                                                                1, 0,                         // major and minor version of the import
                                                                "ScheduleNS",                 // name in QML
                                                                "Error: only enums");          // error in case someone tries to create a MyNamespace object
                            

                            This works fine, but given that we no longer need to use qmlRegisterType(), I was wondering whether there was a more modern way of doing the qmlRegisterUncreatableMetaObject() call.

                            1 Reply Last reply
                            0
                            • M mzimmers
                              11 Oct 2023, 16:22

                              @GrecKo said in using C++ classes and structs in QML:

                              Instead of defining your enums in a separate class I would do it in a namespace with Q_ENUM_NS instead.

                              namespace scheduleNS {
                              Q_NAMESPACE
                              enum StartAction {
                                  START_ACTION_TURN_ON,
                                  START_ACTION_TURN_OFF,
                                  START_ACTION_BE_READY,
                                  START_ACTION_SUSPEND
                              };
                              Q_ENUM_NS(StartAction)
                              } // namespace
                              

                              but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?

                              K Offline
                              K Offline
                              kshegunov
                              Moderators
                              wrote on 12 Oct 2023, 22:03 last edited by
                              #28

                              @mzimmers said in using C++ classes and structs in QML:

                              but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?

                              Have you tried naming the namespace with a capital letter?
                              As far as I recall only value types are supposed to be lower case. QML is somewhat picky on the namings.

                              Read and abide by the Qt Code of Conduct

                              M 1 Reply Last reply 12 Oct 2023, 22:12
                              0
                              • K kshegunov
                                12 Oct 2023, 22:03

                                @mzimmers said in using C++ classes and structs in QML:

                                but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?

                                Have you tried naming the namespace with a capital letter?
                                As far as I recall only value types are supposed to be lower case. QML is somewhat picky on the namings.

                                M Offline
                                M Offline
                                mzimmers
                                wrote on 12 Oct 2023, 22:12 last edited by
                                #29

                                @kshegunov I did try that - I still have to register the uncreatable, and add an import statement to any QML wishing to use it.

                                Still a small price to pay...

                                1 Reply Last reply
                                0
                                • M mzimmers
                                  11 Oct 2023, 16:22

                                  @GrecKo said in using C++ classes and structs in QML:

                                  Instead of defining your enums in a separate class I would do it in a namespace with Q_ENUM_NS instead.

                                  namespace scheduleNS {
                                  Q_NAMESPACE
                                  enum StartAction {
                                      START_ACTION_TURN_ON,
                                      START_ACTION_TURN_OFF,
                                      START_ACTION_BE_READY,
                                      START_ACTION_SUSPEND
                                  };
                                  Q_ENUM_NS(StartAction)
                                  } // namespace
                                  

                                  but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?

                                  G Offline
                                  G Offline
                                  GrecKo
                                  Qt Champions 2018
                                  wrote on 13 Oct 2023, 08:11 last edited by
                                  #30

                                  @mzimmers said in using C++ classes and structs in QML:

                                  but "scheduleNS" isn't recognized in my QML. How do I export a C++ namespace to QML?

                                  With QML_ELEMENT or QML_NAMED_ELEMENT.

                                  1 Reply Last reply
                                  1
                                  • M mzimmers has marked this topic as solved on 13 Oct 2023, 18:44

                                  24/30

                                  11 Oct 2023, 13:18

                                  • Login

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