Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QAbstractItemModel: how can I associate more data with a QModelIndex than just a pointer or integer?
QtWS25 Last Chance

QAbstractItemModel: how can I associate more data with a QModelIndex than just a pointer or integer?

Scheduled Pinned Locked Moved Unsolved General and Desktop
qabstractitemmoqmodelindexinternalpointer
21 Posts 6 Posters 8.3k 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.
  • Guy GizmoG Guy Gizmo

    @mrjj Unfortunately the unique ID represents where in my data model the item is, so there's no way to create a new unique ID in the case of a collision.

    mrjjM Offline
    mrjjM Offline
    mrjj
    Lifetime Qt Champion
    wrote on last edited by
    #6

    @Guy-Gizmo
    ok. so its already "fixed" at that point.
    how many items are we talking about?

    Guy GizmoG 1 Reply Last reply
    0
    • mrjjM mrjj

      @Guy-Gizmo
      ok. so its already "fixed" at that point.
      how many items are we talking about?

      Guy GizmoG Offline
      Guy GizmoG Offline
      Guy Gizmo
      wrote on last edited by
      #7

      @mrjj Hard to say exactly. The model represents a user-editable library of data, so the number of items could range anywhere from tens to thousands.

      1 Reply Last reply
      0
      • SGaistS Offline
        SGaistS Offline
        SGaist
        Lifetime Qt Champion
        wrote on last edited by
        #8

        Hi,

        What structure do you currently use to store your data ?

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        Guy GizmoG 1 Reply Last reply
        0
        • SGaistS SGaist

          Hi,

          What structure do you currently use to store your data ?

          Guy GizmoG Offline
          Guy GizmoG Offline
          Guy Gizmo
          wrote on last edited by Guy Gizmo
          #9

          @SGaist It's nested QObject-derived classes. Their structure is similar to that of files and folders, where the container class can contain any number of other containers and items. One of my goals with this structure was to have it so that any of the objects could be deleted, recreated, or reorganized at any time, and any aspect of my application that needs to access to it uses the unique id of the object (essentially its full path in the structure) and doesn't hold on to references to these objects. I've managed to make that work, and it's resulted in a lot of flexibility in how this structure and any derivative structures can be implemented. Except I'm just not sure how to make it work with QAbstractItemModel when I can't associate a QModelIndex with an object using its address in memory.

          1 Reply Last reply
          0
          • SGaistS Offline
            SGaistS Offline
            SGaist
            Lifetime Qt Champion
            wrote on last edited by
            #10

            So a tree like structure where you have one root item and all the other children at different levels ?

            So you are implementing a model a bit like QFileSystemModel ?

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            Guy GizmoG 2 Replies Last reply
            0
            • SGaistS SGaist

              So a tree like structure where you have one root item and all the other children at different levels ?

              So you are implementing a model a bit like QFileSystemModel ?

              Guy GizmoG Offline
              Guy GizmoG Offline
              Guy Gizmo
              wrote on last edited by
              #11

              @SGaist In structure it is very much like QFileSystemModel.

              1 Reply Last reply
              0
              • SGaistS SGaist

                So a tree like structure where you have one root item and all the other children at different levels ?

                So you are implementing a model a bit like QFileSystemModel ?

                Guy GizmoG Offline
                Guy GizmoG Offline
                Guy Gizmo
                wrote on last edited by
                #12

                @SGaist Although I would have the same problem with any model whose data was ephemeral in the manner that I made mine, regardless of its structure. As long as items in your data can't be identified by a pointer value, be it an ID that fits in 32 or 64 bits, or an address in memory, you can't seem to use it properly with QAbstractItemModel.

                So far I've worked around it by using the "separate structure" method I mentioned in my OP. Specifically, I have a separate map that keys on the unique ID of the items in my model. Every time I create a QModelIndex, I create an element in that map for the unique ID that the QModelIndex represents (if that element in the map doesn't already exist). Then I just have each QModelIndex point to the elements in that map. But as I said before, there's no way for me to know when to delete anything in that map so it can grow without bound. It typically won't grow very fast but a) I don't know that for sure as I can't anticipate all use cases and b) it's still a very bad way to do this and I really want to replace it with something better.

                But unless I'm missing something (and I really hope that I am), I don't think there's any other way to do this at all!

                Maybe this is something to bring up for discussion in one of the Qt mailing lists? It really strikes me that QAbstractItemModel would be a lot more flexible and powerful if a QModelIndex could contain a QVariant, which would instantly solve this issue for me.

                1 Reply Last reply
                0
                • J Offline
                  J Offline
                  jalomic
                  wrote on last edited by
                  #13

                  What's stopping you to create your id in heap ?
                  MyStruct* id_ptr = new MyStruct(...)
                  m_cleanup_ids_stack.push_back(id_ptr)
                  return createIndex(row, column, id_ptr);

                  After that delete all items of m_cleanup_ids_stack in destructor ?

                  Guy GizmoG 1 Reply Last reply
                  0
                  • J jalomic

                    What's stopping you to create your id in heap ?
                    MyStruct* id_ptr = new MyStruct(...)
                    m_cleanup_ids_stack.push_back(id_ptr)
                    return createIndex(row, column, id_ptr);

                    After that delete all items of m_cleanup_ids_stack in destructor ?

                    Guy GizmoG Offline
                    Guy GizmoG Offline
                    Guy Gizmo
                    wrote on last edited by
                    #14

                    @jalomic That will cause two QModelIndexes that refer to the same item to not be considered equal, i.e. the == operator will return false. But I could set it up so that two equal QModelndexes get the same pointer.

                    The other problem is, when do I free all of the stack allocated ids? Whose destructor do I use?

                    J 1 Reply Last reply
                    0
                    • Guy GizmoG Guy Gizmo

                      @jalomic That will cause two QModelIndexes that refer to the same item to not be considered equal, i.e. the == operator will return false. But I could set it up so that two equal QModelndexes get the same pointer.

                      The other problem is, when do I free all of the stack allocated ids? Whose destructor do I use?

                      J Offline
                      J Offline
                      jalomic
                      wrote on last edited by jalomic
                      #15

                      @Guy-Gizmo You can use m_cleanup_ids_stack as map

                      MyStruct* id_ptr;
                      if( m_cleanup_ids_stack.find(my_long_id) != m_cleanup_ids_stack.end())
                      {
                          id_ptr = m_cleanup_ids_stack[my_long_id];
                      }
                      else
                      {
                          id_ptr =  new MyStruct(my_long_id)
                      }
                      return createIndex(row, column, id_ptr);
                      

                      The other problem is, when do I free all of the stack allocated ids? Whose destructor do I use?

                      In model destructor

                      Guy GizmoG 1 Reply Last reply
                      0
                      • J jalomic

                        @Guy-Gizmo You can use m_cleanup_ids_stack as map

                        MyStruct* id_ptr;
                        if( m_cleanup_ids_stack.find(my_long_id) != m_cleanup_ids_stack.end())
                        {
                            id_ptr = m_cleanup_ids_stack[my_long_id];
                        }
                        else
                        {
                            id_ptr =  new MyStruct(my_long_id)
                        }
                        return createIndex(row, column, id_ptr);
                        

                        The other problem is, when do I free all of the stack allocated ids? Whose destructor do I use?

                        In model destructor

                        Guy GizmoG Offline
                        Guy GizmoG Offline
                        Guy Gizmo
                        wrote on last edited by
                        #16

                        @jalomic Unfortunately this model stays around essentially for the lifetime of the application, so it will still be possible for m_cleanup_ids_stack to grow without bound.

                        1 Reply Last reply
                        0
                        • Guy GizmoG Guy Gizmo

                          I'm implemented a class derived from QAbstractItemModel. The standard way to create a QModelIndex that represents an item in my model is use QAbstractItemModel::createIndex and pass it a pointer to that item. However, I can't use that method:

                          The actual structures in my model are ephemeral and are often being destroyed and recreated, causing the internalPointer stored in any given QModelIndex to become invalid. So rather than addressing each item by its location in memory, they have what is essentially a unique ID that indicates where in the model they are, and I can consistently use that to retrieve an item. This unique ID cannot be represented by an 8 byte integer, so I cannot use it with the internalId or internalPointer methods in QModelIndex.

                          Is there any way for me to create a QModelIndex that has more data associated with it than just a pointer?

                          One idea I've played around with is creating a separate structure that contains the unique ID for each of the items in my model, and having each instance of QModelIndex point to an entry in that. But not only does that totally suck because I'm creating an entirely new entity which introduces extra complexity and opportunities for bugs, but there's no good way for me to know when entries in this structure need to be deleted. I can't delete them when items in my model are deleted as that would defeat its purpose, and there's no way for me to know when a QModelIndex is destroyed and no longer referring to an item in this structure. So that's out.

                          What on Earth can I do here? And why can't I construct a QModelIndex with a QVariant? That would easily fix this problem and make QModelIndex significantly more powerful and better to work with.

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

                          @Guy-Gizmo said:

                          The actual structures in my model are ephemeral and are often being destroyed and recreated, causing the internalPointer stored in any given QModelIndex to become invalid.

                          Model indexes are not intended to be stored anyway (see the docs for more details).

                          As long as items in your data can't be identified by a pointer value, be it an ID that fits in 32 or 64 bits, or an address in memory, you can't seem to use it properly with QAbstractItemModel.

                          Why? QSortFilterProxyModel manages just fine. It does the mapping through internal structures (vectors and hashes).

                          But as I said before, there's no way for me to know when to delete anything in that map so it can grow without bound.

                          Well, you delete from the map when the underlying data has gone away. Which you can easily catch by subscribing to the QObject::destroyed() signal.


                          From what I understand you want to expose a QObject tree through an abstract model and you want to attach additional data for each object. I see two options:

                          1. You use dynamic properties or a QObject derived class to "inject" your data directly into the objects and you refer to the object by its address when you need to construct a QModelIndex
                          2. You store your additional data in the model subclass and refer to it by the complementary QObject's address. You can clean up the entry when the QObject is destroyed, thus keeping the mapping clean.
                          class MyModel : public QAbstractItemModel
                          {
                              // ...
                          private:
                              // When inserting/constructing the model connect QObject::destroy to a lambda or a private slot
                              // to clean up the following hash
                              QHash<QObject *, MyObjectData> associatedData;
                          }
                          

                          And for example when you create the MyObjectData instances:

                          QObject * object; //< The object the data refers to
                          MyObjectData data; //< The data you want to attach
                          
                          QObject::connect(object, &QObject::destroyed, [this] (QObject * o) -> void {
                              associatedData.remove(o);
                          });
                          associatedData.insert(object, data);
                          

                          Kind regards.

                          Read and abide by the Qt Code of Conduct

                          J Guy GizmoG 2 Replies Last reply
                          0
                          • kshegunovK kshegunov

                            @Guy-Gizmo said:

                            The actual structures in my model are ephemeral and are often being destroyed and recreated, causing the internalPointer stored in any given QModelIndex to become invalid.

                            Model indexes are not intended to be stored anyway (see the docs for more details).

                            As long as items in your data can't be identified by a pointer value, be it an ID that fits in 32 or 64 bits, or an address in memory, you can't seem to use it properly with QAbstractItemModel.

                            Why? QSortFilterProxyModel manages just fine. It does the mapping through internal structures (vectors and hashes).

                            But as I said before, there's no way for me to know when to delete anything in that map so it can grow without bound.

                            Well, you delete from the map when the underlying data has gone away. Which you can easily catch by subscribing to the QObject::destroyed() signal.


                            From what I understand you want to expose a QObject tree through an abstract model and you want to attach additional data for each object. I see two options:

                            1. You use dynamic properties or a QObject derived class to "inject" your data directly into the objects and you refer to the object by its address when you need to construct a QModelIndex
                            2. You store your additional data in the model subclass and refer to it by the complementary QObject's address. You can clean up the entry when the QObject is destroyed, thus keeping the mapping clean.
                            class MyModel : public QAbstractItemModel
                            {
                                // ...
                            private:
                                // When inserting/constructing the model connect QObject::destroy to a lambda or a private slot
                                // to clean up the following hash
                                QHash<QObject *, MyObjectData> associatedData;
                            }
                            

                            And for example when you create the MyObjectData instances:

                            QObject * object; //< The object the data refers to
                            MyObjectData data; //< The data you want to attach
                            
                            QObject::connect(object, &QObject::destroyed, [this] (QObject * o) -> void {
                                associatedData.remove(o);
                            });
                            associatedData.insert(object, data);
                            

                            Kind regards.

                            J Offline
                            J Offline
                            jalomic
                            wrote on last edited by
                            #18

                            @kshegunov You forgot to notify model about data destruction ! beginRemove... etc

                            1 Reply Last reply
                            0
                            • kshegunovK kshegunov

                              @Guy-Gizmo said:

                              The actual structures in my model are ephemeral and are often being destroyed and recreated, causing the internalPointer stored in any given QModelIndex to become invalid.

                              Model indexes are not intended to be stored anyway (see the docs for more details).

                              As long as items in your data can't be identified by a pointer value, be it an ID that fits in 32 or 64 bits, or an address in memory, you can't seem to use it properly with QAbstractItemModel.

                              Why? QSortFilterProxyModel manages just fine. It does the mapping through internal structures (vectors and hashes).

                              But as I said before, there's no way for me to know when to delete anything in that map so it can grow without bound.

                              Well, you delete from the map when the underlying data has gone away. Which you can easily catch by subscribing to the QObject::destroyed() signal.


                              From what I understand you want to expose a QObject tree through an abstract model and you want to attach additional data for each object. I see two options:

                              1. You use dynamic properties or a QObject derived class to "inject" your data directly into the objects and you refer to the object by its address when you need to construct a QModelIndex
                              2. You store your additional data in the model subclass and refer to it by the complementary QObject's address. You can clean up the entry when the QObject is destroyed, thus keeping the mapping clean.
                              class MyModel : public QAbstractItemModel
                              {
                                  // ...
                              private:
                                  // When inserting/constructing the model connect QObject::destroy to a lambda or a private slot
                                  // to clean up the following hash
                                  QHash<QObject *, MyObjectData> associatedData;
                              }
                              

                              And for example when you create the MyObjectData instances:

                              QObject * object; //< The object the data refers to
                              MyObjectData data; //< The data you want to attach
                              
                              QObject::connect(object, &QObject::destroyed, [this] (QObject * o) -> void {
                                  associatedData.remove(o);
                              });
                              associatedData.insert(object, data);
                              

                              Kind regards.

                              Guy GizmoG Offline
                              Guy GizmoG Offline
                              Guy Gizmo
                              wrote on last edited by
                              #19

                              @kshegunov said:

                              Model indexes are not intended to be stored anyway (see the docs for more details).

                              True, but QPersistentModelIndex has the same issue. If I hold on to a QPersistentModelIndex that's still valid but the actual object its pointer refers to has been deleted, then it's still no good, because:

                              Well, you delete from the map when the underlying data has gone away. Which you can easily catch by subscribing to the QObject::destroyed() signal.

                              That's the thing. When I say the objects in my data are ephemeral, what I mean is that they're constantly being destroyed and recreated even though the item in the model with that particular path / unique ID is still valid. There's a whole lot of machinery happening behind the scenes that neither QAbstractItemModel nor any other part of my app is supposed to know about. These objects may be deleted, and then attempting to access one at a particular key may trigger it to be recreated on demand. Or any number of items in the structure may be rearranged in memory but without actually changing the data it represents. The whole point of me setting it up that way is that the actual logical structure of the data and the physical arrangement and implementation of it are conceptually completely separate. The latter is abstracted away by the interface of the former.

                              So the only safe way to refer to or access any of the objects in this data is by its path / unique ID. And I can't use the object being destroyed as a sign that the unique ID / path that object was associated with no longer exists. The only way to know that an object no longer exists is to ask for it using one of my accessor functions and see that it's not there. My problem seems to be that QAbstractItemModel makes certain assumptions about how your data is implemented that runs contrary to what I have.

                              From what I understand you want to expose a QObject tree through an abstract model and you want to attach additional data for each object. I see two options:

                              Not exactly. I want to be able to identify a particular item in my data through something more complex than an integer or memory address. Using QObject properties doesn't help because I need this ID to get the object in the first place, and the ID can't be represented by an integer.

                              So,

                              That all being said, I think I have a solution. When I said "the only way to know that an object no longer exists is to ask for it using one of my accessor functions and see that it's not there" I realized I could write a function that sweeps through my structure of IDs, finds the ones that don't refer to a valid object any more, and then delete them from the structure. I can then call this function at regular intervals or other points when it's safe to do so.

                              I'm still kind of new to using QAbstractItemModel, though, and if I knew what I know now back when I started implementing this, I would have chosen to do things differently. It's pretty clear to me now that the way I implemented my data just simply doesn't jive with QAbstractItemModel.

                              kshegunovK 1 Reply Last reply
                              0
                              • Guy GizmoG Guy Gizmo

                                I'm implemented a class derived from QAbstractItemModel. The standard way to create a QModelIndex that represents an item in my model is use QAbstractItemModel::createIndex and pass it a pointer to that item. However, I can't use that method:

                                The actual structures in my model are ephemeral and are often being destroyed and recreated, causing the internalPointer stored in any given QModelIndex to become invalid. So rather than addressing each item by its location in memory, they have what is essentially a unique ID that indicates where in the model they are, and I can consistently use that to retrieve an item. This unique ID cannot be represented by an 8 byte integer, so I cannot use it with the internalId or internalPointer methods in QModelIndex.

                                Is there any way for me to create a QModelIndex that has more data associated with it than just a pointer?

                                One idea I've played around with is creating a separate structure that contains the unique ID for each of the items in my model, and having each instance of QModelIndex point to an entry in that. But not only does that totally suck because I'm creating an entirely new entity which introduces extra complexity and opportunities for bugs, but there's no good way for me to know when entries in this structure need to be deleted. I can't delete them when items in my model are deleted as that would defeat its purpose, and there's no way for me to know when a QModelIndex is destroyed and no longer referring to an item in this structure. So that's out.

                                What on Earth can I do here? And why can't I construct a QModelIndex with a QVariant? That would easily fix this problem and make QModelIndex significantly more powerful and better to work with.

                                jerome_isAviableJ Offline
                                jerome_isAviableJ Offline
                                jerome_isAviable
                                wrote on last edited by
                                #20

                                Hi @Guy-Gizmo,
                                I think that the design pattern of QModelIndex has to be ephemeral and offer a great way to handle datas structure between your view and your model. So the idea (i think) of this design, is to let Qt do his job with his QModelIndex and just provide a data structure "well formed". QModelIndex is just something for call back from the view... the model provide it back also from your datas structure (and do it each time you need). The concept of QModelIndex is definitly not to have static adresses.

                                The idea of QModelIndex is also to be able to handle part of datas from a big source from anywhere (database or streams or... whatever datas come from) in time when you need it (and not hold that all the time).

                                Maybe you have a good reason to change the design pattern of Qt by do a special (and not indicate) use of QModelIndex, but i not understand this point. I tried before to do same as you because i was not well understood the idea of this design pattern around QModelIndex, who is a good design pattern, and then i used it the wrong way and loose time for finally do nothing more better than Qt QModelIndex does. I not said ti is you situation, because actually, i not understand the idea.

                                But, it may be an idea to reconsider your design pattern due to existing Qt QModelIndex design pattern, and use Qt QModelIndex has expected in the idea to achieve what you a re searching to do with your view and your model.

                                I will follow this thread for maybe lmearn something more and see the idea you are trusting and trying to achieve.

                                good luck.

                                1 Reply Last reply
                                0
                                • Guy GizmoG Guy Gizmo

                                  @kshegunov said:

                                  Model indexes are not intended to be stored anyway (see the docs for more details).

                                  True, but QPersistentModelIndex has the same issue. If I hold on to a QPersistentModelIndex that's still valid but the actual object its pointer refers to has been deleted, then it's still no good, because:

                                  Well, you delete from the map when the underlying data has gone away. Which you can easily catch by subscribing to the QObject::destroyed() signal.

                                  That's the thing. When I say the objects in my data are ephemeral, what I mean is that they're constantly being destroyed and recreated even though the item in the model with that particular path / unique ID is still valid. There's a whole lot of machinery happening behind the scenes that neither QAbstractItemModel nor any other part of my app is supposed to know about. These objects may be deleted, and then attempting to access one at a particular key may trigger it to be recreated on demand. Or any number of items in the structure may be rearranged in memory but without actually changing the data it represents. The whole point of me setting it up that way is that the actual logical structure of the data and the physical arrangement and implementation of it are conceptually completely separate. The latter is abstracted away by the interface of the former.

                                  So the only safe way to refer to or access any of the objects in this data is by its path / unique ID. And I can't use the object being destroyed as a sign that the unique ID / path that object was associated with no longer exists. The only way to know that an object no longer exists is to ask for it using one of my accessor functions and see that it's not there. My problem seems to be that QAbstractItemModel makes certain assumptions about how your data is implemented that runs contrary to what I have.

                                  From what I understand you want to expose a QObject tree through an abstract model and you want to attach additional data for each object. I see two options:

                                  Not exactly. I want to be able to identify a particular item in my data through something more complex than an integer or memory address. Using QObject properties doesn't help because I need this ID to get the object in the first place, and the ID can't be represented by an integer.

                                  So,

                                  That all being said, I think I have a solution. When I said "the only way to know that an object no longer exists is to ask for it using one of my accessor functions and see that it's not there" I realized I could write a function that sweeps through my structure of IDs, finds the ones that don't refer to a valid object any more, and then delete them from the structure. I can then call this function at regular intervals or other points when it's safe to do so.

                                  I'm still kind of new to using QAbstractItemModel, though, and if I knew what I know now back when I started implementing this, I would have chosen to do things differently. It's pretty clear to me now that the way I implemented my data just simply doesn't jive with QAbstractItemModel.

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

                                  @Guy-Gizmo said:

                                  The whole point of me setting it up that way is that the actual logical structure of the data and the physical arrangement and implementation of it are conceptually completely separate. The latter is abstracted away by the interface of the former.

                                  Then I think the cleanest solution is to have a tree (or w/e) that represents your logical structure, and that will abstract the whole physical layer. For a simple example you can look at the docs - the simple tree model.
                                  You can put anything in that tree and it's basically your "data interface". It also shouldn't care that the physical nodes are destroyed as long as there's a logical node living. This way you don't have with model indexes and things getting destroyed in the background, because it's the middleman's responsibility. You should hover take care to notify the model when a node/leaf from the "logical structure" tree is destroyed (through the appropriate signals), so views can make proper adjustments.

                                  I hope that helps somewhat.

                                  Kind regards.

                                  Read and abide by the Qt Code of Conduct

                                  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