Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt
  • 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 8.0k 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.
  • mzimmersM mzimmers

    Hi all -

    My app uses a C++ (list) model which is exposed to QML via the means mentioned in the docs. Everything seems to work fine.

    I use the model on a summary page to display item information. When the user clicks on a list item, he'll go to a detail page.

    A minimal bit of my model:

    struct Space {
        QUuid m_uuid;
        QString m_name;
    }
    class SpaceModel: public QAbstractListModel
    {
        Q_OBJECT
        QList<Space> *m_list;
        ...
    }
    

    and some of my QML:

    // space summary screen
    delegate: SpaceCard {
        uuid: spaceUuid // from model
        drawerEnabled: false
        onTileClicked: (uuid) => {
                           drawer.open()
                           drawerStack.push("SpaceDetail.qml",
                                            {
                                                "uuid": uuid,
                                                "name": displayName // from model
                                            }
    
    // SpaceDetail.qml
    ColumnLayout {
        id: spaceDetail
        required property string uuid
        required property string name
        // etc...
    

    My question: what if the struct used in the model list were to contain, say, 100 elements? Obviously I don't want to pass each one individually from the summary to the detail. How best to handle this situation?

    Thanks...

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

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

    Obviously I don't want to pass each one individually from the summary to the detail. How best to handle this situation?

    I don't understand the question. What @sierdzio mentioned will allow you to pass and copy your struct around and keep it in a property var, recent Qt should (if it's been pushed) support also named value types. If you want to create the struct from QML though, you'd either need a factory function in C++ that you call, or (what I'd preferred in the past) is to expose a constructor from the struct that takes QJSValue.

    Read and abide by the Qt Code of Conduct

    mzimmersM 1 Reply Last reply
    1
    • kshegunovK kshegunov

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

      Obviously I don't want to pass each one individually from the summary to the detail. How best to handle this situation?

      I don't understand the question. What @sierdzio mentioned will allow you to pass and copy your struct around and keep it in a property var, recent Qt should (if it's been pushed) support also named value types. If you want to create the struct from QML though, you'd either need a factory function in C++ that you call, or (what I'd preferred in the past) is to expose a constructor from the struct that takes QJSValue.

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

      I'm missing something:

      struct Space {
          Q_GADGET
      public:
          QUuid m_uuid;
          QString m_name;
      }
      qmlRegisterType<Space>("Space", 1, 0, "Space");
      

      Now how do I use this in my QML? I tried this:

      ColumnLayout {
          id: spaceDetail
          required property Space mySpace
      

      But I got a runtime error that Space is not a type (along with a warning that value type names should begin with a lowercase letter).

      sierdzioS kshegunovK 2 Replies Last reply
      0
      • mzimmersM mzimmers

        I'm missing something:

        struct Space {
            Q_GADGET
        public:
            QUuid m_uuid;
            QString m_name;
        }
        qmlRegisterType<Space>("Space", 1, 0, "Space");
        

        Now how do I use this in my QML? I tried this:

        ColumnLayout {
            id: spaceDetail
            required property Space mySpace
        

        But I got a runtime error that Space is not a type (along with a warning that value type names should begin with a lowercase letter).

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

        @mzimmers one annoying thing about Q_GADGET is that you cannot instantiate it in QML. But you can create it in C++ and pass around you QML - that's why I suggested it. You can keep you Q_GADGET objects in C++ model and then use them to pass the whole entry data in a single property. Like so:

        drawerStack.push("SpaceDetail.qml",
                                                {
                                                    "mySpace": space // fromModel
                                                }
        

        In SpaceDetail.qml you can have a property but you cannot let it auto-construct because QML can't do it :( So, maybe this will work:

        property Space mySpace: null
        // or
        property Space mySpace: undefined
        

        (Z(:^

        1 Reply Last reply
        1
        • mzimmersM mzimmers

          I'm missing something:

          struct Space {
              Q_GADGET
          public:
              QUuid m_uuid;
              QString m_name;
          }
          qmlRegisterType<Space>("Space", 1, 0, "Space");
          

          Now how do I use this in my QML? I tried this:

          ColumnLayout {
              id: spaceDetail
              required property Space mySpace
          

          But I got a runtime error that Space is not a type (along with a warning that value type names should begin with a lowercase letter).

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

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

          struct Space {
              Q_GADGET
              Q_PROPERTY(QUuid uuid READ uuid WRITE setUuid)
              Q_PROPERTY(QString name READ name WRITE setName)
              QML_VALUE_TYPE(space)
          
              // Method definitions here ...
          
              QUuid m_uuid;
              QString m_name;
          }
          

          (potentially you may want to have Space(const QJSValue&) to be able to construct from QML json.)
          this this should work:

          ColumnLayout {
              id: spaceDetail
              required property space mySpace
              // ...
          

          More details here

          Read and abide by the Qt Code of Conduct

          mzimmersM 1 Reply Last reply
          3
          • kshegunovK kshegunov

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

            struct Space {
                Q_GADGET
                Q_PROPERTY(QUuid uuid READ uuid WRITE setUuid)
                Q_PROPERTY(QString name READ name WRITE setName)
                QML_VALUE_TYPE(space)
            
                // Method definitions here ...
            
                QUuid m_uuid;
                QString m_name;
            }
            

            (potentially you may want to have Space(const QJSValue&) to be able to construct from QML json.)
            this this should work:

            ColumnLayout {
                id: spaceDetail
                required property space mySpace
                // ...
            

            More details here

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

            @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?

            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");
            

            and which I don't understand, because all of my other calls to qmlRegisterType() work without error, eg:

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

            I suspect this is because SpaceModel is a subclass of QAbstractListModel, where Space is just a struct, but I don't fully understand the implications.

            sierdzioS 1 Reply Last reply
            0
            • A Offline
              A Offline
              Aliviya
              wrote on last edited by Aliviya
              #8

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

              Hi all -
              My app uses a C++ (list) model which is exposed to QML via the means mentioned in the docs. Everything seems to work fine.
              I use the model on a summary page to display item information. When the user clicks on a list item, he'll go to a detail page.
              A minimal bit of my model:
              struct Space {
              QUuid m_uuid;
              QString m_name;
              }
              class SpaceModel: public QAbstractListModel
              {
              Q_OBJECT
              QList<Space> *m_list;
              ...
              }

              and some of my QML:
              // space summary screen
              delegate: SpaceCard {
              uuid: spaceUuid // from model
              drawerEnabled: false
              onTileClicked: (uuid) => {
              drawer.open()
              drawerStack.push("SpaceDetail.qml",
              {
              "uuid": uuid,
              "name": displayName // from model
              }

              // SpaceDetail.qml
              ColumnLayout {
              id: spaceDetail
              required property string uuid
              required property string name
              // etc...

              My question: what if the struct used in the model list were to contain, say, 100 elements? Obviously I don't want to pass each one individually from the summary to the detail. How best to handle this situation?
              Thanks...

              Hi there,

              If the struct used in the model list contains 100 elements, it would be inefficient to pass each one individually from the summary to the detail. A better way to handle this situation would be to use a data model.

              A data model is a way of representing data in a way that is easy to access and manipulate. In QML, data models are implemented using the QtObject class.

              To use a data model, you would first need to create a class that inherits from QtObject. This class would then contain the properties that you want to pass to the detail page. For example, you could create a class called SpaceModel that contains the following properties:

              uuid: string
              name: string
              Once you have created the data model, you can then pass it to the detail page using the Object.fromJson() function. For example, you could do the following in the QML:

              // space summary screen
              delegate: SpaceCard {
                  uuid: spaceUuid // from model
                  drawerEnabled: false
                  onTileClicked: (uuid) => {
                                     drawer.open()
                                     drawerStack.push("SpaceDetail.qml",
                                                      JSON.stringify({
                                                          "uuid": uuid,
                                                          "name": displayName // from model
                                                      })
                  }
              }
              
              // SpaceDetail.qml
              ColumnLayout {
                  id: spaceDetail
                  required property string uuid
                  required property string name
                  // etc...
                  SpaceModel {
                      uuid: uuid
                      name: name
                  }
              }
              

              This would create a data model object in the detail page that contains the uuid and name properties of the selected item from the summary page.

              1 Reply Last reply
              0
              • mzimmersM mzimmers

                @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?

                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");
                

                and which I don't understand, because all of my other calls to qmlRegisterType() work without error, eg:

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

                I suspect this is because SpaceModel is a subclass of QAbstractListModel, where Space is just a struct, but I don't fully understand the implications.

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

                @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.

                (Z(:^

                mzimmersM kshegunovK 2 Replies Last reply
                1
                • 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.

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

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

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

                  Great idea - I'm going to do this. It never occurred to me to use data() for the entire object as well as the individual roles.

                  But: again, how do I code the access to the entire object in my QML? What is the construct for accessing a complete object from within a delegate?

                  Thanks...

                  GrecKoG sierdzioS 2 Replies Last reply
                  0
                  • mzimmersM mzimmers

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

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

                    Great idea - I'm going to do this. It never occurred to me to use data() for the entire object as well as the individual roles.

                    But: again, how do I code the access to the entire object in my QML? What is the construct for accessing a complete object from within a delegate?

                    Thanks...

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

                    @mzimmers is the question about how to access a model role from a delegate or how to access properties of an object?

                    mzimmersM 1 Reply Last reply
                    0
                    • GrecKoG GrecKo

                      @mzimmers is the question about how to access a model role from a delegate or how to access properties of an object?

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

                      @GrecKo more the latter. I'm trying to pass an instance from my model list from a delegate to another QML module, all in one line of code (if possible).

                      Right now, I only know how to access individual properties; I'm sure there's a way to refer to the entire object that the delegate is currently set on, but I don't know what that is.

                      EDIT: this is what I want to be able to do:
                      EDIT 2: simplified example (no need for the drawer)

                      // space summary screen
                      ListView {
                          model: spaceModel
                          delegate: SpaceCard {
                              // MAGIC to pass the model element
                          }
                          // ...
                      
                      // SpaceCard.qml                                          
                      Item {
                          id: spaceCard
                          // MAGIC to reference the model element
                          
                          text: MAGIC.text
                          uuid: MAGIC.uuid
                          // etc. (imagine that the model element had 100 fields
                      
                      1 Reply Last reply
                      0
                      • mzimmersM mzimmers

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

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

                        Great idea - I'm going to do this. It never occurred to me to use data() for the entire object as well as the individual roles.

                        But: again, how do I code the access to the entire object in my QML? What is the construct for accessing a complete object from within a delegate?

                        Thanks...

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

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

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

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

                        Great idea - I'm going to do this. It never occurred to me to use data() for the entire object as well as the individual roles.

                        But: again, how do I code the access to the entire object in my QML? What is the construct for accessing a complete object from within a delegate?

                        Thanks...

                        If you use roleNames() and custom roles then it is entirely up to you.
                        If you just return a list of objects to act as a model, then you can access current element through modelData, for example modelData.uuid.

                        (Z(:^

                        mzimmersM 1 Reply Last reply
                        2
                        • sierdzioS sierdzio

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

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

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

                          Great idea - I'm going to do this. It never occurred to me to use data() for the entire object as well as the individual roles.

                          But: again, how do I code the access to the entire object in my QML? What is the construct for accessing a complete object from within a delegate?

                          Thanks...

                          If you use roleNames() and custom roles then it is entirely up to you.
                          If you just return a list of objects to act as a model, then you can access current element through modelData, for example modelData.uuid.

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

                          @sierdzio OK, I think I now understand what you're saying. I've done the following:

                          • modified my Space struct:
                          struct Space {
                              Q_GADGET
                              QML_VALUE_TYPE(space) // so "space" can be used in QML.
                          public:
                              QString m_name;
                              Q_INVOKABLE QString name() { return m_name; }
                          
                          • added a CompleteObjectRole to my SpaceModel roles

                          • added to SpaceModel::data() for this role:

                          QVariant SpaceModel::data(const QModelIndex &index, int role) const
                          {
                              QVariant qv = QVariant();
                          
                              do {
                                  Space item = m_list->at(index.row());
                                  switch (role) {
                                  case CompleteObjectRole:
                                      qv = QVariant::fromValue(item));
                                      break;
                          
                          • use this in my QML:
                          // space summary screen
                          ListView {
                              id: spaceCards
                          
                              model: spaceProxyModel
                              delegate: SpaceCard {
                                  mySpace: completeObject
                          
                          // SpaceCard.qml
                          Item { 
                              id: spaceCard
                          
                              property space mySpace
                              property string titleText: mySpace.name()
                          

                          It works, and is what I was asking for. Is this what you expected/intended? Thanks...

                          EDIT:

                          I discovered a flaw in this approach: the property mySpace in SpaceCard.qml is evidently a static copy, and so its data isn't updated when the model changes. Is there a way to fix this?

                          Thanks...

                          sierdzioS 1 Reply Last reply
                          0
                          • mzimmersM mzimmers

                            @sierdzio OK, I think I now understand what you're saying. I've done the following:

                            • modified my Space struct:
                            struct Space {
                                Q_GADGET
                                QML_VALUE_TYPE(space) // so "space" can be used in QML.
                            public:
                                QString m_name;
                                Q_INVOKABLE QString name() { return m_name; }
                            
                            • added a CompleteObjectRole to my SpaceModel roles

                            • added to SpaceModel::data() for this role:

                            QVariant SpaceModel::data(const QModelIndex &index, int role) const
                            {
                                QVariant qv = QVariant();
                            
                                do {
                                    Space item = m_list->at(index.row());
                                    switch (role) {
                                    case CompleteObjectRole:
                                        qv = QVariant::fromValue(item));
                                        break;
                            
                            • use this in my QML:
                            // space summary screen
                            ListView {
                                id: spaceCards
                            
                                model: spaceProxyModel
                                delegate: SpaceCard {
                                    mySpace: completeObject
                            
                            // SpaceCard.qml
                            Item { 
                                id: spaceCard
                            
                                property space mySpace
                                property string titleText: mySpace.name()
                            

                            It works, and is what I was asking for. Is this what you expected/intended? Thanks...

                            EDIT:

                            I discovered a flaw in this approach: the property mySpace in SpaceCard.qml is evidently a static copy, and so its data isn't updated when the model changes. Is there a way to fix this?

                            Thanks...

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

                            @mzimmers yup, that's exactly what I had in mind!

                            EDIT:

                            I discovered a flaw in this approach: the property mySpace in SpaceCard.qml is evidently a static copy, and so its data isn't updated when the model changes. Is there a way to fix this?

                            First, try adding a MEMBER Q_PROPERTY to your gadget, it might work.

                            struct Space {
                                Q_GADGET
                                QML_VALUE_TYPE(space) // so "space" can be used in QML.
                            
                                Q_PROPERTY(QString name MEMBER m_name)
                            
                            public:
                                QString m_name;
                                Q_INVOKABLE QString name() { return m_name; }
                            
                            // ...
                            
                            property string titleText: mySpace.name
                            

                            If not, then you'll have to change Space to Q_OBJECT which does support signals & slots so it will also dynamically update for sure.

                            (Z(:^

                            mzimmersM sierdzioS 2 Replies Last reply
                            0
                            • sierdzioS sierdzio

                              @mzimmers yup, that's exactly what I had in mind!

                              EDIT:

                              I discovered a flaw in this approach: the property mySpace in SpaceCard.qml is evidently a static copy, and so its data isn't updated when the model changes. Is there a way to fix this?

                              First, try adding a MEMBER Q_PROPERTY to your gadget, it might work.

                              struct Space {
                                  Q_GADGET
                                  QML_VALUE_TYPE(space) // so "space" can be used in QML.
                              
                                  Q_PROPERTY(QString name MEMBER m_name)
                              
                              public:
                                  QString m_name;
                                  Q_INVOKABLE QString name() { return m_name; }
                              
                              // ...
                              
                              property string titleText: mySpace.name
                              

                              If not, then you'll have to change Space to Q_OBJECT which does support signals & slots so it will also dynamically update for sure.

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

                              @sierdzio I'd already tried using Q_PROPERTY; sadly, no success. I guess I'm going to have to go through the effort of basing Space off of QObject...ugh.

                              I'll report back when I'm done.

                              EDIT:

                              @sierdzio is there really no other way to accomplish this? I ask because

                              1. this is going to be a non-trivial effort, and
                              2. the example in the docs doesn't do it this way.

                              Any alternative is welcome.

                              1 Reply Last reply
                              0
                              • sierdzioS sierdzio

                                @mzimmers yup, that's exactly what I had in mind!

                                EDIT:

                                I discovered a flaw in this approach: the property mySpace in SpaceCard.qml is evidently a static copy, and so its data isn't updated when the model changes. Is there a way to fix this?

                                First, try adding a MEMBER Q_PROPERTY to your gadget, it might work.

                                struct Space {
                                    Q_GADGET
                                    QML_VALUE_TYPE(space) // so "space" can be used in QML.
                                
                                    Q_PROPERTY(QString name MEMBER m_name)
                                
                                public:
                                    QString m_name;
                                    Q_INVOKABLE QString name() { return m_name; }
                                
                                // ...
                                
                                property string titleText: mySpace.name
                                

                                If not, then you'll have to change Space to Q_OBJECT which does support signals & slots so it will also dynamically update for sure.

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

                                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?

                                (Z(:^

                                mzimmersM 1 Reply Last reply
                                0
                                • 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

                                          • Login

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