Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QSortFilterProxyModel has an internal index failure that does not map to the source correctly



  • QSortFilterProxyModel seems to be failing to retrieve its own internal data when mapping to a source model QFileSystemModel in my case. I have been able to condense it down to as small as I can get. I have not had issues with QSortFilterProxyModel up to this point, so I have to assume I am doing something wrong.

    This is the function where I notice the internal structure in QSortFilterProxyModel seems to be pointing to garbage:

    QModelIndex QSortFilterProxyModelPrivate::proxy_to_source(const QModelIndex &proxy_index) const
    {
        if (!proxy_index.isValid())
            return QModelIndex(); // for now; we may want to be able to set a root index later
        if (proxy_index.model() != q_func()) {
            qWarning("QSortFilterProxyModel: index from wrong model passed to mapToSource");
            Q_ASSERT(!"QSortFilterProxyModel: index from wrong model passed to mapToSource");
            return QModelIndex();
        }
        IndexMap::const_iterator it = index_to_iterator(proxy_index);
        Mapping *m = it.value();
        if ((proxy_index.row() >= m->source_rows.size()) || (proxy_index.column() >= m->source_columns.size()))
            return QModelIndex();
        int source_row = m->source_rows.at(proxy_index.row());
        int source_col = m->source_columns.at(proxy_index.column());
        return model->index(source_row, source_col, it.key());
    }
    

    The "m" variable seems to be looking wrong at this point when it crashes on me.

    Here is code which reproduces:
    tableproxymodel.h:

    #include <QAbstractProxyModel>
    #include <QSortFilterProxyModel>
    #include <QAbstractItemModel>
    #include <QModelIndex>
    #include <QFileSystemModel>
    
    #include <QDebug>
    
    #ifndef TABLEPROXYMODEL_H
    #define TABLEPROXYMODEL_H
    
    class SortFilterProxyModel : public QSortFilterProxyModel
    {
        Q_OBJECT
    
    public:
        SortFilterProxyModel(QObject* parent)
            : QSortFilterProxyModel(parent)
        {
    
        }
    };
    
    class FileSystemModel : public QFileSystemModel
    {
        Q_OBJECT
    
        Q_PROPERTY(QString rootPath READ getPathRoot WRITE setPathRoot NOTIFY rootPathChanged)
        Q_PROPERTY(QModelIndex rootIndex READ rootIndex NOTIFY rootIndexChanged)
        Q_PROPERTY(QAbstractItemModel* proxy READ proxy)
    public:
        FileSystemModel(QObject* parent=nullptr)
            : QFileSystemModel(parent)
            , m_proxy(nullptr)
        {
            setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
    
            m_proxy = new SortFilterProxyModel(this);
            m_proxy->setSourceModel(this);
        }
        QString getPathRoot() const
        {
            return rootPath();
        }
        QModelIndex rootIndex() const
        {
            //return index(rootPath());
            return m_proxy->mapFromSource(index(rootPath()));
        }
    
        QAbstractItemModel* proxy(){
            return m_proxy;
        }
    
    public slots:
        void setPathRoot(QString rootPath)
        {
            if (m_rootPath == rootPath)
                return;
    
            beginResetModel();
            m_rootPath = rootPath;
            setRootPath(rootPath);
            endResetModel();
    
            emit rootPathChanged(m_rootPath);
            emit rootIndexChanged(rootIndex());
        }
    
    signals:
        void rootPathChanged(QString rootPath);
        void rootIndexChanged(QModelIndex rootIndex);
    
    protected:
        QString m_rootPath;
        QModelIndex m_rootIndex;
    
        SortFilterProxyModel* m_proxy;
    };
    
    class FileSystemTableProxyModel : public QAbstractProxyModel
    {
        Q_OBJECT
    
        Q_PROPERTY(QAbstractItemModel* model READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged)
        Q_PROPERTY(QModelIndex rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged)
    public:
        FileSystemTableProxyModel(QObject* parent=nullptr)
            : QAbstractProxyModel(parent)
        {
        }
    
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
        {
            if(index.model() != this || !index.isValid())
                return QVariant();
    
            //auto sourcemodel = qobject_cast<QFileSystemModel*>(sourceModel());
            auto sourcemodel = sourceModel();
            if(!sourcemodel)
                return QVariant();
    
            auto sourceindex = mapToSource(index);
            if(!sourceindex.isValid())
                return QVariant();
    
            if(role == Qt::DisplayRole){
                switch(index.column()){
                    case 0:
                        return sourcemodel->data(sourceindex, QFileSystemModel::FileNameRole);
                    case 1:
                        return sourcemodel->data(sourceindex, QFileSystemModel::FilePathRole);
                    case 2:
                        return QString().setNum(sourcemodel->data(sourceindex, QFileSystemModel::FilePermissions).toInt(), 16);
                    case 3:
                        return "dummy";
                    default:
                        return QVariant();
                }
            }
    
            return sourcemodel->data(sourceindex, role);
        }
        QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
        {
            Q_UNUSED(parent)
    
            if(row < 0 || row >= rowCount())
                return QModelIndex();
            if(column < 0 || column >= columnCount())
                return QModelIndex();
    
            return createIndex(row, column);
        }
        QModelIndex parent(const QModelIndex &index) const override
        {
            Q_UNUSED(index)
    
            return QModelIndex();
        }
        int rowCount(const QModelIndex &parent = QModelIndex()) const override
        {
            Q_UNUSED(parent)
    
            return sourceModel()->rowCount(m_rootIndex);
        }
        int columnCount(const QModelIndex &parent = QModelIndex()) const override
        {
            Q_UNUSED(parent)
    
            return 4;
        }
    
        QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
        {
            if(sourceIndex.model() != sourceModel() || !sourceIndex.isValid())
                return QModelIndex();
    
            auto proxyindex = index(sourceIndex.row(), sourceIndex.column());
            if(!proxyindex.isValid())
                return QModelIndex();
    
            return proxyindex;
        }
        QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
        {
            if(proxyIndex.model() != this || !proxyIndex.isValid())
                return QModelIndex();
    
            //if(proxyIndex == QModelIndex())
            //    return m_rootIndex;
    
            //if(!proxyIndex.isValid())
            //    return QModelIndex();
    
            auto sourceindex = sourceModel()->index(proxyIndex.row(), proxyIndex.column(), m_rootIndex);
            if(!sourceindex.isValid())
                return QModelIndex();
    
            return sourceindex;
        }
    
        QModelIndex rootIndex() const
        {
            return m_rootIndex;
        }
    
        QAbstractItemModel* sourceModel() const
        {
            return QAbstractProxyModel::sourceModel();
        }
    
    public slots:
        void setRootIndex(const QModelIndex& rootindex)
        {
            if (m_rootIndex == rootindex)
                return;
    
            if(sourceModel() != rootindex.model())
                return;
    
            beginResetModel();
            m_rootIndex = rootindex;
            endResetModel();
    
            emit rootIndexChanged();
        }
    
        void setSourceModel(QAbstractItemModel* model) override
        {
            if (QAbstractProxyModel::sourceModel() == model)
                return;
    
            beginResetModel();
    
            auto oldmodel = sourceModel();
            if(oldmodel){
                disconnect(oldmodel, &QAbstractItemModel::columnsAboutToBeInserted, this, &FileSystemTableProxyModel::on_columnsAboutToBeInserted);
                disconnect(oldmodel, &QAbstractItemModel::columnsInserted, this, &FileSystemTableProxyModel::on_columnsInserted);
                disconnect(oldmodel, &QAbstractItemModel::columnsAboutToBeMoved, this, &FileSystemTableProxyModel::on_columnsAboutToBeMoved);
                disconnect(oldmodel, &QAbstractItemModel::columnsMoved, this, &FileSystemTableProxyModel::on_columnsMoved);
                disconnect(oldmodel, &QAbstractItemModel::columnsAboutToBeRemoved, this, &FileSystemTableProxyModel::on_columnsAboutToBeRemoved);
                disconnect(oldmodel, &QAbstractItemModel::columnsRemoved, this, &FileSystemTableProxyModel::on_columnsRemoved);
    
                disconnect(oldmodel, &QAbstractItemModel::rowsAboutToBeInserted, this, &FileSystemTableProxyModel::on_rowsAboutToBeInserted);
                disconnect(oldmodel, &QAbstractItemModel::rowsInserted, this, &FileSystemTableProxyModel::on_rowsInserted);
                disconnect(oldmodel, &QAbstractItemModel::rowsAboutToBeMoved, this, &FileSystemTableProxyModel::on_rowsAboutToBeMoved);
                disconnect(oldmodel, &QAbstractItemModel::rowsMoved, this, &FileSystemTableProxyModel::on_rowsMoved);
                disconnect(oldmodel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &FileSystemTableProxyModel::on_rowsAboutToBeRemoved);
                disconnect(oldmodel, &QAbstractItemModel::rowsRemoved, this, &FileSystemTableProxyModel::on_rowsRemoved);
    
                disconnect(oldmodel, &QAbstractItemModel::layoutAboutToBeChanged, this, &FileSystemTableProxyModel::on_layoutAboutToBeChanged);
                disconnect(oldmodel, &QAbstractItemModel::layoutChanged, this, &FileSystemTableProxyModel::on_layoutChanged);
    
                disconnect(oldmodel, &QAbstractItemModel::modelAboutToBeReset, this, &FileSystemTableProxyModel::on_modelAboutToBeReset);
                disconnect(oldmodel, &QAbstractItemModel::modelReset, this, &FileSystemTableProxyModel::on_modelReset);
    
                disconnect(oldmodel, &QAbstractItemModel::dataChanged, this, &FileSystemTableProxyModel::on_dataChanged);
                disconnect(oldmodel, &QAbstractItemModel::headerDataChanged, this, &FileSystemTableProxyModel::on_headerDataChanged);
            }
    
            QAbstractProxyModel::setSourceModel(model);
            auto newmodel = sourceModel();
            if(newmodel){
                connect(newmodel, &QAbstractItemModel::columnsAboutToBeInserted, this, &FileSystemTableProxyModel::on_columnsAboutToBeInserted);
                connect(newmodel, &QAbstractItemModel::columnsInserted, this, &FileSystemTableProxyModel::on_columnsInserted);
                connect(newmodel, &QAbstractItemModel::columnsAboutToBeMoved, this, &FileSystemTableProxyModel::on_columnsAboutToBeMoved);
                connect(newmodel, &QAbstractItemModel::columnsMoved, this, &FileSystemTableProxyModel::on_columnsMoved);
                connect(newmodel, &QAbstractItemModel::columnsAboutToBeRemoved, this, &FileSystemTableProxyModel::on_columnsAboutToBeRemoved);
                connect(newmodel, &QAbstractItemModel::columnsRemoved, this, &FileSystemTableProxyModel::on_columnsRemoved);
    
                connect(newmodel, &QAbstractItemModel::rowsAboutToBeInserted, this, &FileSystemTableProxyModel::on_rowsAboutToBeInserted);
                connect(newmodel, &QAbstractItemModel::rowsInserted, this, &FileSystemTableProxyModel::on_rowsInserted);
                connect(newmodel, &QAbstractItemModel::rowsAboutToBeMoved, this, &FileSystemTableProxyModel::on_rowsAboutToBeMoved);
                connect(newmodel, &QAbstractItemModel::rowsMoved, this, &FileSystemTableProxyModel::on_rowsMoved);
                connect(newmodel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &FileSystemTableProxyModel::on_rowsAboutToBeRemoved);
                connect(newmodel, &QAbstractItemModel::rowsRemoved, this, &FileSystemTableProxyModel::on_rowsRemoved);
    
                connect(newmodel, &QAbstractItemModel::layoutAboutToBeChanged, this, &FileSystemTableProxyModel::on_layoutAboutToBeChanged);
                connect(newmodel, &QAbstractItemModel::layoutChanged, this, &FileSystemTableProxyModel::on_layoutChanged);
    
                connect(newmodel, &QAbstractItemModel::modelAboutToBeReset, this, &FileSystemTableProxyModel::on_modelAboutToBeReset);
                connect(newmodel, &QAbstractItemModel::modelReset, this, &FileSystemTableProxyModel::on_modelReset);
    
                connect(newmodel, &QAbstractItemModel::dataChanged, this, &FileSystemTableProxyModel::on_dataChanged);
                connect(newmodel, &QAbstractItemModel::headerDataChanged, this, &FileSystemTableProxyModel::on_headerDataChanged);
            }
    
            endResetModel();
    
            auto tindex = QModelIndex();
            setRootIndex(tindex);
    
            emit sourceModelChanged();
        }
    
    private slots:
        // slots from hell
        void on_columnsAboutToBeInserted(const QModelIndex &parent, int first, int last){
            beginInsertColumns(mapToSource(parent), first, last);
        }
        void on_columnsInserted(const QModelIndex &parent, int first, int last){
            endInsertColumns();
        }
        void on_columnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn){
            beginMoveColumns(mapToSource(sourceParent), sourceStart, sourceEnd, mapToSource(destinationParent), destinationColumn);
        }
        void on_columnsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column){
            endMoveColumns();
        }
        void on_columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last){
            beginRemoveColumns(mapToSource(parent), first, last);
        }
        void on_columnsRemoved(const QModelIndex &parent, int first, int last){
            endRemoveColumns();
        }
    
        void on_rowsAboutToBeInserted(const QModelIndex &parent, int first, int last){
            beginInsertRows(mapFromSource(parent), first, last);
        }
        void on_rowsInserted(const QModelIndex &parent, int first, int last){
            endInsertRows();
        }
        void on_rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow){
            beginMoveRows(mapFromSource(sourceParent), sourceStart, sourceEnd, mapFromSource(destinationParent), destinationRow);
        }
        void on_rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row){
            endMoveRows();
        }
        void on_rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last){
            beginRemoveRows(mapFromSource(parent), first, last);
        }
        void on_rowsRemoved(const QModelIndex &parent, int first, int last){
            endRemoveRows();
        }
    
        void on_layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint){
            QList<QPersistentModelIndex> proxyparents;
            for(auto parent: parents){
                proxyparents.append(mapFromSource(parent));
            }
            emit layoutAboutToBeChanged(proxyparents, hint);
        }
        void on_layoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint){
            QList<QPersistentModelIndex> proxyparents;
            for(auto parent: parents){
                proxyparents.append(mapFromSource(parent));
            }
            emit layoutChanged(proxyparents, hint);
        }
        void on_modelAboutToBeReset(){
            beginResetModel();
        }
        void on_modelReset(){
            endResetModel();
        }
    
        void on_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles){
            emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight), roles);
        }
        void on_headerDataChanged(Qt::Orientation orientation, int first, int last){
            emit headerDataChanged(orientation, first, last);
        }
    
    
    signals:
        void rootIndexChanged();
        void sourceModelChanged();
    
    private:
        QModelIndex m_rootIndex;
    };
    
    #endif // TABLEPROXYMODEL_H
    

    main.cpp:

    #include <QGuiApplication>
    #include <QApplication>
    #include <QQmlApplicationEngine>
    
    #include <tableproxymodel.h>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        //QGuiApplication app(argc, argv);
        QApplication app(argc, argv);
    
        qmlRegisterType<FileSystemModel>("Models",1,0,"FileSystemModel");
        qmlRegisterType<FileSystemTableProxyModel>("Models",1,0,"FileSystemTableProxyModel");
    
        QQmlApplicationEngine engine;
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
        return app.exec();
    }
    

    qml:

    import QtQuick 2.12
    import QtQuick.Window 2.12
    
    import Models 1.0
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Proxy Model Testing")
    
        FileSystemModel {
            id: filesystemmodel
            rootPath: "/"
        }
    
        FileSystemTableProxyModel {
            id: tableproxymodel
    
            model: filesystemmodel.proxy
            rootIndex: filesystemmodel.rootIndex
        }
    
        TableView {
            anchors.fill: parent
    
            model: tableproxymodel
    
            delegate: Item {
                implicitWidth: 100
                implicitHeight: 25
                Rectangle {
                    anchors.fill: parent
    
                    color: "blue"
                }
    
                Text {
                    text: display
                }
    
                Component.onCompleted: {
                    console.log("tableproxymodel:delegate")
                }
            }
    
            Component.onCompleted: {
                console.log(model.rowCount())
            }
        }
    }
    

    I will point out, that I originally developed this code having FileSystemTableProxyModel talk directly to FileSystemModel and it never had issues. Once I noticed I was having proxy issues in other code I injected the proxy into FileSystemModel to see if it crashes at the same place. It does. So I have to assume there is either a usage problem on my end with QSortFilterProxyModel, or there is an issue with that way it handles its own internal objects. I could sort of find issues the Qt Debug reports, and I did find one that sounded similar, but it was having different errors for different usage reasons.

    OS: Fails under both Windows and Linux (mingw, g++)
    Qt: 5.12.5 and 5.12.8
    64 bit



  • Here is the pro file too:

    QT += quick widgets
    
    CONFIG += c++11
    
    # The following define makes your compiler emit warnings if you use
    # any Qt feature that has been marked deprecated (the exact warnings
    # depend on your compiler). Refer to the documentation for the
    # deprecated API to know how to port your code away from it.
    DEFINES += QT_DEPRECATED_WARNINGS
    
    # You can also make your code fail to compile if it uses deprecated APIs.
    # In order to do so, uncomment the following line.
    # You can also select to disable deprecated APIs only up to a certain version of Qt.
    #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
    
    SOURCES += \
            main.cpp \
            tableproxymodel.cpp
    
    RESOURCES += qml.qrc
    
    # Additional import path used to resolve QML modules in Qt Creator's code model
    QML_IMPORT_PATH =
    
    # Additional import path used to resolve QML modules just for Qt Quick Designer
    QML_DESIGNER_IMPORT_PATH =
    
    # Default rules for deployment.
    qnx: target.path = /tmp/$${TARGET}/bin
    else: unix:!android: target.path = /opt/$${TARGET}/bin
    !isEmpty(target.path): INSTALLS += target
    
    HEADERS += \
        tableproxymodel.h
    


  • Also, it only ever seems to crash when calling rowCount on the root index.



  • I modified the code to take the proxy out if noproxy is set to true on the FileSystemModel:

    import QtQuick 2.12
    import QtQuick.Window 2.12
    
    import Models 1.0
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Proxy Model Testing")
    
        FileSystemModel {
            id: filesystemmodel
            rootPath: "/"
            noproxy: true
        }
    
        FileSystemTableProxyModel {
            id: tableproxymodel
    
            model: filesystemmodel.proxy
            rootIndex: filesystemmodel.rootIndex
        }
    
        TableView {
            anchors.fill: parent
    
            model: tableproxymodel
    
            delegate: Item {
                implicitWidth: 100
                implicitHeight: 25
                Rectangle {
                    anchors.fill: parent
    
                    color: "blue"
                }
    
                Text {
                    text: display
                }
            }
        }
    }
    

    Also, it seems like it has issues starting or showing anything when the proxy is enabled. Very odd.



  • I think (hope) I found some relevant bug reports.
    https://bugreports.qt.io/browse/QTBUG-44611
    https://bugreports.qt.io/browse/QTBUG-47711

    It looks like I "might" be able to sidestep this by using the QIdentityProxyModel. No idea if this is a good idea. I will be testing this.

    Edit:
    Well it doesn't crash. But it won't filter either: "Because it does no sorting or filtering, this class is most suitable to proxy models which transform the data() of the source model."



  • And this might be relevant too:
    https://bugreports.qt.io/browse/QTBUG-14336
    This indicates it happens when data changes. Which is what happens on a QFileSystemModel. It returns 0 rows at first and then gets updated after data is loaded in another thread.


  • Qt Champions 2017

    Why derive the file system model? Also please provide a trace of the crash, for one my just-in-head debugging isn't that advanced.



    1. FileSystemTableProxyModel looks like a QIdentityProxyModel with custom columnCount() and data() methods. There is no need to inherit from QAbstractProxyModel and implement the other dangerous stuff
    2. FileSystemModel is nesting beginResetModel(); (setRootPath(rootPath); will call beginResetModel(); internally)
    3. When you have problems with models (i.e. always if you are like me) the absolute first thing to do is run the custom models through the model test. I can't even count the times it detected a mistake I did in my implementations


  • @kshegunov said in QSortFilterProxyModel has an internal index failure that does not map to the source correctly:

    Why derive the file system model?

    I have a much more complex version of qfilesystemmodel that provides images for qml. This one is a placeholder for testing and I add properties to use in QML.



  • @VRonin said in QSortFilterProxyModel has an internal index failure that does not map to the source correctly:

    FileSystemTableProxyModel looks like a QIdentityProxyModel with custom columnCount() and data() methods. There is no need to inherit from QAbstractProxyModel and implement the other dangerous stuff
    FileSystemModel is nesting beginResetModel(); (setRootPath(rootPath); will call beginResetModel(); internally)
    When you have problems with models (i.e. always if you are like me) the absolute first thing to do is run the custom models through the model test. I can't even count the times it detected a mistace I did in my implementations

    When I read the docs on the indentity model I was under the impression you should not reinterpret the data as columns. That seemed like it was a transformation of the data. Maybe I don't know what they are referring to when talking about transforming data.

    Yeah, I forgot about the model test. Thanks.



  • @kshegunov said in QSortFilterProxyModel has an internal index failure that does not map to the source correctly:

    provide a trace of the crash

    1   QSortFilterProxyModelPrivate::proxy_to_source                                                                                                                                          qsortfilterproxymodel.cpp     553  0x7ffff6742f54 
    2   QSortFilterProxyModel::mapToSource                                                                                                                                                     qsortfilterproxymodel.cpp     3061 0x7ffff6746316 
    3   QSortFilterProxyModel::rowCount                                                                                                                                                        qsortfilterproxymodel.cpp     2184 0x7ffff6746316 
    4   FileSystemTableProxyModel::rowCount                                                                                                                                                    tableproxymodel.h             191  0x55555555da16 
    5   FileSystemTableProxyModel::index                                                                                                                                                       tableproxymodel.h             159  0x55555555d7f6 
    6   FileSystemTableProxyModel::mapFromSource                                                                                                                                               tableproxymodel.h             205  0x55555555db0a 
    7   FileSystemTableProxyModel::on_rowsAboutToBeInserted                                                                                                                                    tableproxymodel.h             345  0x55555555f097 
    8   QtPrivate::FunctorCall<QtPrivate::IndexesList<0, 1, 2>, QtPrivate::List<QModelIndex const&, int, int>, void, void (FileSystemTableProxyModel:: *)(QModelIndex const&, int, int)>::call qobjectdefs_impl.h            152  0x555555564189 
    9   QtPrivate::FunctionPointer<void (FileSystemTableProxyModel:: *)(QModelIndex const&, int, int)>::call<QtPrivate::List<QModelIndex const&, int, int>, void>                              qobjectdefs_impl.h            185  0x5555555638fe 
    10  QtPrivate::QSlotObject<void (FileSystemTableProxyModel:: *)(QModelIndex const&, int, int), QtPrivate::List<QModelIndex const&, int, int>, void>::impl                                  qobjectdefs_impl.h            414  0x5555555627f5 
    11  QtPrivate::QSlotObjectBase::call                                                                                                                                                       qobjectdefs_impl.h            394  0x7ffff679cdf6 
    12  QMetaObject::activate                                                                                                                                                                  qobject.cpp                   3783 0x7ffff679cdf6 
    13  QMetaObject::activate                                                                                                                                                                  qobject.cpp                   3656 0x7ffff679d3d7 
    14  QAbstractItemModel::rowsAboutToBeInserted                                                                                                                                              moc_qabstractitemmodel.cpp    584  0x7ffff672015e 
    15  QAbstractItemModel::beginInsertRows                                                                                                                                                    qabstractitemmodel.cpp        2735 0x7ffff67278f3 
    16  QSortFilterProxyModelPrivate::insert_source_items                                                                                                                                      qsortfilterproxymodel.cpp     890  0x7ffff674788a 
    17  QSortFilterProxyModelPrivate::source_items_inserted                                                                                                                                    qsortfilterproxymodel.cpp     1013 0x7ffff674ce3b 
    18  QSortFilterProxyModelPrivate::_q_sourceRowsInserted                                                                                                                                    qsortfilterproxymodel.cpp     1644 0x7ffff674ce3b 
    19  QSortFilterProxyModel::qt_static_metacall                                                                                                                                              moc_qsortfilterproxymodel.cpp 231  0x7ffff674dd49 
    20  QMetaObject::activate                                                                                                                                                                  qobject.cpp                   3803 0x7ffff679cac9 
    ... <More>                                                                                                                                                                                                                                   
    

    The m object in seems to have bad data in it:

    QModelIndex QSortFilterProxyModelPrivate::proxy_to_source(const QModelIndex &proxy_index) const
    {
        if (!proxy_index.isValid())
            return QModelIndex(); // for now; we may want to be able to set a root index later
        if (proxy_index.model() != q_func()) {
            qWarning("QSortFilterProxyModel: index from wrong model passed to mapToSource");
            Q_ASSERT(!"QSortFilterProxyModel: index from wrong model passed to mapToSource");
            return QModelIndex();
        }
        IndexMap::const_iterator it = index_to_iterator(proxy_index);
        Mapping *m = it.value();
        if ((proxy_index.row() >= m->source_rows.size()) || (proxy_index.column() >= m->source_columns.size()))
            return QModelIndex();
        int source_row = m->source_rows.at(proxy_index.row());
        int source_col = m->source_columns.at(proxy_index.column());
        return model->index(source_row, source_col, it.key());
    }
    

    The object itself:

    	Locals		
    		it		QHash<QModelIndex, QSortFilterProxyModelPrivate::Mapping*>::const_iterator
    		m	@0x555555ca57d0	QSortFilterProxyModelPrivate::Mapping
    			map_iter	@0x555555ca57f8	QHash<QModelIndex, QSortFilterProxyModelPrivate::Mapping*>::const_iterator
    			mapped_children	<21845 items>	QVector<QModelIndex>
    			proxy_columns	<32767 items>	QVector<int>
    			proxy_rows	<not accessible>	QVector<int>
    			source_columns	<not accessible>	QVector<int>
    			source_rows	<21845 items>	QVector<int>
    		proxy_index	@0x555555cac620	QModelIndex &
    		source_col	<optimized out>	
    		source_row	<optimized out>	
    		this	@0x555555b1dfc0	QSortFilterProxyModelPrivate
    	Inspector		
    	Expressions		
    		item	<no such value>	 
    	Return Value		
    	Tooltip		
    		QModelIndex	<no such value>	 
    
    

    Note the mapped children and proxy columns. This tells me the object pointed to by m is no longer a valid object.



  • This is getting even more interesting. I created a version of the proxy model using the QIdentityProxyModel. It doesn't really work right, because I didnt redefine mapToSource and mapFromSource, but it does still crash when, and only when, I put a proxy model between QFileSystemModel and itself.

    tablemodelproxy.h:

    #include <QAbstractProxyModel>
    #include <QSortFilterProxyModel>
    #include <QIdentityProxyModel>
    #include <QAbstractItemModel>
    #include <QModelIndex>
    #include <QFileSystemModel>
    
    #include <QDebug>
    #include <QAbstractItemModelTester>
    
    #ifndef TABLEPROXYMODEL_H
    #define TABLEPROXYMODEL_H
    
    class SortFilterProxyModel :
            public QSortFilterProxyModel
            //public QIdentityProxyModel
    {
        Q_OBJECT
    
    public:
        SortFilterProxyModel(QObject* parent)
            : QSortFilterProxyModel(parent)
            //: QIdentityProxyModel(parent)
        {
    
        }
    };
    
    class FileSystemModel : public QFileSystemModel
    {
        Q_OBJECT
    
        Q_PROPERTY(QString rootPath READ getPathRoot WRITE setPathRoot NOTIFY rootPathChanged)
        Q_PROPERTY(QModelIndex rootIndex READ rootIndex NOTIFY rootIndexChanged)
        Q_PROPERTY(bool noproxy READ noproxy WRITE setnoproxy NOTIFY noproxyChanged)
        Q_PROPERTY(QAbstractItemModel* proxy READ proxy)
    public:
        FileSystemModel(QObject* parent=nullptr)
            : QFileSystemModel(parent)
            , m_proxy(nullptr)
            , m_noproxy(false)
        {
            setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
    
            m_proxy = new SortFilterProxyModel(this);
            m_proxy->setSourceModel(this);
    
            //new QAbstractItemModelTester(m_proxy, QAbstractItemModelTester::FailureReportingMode::Warning, this);
        }
        QString getPathRoot() const
        {
            return rootPath();
        }
        QModelIndex rootIndex() const
        {
            if(m_noproxy)
                return index(rootPath());
            else
                return m_proxy->mapFromSource(index(rootPath()));
        }
    
        QAbstractItemModel* proxy(){
            if(m_noproxy)
                return this;
            else
                return m_proxy;
        }
    
        bool noproxy() const
        {
            return m_noproxy;
        }
    
    public slots:
        void setPathRoot(QString rootPath)
        {
            if (m_rootPath == rootPath)
                return;
    
            beginResetModel();
            m_rootPath = rootPath;
            setRootPath(rootPath);
            endResetModel();
    
            emit rootPathChanged(m_rootPath);
            emit rootIndexChanged(rootIndex());
        }
    
        void setnoproxy(bool noproxy)
        {
            if (m_noproxy == noproxy)
                return;
    
            m_noproxy = noproxy;
            emit noproxyChanged(m_noproxy);
        }
    
    signals:
        void rootPathChanged(QString rootPath);
        void rootIndexChanged(QModelIndex rootIndex);
    
        void noproxyChanged(bool noproxy);
    
    protected:
        QString m_rootPath;
        QModelIndex m_rootIndex;
    
        SortFilterProxyModel* m_proxy;
        bool m_noproxy;
    };
    
    class FileSystemTableProxyModel : public QAbstractProxyModel
    {
        Q_OBJECT
    
        Q_PROPERTY(QAbstractItemModel* model READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged)
        Q_PROPERTY(QModelIndex rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged)
    public:
        FileSystemTableProxyModel(QObject* parent=nullptr)
            : QAbstractProxyModel(parent)
        {
        }
    
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
        {
            if(index.model() != this || !index.isValid())
                return QVariant();
    
            //auto sourcemodel = qobject_cast<QFileSystemModel*>(sourceModel());
            auto sourcemodel = sourceModel();
            if(!sourcemodel)
                return QVariant();
    
            auto sourceindex = mapToSource(index);
            if(!sourceindex.isValid())
                return QVariant();
    
            if(role == Qt::DisplayRole){
                switch(index.column()){
                    case 0:
                        return sourcemodel->data(sourceindex, QFileSystemModel::FileNameRole);
                    case 1:
                        return sourcemodel->data(sourceindex, QFileSystemModel::FilePathRole);
                    case 2:
                        return QString().setNum(sourcemodel->data(sourceindex, QFileSystemModel::FilePermissions).toInt(), 16);
                    case 3:
                        return "dummy";
                    default:
                        return QVariant();
                }
            }
    
            return sourcemodel->data(sourceindex, role);
        }
        QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
        {
            Q_UNUSED(parent)
    
            if(row < 0 || row >= rowCount())
                return QModelIndex();
            if(column < 0 || column >= columnCount())
                return QModelIndex();
    
            return createIndex(row, column);
        }
        QModelIndex parent(const QModelIndex &index) const override
        {
            Q_UNUSED(index)
    
            return QModelIndex();
        }
        bool hasChildren(const QModelIndex &parent = QModelIndex()) const override
        {
            if(parent == QModelIndex()){
                return rowCount();
            }
    
            return false;
        }
        int rowCount(const QModelIndex &parent = QModelIndex()) const override
        {
            Q_UNUSED(parent)
    
            if(parent != QModelIndex())
                return 0;
    
            auto sourcemodel = sourceModel();
            if(!sourcemodel || !m_rootIndex.isValid())
                return 0;
    
            return sourceModel()->rowCount(m_rootIndex);
        }
        int columnCount(const QModelIndex &parent = QModelIndex()) const override
        {
            Q_UNUSED(parent)
    
            return 4;
        }
    
        QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
        {
            if(sourceIndex.model() != sourceModel() || !sourceIndex.isValid())
                return QModelIndex();
    
            auto proxyindex = index(sourceIndex.row(), sourceIndex.column());
            if(!proxyindex.isValid())
                return QModelIndex();
    
            return proxyindex;
        }
        QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
        {
            if(proxyIndex.model() != this || !proxyIndex.isValid())
                return QModelIndex();
    
            //if(proxyIndex == QModelIndex())
            //    return m_rootIndex;
    
            //if(!proxyIndex.isValid())
            //    return QModelIndex();
    
            auto sourceindex = sourceModel()->index(proxyIndex.row(), proxyIndex.column(), m_rootIndex);
            if(!sourceindex.isValid())
                return QModelIndex();
    
            return sourceindex;
        }
    
        QModelIndex rootIndex() const
        {
            return m_rootIndex;
        }
    
        QAbstractItemModel* sourceModel() const
        {
            return QAbstractProxyModel::sourceModel();
        }
    
    public slots:
        void setRootIndex(const QModelIndex& rootindex)
        {
            if (m_rootIndex == rootindex)
                return;
    
            if(sourceModel() != rootindex.model())
                return;
    
            beginResetModel();
            m_rootIndex = rootindex;
            endResetModel();
    
            emit rootIndexChanged();
        }
    
        void setSourceModel(QAbstractItemModel* model) override
        {
            if (QAbstractProxyModel::sourceModel() == model)
                return;
    
            beginResetModel();
    
            auto oldmodel = sourceModel();
            if(oldmodel){
                disconnect(oldmodel, &QAbstractItemModel::columnsAboutToBeInserted, this, &FileSystemTableProxyModel::on_columnsAboutToBeInserted);
                disconnect(oldmodel, &QAbstractItemModel::columnsInserted, this, &FileSystemTableProxyModel::on_columnsInserted);
                disconnect(oldmodel, &QAbstractItemModel::columnsAboutToBeMoved, this, &FileSystemTableProxyModel::on_columnsAboutToBeMoved);
                disconnect(oldmodel, &QAbstractItemModel::columnsMoved, this, &FileSystemTableProxyModel::on_columnsMoved);
                disconnect(oldmodel, &QAbstractItemModel::columnsAboutToBeRemoved, this, &FileSystemTableProxyModel::on_columnsAboutToBeRemoved);
                disconnect(oldmodel, &QAbstractItemModel::columnsRemoved, this, &FileSystemTableProxyModel::on_columnsRemoved);
    
                disconnect(oldmodel, &QAbstractItemModel::rowsAboutToBeInserted, this, &FileSystemTableProxyModel::on_rowsAboutToBeInserted);
                disconnect(oldmodel, &QAbstractItemModel::rowsInserted, this, &FileSystemTableProxyModel::on_rowsInserted);
                disconnect(oldmodel, &QAbstractItemModel::rowsAboutToBeMoved, this, &FileSystemTableProxyModel::on_rowsAboutToBeMoved);
                disconnect(oldmodel, &QAbstractItemModel::rowsMoved, this, &FileSystemTableProxyModel::on_rowsMoved);
                disconnect(oldmodel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &FileSystemTableProxyModel::on_rowsAboutToBeRemoved);
                disconnect(oldmodel, &QAbstractItemModel::rowsRemoved, this, &FileSystemTableProxyModel::on_rowsRemoved);
    
                disconnect(oldmodel, &QAbstractItemModel::layoutAboutToBeChanged, this, &FileSystemTableProxyModel::on_layoutAboutToBeChanged);
                disconnect(oldmodel, &QAbstractItemModel::layoutChanged, this, &FileSystemTableProxyModel::on_layoutChanged);
    
                disconnect(oldmodel, &QAbstractItemModel::modelAboutToBeReset, this, &FileSystemTableProxyModel::on_modelAboutToBeReset);
                disconnect(oldmodel, &QAbstractItemModel::modelReset, this, &FileSystemTableProxyModel::on_modelReset);
    
                disconnect(oldmodel, &QAbstractItemModel::dataChanged, this, &FileSystemTableProxyModel::on_dataChanged);
                disconnect(oldmodel, &QAbstractItemModel::headerDataChanged, this, &FileSystemTableProxyModel::on_headerDataChanged);
            }
    
            QAbstractProxyModel::setSourceModel(model);
            auto newmodel = sourceModel();
            if(newmodel){
                connect(newmodel, &QAbstractItemModel::columnsAboutToBeInserted, this, &FileSystemTableProxyModel::on_columnsAboutToBeInserted);
                connect(newmodel, &QAbstractItemModel::columnsInserted, this, &FileSystemTableProxyModel::on_columnsInserted);
                connect(newmodel, &QAbstractItemModel::columnsAboutToBeMoved, this, &FileSystemTableProxyModel::on_columnsAboutToBeMoved);
                connect(newmodel, &QAbstractItemModel::columnsMoved, this, &FileSystemTableProxyModel::on_columnsMoved);
                connect(newmodel, &QAbstractItemModel::columnsAboutToBeRemoved, this, &FileSystemTableProxyModel::on_columnsAboutToBeRemoved);
                connect(newmodel, &QAbstractItemModel::columnsRemoved, this, &FileSystemTableProxyModel::on_columnsRemoved);
    
                connect(newmodel, &QAbstractItemModel::rowsAboutToBeInserted, this, &FileSystemTableProxyModel::on_rowsAboutToBeInserted);
                connect(newmodel, &QAbstractItemModel::rowsInserted, this, &FileSystemTableProxyModel::on_rowsInserted);
                connect(newmodel, &QAbstractItemModel::rowsAboutToBeMoved, this, &FileSystemTableProxyModel::on_rowsAboutToBeMoved);
                connect(newmodel, &QAbstractItemModel::rowsMoved, this, &FileSystemTableProxyModel::on_rowsMoved);
                connect(newmodel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &FileSystemTableProxyModel::on_rowsAboutToBeRemoved);
                connect(newmodel, &QAbstractItemModel::rowsRemoved, this, &FileSystemTableProxyModel::on_rowsRemoved);
    
                connect(newmodel, &QAbstractItemModel::layoutAboutToBeChanged, this, &FileSystemTableProxyModel::on_layoutAboutToBeChanged);
                connect(newmodel, &QAbstractItemModel::layoutChanged, this, &FileSystemTableProxyModel::on_layoutChanged);
    
                connect(newmodel, &QAbstractItemModel::modelAboutToBeReset, this, &FileSystemTableProxyModel::on_modelAboutToBeReset);
                connect(newmodel, &QAbstractItemModel::modelReset, this, &FileSystemTableProxyModel::on_modelReset);
    
                connect(newmodel, &QAbstractItemModel::dataChanged, this, &FileSystemTableProxyModel::on_dataChanged);
                connect(newmodel, &QAbstractItemModel::headerDataChanged, this, &FileSystemTableProxyModel::on_headerDataChanged);
            }
    
            endResetModel();
    
            auto tindex = QModelIndex();
            setRootIndex(tindex);
    
            emit sourceModelChanged();
        }
    
    private slots:
        // slots from hell
        void on_columnsAboutToBeInserted(const QModelIndex &parent, int first, int last){
            beginInsertColumns(mapToSource(parent), first, last);
        }
        void on_columnsInserted(const QModelIndex &parent, int first, int last){
            endInsertColumns();
        }
        void on_columnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn){
            beginMoveColumns(mapToSource(sourceParent), sourceStart, sourceEnd, mapToSource(destinationParent), destinationColumn);
        }
        void on_columnsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column){
            endMoveColumns();
        }
        void on_columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last){
            beginRemoveColumns(mapToSource(parent), first, last);
        }
        void on_columnsRemoved(const QModelIndex &parent, int first, int last){
            endRemoveColumns();
        }
    
        void on_rowsAboutToBeInserted(const QModelIndex &parent, int first, int last){
            beginInsertRows(mapFromSource(parent), first, last);
        }
        void on_rowsInserted(const QModelIndex &parent, int first, int last){
            endInsertRows();
        }
        void on_rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow){
            beginMoveRows(mapFromSource(sourceParent), sourceStart, sourceEnd, mapFromSource(destinationParent), destinationRow);
        }
        void on_rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row){
            endMoveRows();
        }
        void on_rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last){
            beginRemoveRows(mapFromSource(parent), first, last);
        }
        void on_rowsRemoved(const QModelIndex &parent, int first, int last){
            endRemoveRows();
        }
    
        void on_layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint){
            QList<QPersistentModelIndex> proxyparents;
            for(auto parent: parents){
                proxyparents.append(mapFromSource(parent));
            }
            emit layoutAboutToBeChanged(proxyparents, hint);
        }
        void on_layoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint){
            QList<QPersistentModelIndex> proxyparents;
            for(auto parent: parents){
                proxyparents.append(mapFromSource(parent));
            }
            emit layoutChanged(proxyparents, hint);
        }
        void on_modelAboutToBeReset(){
            beginResetModel();
        }
        void on_modelReset(){
            endResetModel();
        }
    
        void on_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles){
            emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight), roles);
        }
        void on_headerDataChanged(Qt::Orientation orientation, int first, int last){
            emit headerDataChanged(orientation, first, last);
        }
    
    
    signals:
        void rootIndexChanged();
        void sourceModelChanged();
    
    private:
        QModelIndex m_rootIndex;
    };
    
    class FileSystemTableIdentityModel : public QIdentityProxyModel
    {
        Q_OBJECT
    
        Q_PROPERTY(QAbstractItemModel* model READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged)
        Q_PROPERTY(QModelIndex rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged)
    public:
        FileSystemTableIdentityModel(QObject* parent=nullptr)
            : QIdentityProxyModel(parent)
        {
        }
    
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
        {
            if(index.model() != this || !index.isValid())
                return QVariant();
    
            //auto sourcemodel = qobject_cast<QFileSystemModel*>(sourceModel());
            auto sourcemodel = sourceModel();
            if(!sourcemodel)
                return QVariant();
    
            auto sourceindex = mapToSource(index);
            if(!sourceindex.isValid())
                return QVariant();
    
            if(role == Qt::DisplayRole){
                switch(index.column()){
                    case 0:
                        return sourcemodel->data(sourceindex, QFileSystemModel::FileNameRole);
                    case 1:
                        return sourcemodel->data(sourceindex, QFileSystemModel::FilePathRole);
                    case 2:
                        return QString().setNum(sourcemodel->data(sourceindex, QFileSystemModel::FilePermissions).toInt(), 16);
                    case 3:
                        return "dummy";
                    default:
                        return QVariant();
                }
            }
    
            return sourcemodel->data(sourceindex, role);
        }
    
        int rowCount(const QModelIndex &parent = QModelIndex()) const override
        {
            Q_UNUSED(parent)
    
            if(parent != QModelIndex())
                return 0;
    
            auto sourcemodel = sourceModel();
            //if(!sourcemodel || !m_rootIndex.isValid())
            //    return 0;
    
            return sourceModel()->rowCount(m_rootIndex);
            //return sourceModel()->rowCount(mapToSource(parent));
        }
        int columnCount(const QModelIndex &parent = QModelIndex()) const override
        {
            Q_UNUSED(parent)
    
            return 4;
        }
    
        void setSourceModel(QAbstractItemModel* model) override
        {
            if (QAbstractProxyModel::sourceModel() == model)
                return;
    
            beginResetModel();
            QIdentityProxyModel::setSourceModel(model);
            m_rootIndex = QModelIndex();
            endResetModel();
        }
    
        QAbstractItemModel* sourceModel() const
        {
            return QIdentityProxyModel::sourceModel();
        }
        QModelIndex rootIndex() const
        {
            return m_rootIndex;
        }
    
    public slots:
        void setRootIndex(QModelIndex rootIndex)
        {
            if (m_rootIndex == rootIndex)
                return;
    
            beginResetModel();
            m_rootIndex = rootIndex;
            endResetModel();
    
            emit rootIndexChanged(m_rootIndex);
        }
    
    signals:
        void sourceModelChanged(QAbstractItemModel* model);
        void rootIndexChanged(QModelIndex rootIndex);
    
    private:
        QModelIndex m_rootIndex;
    };
    
    class ModelTester : public QObject
    {
        Q_OBJECT
    
        Q_PROPERTY(QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged)
    public:
        ModelTester(QObject* parent=nullptr)
            : QObject(parent)
            , m_model(nullptr)
            , m_tester(nullptr)
        {
            //new QAbstractItemModelTester(m_proxy, QAbstractItemModelTester::FailureReportingMode::Warning, this);
        }
        QAbstractItemModel* model() const
        {
            return m_model;
        }
    public slots:
        void setModel(QAbstractItemModel* model)
        {
            if (m_model == model)
                return;
    
            if(m_tester){
                qInfo() << "Done testing:" << m_model;
                m_tester->deleteLater();
            }
    
            m_model = model;
            if(m_model){
                qInfo() << "Beging testing:" << m_model;
                m_tester = new QAbstractItemModelTester(m_model, QAbstractItemModelTester::FailureReportingMode::Warning, this);
            }
            else{
                m_tester = nullptr;
            }
    
            emit modelChanged(m_model);
        }
    signals:
        void modelChanged(QAbstractItemModel* model);
    
    protected:
        QAbstractItemModel* m_model;
        QAbstractItemModelTester* m_tester;
    };
    
    #endif // TABLEPROXYMODEL_H
    

    qml:

    import QtQuick 2.12
    import QtQuick.Window 2.12
    
    import Models 1.0
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Proxy Model Testing")
    
        FileSystemModel {
            id: filesystemmodel
            rootPath: "/"
            noproxy: false
        }
    
        /*
        FileSystemTableProxyModel {
            id: tableproxymodel
    
            model: filesystemmodel.proxy
            rootIndex: filesystemmodel.rootIndex
        }
        */
    
        FileSystemTableIdentityModel {
            id: tableproxymodel
    
            model: filesystemmodel.proxy
            rootIndex: filesystemmodel.rootIndex
        }
    
    
        TableView {
            anchors.fill: parent
    
            model: tableproxymodel
            //model: filesystemmodel.proxy
    
            delegate: Item {
                implicitWidth: 100
                implicitHeight: 25
                Rectangle {
                    anchors.fill: parent
    
                    color: "blue"
                }
    
                Text {
                    text: display
                }
            }
        }
    
        ModelTester {
            //model: tableproxymodel
            //model: filesystemmodel.proxy
        }
    }
    


  • I really don't know how to do what I want to do I guess. I think I need to backup and think if I really need to create a translation table at all. I have spent so much time trying to understand how to make this work that it is not productive at all.



  • Minimalist version to reproduce:
    main.cpp:

    #include <QApplication>
    #include <QQmlApplicationEngine>
    
    #include <QSortFilterProxyModel>
    #include <QFileSystemModel>
    #include <QAbstractTableModel>
    #include <QAbstractItemModel>
    
    #include <QQmlContext>
    
    #include <QDebug>
    
    // https://doc.qt.io/qt-5/qml-qtquick-tableview.html#example-usage
    class TableModel : public QAbstractTableModel
    {
        Q_OBJECT
    
        Q_PROPERTY(QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged)
        Q_PROPERTY(QModelIndex rootindex READ rootindex WRITE setRootindex NOTIFY rootindexChanged)
    public:
    
        int rowCount(const QModelIndex & = QModelIndex()) const override
        {
            if(!m_model)
                return 0;
    
            return m_model->rowCount(m_rootindex);
        }
    
        int columnCount(const QModelIndex & = QModelIndex()) const override
        {
            return 1;
        }
    
        QVariant data(const QModelIndex &index, int role) const override
        {
            if(!m_model)
                return QVariant();
    
            switch (role) {
                case Qt::DisplayRole:
                    return m_model->data(m_model->index(index.row(), index.column(), m_rootindex), QFileSystemModel::FileNameRole);
                default:
                    break;
            }
    
            return QVariant();
        }
    
        QHash<int, QByteArray> roleNames() const override
        {
            return { {Qt::DisplayRole, "display"} };
        }
        QAbstractItemModel* model() const
        {
            return m_model;
        }
        QModelIndex rootindex() const
        {
            return m_rootindex;
        }
    
    public slots:
        void setModel(QAbstractItemModel* model)
        {
            if (m_model == model)
                return;
    
            m_model = model;
    
            if(m_model) {
                auto newmodel = m_model;
                connect(newmodel, &QAbstractItemModel::columnsAboutToBeInserted, this, &TableModel::beginreset);
                connect(newmodel, &QAbstractItemModel::columnsInserted, this, &TableModel::endreset);
                connect(newmodel, &QAbstractItemModel::columnsAboutToBeMoved, this, &TableModel::beginreset);
                connect(newmodel, &QAbstractItemModel::columnsMoved, this, &TableModel::endreset);
                connect(newmodel, &QAbstractItemModel::columnsAboutToBeRemoved, this, &TableModel::beginreset);
                connect(newmodel, &QAbstractItemModel::columnsRemoved, this, &TableModel::endreset);
    
                connect(newmodel, &QAbstractItemModel::rowsAboutToBeInserted, this, &TableModel::beginreset);
                connect(newmodel, &QAbstractItemModel::rowsInserted, this, &TableModel::endreset);
                connect(newmodel, &QAbstractItemModel::rowsAboutToBeMoved, this, &TableModel::beginreset);
                connect(newmodel, &QAbstractItemModel::rowsMoved, this, &TableModel::endreset);
                connect(newmodel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &TableModel::beginreset);
                connect(newmodel, &QAbstractItemModel::rowsRemoved, this, &TableModel::endreset);
    
                connect(newmodel, &QAbstractItemModel::layoutAboutToBeChanged, this, &TableModel::beginreset);
                connect(newmodel, &QAbstractItemModel::layoutChanged, this, &TableModel::endreset);
    
                connect(newmodel, &QAbstractItemModel::modelAboutToBeReset, this, &TableModel::beginreset);
                connect(newmodel, &QAbstractItemModel::modelReset, this, &TableModel::endreset);
    
                connect(newmodel, &QAbstractItemModel::dataChanged, this, [this](){
                    beginResetModel();
                    endResetModel();
                });
                connect(newmodel, &QAbstractItemModel::headerDataChanged, this, [this](){
                    beginResetModel();
                    endResetModel();
                });
            }
    
            emit modelChanged(m_model);
        }
        void setRootindex(QModelIndex rootindex)
        {
            if (m_rootindex == rootindex)
                return;
    
            m_rootindex = rootindex;
            emit rootindexChanged(m_rootindex);
        }
    
        void beginreset(){
            beginResetModel();
        }
        void endreset(){
            endResetModel();
        }
    
    signals:
        void modelChanged(QAbstractItemModel* model);
        void rootindexChanged(QModelIndex rootindex);
    
    protected:
        QAbstractItemModel* m_model;
        QModelIndex m_rootindex;
    };
    
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QApplication app(argc, argv);
    
        QFileSystemModel fsmodel;
        QSortFilterProxyModel sfpmodel;
    
        TableModel tbmodel;
    
        sfpmodel.setSourceModel(&fsmodel);
    
        fsmodel.setRootPath("/");
    
        bool nocrash = false;
        if(nocrash){
            tbmodel.setModel(&fsmodel);
            tbmodel.setRootindex(fsmodel.index(fsmodel.rootPath()));
            qInfo() << fsmodel.index(fsmodel.rootPath()) << QModelIndex();
        }else{
            tbmodel.setModel(&sfpmodel);
            tbmodel.setRootindex(sfpmodel.index(0,0));
        }
    
        QQmlApplicationEngine engine;
    
        engine.rootContext()->setContextProperty("tbmodel", &tbmodel);
    
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
        return app.exec();
    }
    
    #include "main.moc"
    

    main.qml:

    import QtQuick 2.12
    import QtQuick.Window 2.12
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Sort Proxy Model Crash")
    
        TableView {
            anchors.fill: parent
    
            model: tbmodel
    
            delegate: Item {
                implicitWidth: 100
                implicitHeight: 25
                Rectangle {
                    anchors.fill: parent
    
                    color: "blue"
                }
    
                Text {
                    text: display
                }
            }
        }
    }
    

    Crashes in the exact place if nocrash is set to false in main.cpp. May take a few tries to trigger crash as sometimes it shows as blank. Another issues is the asynchronous behavior of QFileSystemModel. However, the code works perfectly if nocrash is set to true, but once a QSortProxyFilter is in the middle it has problems. Really really really frustrated because either I need to work around this, not use QSortProxyFilter and roll my own, or go cry in the corner hoping it goes away.


  • Qt Champions 2017

    As a first attempt, could you try with the system available QFileSystemModel and QIdentityProxyModel?

    One thing that caught my eye while going through the stack trace is the m_rootIndex you have. You appear to store a QModelIndex (you shouldn't) which may get invalidated due to the way QFileSystemModel does its (re)loading. An easy thing to try is to substitute that with QPersistentModelIndex and see where that leads you.



  • @kshegunov said in QSortFilterProxyModel has an internal index failure that does not map to the source correctly:

    and see where that leads you

    And this seems to have resolved the problem. Thank you.

    Edit:
    QPersistentModelIndex for variable storage is the fix. Just to be clear. It starts to make sense now. It tells me why the index could invalidate its memory due to updates.


  • Qt Champions 2017

    You're very welcome.


Log in to reply