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.5k 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.
  • 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

                                25/30

                                11 Oct 2023, 13:45

                                • Login

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