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. Binding C++ objects in std::vector to QML components
QtWS25 Last Chance

Binding C++ objects in std::vector to QML components

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
repeaterc++qtbinding
14 Posts 4 Posters 1.9k Views
  • 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.
  • E Offline
    E Offline
    ECEC
    wrote on 11 Oct 2023, 01:17 last edited by
    #1

    I'm working with an existing application that needs modifying to support a GUI.

    To simplify the explanation, I'm going to use an equivalent scenario.

    Suppose I have an std::vector (happy to change to a QList) of "Room" objects which is populated on startup. The size of the vector does not change during execution - no elements are added or removed. Each "Room" object contains several properties we wish to display in the GUI, such as temperature, lux, room dimensions, etc. These values can and do change throughout execution.

    On the QML side, I have a "Room" component which is no more than a rectangle with a few text fields. Each instance of the component will represent the relevant data contained in its respective C++ "Room" object.

    I need to find a way to instantiate a "Room" component for each object and insert it into a grid layout. I was thinking of using a Repeater for this, but it looks like that would require a model, which I don't think is appropriate in this case. What are my options here for achieving the binding?

    S G 2 Replies Last reply 11 Oct 2023, 05:27
    0
    • E ECEC
      11 Oct 2023, 01:17

      I'm working with an existing application that needs modifying to support a GUI.

      To simplify the explanation, I'm going to use an equivalent scenario.

      Suppose I have an std::vector (happy to change to a QList) of "Room" objects which is populated on startup. The size of the vector does not change during execution - no elements are added or removed. Each "Room" object contains several properties we wish to display in the GUI, such as temperature, lux, room dimensions, etc. These values can and do change throughout execution.

      On the QML side, I have a "Room" component which is no more than a rectangle with a few text fields. Each instance of the component will represent the relevant data contained in its respective C++ "Room" object.

      I need to find a way to instantiate a "Room" component for each object and insert it into a grid layout. I was thinking of using a Repeater for this, but it looks like that would require a model, which I don't think is appropriate in this case. What are my options here for achieving the binding?

      S Offline
      S Offline
      sierdzio
      Moderators
      wrote on 11 Oct 2023, 05:27 last edited by
      #2

      @ECEC said in Binding C++ objects in std::vector to QML components:

      was thinking of using a Repeater for this, but it looks like that would require a model, which I don't think is appropriate in this case.

      A QList<QObject*> is a valid model for Repeater. From your description I would say it is the best solution for you. If you define your Room object properties as Q_PROPERTY they will be visible to QML.

      (Z(:^

      1 Reply Last reply
      1
      • E ECEC
        11 Oct 2023, 01:17

        I'm working with an existing application that needs modifying to support a GUI.

        To simplify the explanation, I'm going to use an equivalent scenario.

        Suppose I have an std::vector (happy to change to a QList) of "Room" objects which is populated on startup. The size of the vector does not change during execution - no elements are added or removed. Each "Room" object contains several properties we wish to display in the GUI, such as temperature, lux, room dimensions, etc. These values can and do change throughout execution.

        On the QML side, I have a "Room" component which is no more than a rectangle with a few text fields. Each instance of the component will represent the relevant data contained in its respective C++ "Room" object.

        I need to find a way to instantiate a "Room" component for each object and insert it into a grid layout. I was thinking of using a Repeater for this, but it looks like that would require a model, which I don't think is appropriate in this case. What are my options here for achieving the binding?

        G Offline
        G Offline
        GrecKo
        Qt Champions 2018
        wrote on 11 Oct 2023, 05:40 last edited by
        #3

        A std::vector<Room*> is also a valid model if Room inherits from QObject and is fine if no rooms gets added or removed. Or as a value type if Room is a Q_GADGET. QML_FOREIGN might be an option if you don't want to modify your Room type.

        You'll need to use Q_DECLARE_METATYPE to use the std vector in a Q_PROPERTY (or figure out how to use QML_SEQUENTIAL_CONTAINER).

        1 Reply Last reply
        1
        • E Offline
          E Offline
          ECEC
          wrote on 11 Oct 2023, 17:19 last edited by
          #4

          Thanks for the suggestions, I think QList is probably the way to go.
          If I do decide to extend the application such that "Room" objects can be added and removed from the QList, would these changes be reflected on the QML side?

          S 1 Reply Last reply 12 Oct 2023, 05:21
          0
          • E ECEC
            11 Oct 2023, 17:19

            Thanks for the suggestions, I think QList is probably the way to go.
            If I do decide to extend the application such that "Room" objects can be added and removed from the QList, would these changes be reflected on the QML side?

            S Offline
            S Offline
            sierdzio
            Moderators
            wrote on 12 Oct 2023, 05:21 last edited by
            #5

            @ECEC said in Binding C++ objects in std::vector to QML components:

            Thanks for the suggestions, I think QList is probably the way to go.
            If I do decide to extend the application such that "Room" objects can be added and removed from the QList, would these changes be reflected on the QML side?

            No, QList does not send any change signals. But you can manually replace the list on QML side, then it will work. Something like:

            // QML code
            addNewRoom()
            model = null
            model = myModel
            

            Or, another way to do it is to emit a change signal for your model property. For example:

            // C++ code
            Q_PROPERTY(QList<Room*> myModel READ myModel NOTIFY myModelChanged)
            // In your model changing routine:
            _myModel.append(new Room);
            emit myModelChanged();
            

            At some point, if you need more model functionality, it will be easier to just port the model to a standard QAbstractListModel subclass or something similar.

            (Z(:^

            G 1 Reply Last reply 12 Oct 2023, 15:12
            0
            • S sierdzio
              12 Oct 2023, 05:21

              @ECEC said in Binding C++ objects in std::vector to QML components:

              Thanks for the suggestions, I think QList is probably the way to go.
              If I do decide to extend the application such that "Room" objects can be added and removed from the QList, would these changes be reflected on the QML side?

              No, QList does not send any change signals. But you can manually replace the list on QML side, then it will work. Something like:

              // QML code
              addNewRoom()
              model = null
              model = myModel
              

              Or, another way to do it is to emit a change signal for your model property. For example:

              // C++ code
              Q_PROPERTY(QList<Room*> myModel READ myModel NOTIFY myModelChanged)
              // In your model changing routine:
              _myModel.append(new Room);
              emit myModelChanged();
              

              At some point, if you need more model functionality, it will be easier to just port the model to a standard QAbstractListModel subclass or something similar.

              G Offline
              G Offline
              GrecKo
              Qt Champions 2018
              wrote on 12 Oct 2023, 15:12 last edited by
              #6

              Use https://github.com/OlivierLDff/ObjectListModel if you need a dynamic model. It has the API of a QList but expose itself as a QAbstractListModel automagically.

              1 Reply Last reply
              0
              • E Offline
                E Offline
                ECEC
                wrote on 7 Dec 2023, 02:54 last edited by
                #7

                Sorry to reopen this again!
                I've decided to move to a QAbstractListModel subclass as adding/removing Rooms is something that needs to be supported. The Room classes' variables have been registered with Q_PROPERTY, however, they are accessed in the view via the model's roles.

                So, my question is, if these Q_PROPERTY variables are modified, will the view automatically update, or do I need to be responsible for that?

                jeremy_kJ 1 Reply Last reply 7 Dec 2023, 03:30
                0
                • E ECEC
                  7 Dec 2023, 02:54

                  Sorry to reopen this again!
                  I've decided to move to a QAbstractListModel subclass as adding/removing Rooms is something that needs to be supported. The Room classes' variables have been registered with Q_PROPERTY, however, they are accessed in the view via the model's roles.

                  So, my question is, if these Q_PROPERTY variables are modified, will the view automatically update, or do I need to be responsible for that?

                  jeremy_kJ Offline
                  jeremy_kJ Offline
                  jeremy_k
                  wrote on 7 Dec 2023, 03:30 last edited by
                  #8

                  @ECEC said in Binding C++ objects in std::vector to QML components:

                  Sorry to reopen this again!
                  I've decided to move to a QAbstractListModel subclass as adding/removing Rooms is something that needs to be supported. The Room classes' variables have been registered with Q_PROPERTY, however, they are accessed in the view via the model's roles.

                  So, my question is, if these Q_PROPERTY variables are modified, will the view automatically update, or do I need to be responsible for that?

                  If I understand the description, no, the property change signal will not trigger a re-read of model roles via QAbstractItemModel::data(). That requires emitting the QAIM:dataChanged() signal.

                  Asking a question about code? http://eel.is/iso-c++/testcase/

                  E 1 Reply Last reply 7 Dec 2023, 04:16
                  1
                  • jeremy_kJ jeremy_k
                    7 Dec 2023, 03:30

                    @ECEC said in Binding C++ objects in std::vector to QML components:

                    Sorry to reopen this again!
                    I've decided to move to a QAbstractListModel subclass as adding/removing Rooms is something that needs to be supported. The Room classes' variables have been registered with Q_PROPERTY, however, they are accessed in the view via the model's roles.

                    So, my question is, if these Q_PROPERTY variables are modified, will the view automatically update, or do I need to be responsible for that?

                    If I understand the description, no, the property change signal will not trigger a re-read of model roles via QAbstractItemModel::data(). That requires emitting the QAIM:dataChanged() signal.

                    E Offline
                    E Offline
                    ECEC
                    wrote on 7 Dec 2023, 04:16 last edited by
                    #9

                    @jeremy_k

                    That seems to go against Qt's philosophy of declaratively binding UI elements to their C++ counterparts, but I'm going to assume that to return the entire Room object as a role isn't a good idea.

                    So I suppose I'll have to call datachanged() to update the view. Room's data comes externally via a NetworkProcessor class. Would you recommend making changes directly on the Room objects (and then calling datachanged() on the model), or performing all changes via the model? For example, NetworkProcessor could emit a roomUpdated() signal, passing a struct containing a Room's id and relevant data we want to update. And then connect this signal to a slot in the model and do the actual updating of the Room list's objects, etc, there.

                    jeremy_kJ 1 Reply Last reply 7 Dec 2023, 04:38
                    0
                    • E ECEC
                      7 Dec 2023, 04:16

                      @jeremy_k

                      That seems to go against Qt's philosophy of declaratively binding UI elements to their C++ counterparts, but I'm going to assume that to return the entire Room object as a role isn't a good idea.

                      So I suppose I'll have to call datachanged() to update the view. Room's data comes externally via a NetworkProcessor class. Would you recommend making changes directly on the Room objects (and then calling datachanged() on the model), or performing all changes via the model? For example, NetworkProcessor could emit a roomUpdated() signal, passing a struct containing a Room's id and relevant data we want to update. And then connect this signal to a slot in the model and do the actual updating of the Room list's objects, etc, there.

                      jeremy_kJ Offline
                      jeremy_kJ Offline
                      jeremy_k
                      wrote on 7 Dec 2023, 04:38 last edited by
                      #10

                      @ECEC said in Binding C++ objects in std::vector to QML components:

                      @jeremy_k

                      That seems to go against Qt's philosophy of declaratively binding UI elements to their C++ counterparts, but I'm going to assume that to return the entire Room object as a role isn't a good idea.

                      I have seen that strategy work.

                      So I suppose I'll have to call datachanged() to update the view. Room's data comes externally via a NetworkProcessor class. Would you recommend making changes directly on the Room objects (and then calling datachanged() on the model), or performing all changes via the model? For example, NetworkProcessor could emit a roomUpdated() signal, passing a struct containing a Room's id and relevant data we want to update. And then connect this signal to a slot in the model and do the actual updating of the Room list's objects, etc, there.

                      Either way sounds fine. If the existing code base is large, I tend to vote for harmony.

                      Asking a question about code? http://eel.is/iso-c++/testcase/

                      E 1 Reply Last reply 7 Dec 2023, 15:41
                      1
                      • jeremy_kJ jeremy_k
                        7 Dec 2023, 04:38

                        @ECEC said in Binding C++ objects in std::vector to QML components:

                        @jeremy_k

                        That seems to go against Qt's philosophy of declaratively binding UI elements to their C++ counterparts, but I'm going to assume that to return the entire Room object as a role isn't a good idea.

                        I have seen that strategy work.

                        So I suppose I'll have to call datachanged() to update the view. Room's data comes externally via a NetworkProcessor class. Would you recommend making changes directly on the Room objects (and then calling datachanged() on the model), or performing all changes via the model? For example, NetworkProcessor could emit a roomUpdated() signal, passing a struct containing a Room's id and relevant data we want to update. And then connect this signal to a slot in the model and do the actual updating of the Room list's objects, etc, there.

                        Either way sounds fine. If the existing code base is large, I tend to vote for harmony.

                        E Offline
                        E Offline
                        ECEC
                        wrote on 7 Dec 2023, 15:41 last edited by
                        #11

                        @jeremy_k

                        Another concern is the maintainability of the model. I understand it's best practice to make changes to the underlying Room objects via the model. e.g. if Room has an updateConfig() method, the model should have an equivalent updateRoomConfig(id) method. However, this doesn't give much freedom to interact with the Room objects outside of the model. Would it be acceptable to expose the model's Room list to other areas of the system and perhaps have them emit a roomUpdated() signal (attached to a corresponding slot in the model that calls dataChanged()) whenever "displayable" changes are made? I'm really only wanting to use the model as a means to display the data, while leaving ownership to other classes.

                        1 Reply Last reply
                        0
                        • jeremy_kJ Offline
                          jeremy_kJ Offline
                          jeremy_k
                          wrote on 7 Dec 2023, 19:47 last edited by jeremy_k 12 Jul 2023, 20:06
                          #12

                          Have you considered using QStandardItemModel? I shy away from it for heavy C++ use, but it's very convenient as a low effort, thin wrapper. I don't bother with the subclassing of QStandardItem that the examples usually demonstrate.

                          main.cpp:

                          #include <QGuiApplication>
                          #include <QQmlApplicationEngine>
                          #include <QStandardItemModel>
                          #include <QStandardItem>
                          #include <QObject>
                          
                          int main(int argc, char *argv[])
                          {
                              QGuiApplication app(argc, argv);
                              QStandardItemModel model;
                              QStandardItem item;
                              QObject obj;
                              obj.setObjectName("myObject");
                              item.setData(QVariant::fromValue(&obj), Qt::ItemDataRole::DisplayRole);
                              model.appendRow(&item);
                              QQmlApplicationEngine engine;
                              engine.setInitialProperties({ {"myModel", QVariant::fromValue(&model)} });
                              const QUrl url(u"qrc:/untitled7/Main.qml"_qs);
                              QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
                                  &app, []() { QCoreApplication::exit(-1); },
                                  Qt::QueuedConnection);
                              engine.load(url);
                          
                              return app.exec();
                          }
                          

                          main.qml:

                          import QtQuick
                          import QtQuick.Window
                          
                          Window {
                              id: root
                              width: 640
                              height: 480
                              visible: true
                              property var myModel
                              ListView {
                                  anchors.fill: parent
                                  model: root.myModel
                                  delegate: Text {
                                      text: display.objectName
                                  }
                              }
                          }
                          

                          Asking a question about code? http://eel.is/iso-c++/testcase/

                          E 1 Reply Last reply 7 Dec 2023, 22:08
                          0
                          • jeremy_kJ jeremy_k
                            7 Dec 2023, 19:47

                            Have you considered using QStandardItemModel? I shy away from it for heavy C++ use, but it's very convenient as a low effort, thin wrapper. I don't bother with the subclassing of QStandardItem that the examples usually demonstrate.

                            main.cpp:

                            #include <QGuiApplication>
                            #include <QQmlApplicationEngine>
                            #include <QStandardItemModel>
                            #include <QStandardItem>
                            #include <QObject>
                            
                            int main(int argc, char *argv[])
                            {
                                QGuiApplication app(argc, argv);
                                QStandardItemModel model;
                                QStandardItem item;
                                QObject obj;
                                obj.setObjectName("myObject");
                                item.setData(QVariant::fromValue(&obj), Qt::ItemDataRole::DisplayRole);
                                model.appendRow(&item);
                                QQmlApplicationEngine engine;
                                engine.setInitialProperties({ {"myModel", QVariant::fromValue(&model)} });
                                const QUrl url(u"qrc:/untitled7/Main.qml"_qs);
                                QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
                                    &app, []() { QCoreApplication::exit(-1); },
                                    Qt::QueuedConnection);
                                engine.load(url);
                            
                                return app.exec();
                            }
                            

                            main.qml:

                            import QtQuick
                            import QtQuick.Window
                            
                            Window {
                                id: root
                                width: 640
                                height: 480
                                visible: true
                                property var myModel
                                ListView {
                                    anchors.fill: parent
                                    model: root.myModel
                                    delegate: Text {
                                        text: display.objectName
                                    }
                                }
                            }
                            
                            E Offline
                            E Offline
                            ECEC
                            wrote on 7 Dec 2023, 22:08 last edited by
                            #13

                            @jeremy_k

                            I'm not too sure what benefit you are suggesting QStandardItemModel would have over subclassing QAbstractListModel.

                            I'm happy to use QAbstractListModel, and I think I should bring my previous question about whether to interact with objects via the model or directly, to a new thread.

                            Thanks for your help!

                            jeremy_kJ 1 Reply Last reply 8 Dec 2023, 05:23
                            0
                            • E ECEC
                              7 Dec 2023, 22:08

                              @jeremy_k

                              I'm not too sure what benefit you are suggesting QStandardItemModel would have over subclassing QAbstractListModel.

                              I'm happy to use QAbstractListModel, and I think I should bring my previous question about whether to interact with objects via the model or directly, to a new thread.

                              Thanks for your help!

                              jeremy_kJ Offline
                              jeremy_kJ Offline
                              jeremy_k
                              wrote on 8 Dec 2023, 05:23 last edited by
                              #14

                              @ECEC said in Binding C++ objects in std::vector to QML components:

                              @jeremy_k

                              I'm not too sure what benefit you are suggesting QStandardItemModel would have over subclassing QAbstractListModel.

                              The only advantage is less stuff to write and maintain. I count 6 lines of code above, including headers, specific to the standard item model.

                              Asking a question about code? http://eel.is/iso-c++/testcase/

                              1 Reply Last reply
                              1

                              • Login

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