Item Model/View Selection/ScrollTo
I'm still wondering about the recommended approach to achieve certain behaviour of a view depending on the underlaying data.
Here's my example:
- Two models, two views (model/view 1, model/view 2)
- Items of the underlaying data can by related. But they are not the same.
- Now I would like the two views to in turn select the related items. E.g. I select item 1.b in model/view 1, the underlaying logic tells me that item 2.c is the related item of model/view 2. Now view 2 should select the corresponding index and scroll to it.
I can think of two ways to achieve this:
Add an attribute to the underlying data item, like item.to_be_selected; then emit a dataChanged signal. In the data method of my model I could check for the item.to_be_selected attribute and trigger the selection from there. I wouldn't need to mess around with indexes, I would wait for the view to call my data method with the index.
Create persistent indexes and a mapping item_to_index. That seems to be easier. However, I havn't any understanding of what's the intended use of persistent indexes is. Do I screw performance and memory efficiency by using them for really large models (300000 elements)? (By the way, the performance of Qt TreeViews (even wrapped by PyQt) absolutely mindblowing ;-))
What's the recommended way to go?
Thanks in advance and cheers,
Where do you have the logic that maps related items? You could just invoke that logic every time the user selects something in a view (hint: a signal gets emitted).
You then obtain the index(es) in the other model and select+scroll to it in the associated view.
Do the related items indices have an identity relationship? ie Is item 1,1 in model one related to item 1,1 in model 2? If so then you can do this with a single source model and two proxies. Your two views then show the specialised data representation. This is a rather special case of course but if it matches your scenario then it is quite easy to do.
Thanks for your suggestions.
Unfortunately, the models are not 1:1. They hold unique objects of items each. The relation is modelled by an additional object, which holds some more details about the relation of the two items.
@ peppe: That's a good idea. But as the models can be quite different, I need to get the index of the second model from the item. (It's not always the same index as in the first model.)
I know --- in Qt, indexes are per-model. But since you have the mapper object, it's easy to ask him "I have this index in model 1, which index in model 2 does it map to?" and then implement your GUI logic on top of that.
The question is how do I get the indexes from the item object in the model 2. It's like this:
Get selected index in view 1
Get item 1 in view 1 from the commonly implemented item_from_index() method; the index knows the item ... easy done.
Get the item 2 in model 2 from my mapper object
Now I need the index in view 2 from item 2 ... here's where I'm not sure what's the best way to get the index in view 2 from the item in model 2.
I tend to the approach No. 1 of my first post. I'll let you know if there're performance implications. After thinking about it a bit it seems to be the logical way to go and not to hard to implement.
Python like Pseudo-Code of what I'm about to try out:
item = item_from_index(index)
if item.please_select_me == True:
# maybe use a custom signal to allow views to decide wether to react ...
item.please_select_me = False
The please_select_me attribute is set according to the mapping logic on request. To trigger the selection I call the dataChanged() signal.
You can connect a slot to a currentChanged/selectionChanged signal of view 1. That slot asks model 1 for an identifier of a dataset in model 2. In model 2 implement a getIndexForId method and use that index to call setCurrentItem on view 2. If you use subclasses of QStandardXxxModel, there is also a pre-defined findItems method.
Hmmm, maybe "I don't see the forest because of all the trees" (nothing against some nice Genglish ;-)) but how to I implement a getIndexForId method? I would need to store the index somewhere. I mean, it would be easy to create a dictionary (Python data structure for hash table) in my model with items as keys and the current index as values. But will the indexes be static? Or would I need to update the dictionary when my model changes due to some other influences?
Ahhhhh, when I can be sure, that no index is created without calling methods of my model, I'm in control! It should be sufficient to update this dictionary (id_to_index) after every call to createIndex() within my overriden index() and parent() methods?
Just make sure your data "knows" its position (row and probably column). With that you just call index(row, column, parentIndex). With parentIndex probably invalid (QModelIndex()), if you're not on a tree.
I see. My data knows it's row, column and parent. I'm in a tree, so I just call the getIndexForId() method for all the item's parents, starting with the parent on level 1 with invalid index as parentIndex.
No indexes are created without your model knowing about, as the only way to create a valid index is through a call to the createIndex method (which should be used from the index() method).
However, in general, a QModelIndex should not be assumed to stay valid outside the call it was retreived in or passed in as a method. They should not be stored. If you need stable indexes, use a QPersistentModelIndex instead.
However, there may be more efficient ways to create the mapping in your case. It all depends on your actual data structures though. Normally, a model index makes it easy for a model to get access to the actual item the index represents. For that, it has both the row, the column, the parent and an additional void pointer or internalId available. These should be used to map a QModelIndex back to an item in your internal data structures. Your own internal data structures should support finding the related item from there.
I would represent the information you need to look up such a related item as a custom data role from your model. The logic connected to changes in the views selection can then use that information to query for the appropriate model index for the other view.
You have your data stored in some structure in the model. You will have to find the one data set you need by that mentioned identifier. You then know out of your data the row and column and create an index on the fly. Maybe you need some more work to get the correct parent index. Do not store indexes in your data, as Andre already mentioned, these are not meant to be used permanently!