Unsolved Crash sorting QTreeView using QSortFilterProxyModel
-
Hi
I have a QTreeView widget that I am has sorting implemented by clicking the headers. The sorting works ok, but the program after fiddling the program eventually crashes with
FATAL: qabstractitemmodel.cpp:643 - ASSERT failure in QPersistentModelIndex::~QPersistentModelIndex: "persistent model indexes corrupted", file itemmodels\qabstractitemmodel.cpp, line 643
A derived class
class CredentialModelFilter : public QSortFilterProxyModel
is used for the sorting. The less than function is/ bool CredentialModelFilter::lessThan(const QModelIndex &srcLeft, const QModelIndex &srcRight) const { // Get src model CredentialModel *pSrcModel = dynamic_cast<CredentialModel *>(sourceModel()); TreeItem *pLeftItem = pSrcModel->getItemByIndex(srcLeft); TreeItem *pRightItem = pSrcModel->getItemByIndex(srcRight); if ((pLeftItem != nullptr) && (pRightItem != nullptr)) return pLeftItem->name() < pRightItem->name(); return false; }
Taking note of http://doc.qt.io/qt-5/model-view-programming.html I tried overriding the sort function like
void CredentialModelFilter::sort(int column, Qt::SortOrder order) { emit layoutAboutToBeChanged(); QModelIndexList oldPersistantIndexes = persistentIndexList(); QSortFilterProxyModel::sort(column, order); QModelIndexList newPersistantIndexes = persistentIndexList(); changePersistentIndexList(oldPersistantIndexes, newPersistantIndexes); emit layoutChanged(); }
Unfortunately, that did not make any difference, it still crashes on a subsequent click in the QTreeView.. I tried various other permutations to no effect.
So any suggestions as to sort without messing everything up?
-
Unfortunately it looks like it's not a problem of sorting or of the proxy.
CredentialModel
is probably not implemented correctly. SubclassingQAbstractItemModel
is a minefield and if you don't do it correctly a lot of stuff will stop working. In your case, the persistent model indexes do not update correctly when the model changes and they become corrupt.A good place to start is the model test. download
modeltest.h
andmodeltest.cpp
. When you create the source model (you have something likeCredentialModel* myModel = new CredentialModel(this);
) add a line withnew ModelTest(myModel ,this);
and use it as normal. it will assert if it detects something wrongP.S.
My suggestion is always to useQStandardItemModel
and switch to a custom model only if you have serious performance needs -
@VRonin Thanks for your help.
I looked at Model_Test. Some thoughts.
The usage instructions were not very good. The .pri file mentioned was probably removed years ago.
I followed the instructions about including modeltest.h in the code but with no result.
The group of 6 files make an interesting test project. With dynamictreemodel.h/cpp being a custom QAbstractItemModel object to be tested. and tst_modeltest.cpp being a test script that you need to modify to fit your own model. So it needs a bit of work to get working, and the model I was working on passed all the tests. -
The issue I have occurs in this bit of code bellow from QAbstractItemModelPrivate::removePersistentIndexData. The Q_ASSERT_X happens. This is during a mouse press and setCurrentIndex in the QTreeView.
I put in some debug code that calls QAbstractItemModel::persistentIndexList and dumps the list to debug. I put this in the virtual CredentialModel::parent function. The only time any persistent indexes appear is during a sort, and they are removed by the time the sort function is exited.
QItemSelectionModel::setCurrentIndex is called when clicking on the QTreeView GUI object. This function creates a QPeristentModelIndex called previous. The assert seems to happen when this object is deleted on leaving scope.
void QItemSelectionModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) { ... QPersistentModelIndex previous = d->currentIndex; d->currentIndex = index; // set current before emitting selection changed below if (command != NoUpdate) select(d->currentIndex, command); // select item emit currentChanged(d->currentIndex, previous); if (d->currentIndex.row() != previous.row() || d->currentIndex.parent() != previous.parent()) emit currentRowChanged(d->currentIndex, previous); if (d->currentIndex.column() != previous.column() || d->currentIndex.parent() != previous.parent()) emit currentColumnChanged(d->currentIndex, previous); }
This is the point of the assert
void QAbstractItemModelPrivate::removePersistentIndexData(QPersistentModelIndexData *data) { if (data->index.isValid()) { int removed = persistent.indexes.remove(data->index); Q_ASSERT_X(removed == 1, "QPersistentModelIndex::~QPersistentModelIndex", "persistent model indexes corrupted"); //maybe the index was somewhat invalid? // This assert may happen if the model use changePersistentIndex in a way that could result on two // QPersistentModelIndex pointing to the same index. Q_UNUSED(removed); } ... }
-
@Sriep said in Crash sorting QTreeView using QSortFilterProxyModel:
The usage instructions were not very good.
I know, that's why I included the usage instructions above
download
modeltest.h
andmodeltest.cpp
[and add them to your project]. When you create the source model (you have something likeCredentialModel* myModel = new CredentialModel(this);
) add a line withnew ModelTest(myModel ,this);