Problem with QTreeView and QAbstractItemModel
-
Windows. Qt 5.15
I have a QTreeView and QAbstractItemModel. When I remove rows from the model, it doesn't appear that things are properly updated. At some point, Qt calls parent() with a QModelIndex pointing to one of my objects that has been deleted. Calling isValid on the QModelIndex returns valid.
I call begin/end remove rows
beginRemoveRows(parentIndex, row, row); parentItem->RemoveChild(pItem); endRemoveRows();I even added an
emit layoutChanged();for good measure.
I added a call to persistentIndexList() in the parent method and discovered that the problem QModelIndex is in the list of persistent model indexes returned by persistentIndexList(). These aren't being created by me. The number of indexes returned is the same as the number of rows that I have in the tree. It appears that a persistent model index is created when I call QTreeView::expand. The item that I expand seems to be put into the persistent model list.
I'm going to see what happens when I collapse the item before removing it, but it seems like I shouldn't have to do this. Everything I can find in the documentation says that persistent indexes are only created by user code, although the QT code seems to show otherwise.
-
Windows. Qt 5.15
I have a QTreeView and QAbstractItemModel. When I remove rows from the model, it doesn't appear that things are properly updated. At some point, Qt calls parent() with a QModelIndex pointing to one of my objects that has been deleted. Calling isValid on the QModelIndex returns valid.
I call begin/end remove rows
beginRemoveRows(parentIndex, row, row); parentItem->RemoveChild(pItem); endRemoveRows();I even added an
emit layoutChanged();for good measure.
I added a call to persistentIndexList() in the parent method and discovered that the problem QModelIndex is in the list of persistent model indexes returned by persistentIndexList(). These aren't being created by me. The number of indexes returned is the same as the number of rows that I have in the tree. It appears that a persistent model index is created when I call QTreeView::expand. The item that I expand seems to be put into the persistent model list.
I'm going to see what happens when I collapse the item before removing it, but it seems like I shouldn't have to do this. Everything I can find in the documentation says that persistent indexes are only created by user code, although the QT code seems to show otherwise.
@james-b-s Hi James,
It sounds like you’re running into the classic issue with persistent indexes in QAbstractItemModel. A common workaround is to temporarily collapse the item before removing it, as you mentioned. Another approach is to use utility tools for model debugging to track which indexes are still active and ensure they’re properly cleared before calling endRemoveRows().
These tools can help visualize the model structure and persistent indexes, making it easier to identify why some indexes remain valid after removal.
Hope this helps you debug the issue more efficiently!
-
@james-b-s Hi James,
It sounds like you’re running into the classic issue with persistent indexes in QAbstractItemModel. A common workaround is to temporarily collapse the item before removing it, as you mentioned. Another approach is to use utility tools for model debugging to track which indexes are still active and ensure they’re properly cleared before calling endRemoveRows().
These tools can help visualize the model structure and persistent indexes, making it easier to identify why some indexes remain valid after removal.
Hope this helps you debug the issue more efficiently!
-
J james b-s has marked this topic as solved
-
It appears that the problem is a bit more complicated that that. Collapsing things seem to work better, but there is still a problem. It also appears that selected items create persistent indexes, so I now make sure that items are both unselected and collapsed. Things work a bit better, but I'm still running into the same problem, just not as often.
Right now, parent() is called with a QModelIndex that shouldn't be there, when Qt is trying to determine what is on the screen.
-
Got a minimal working example?
I'm not seeing a problem removing a row in an expanded tree, with a persistent index, testing with Qt 5.15.14 and 6.9.2.
#include <QApplication> #include <QTreeView> #include <QStandardItemModel> #include <QStandardItem> #include <QTimer> #include <QPersistentModelIndex> #include <QDebug> int main(int argc, char *argv[]) { QApplication app(argc, argv); QTreeView view; QStandardItemModel model; QStandardItem *parentItem{new QStandardItem("parent")}; QStandardItem *childItem{new QStandardItem("child")}; parentItem->appendRow(childItem); model.appendRow(parentItem); view.setModel(&model); view.expandAll(); view.setSelectionMode(QAbstractItemView::SelectionMode::MultiSelection); view.selectAll(); QPersistentModelIndex p{childItem->index()}; qDebug() << p.isValid() << p.data(); view.show(); QTimer::singleShot(1000, [&]() { parentItem->removeRow(0); qDebug() << p.isValid(); }); return app.exec(); }QAbstractItemModelTester is worth a look.
-
It turns out I also have to reset the current index in the selection model.
summary:- Collapse row being removed and all descendant rows
- Unselect row being removed and all descendant rows (not sure this was necessary)
- Clear the current index for the selection model.
-
Adding
view.setCurrentIndex(childItem.index())to the example posted above still doesn't exhibit the described behavior. The item and any children delegate instances are removed from the view without collapsing, deselecting, clearing the current index, or other mitigations.Does the example work as expected for you?