Qt proxy models and their friends

When developing applications in Qt or PyQt/PySide2, sooner or later you will face the need to create your own implementation QAbstractItemModel and/or QAbstractProxyModel. I want to talk about one specific problem that I encountered while developing a proxy model with the ability to group tree objects. .

TL;DR

I implemented a proxy model based on QAbstractProxyModel, when I tried to edit elements of the proxy model I got an error edit: editing failed I didn’t find a solution on the Internet, but I figured out that this error can be fixed if you implement the buddy method in the proxy model.

Now it's a long story

It is worth saying why there may be a need to develop your own model based on QAbstractItemModel or QAbstractProxyModel, and why there are not enough standard implementations.

The answer is very simple – it would seem – when the capabilities of ready-made implementations are not enough. Qt doesn't provide many implemented models out of the box. And the standard advice from the documentation is to take a ready-made model and modify its behavior to suit your needs. But there are tasks for which standard models are not suitable at all. For example, there is a QSortFilterProxyModel model (based on QAbstractProxyModel), which can be used to implement search for objects in a tree (the filter is in the name about this), but you will not be able to implement incremental search to significantly reduce search time using this model. Or, another example, you need to implement grouping of objects based on attributes or allow the user to arbitrarily group objects in the source tree. In these cases, you will have to implement your proxy model based on QAbstractProxyModel.

How to implement it correctly – we can talk about this separately. Today I wanted to talk about an error that you may encounter if you implement the proxy model from the last example:

edit: editing failed

Yes, here is such a laconic error from the depths of Qt.

It occurs when you try to edit objects, for example, in QTreeView (I will further call it view, because 'performance'too formal in my opinion) via F2 or others EditTriggers. Moreover, your setData method is not even called in this case.

Common advice from the Internet advises (pardon the tautology) to set the correct flags for your objects. In our case, we must implement the method like this flags for the proxy model, in order for it to return the correct QModelIndex (hereinafter I will call them indexes for simplicity) – ItemIsEnabled and ItemIsEditable must be set for them. But this may not help you. Even so – you will notice that for some indexes setData is called and editing works without problems, but for others it does not.

Further searches on the Internet do not give intelligible results, and you try to read the Qt source code – everything is simple and straightforward there. One of the checks is related to the fact that your index is in the correct state (IsValid == true), another that the view is no longer in editing mode, and several others that you, for example, cannot check from the outside. And you check your index – it is correct, with the necessary flags, you check the view – it is not in editing mode and something else is checked, because something needs to be done. But there is no result – editing does not work.

Hidden text

On some Internets they also advise setting setCurrentIndex before calling the edit method of QAbstractItemView and supposedly this helped someone – but this advice is about something else and does not apply to our problem.

Then you frantically start re-reading the documentation for QAbstractProxyModel – without results, then read the documentation for QAbstractItemModel in the same way and come across a description of the method buddy:

Returns a model index for the buddy of the item represented by index. When the user wants to edit an item, the view will call this function to check whether another item in the model should be edited instead. Then, the view will construct a delegate using the model index returned by the buddy item.

The default implementation of this function has each item as its own buddy.

And you remember that you saw the use of this method when you studied the source code of QAbstractItemView.

This gets you thinking and you go back to the QAbstractProxyModel documentation and see the following (colorful nothing):

Reimplements: QAbstractItemModel::buddy(const QModelIndex &index) const.

We have to take up the Qt source code again and look for how this method is implemented – it turns out that in its implementation it uses a pair of mapToSource and mapFromSource to obtain buddy index. And then everything comes together in your head:
For proxy indexes that have an index from the source model, editing will work because mapToSource will return the correct index from the source model, with the necessary flags, and mapFromSource will return the correct proxy index for it. And for proxy indexes for which there are no indexes in the original model (and we remember that we wanted to create custom groups), editing will not work, because mapToSource in this case can only return an empty index with IsValid == false.

And by adding the simplest implementation of the buddy method (which, for example, returns the resulting proxy index), you will get working editing of objects in your view.

This banal story tells us once again that you need to carefully read the documentation and not only for what you are working with at the moment, but also a little nearby. She also says that you need to think with your head more often.

Resume

If you create a proxy model based on QAbstractProxyModel, create objects in this model that are not in the original model and want them to be editable in your view, then you must (among other things):

  1. Implement QAbstractProxyModel::flags on these objects so that it returns the ItemIsEnabled and ItemIsEditable flags.

  2. Implement QAbstractProxyModel::setData, which will handle requests to change objects.

  3. Implement QAbstractProxyModel::buddy so that it returns the correct index for objects (indexes) that are not in the source model.

Thank you for your attention.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *