Solved All I wanted was a simple file table of a directory, now I have constructed this mess...
-
main.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 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()) } } }
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(); }
tableproxymodel.h:
#include <QAbstractProxyModel> #include <QAbstractItemModel> #include <QModelIndex> #include <QFileSystemModel> #ifndef TABLEPROXYMODEL_H #define TABLEPROXYMODEL_H class FileSystemModel : public QFileSystemModel { Q_OBJECT Q_PROPERTY(QString rootPath READ getPathRoot WRITE setPathRoot NOTIFY rootPathChanged) Q_PROPERTY(QModelIndex rootIndex READ rootIndex NOTIFY rootIndexChanged) public: FileSystemModel(QObject* parent=nullptr) : QFileSystemModel(parent) { connect(this, &QFileSystemModel::directoryLoaded, [this](){ //fetchMore(rootIndex()); }); } QString getPathRoot() const { return rootPath(); } QModelIndex rootIndex() const { return index(rootPath()); } public slots: void setPathRoot(QString rootPath) { if (m_rootPath == rootPath) return; m_rootPath = rootPath; setRootPath(rootPath); //fetchMore(rootIndex()); emit rootPathChanged(m_rootPath); emit rootIndexChanged(rootIndex()); } signals: void rootPathChanged(QString rootPath); void rootIndexChanged(QModelIndex rootIndex); protected: QString m_rootPath; QModelIndex m_rootIndex; }; 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) { // connect(this, &QAbstractProxyModel::sourceModelChanged, [this](){ // auto tindex = QModelIndex(); // setRootIndex(tindex); // emit sourceModelChanged(); // }); } 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()); 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(); 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(); QAbstractProxyModel::setSourceModel(model); endResetModel(); auto tindex = QModelIndex(); setRootIndex(tindex); emit sourceModelChanged(); } signals: void rootIndexChanged(); void sourceModelChanged(); private: QModelIndex m_rootIndex; }; #endif // TABLEPROXYMODEL_H
I had some goals:
- learn how to do a proxy model
- create a table view of QFileSystemModel
- get better at creating models
So far I am frustrated as I don't know what the issue is. I can view the filesystemmodel using the TableView. Great, I know the filesystemmodel is working. Then when I try to see the data through tableproxymodel it never returns any rows. I have read other implementations, but honestly I am really fuzzy on how to properly create a root QModelIndex. I thought setting it to QModelIndex() would tell the view that this is the root node. I am not sure that is correct.
Is this even close to what I should be doing for a customized table model for QFileSystemModel?
-
It looks like there may be a lot more boiler plate than I expected:
https://github.com/thirtythreeforty/UtilityProxies/blob/master/TranspositionProxyModel.h
https://github.com/thirtythreeforty/UtilityProxies/blob/master/TranspositionProxyModel.cppI wonder if I should have used the QIdentityProxyModel. It seemed like I wasn't supposed to modify the layout of the data with that though.
-
It seems to me the only thing that is failing is the returned rowCount is always zero for rootIndex. The rootIndex is the index pointed to in QFileSystemModel for the directory I want to look at.
int size = sourceModel()->rowCount(m_rootIndex);
This is always returning zero. If I set this to an invalid index it returns 1 because it is looking at the very root of the filesystem which returns the volume. If I stuff a different value in my rowCount function to test things it shows the entries in the directory I am pointing to. So I am missing something very important here and I don't know what.
-
Some assembly required, finally getting this straight in my head:
#include <QAbstractProxyModel> #include <QSortFilterProxyModel> #include <QAbstractItemModel> #include <QModelIndex> #include <QFileSystemModel> #include <QDebug> #ifndef TABLEPROXYMODEL_H #define TABLEPROXYMODEL_H class FileSystemModel : public QFileSystemModel { Q_OBJECT Q_PROPERTY(QString rootPath READ getPathRoot WRITE setPathRoot NOTIFY rootPathChanged) Q_PROPERTY(QModelIndex rootIndex READ rootIndex NOTIFY rootIndexChanged) public: FileSystemModel(QObject* parent=nullptr) : QFileSystemModel(parent) { connect(this, &QFileSystemModel::directoryLoaded, [this](){ //fetchMore(rootIndex()); //setPathRoot(m_rootPath); }); setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); } QString getPathRoot() const { return rootPath(); } QModelIndex rootIndex() const { return index(rootPath()); //return setRootPath(rootPath()); } 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; }; 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()); 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) Q_ASSERT(m_rootIndex.model() == sourceModel()); auto sourcemodel = qobject_cast<QFileSystemModel*>(sourceModel()); qInfo() << sourcemodel->filePath(m_rootIndex); int size = sourceModel()->rowCount(m_rootIndex); return size; } 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) 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(); qInfo() << rootindex << rootindex.parent(); 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