Solved 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-47711It 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. -
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.
-
FileSystemTableProxyModel
looks like aQIdentityProxyModel
with customcolumnCount()
anddata()
methods. There is no need to inherit fromQAbstractProxyModel
and implement the other dangerous stuffFileSystemModel
is nestingbeginResetModel();
(setRootPath(rootPath);
will callbeginResetModel();
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 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 implementationsWhen 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.
-
As a first attempt, could you try with the system available
QFileSystemModel
andQIdentityProxyModel
?One thing that caught my eye while going through the stack trace is the
m_rootIndex
you have. You appear to store aQModelIndex
(you shouldn't) which may get invalidated due to the wayQFileSystemModel
does its (re)loading. An easy thing to try is to substitute that withQPersistentModelIndex
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. -
You're very welcome.