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

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.cpp

    I 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
    
    

Log in to reply