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

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

              J Offline
              J 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
              • J 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.

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

                  J Offline
                  J 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
                  • J 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
                    • J Offline
                      J 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
                      • J 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!

                        J 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!

                          J Offline
                          J 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