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. Subclassing QAbstractItemModel 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 and modeltest.cpp. When you create the source model (you have something like CredentialModel* myModel = new CredentialModel(this);) add a line with new ModelTest(myModel ,this); and use it as normal. it will assert if it detects something wrong

    P.S.
    My suggestion is always to use QStandardItemModel 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 and modeltest.cpp [and add them to your project]. When you create the source model (you have something like CredentialModel* myModel = new CredentialModel(this);) add a line with new ModelTest(myModel ,this);


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.