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 401 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 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 Offline
      JonBJ Offline
      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 Offline
          JonBJ Offline
          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 Offline
              JonBJ Offline
              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 Offline
                  JonBJ Offline
                  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