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. Bind to another model's data in a delegate

Bind to another model's data in a delegate

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
18 Posts 4 Posters 342 Views 1 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.
  • E Offline
    E Offline
    ECEC
    wrote last edited by
    #1

    I need to bind a text property in my delegate to data from another QAbstractListModel. The only way I can think to achieve this would be to bind it to the data method of the external model, but this doesn't appear to react to data changes. The alternative, which I'm trying to avoid, would be to expose it through the model to which the delegate is tied.

    What is the "correct" way here?

    jeremy_kJ 1 Reply Last reply
    0
    • GrecKoG Offline
      GrecKoG Offline
      GrecKo
      Qt Champions 2018
      wrote last edited by
      #2

      Can you give us more context? how is the data from the other model selected (row, column, index)?
      Do you need this for one delegate at a time or for all delegates?

      E 1 Reply Last reply
      0
      • GrecKoG GrecKo

        Can you give us more context? how is the data from the other model selected (row, column, index)?
        Do you need this for one delegate at a time or for all delegates?

        E Offline
        E Offline
        ECEC
        wrote last edited by
        #3

        @GrecKo The delegate has the id of the item in the other model, and is able to call a Q_INVOKABLE method to retrieve the row which is then fed into the call to data().

        1 Reply Last reply
        0
        • E Offline
          E Offline
          ECEC
          wrote last edited by
          #4

          I would need this for all delegates.
          It occurs to me I expose this to QML as a property in the C++ model, which connects to the other model's dataChanged(). What do you think?

          1 Reply Last reply
          0
          • E ECEC

            I need to bind a text property in my delegate to data from another QAbstractListModel. The only way I can think to achieve this would be to bind it to the data method of the external model, but this doesn't appear to react to data changes. The alternative, which I'm trying to avoid, would be to expose it through the model to which the delegate is tied.

            What is the "correct" way here?

            jeremy_kJ Offline
            jeremy_kJ Offline
            jeremy_k
            wrote last edited by
            #5

            @ECEC said in Bind to another model's data in a delegate:

            The alternative, which I'm trying to avoid, would be to expose it through the model to which the delegate is tied.

            Why? This sounds like the correct way to me, rather than duplicating data and effectively reimplementing the model view framework.

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

            1 Reply Last reply
            0
            • E Offline
              E Offline
              ECEC
              wrote last edited by
              #6

              @jeremy_k , Just to make sure we're on the same page, you are suggesting that I should connect Model B's dataChanged() to a slot in Model A, which will then check whether the relevant data has changed and emit its own dataChanged? Model A's data() will simply query Model B's data() to get the data it needs.

              Is that right?

              jeremy_kJ 1 Reply Last reply
              0
              • E ECEC

                @jeremy_k , Just to make sure we're on the same page, you are suggesting that I should connect Model B's dataChanged() to a slot in Model A, which will then check whether the relevant data has changed and emit its own dataChanged? Model A's data() will simply query Model B's data() to get the data it needs.

                Is that right?

                jeremy_kJ Offline
                jeremy_kJ Offline
                jeremy_k
                wrote last edited by
                #7

                @ECEC said in Bind to another model's data in a delegate:

                @jeremy_k , Just to make sure we're on the same page, you are suggesting that I should connect Model B's dataChanged() to a slot in Model A, which will then check whether the relevant data has changed and emit its own dataChanged? Model A's data() will simply query Model B's data() to get the data it needs.

                No, although you may be highlighting a gap in my understanding. My suggestion is to have a single model which feeds both views. Maintaining a single source of truth rather than multiple related models is usually easier to work with.

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

                1 Reply Last reply
                0
                • E Offline
                  E Offline
                  ECEC
                  wrote last edited by
                  #8

                  @jeremy_k

                  The models are totally independent and I don't think it would make sense to combine them. The property from model B that we want to access in model A is only units (i.e. mm, cm, inches, etc) and we want to display this for convenience, although it has no effect on how the user interacts with model A's data.

                  Each item in model A is associated with the id of an item in model B.

                  jeremy_kJ 1 Reply Last reply
                  0
                  • E ECEC

                    @jeremy_k

                    The models are totally independent and I don't think it would make sense to combine them. The property from model B that we want to access in model A is only units (i.e. mm, cm, inches, etc) and we want to display this for convenience, although it has no effect on how the user interacts with model A's data.

                    Each item in model A is associated with the id of an item in model B.

                    jeremy_kJ Offline
                    jeremy_kJ Offline
                    jeremy_k
                    wrote last edited by
                    #9

                    @ECEC said in Bind to another model's data in a delegate:

                    @jeremy_k

                    The models are totally independent and I don't think it would make sense to combine them. The property from model B that we want to access in model A is only units (i.e. mm, cm, inches, etc) and we want to display this for convenience, although it has no effect on how the user interacts with model A's data.

                    Each item in model A is associated with the id of an item in model B.

                    I'm even more convinced now. Units not attached to scalars?

                    The single model can be a proxy model that combines the two, similar to how a SQL join combines multiple tables into one for the sake of presentation. That allows the view to be unconcerned with the details of different sources of information.

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

                    JonBJ 1 Reply Last reply
                    1
                    • jeremy_kJ jeremy_k

                      @ECEC said in Bind to another model's data in a delegate:

                      @jeremy_k

                      The models are totally independent and I don't think it would make sense to combine them. The property from model B that we want to access in model A is only units (i.e. mm, cm, inches, etc) and we want to display this for convenience, although it has no effect on how the user interacts with model A's data.

                      Each item in model A is associated with the id of an item in model B.

                      I'm even more convinced now. Units not attached to scalars?

                      The single model can be a proxy model that combines the two, similar to how a SQL join combines multiple tables into one for the sake of presentation. That allows the view to be unconcerned with the details of different sources of information.

                      JonBJ Online
                      JonBJ Online
                      JonB
                      wrote last edited by JonB
                      #10

                      @jeremy_k said in Bind to another model's data in a delegate:

                      The single model can be a proxy model that combines the two, similar to how a SQL join combines multiple tables into one for the sake of presentation. That allows the view to be unconcerned with the details of different sources of information.

                      As @jeremy_k says. I did not comment before because I know nothing about QML or its exact relationship with a QAbstractListModel. But assuming you can do things at the backend model side (e.g. not worry about delegates, ids or Q_INVOKABLE) one way is to use a QAbstractProxyModel (start from QIdentityProxyModel for this) to add a column (with id lookup) from another table. Another similar is what QSqlRelationalTableModel is doing, which is precisely what it sounds like you want: "combine two tables via foreign key, so an "id" in the child table points to a "word" to use for it in the parent table". You can also do it directly in your child table's data() method. Finally you can also do it by performing a JOIN at the table side. If you then need or want to still present this as a QAbstractListModel to keep QML happy you can wrap the above to present it that way to the outside world.

                      1 Reply Last reply
                      0
                      • E Offline
                        E Offline
                        ECEC
                        wrote last edited by
                        #11

                        @jeremy_k , A proxy model can only have one source though, so surely this would still require connecting to model B's dataChanged to be aware of relevant changes (i.e units changed)?

                        @jeremy_k , I'd like to avoid redeveloping a significant portion of the application to use a database. Seems overkill for what I'm trying to achieve.

                        JonBJ jeremy_kJ 2 Replies Last reply
                        0
                        • E ECEC

                          @jeremy_k , A proxy model can only have one source though, so surely this would still require connecting to model B's dataChanged to be aware of relevant changes (i.e units changed)?

                          @jeremy_k , I'd like to avoid redeveloping a significant portion of the application to use a database. Seems overkill for what I'm trying to achieve.

                          JonBJ Online
                          JonBJ Online
                          JonB
                          wrote last edited by JonB
                          #12

                          @ECEC You have addressed these two points to @jeremy_k . Did you intend to address them to me?

                          1 Reply Last reply
                          0
                          • E Offline
                            E Offline
                            ECEC
                            wrote last edited by
                            #13

                            @JonB, apologies, the second point was for you. Although please feel free to respond to both.

                            JonBJ 1 Reply Last reply
                            0
                            • E ECEC

                              @JonB, apologies, the second point was for you. Although please feel free to respond to both.

                              JonBJ Online
                              JonBJ Online
                              JonB
                              wrote last edited by JonB
                              #14

                              @ECEC

                              1. I never intended you to use an actual database. Only the QSqlRelationaltableModel has anything to do with databases, and that was merely an illustration of something which is probably doing what you want might be written. The principle applies whether backend database or not, it's actually about looking up a value in one table from another table with both in memory.

                              2. I don't understand what you mean about your stuff "doesn't appear to react to data changes", "dataChanged()" or "units changed". Presumably the units table which "we want to display this for convenience" does not itself change? It holds display information for "mm, cm, inches, etc" and your real data table ("child") simply has a column in each row stating what units (another column) is using which you want to look up (in the "parent") to show the units against the value? Changes get made in the "parent" (value) table, not in the "child) (unit lookup) table?

                              1 Reply Last reply
                              0
                              • E Offline
                                E Offline
                                ECEC
                                wrote last edited by
                                #15

                                @JonB
                                I fear that I haven't explained this clearly. Let me illustrate the problem with the following simplified scenario:

                                struct Building
                                {
                                  int id;
                                  QString name;
                                  QColor color;
                                  int width;
                                  int depth;
                                  int height;
                                }
                                

                                We have a BuildingListModel with a vector of Building which inherits from QAbstractListModel, and exposes the data above via custom roles. This all works fine.

                                struct Trip
                                {
                                  int fromBuildingID;
                                  int toBuildingID;
                                  QString tripName;
                                  VehicleType vehicle;
                                }
                                

                                And here we have a TripListModel with a vector of Trips which also inherits from QAbstractListModel and exposes the data via custom roles. This also works fine.

                                In the Trip list view's delegate, the user selects the from building and to building ids from a combo box which connects to the BuildingListModel. Again this is all good.

                                But now, at this point, we decide it might be helpful to the user if we display the color of the "from" building on the left side of the delegate and the color of the "to" building on the right side of the delegate. This has no bearing on the inner workings of the TripListModel and is only for visual convenience.

                                We also have to consider that the user might change a Building's color at any point via the Building's list view, and any such changes should be reflected in the Trip list view's delegates.

                                To me at least, the most logical solution would be to connect BuildingListModel's dataChanged() to a slot in TripListModel. When a Building's data changes, we can then check whether it was for the Color role, retrieve the changed Building's ID, and then search our vector of Trip for any items that are associated with that Building ID, and finally emit dataChanged() for a color role in the TripListModel.

                                I hope that makes sense.

                                JonBJ 1 Reply Last reply
                                0
                                • E ECEC

                                  @JonB
                                  I fear that I haven't explained this clearly. Let me illustrate the problem with the following simplified scenario:

                                  struct Building
                                  {
                                    int id;
                                    QString name;
                                    QColor color;
                                    int width;
                                    int depth;
                                    int height;
                                  }
                                  

                                  We have a BuildingListModel with a vector of Building which inherits from QAbstractListModel, and exposes the data above via custom roles. This all works fine.

                                  struct Trip
                                  {
                                    int fromBuildingID;
                                    int toBuildingID;
                                    QString tripName;
                                    VehicleType vehicle;
                                  }
                                  

                                  And here we have a TripListModel with a vector of Trips which also inherits from QAbstractListModel and exposes the data via custom roles. This also works fine.

                                  In the Trip list view's delegate, the user selects the from building and to building ids from a combo box which connects to the BuildingListModel. Again this is all good.

                                  But now, at this point, we decide it might be helpful to the user if we display the color of the "from" building on the left side of the delegate and the color of the "to" building on the right side of the delegate. This has no bearing on the inner workings of the TripListModel and is only for visual convenience.

                                  We also have to consider that the user might change a Building's color at any point via the Building's list view, and any such changes should be reflected in the Trip list view's delegates.

                                  To me at least, the most logical solution would be to connect BuildingListModel's dataChanged() to a slot in TripListModel. When a Building's data changes, we can then check whether it was for the Color role, retrieve the changed Building's ID, and then search our vector of Trip for any items that are associated with that Building ID, and finally emit dataChanged() for a color role in the TripListModel.

                                  I hope that makes sense.

                                  JonBJ Online
                                  JonBJ Online
                                  JonB
                                  wrote last edited by JonB
                                  #16

                                  @ECEC said in Bind to another model's data in a delegate:

                                  We also have to consider that the user might change a Building's color at any point via the Building's list view, and any such changes should be reflected in the Trip list view's delegates.

                                  Yes, but this example is rather different from your earlier one where the (parent) "lookup" table was only going to hold a list of "unit measurement words". There presumably the parent, lookup table was not going to change: if a child row had the id for "inches" in the lookup column you were not going to change the lookup table for that to suddenly be "feet". So you don't to know if the parent table is changing, it isn't.

                                  In your new one the "parent" table, Buildings, can apparently change. So, yes, you do need to intercept Building's dataChanged() and then emit a Trip's dataChanged() against each cell whose fromBuildingID or toBuildingID equals the Building's id whose color has consequently altered. Even though the building id value in trips has not itself been changed, you need the color to be re-read and updated.

                                  But all this is good and fine. There is no reason for you not to implement something like this. You don't need to expose the knowledge of the link to the outside world. At least this is fine to do in the backend I know about, in C++ with abstract models and/or proxies as you please. I can't say about QML or roles or delegates.

                                  It's not for the same purpose as your situation, but in the work I am doing (an "emulator") now I happen to have:

                                  • A model for the contents of memory.
                                  • A tableview to display that, with updates as memory changes. This is a complete "memory view".
                                  • A model for a "watch view". This is separate and user can set rows to "watch" individual memory locations.
                                  • The tableview to display that, with updates as memory changes. This is a selective memory "watch view".

                                  Obviously the complete memory model's dataChanged() is connected to the memory view, so that updates. But the watch model has its own columns (address watched, label etc.) as well as a column for the memory value which is in the memory model. So the watch model has to attach to the memory model's dataChanged(): when it fires, go through all watch rows, if they are watching a changed memory location then emit a dataChanged() against whatever row/column that happens to be in watch model so that watch view updates. Same kind of situation as yours. And it works fine. Whether you do that with subclassing, proxies or other (e.g. just data() method) is a detail.

                                  If you look at the implementation of QIdentityProxyModel/QAbstractProxyModel it hooks onto all the signals of the underlying source QAbstractItemModel to bring them into the proxy. So this procedure of "chaining" signals (like dataChanged()) from a base model to a wrapped model is not unusual.

                                  1 Reply Last reply
                                  0
                                  • E Offline
                                    E Offline
                                    ECEC
                                    wrote last edited by
                                    #17

                                    @JonB

                                    Sorry, it seems I led you astray. The scenario above is equivalent to what I was trying to describe originally.
                                    Units are part of a struct which are held by a vector in Model B, and can change during runtime.

                                    So, I think we are both in agreement then that the best thing would be to do as I suggested and connect one model to the other's dataChanged().

                                    Thanks!

                                    1 Reply Last reply
                                    1
                                    • E ECEC

                                      @jeremy_k , A proxy model can only have one source though, so surely this would still require connecting to model B's dataChanged to be aware of relevant changes (i.e units changed)?

                                      @jeremy_k , I'd like to avoid redeveloping a significant portion of the application to use a database. Seems overkill for what I'm trying to achieve.

                                      jeremy_kJ Offline
                                      jeremy_kJ Offline
                                      jeremy_k
                                      wrote last edited by
                                      #18

                                      @ECEC said in Bind to another model's data in a delegate:

                                      @jeremy_k , A proxy model can only have one source though, so surely this would still require connecting to model B's dataChanged to be aware of relevant changes (i.e units changed)?

                                      QAbstractProxyModel::setSourceModel() only accepts one model. That doesn't mean that a proxy must use or limit itself to this interface.

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

                                      1 Reply Last reply
                                      2

                                      • Login

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