PDA

View Full Version : Some unclarities about QAbstractItemModel and dynamic underlying data



gdiepen
19th August 2009, 12:11
At the moment I am working on a Remote Registry Editor for Windows Mobile devices under Linux. I am writing this application in PyQt4, though my question is not related to the Python part, but Qt in general, hence I am asking it here.

I would like to show the registry key structure in a QTreeView. In order to do this, I created a RegistryModel, which is subclassed from the QAbstractItemModel. Other classes I created are:
* RegistryKey. This class contains all sub keys and all values for a particular key in the registry. The RegistryModel gets the actual data from the Registrykey object that corresponds to the item in the model.
* Registry. This class takes care of all the communication with other layers of my program (i.e. communication required for fetching all keys / values of a particular key). Each of the RegistryKey objects has a reference to this object.

Because fetching the complete tree structure of the keys already takes more than 5 minutes, I would like to do this in a more dynamic way.

What I would like to do is the following:
1. User expands a registry key in the QTreeView for which the underlying model has no data yet. Whenever no data is known, the underlying model will have one subkey, namely one with the name "Fetching...".
2. The user sees one subkey under the just expanded subkey: "Fetching..."
3. The underlying RegistryKey object will fetch the data from the device, via the Registry object
4. After all data has been fetched the Registry object provides the RegistryKey object with the new data
5. The RegistryKey object first removes its only sub key (i.e. the "Fetching..." sub-key)
6. The RegistryKey object creates new RegistryKey objects for each of its sub keys and add these as children
7. The enduser must see that the "Fetching..." subkey vanishes, while the subkeys under the key he expanded should appear instead.


Everything upto and including step 4 I understand. From the signal emitted I obtain a QModelIndex, from which I can determine which RegistryKey object corresponds to the item the user selected. I can then do all the stuff I need to do to get the actual contents from the device.

After this, with step 5, I am running into problems with not knowing how to model this correctly. If I understood correctly, whenever you change the underlying model you would have to call beginInsertRows() on the RegistryModel with the corresponding QModelIndex you are about to add child-keys to, followed by the actual adding of the keys and finally a endInsertRows(). The same holds for deleting the "Fetching..." sub-key, but then with the beginRemoveRows and endRemoveRows.

My main question is now, what is the best/nicest way of determining which QModelIndex corresponds to a given RegistryKey object? My first idea was to make create a dictionary of RegistryKey -> QModelIndex, but somewhere I read that you should not keep refrences to a QModelIndex, but discard it as soon as possible after use again.

I have looked at all kinds of examples, but still have not found any that show me how to get from an object in the underlying data model back to a QModelIndex that I can use. Is there actually a way to do this correctly, or is my original setup of classes not ok for this? Is it actually OK to have an underlying data model under the QAbstractItemModel that can be changed via some other way besides the QTreeView?

If more information is needed, please let me know and I will try to provide it.

Kind regards,

Guido Diepen

franz
19th August 2009, 13:49
The QModelIndex should be used directly after being obtained and discarded directly after being used. The way to associate a certain QModelIndex with it's actual data is to use the internal pointer. When you implement the index (http://doc.trolltech.com/4.5/qabstractitemmodel.html#index)() function, you provide the caller with the QModelIndex associated with the data you want to get:

return createIndex(row, column);

You can pass the createIndex function another piece of information, which is the internalId or the internalPointer:



RegItem *item = getItem(row, column, parentItem);
return createIndex(row, column, item);

Using the internal pointer, you can also implement the parent (http://doc.trolltech.com/4.5/qabstractitemmodel.html#parent)() function.

Have a good look at the Editable Tree Model (http://doc.trolltech.com/4.5/itemviews-editabletreemodel.html) example.

gdiepen
19th August 2009, 14:39
The QModelIndex should be used directly after being obtained and discarded directly after being used. The way to associate a certain QModelIndex with it's actual data is to use the internal pointer. When you implement the index (http://doc.trolltech.com/4.5/qabstractitemmodel.html#index)() function, you provide the caller with the QModelIndex associated with the data you want to get:

return createIndex(row, column);

I have the feeling that I almost understand, but not yet quite (which is really annoying ;) ). If I understand correctly, the way you are using createIndex here, with only row and column as arguments, means that automatically QModelIndex() is used as the parent, which is the main root index, or not?



You can pass the createIndex function another piece of information, which is the internalId or the internalPointer:



RegItem *item = getItem(row, column, parentItem);
return createIndex(row, column, item);

Using the internal pointer, you can also implement the parent (http://doc.trolltech.com/4.5/qabstractitemmodel.html#parent)() function.

Have a good look at the Editable Tree Model (http://doc.trolltech.com/4.5/itemviews-editabletreemodel.html) example.

I have looked at the example and one part is particular interesting (i guess):


In the case shown in the diagram, the piece of information represented by a can be obtained using the standard model/view API:


QVariant a = model->index(0, 0, QModelIndex()).data();

Since each items holds pieces of data for each column in a given row, there can be many model indexes that map to the same TreeItem object. For example, the information represented by b can be obtained using the following code:


QVariant b = model->index(1, 0, QModelIndex()).data();

The same underlying TreeItem would be accessed to obtain information for the other model indexes in the same row as b.


I understand the above (again, almost ;) ). In my case I would not need the .data() method, but I could just proivde the model->index(......). However, this is only shown for items a and b, which are directly underneath the root node, which can be found with QModelIndex(). However, for me it would be interesting to know what the third line would be, namely accessing the data of c via the model this way (I saw that the data for c is in the second column, but I don't think that part is relevant for my question). What would be the parent you would need to give to the index method?

Is it that if in the RegistryKey object A I want to provide a QModelIndex (that could be used for telling the model, which can tell the view, about upcoming changes), I would have to determine all ancestors up till the root in my underlying data (so all Registrykey objects till the main root), after which in reverse order I use this as parent information for creating the final QModelIndex that refers to my RegistryKey object A?

gdiepen
21st August 2009, 08:52
Does anybody have an answer to the question about how to get the QModelIndex for item c?

Furthermore, can anybody confirm that the steps I described in the last two paragraphs are correct?

Kind regards,

Guido Diepen

gdiepen
4th September 2009, 09:33
Just wanted to say that with some more reading and looking into the suggested example you gave, everything is working perfectly at the moment.

Initially I had some major problems with my application segfaulting. I think this was because Qt does not like it when I have the following code in the slot connected to the expand signal of the QTreeView:

beginRemoveRows(0,0)
remove fetching node (i.e. the first node)
endRemoveRows()

beginInsertRows(0,x)
insert the actual fetched nodes
endInsertRows()

Whenever I had the above code combined in the slot, every now and then I would get segfaults. If I left the removing of the "Fetching..." node out, everything was working without a problem.

In the current implementation however, this is not a problem anymore, as the fetching is done via another process that is communicated with via Dbus. This means that from the other process I first instruct to remove the Fetching node and after that instruct for the insertion of the fetched data. Because these are two different events, they are handled separately by the eventloop now.

Small demo to show that I really have it working can be found on my website (http://www.guidodiepen.nl/2009/09/viewing-registry-keys-only-from-within-synce-kpm/).

Thanks for the help, I think I finally started to understand how the whole thing with QAbstractItemModel and QModelIndex is working.