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. exposing entire C++ object to QML
Qt 6.11 is out! See what's new in the release blog

exposing entire C++ object to QML

Scheduled Pinned Locked Moved Solved QML and Qt Quick
31 Posts 7 Posters 5.6k Views 4 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.
  • sierdzioS sierdzio

    But have you used the property (name without ()) instead of invokable method?

    property string titleText: mySpace.name

    Also, do you emit dataChanged() in your model when a change occurs?

    mzimmersM Offline
    mzimmersM Offline
    mzimmers
    wrote on last edited by mzimmers
    #18

    @sierdzio said in exposing entire C++ object to QML:

    But have you used the property (name without ()) instead of invokable method?

    property string titleText: mySpace.name

    Yes:

    Item { // needed for the DropShadow.
        id: spaceCard
        property space spaceObject
        property string spaceName: spaceObject.name
    

    Also, do you emit dataChanged() in your model when a change occurs?

    Yes. When I receive a change, my slot loops through the list of spaces, and updates the appropriate list items:

    for (int spaceIndex = 0; spaceIndex < m_list->size(); spaceIndex++) {
        space = m_list->at(spaceIndex);
        if (space.containsEquipment(equipmentUuid)) {
    
            // update the temperature-related fields.
            QModelIndex qmi = index(spaceIndex, 0, QModelIndex());
    
            space.m_temperature = temperature;
            space.m_temperatureTimestamp = temperatureTimestamp;
            space.m_temperatureUpdated = true;
            changedRoles << TemperatureRole << TemperatureTimestampRole << TimestampUpdatedRole;
            m_list->replace(spaceIndex, space);
            emit dataChanged(qmi, qmi, changedRoles);
        }
    }
    

    I've verified this behavior with telltales.

    Rather strange, isn't it...

    GrecKoG 1 Reply Last reply
    0
    • mzimmersM mzimmers

      @sierdzio said in exposing entire C++ object to QML:

      But have you used the property (name without ()) instead of invokable method?

      property string titleText: mySpace.name

      Yes:

      Item { // needed for the DropShadow.
          id: spaceCard
          property space spaceObject
          property string spaceName: spaceObject.name
      

      Also, do you emit dataChanged() in your model when a change occurs?

      Yes. When I receive a change, my slot loops through the list of spaces, and updates the appropriate list items:

      for (int spaceIndex = 0; spaceIndex < m_list->size(); spaceIndex++) {
          space = m_list->at(spaceIndex);
          if (space.containsEquipment(equipmentUuid)) {
      
              // update the temperature-related fields.
              QModelIndex qmi = index(spaceIndex, 0, QModelIndex());
      
              space.m_temperature = temperature;
              space.m_temperatureTimestamp = temperatureTimestamp;
              space.m_temperatureUpdated = true;
              changedRoles << TemperatureRole << TemperatureTimestampRole << TimestampUpdatedRole;
              m_list->replace(spaceIndex, space);
              emit dataChanged(qmi, qmi, changedRoles);
          }
      }
      

      I've verified this behavior with telltales.

      Rather strange, isn't it...

      GrecKoG Offline
      GrecKoG Offline
      GrecKo
      Qt Champions 2018
      wrote on last edited by
      #19

      @mzimmers What role are you using for returning your complete object? What roles do you emit dataChanged on?

      mzimmersM GrecKoG 2 Replies Last reply
      0
      • GrecKoG GrecKo

        @mzimmers What role are you using for returning your complete object? What roles do you emit dataChanged on?

        mzimmersM Offline
        mzimmersM Offline
        mzimmers
        wrote on last edited by
        #20

        @GrecKo I've created a separate role for the complete object:

        enum SpaceRoleNames {
            UuidRole = Qt::UserRole,
            ...
            CompleteObjectRole,
            NbrRoles
        };
        

        and my data() function does this:

        QVariant SpaceModel::data(const QModelIndex &index, int role) const
        {
            QVariant qv = QVariant();
        
            do {
                if (!index.isValid() || m_list == nullptr)
                    continue;
        
                Space item = m_list->at(index.row());
                switch (role) {
                case UuidRole:
                    qv = item.m_uuid;
                    break;
                ...
                case CompleteObjectRole:
                    qv = QVariant::fromValue(item);
                    break;
        

        When any of the temperature related stuff changes, I perform the code in my last post.

        1 Reply Last reply
        0
        • Marko StankeM Offline
          Marko StankeM Offline
          Marko Stanke
          wrote on last edited by Marko Stanke
          #21

          If you want to purely show data inside SpaceCard.qml, you can bind it through QML. All the mentioned things below should work if the data() function has been implemented properly. (Note: sometimes you need to call dataChanged(index,index) inside the setData() function for it to chage it on QML side.

          ListView {
              id: spaceCards
          
              model: spaceProxyModel
              delegate: SpaceCard {
                  mySpace: completeObject
          
          // SpaceCard.qml
          Item { 
              id: spaceCard
          
              property space mySpace: spaceCards.currentItem.mySpace
              property string titleText: mySpace.name
          }
          

          Also, make sure that inside your list view the currentIndex changes accordingly.

          mzimmersM 1 Reply Last reply
          0
          • Marko StankeM Marko Stanke

            If you want to purely show data inside SpaceCard.qml, you can bind it through QML. All the mentioned things below should work if the data() function has been implemented properly. (Note: sometimes you need to call dataChanged(index,index) inside the setData() function for it to chage it on QML side.

            ListView {
                id: spaceCards
            
                model: spaceProxyModel
                delegate: SpaceCard {
                    mySpace: completeObject
            
            // SpaceCard.qml
            Item { 
                id: spaceCard
            
                property space mySpace: spaceCards.currentItem.mySpace
                property string titleText: mySpace.name
            }
            

            Also, make sure that inside your list view the currentIndex changes accordingly.

            mzimmersM Offline
            mzimmersM Offline
            mzimmers
            wrote on last edited by
            #22

            @Marko-Stanke thanks for the suggestion. I still can't get it to work. Here's the QML:

            // SpacesScreen.qml
            ListView {
                id: spaceCards
            
                model: spaceProxyModel
                delegate: SpaceCard {
                    spaceObject: completeObject
            
            // SpaceCard.qml
            Item { // needed for the DropShadow.
                id: spaceCard
            
                property space thisSpaceObject: spaceCards.currentItem().spaceObject
                property real temperatureReading: thisSpaceObject.temperature()
            

            And the struct:

            // space.h
            struct Space {
                Q_GADGET
                QML_VALUE_TYPE(space)
                Q_PROPERTY(float temperature MEMBER m_temperature)
            public:
                float m_temperature;
                Q_INVOKABLE float temperature () { return m_temperature; }
            

            Why would I need to call dataChanged() inside data()? That's a read-only function, right?

            I'm also not sure what you meant about the currentIndex. The ListView should handle that for me, shouldn't it?

            Thanks...

            Marko StankeM 1 Reply Last reply
            0
            • GrecKoG GrecKo

              @mzimmers What role are you using for returning your complete object? What roles do you emit dataChanged on?

              GrecKoG Offline
              GrecKoG Offline
              GrecKo
              Qt Champions 2018
              wrote on last edited by GrecKo
              #23

              @GrecKo said in exposing entire C++ object to QML:

              @mzimmers What role are you using for returning your complete object? What roles do you emit dataChanged on?

              Sorry if this wasn't clear but that was meant to push you to find the correct solution.

              You don't emit dataChanged for the CompleteObjectRole, so if you use this role and change the data, the view and delegates won't be aware of any change.

              Don't do what Marko suggested with

                  property space mySpace: spaceCards.currentItem().mySpace
                  property string titleText: mySpace.name()
              

              or emitting dataChanged in the data function.

              You also don't need the Q_INVOKABLE macro on your getters, they are are already exposed with Q_PROPERTY. You don't even need the getters if you are using MEMBER in the Q_PROPERTY (which makes sense for a Q_GADGET).

              mzimmersM 1 Reply Last reply
              1
              • mzimmersM mzimmers

                @Marko-Stanke thanks for the suggestion. I still can't get it to work. Here's the QML:

                // SpacesScreen.qml
                ListView {
                    id: spaceCards
                
                    model: spaceProxyModel
                    delegate: SpaceCard {
                        spaceObject: completeObject
                
                // SpaceCard.qml
                Item { // needed for the DropShadow.
                    id: spaceCard
                
                    property space thisSpaceObject: spaceCards.currentItem().spaceObject
                    property real temperatureReading: thisSpaceObject.temperature()
                

                And the struct:

                // space.h
                struct Space {
                    Q_GADGET
                    QML_VALUE_TYPE(space)
                    Q_PROPERTY(float temperature MEMBER m_temperature)
                public:
                    float m_temperature;
                    Q_INVOKABLE float temperature () { return m_temperature; }
                

                Why would I need to call dataChanged() inside data()? That's a read-only function, right?

                I'm also not sure what you meant about the currentIndex. The ListView should handle that for me, shouldn't it?

                Thanks...

                Marko StankeM Offline
                Marko StankeM Offline
                Marko Stanke
                wrote on last edited by Marko Stanke
                #24

                @mzimmers Sorry for the confusion. I edited my response.

                The dataChanged signal is needed (sometimes) in the setData() method, not data().
                Also, the currentItem,name are properties. No brackets () needed.

                ListView has a build in mechanism for handling currentIndex with keyboard navigation, but only if the ListView is in focus,
                if you want to change the currentIndex by click, you need to implement a click() signal handler. (By using MouseArea, TapHandler), and changing the currentIndex of your list based on the index of the clicked item.

                As for the Q_INVOKABLE goes that @GrecKo mentioned, he's right. No Q_INVOKABLE needed if you're using MEMBER in Q_PROPERTY.

                Note: the solution I posted is only viable if you want only to read data.

                1 Reply Last reply
                0
                • GrecKoG GrecKo

                  @GrecKo said in exposing entire C++ object to QML:

                  @mzimmers What role are you using for returning your complete object? What roles do you emit dataChanged on?

                  Sorry if this wasn't clear but that was meant to push you to find the correct solution.

                  You don't emit dataChanged for the CompleteObjectRole, so if you use this role and change the data, the view and delegates won't be aware of any change.

                  Don't do what Marko suggested with

                      property space mySpace: spaceCards.currentItem().mySpace
                      property string titleText: mySpace.name()
                  

                  or emitting dataChanged in the data function.

                  You also don't need the Q_INVOKABLE macro on your getters, they are are already exposed with Q_PROPERTY. You don't even need the getters if you are using MEMBER in the Q_PROPERTY (which makes sense for a Q_GADGET).

                  mzimmersM Offline
                  mzimmersM Offline
                  mzimmers
                  wrote on last edited by
                  #25

                  @GrecKo said in exposing entire C++ object to QML:

                  You don't emit dataChanged for the CompleteObjectRole, so if you use this role and change the data, the view and delegates won't be aware of any change.

                  Aaaah...that makes sense, thank you. I'm not sure how to fix this, though -- can my idea of a complete object role co-exist with the individual roles? In other words, would it be OK to simply emit a dataChanged() on the complete object in my code above (instead of the three roles I'm currently using)?

                  GrecKoG 1 Reply Last reply
                  0
                  • mzimmersM mzimmers

                    @GrecKo said in exposing entire C++ object to QML:

                    You don't emit dataChanged for the CompleteObjectRole, so if you use this role and change the data, the view and delegates won't be aware of any change.

                    Aaaah...that makes sense, thank you. I'm not sure how to fix this, though -- can my idea of a complete object role co-exist with the individual roles? In other words, would it be OK to simply emit a dataChanged() on the complete object in my code above (instead of the three roles I'm currently using)?

                    GrecKoG Offline
                    GrecKoG Offline
                    GrecKo
                    Qt Champions 2018
                    wrote on last edited by
                    #26

                    @mzimmers emit data changed for everything that changes. The individual roles if you want to keep them and the role for your complete object.

                    mzimmersM 1 Reply Last reply
                    0
                    • GrecKoG GrecKo

                      @mzimmers emit data changed for everything that changes. The individual roles if you want to keep them and the role for your complete object.

                      mzimmersM Offline
                      mzimmersM Offline
                      mzimmers
                      wrote on last edited by
                      #27

                      @GrecKo I think I'm very close to a solution. I modified my updater routine as follows:

                      changedRoles << SpaceRoles::TemperatureRole
                                   << SpaceRoles::TemperatureTimestampRole
                                   << SpaceRoles::TimestampUpdatedRole
                                   << SpaceRoles::CompleteObjectRole; // <== THIS IS NEW
                      m_list->replace(spaceIndex, space);
                      emit dataChanged(qmi, qmi, changedRoles);
                      

                      And it seems to work.

                      So, for my education, do I correctly understand that the signal must "agree with" the QML property being used? And, since I'm referencing an entire object in SpaceCard, that's what role the dataChanged() signal must include?

                      1 Reply Last reply
                      0
                      • B Bob64 referenced this topic on
                      • sierdzioS sierdzio

                        @mzimmers said in exposing entire C++ object to QML:

                        @kshegunov @sierdzio I'm still missing something here -- I have access to the Space object from the model used by the QML code that invokes SpaceDetail.qml. I want to use that object (not create a new one) to pass to SpaceDetail. How do I do this?

                        You can return Space object in your data() method of your model. Something like (pseudocode):

                        class SpaceModel: public QAbstractListModel
                        {
                          // ...
                          QVartiant data(const QModelIndex &index, const int role) const
                          {
                            if (index is valid and role is correct etc.) { 
                              m_list.at(index.row());
                            }
                          }
                        }
                        

                        I'm also getting a startup error:

                        qt.qml.typeregistration: Invalid QML element name "Space"; value type names should begin with a lowercase letter
                        

                        which I assume comes from this line:

                            qmlRegisterType<Space>("Space", 1, 0, "Space");
                        

                        This is some recent change in Qt 6. Uppercase gadgets work fine with Qt 5 & 6 but Qt 6 warns against them. If you don't need Qt 5 support, just make it lower-case and it will be fine.

                        Ah, one more thing: since Q_GADGETs cannot be constructed in QML, you should use qmlRegisterUncreatableType instead.

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

                        @sierdzio said in exposing entire C++ object to QML:

                        Ah, one more thing: since Q_GADGETs cannot be constructed in QML, you should use qmlRegisterUncreatableType instead.

                        You shouldn't. Manual registration is deprecated, you should use the proper macros to register it as value type (as shown above).

                        Read and abide by the Qt Code of Conduct

                        mzimmersM sierdzioS 2 Replies Last reply
                        0
                        • kshegunovK kshegunov

                          @sierdzio said in exposing entire C++ object to QML:

                          Ah, one more thing: since Q_GADGETs cannot be constructed in QML, you should use qmlRegisterUncreatableType instead.

                          You shouldn't. Manual registration is deprecated, you should use the proper macros to register it as value type (as shown above).

                          mzimmersM Offline
                          mzimmersM Offline
                          mzimmers
                          wrote on last edited by
                          #29

                          @kshegunov said in exposing entire C++ object to QML:

                          Manual registration is deprecated

                          It is? I found no mention of this in the documentation.

                          kshegunovK 1 Reply Last reply
                          0
                          • mzimmersM mzimmers

                            @kshegunov said in exposing entire C++ object to QML:

                            Manual registration is deprecated

                            It is? I found no mention of this in the documentation.

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

                            @mzimmers said in exposing entire C++ object to QML:

                            It is? I found no mention of this in the documentation.

                            As far as I remember the idea is to move it to the QML private code at some point (if it's not done already). Use the qqmlregistration header and the appropriate macros to annotate your types instead.

                            Notice the functions are not mentioned at all here: https://doc.qt.io/qt-6/qtqml-cppintegration-definetypes.html

                            Read and abide by the Qt Code of Conduct

                            1 Reply Last reply
                            1
                            • mzimmersM mzimmers has marked this topic as solved on
                            • kshegunovK kshegunov

                              @sierdzio said in exposing entire C++ object to QML:

                              Ah, one more thing: since Q_GADGETs cannot be constructed in QML, you should use qmlRegisterUncreatableType instead.

                              You shouldn't. Manual registration is deprecated, you should use the proper macros to register it as value type (as shown above).

                              sierdzioS Offline
                              sierdzioS Offline
                              sierdzio
                              Moderators
                              wrote on last edited by
                              #31

                              @kshegunov said in exposing entire C++ object to QML:

                              @sierdzio said in exposing entire C++ object to QML:

                              Ah, one more thing: since Q_GADGETs cannot be constructed in QML, you should use qmlRegisterUncreatableType instead.

                              You shouldn't. Manual registration is deprecated, you should use the proper macros to register it as value type (as shown above).

                              Ah, sorry, I still live in Qt 5 world :D

                              (Z(:^

                              1 Reply Last reply
                              0
                              • GrecKoG GrecKo referenced this topic on

                              • Login

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