qml中使用TreeView实现指定文件目录的显示

视频效果如下:

qml实现指定文件目录的显示功能

图片效果如下:

 实现方法也是研究了许久,从一开始继承QFileSystemModel来实现文件目录的显示,但是使用QFileSystemModel只有和QTreeView才能实现指定目录显示的效果,因为QFileSystemModel的SetRootPath()方法只能起到监控指定目录的方法,设置了SetRootPath()方法后,其他盘符依然会显示出来,而QTreeView中的setRootIndex()方法配合SetRootPath()方法能实现只显示指定的目录的效果,但是这是在Qt Widgets项目中才能这么实现,在Qt Quick项目中想要使用QFileSystemModel模型来实现指定目录的显示,我也是做了许久依旧无法实现出来,如果有实现出来的小伙伴欢迎私信交流,后面又试过使用qml端官方写的FolderListModel模型来实现指定目录的显示,虽然这个FolderListModel模型,确实可以将指定目录下的所有子目录和文件都显示出来,但是它也紧紧只能显示一层,意思就是,它所显示出来的子目录无法打开,如果想要通过这个模型实现文件系统目录,可能要做的工作需要更多,于是后面研究后,决定继承QStandardItemModel类和使用qml的TreeView来实现指定目录的显示效果。效果如上,笔者还给文件目录加上了每行可以选中的效果,和目录前面+号变成-号的效果。目前还未实现可以右键鼠标进行相关目录的操作行为。实现代码如下:

displayfilesystemmodel.h头文件内容如下:

#ifndef DISPLAYFILESYSTEMMODEL_H
#define DISPLAYFILESYSTEMMODEL_H

#include <QStandardItemModel>
#include <QStandardItem>
#include <QDir>
#include <QFileInfoList>

class DisplayFileSystemModel : public QStandardItemModel
{
    Q_OBJECT

public:
    enum Roles {
        FileNameRole = Qt::UserRole + 100,
        FilePathRole ,
        IsDirRole,
        DepthRole,
        CheckedRole,
        SelectedRole,
        EmbellishRole
    };

    explicit DisplayFileSystemModel(QObject *parent = nullptr);
    ~DisplayFileSystemModel();

    Q_INVOKABLE void setRootPath(const QString &path);
    int getDepth(const QModelIndex &index) const;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    Q_INVOKABLE void updateChecked(const QString &path, bool checked);
    QHash<int, QByteArray> roleNames() const override;
    Q_INVOKABLE void updateSelected(const QString &path, bool selected);

signals:
    void dirSet();

private:
    void loadDirectory(const QDir &dir);
    void loadDirectory(const QDir &dir, QStandardItem *parentItem);
    QStandardItem *root;
    QHash<QString, QStandardItem*> itemMap;
    QHash<QString, bool> checkedItems;
    QString selectedItem;

};

#endif // DISPLAYFILESYSTEMMODEL_H

 


displayfilesystemmodel.cpp文件内容如下:


#include "displayFileSystemModel.h"

DisplayFileSystemModel::DisplayFileSystemModel(QObject *parent) : QStandardItemModel(parent)
{

}

DisplayFileSystemModel::~DisplayFileSystemModel()
{

}

void DisplayFileSystemModel::setRootPath(const QString &path)
{
    QDir dir(path);
    if (!dir.exists()) {
        return;
    }

    clear();
    itemMap.clear(); // 清空itemMap
    loadDirectory(dir);
}

int DisplayFileSystemModel::getDepth(const QModelIndex &index) const
{
    int depth = 0;
    QModelIndex parentIndex = index.parent();
    while (parentIndex.isValid()) {
        depth++;
        parentIndex = parentIndex.parent();
    }
    return depth;
}

QVariant DisplayFileSystemModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
    {
        return QVariant();
    }

    QStandardItem *item = itemFromIndex(index);

    switch (role) {
    case FileNameRole:
        return item->data(FileNameRole);
    case EmbellishRole:
        // 返回设置的图标
        return item->data(EmbellishRole);
    case DepthRole:
        return getDepth(index);
    case CheckedRole: {
        QString path = item->data(FilePathRole).toString(); // 获取路径数据
        return checkedItems.value(path, false);
    }
    case SelectedRole: {
        // 返回这一行是否被选中
        QString path = item->data(FilePathRole).toString(); // 获取路径数据
        return path == selectedItem;
    }
    default:
        return QStandardItemModel::data(index, role);
    }
}

void DisplayFileSystemModel::loadDirectory(const QDir &dir)
{
    QStandardItem *parentItem = invisibleRootItem();
    loadDirectory(dir, parentItem);
}

void DisplayFileSystemModel::loadDirectory(const QDir &dir, QStandardItem *parentItem)
{
    QFileInfoList list = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
    for (const QFileInfo &info : list) {
        // 如果文件的扩展名是.gpkg或.qgz,就跳过这个文件
        if (info.isFile() && (info.suffix() == "gpkg" || info.suffix() == "qgz")) {
            continue;
        }

        QStandardItem *item = new QStandardItem();
        item->setData(info.fileName(), FileNameRole);
        item->setData(info.absoluteFilePath(), FilePathRole); // 设置路径数据
        // 根据是否是目录设置不同的图标
        if (info.isDir()) {
            item->setData("qrc:/2.png", EmbellishRole);
        } else {
            item->setData("qrc:/1.png", EmbellishRole);
        }
        parentItem->appendRow(item);
        itemMap[info.absoluteFilePath()] = item; // 更新itemMap

        if (info.isDir()) {
            loadDirectory(QDir(info.absoluteFilePath()), item);
        }
    }
}

void DisplayFileSystemModel::updateChecked(const QString &path, bool checked)
{
    if (!itemMap.contains(path)) { // 处理不存在的项
        return;
    }

    if (checkedItems[path] != checked) {
        checkedItems[path] = checked;
        QStandardItem *item = itemMap.value(path);
        QModelIndex parentIndex = item->parent() ? item->parent()->index() : QModelIndex();
        QModelIndex firstIndex = this->index(item->row(), 0, parentIndex);
        QModelIndex lastIndex = this->index(item->row(), this->columnCount(parentIndex) - 1, parentIndex);
        emit dataChanged(firstIndex, lastIndex, { CheckedRole });
    }
}

QHash<int, QByteArray> DisplayFileSystemModel::roleNames() const
{
    QHash<int, QByteArray> roles = QStandardItemModel::roleNames();
    roles[FileNameRole] = "filename";
    roles[FilePathRole] = "filepath";
    roles[IsDirRole] = "isdir";
    roles[DepthRole] = "depth";
    roles[CheckedRole] = "checked";
    roles[SelectedRole] = "selected";
    roles[EmbellishRole] = "embellish";
    return roles;
}

void DisplayFileSystemModel::updateSelected(const QString &path, bool selected)
{
    if (!itemMap.contains(path)) { // 处理不存在的项
        return;
    }

    if (selected) {
        if (!selectedItem.isEmpty()) {
            // 取消之前选中的行的选中状态
            QString oldSelectedItem = selectedItem;
            selectedItem.clear();
            QStandardItem *oldItem = itemMap.value(oldSelectedItem);
            QModelIndex parentIndex = oldItem->parent() ? oldItem->parent()->index() : QModelIndex();
            QModelIndex firstIndex = this->index(oldItem->row(), 0, parentIndex);
            QModelIndex lastIndex = this->index(oldItem->row(), this->columnCount(parentIndex) - 1, parentIndex);
            emit dataChanged(firstIndex, lastIndex, { SelectedRole });
        }
        // 选中新的行
        selectedItem = path;
    } else {
        selectedItem.clear();
    }
    QStandardItem *item = itemMap.value(path);
    QModelIndex parentIndex = item->parent() ? item->parent()->index() : QModelIndex();
    QModelIndex firstIndex = this->index(item->row(), 0, parentIndex);
    QModelIndex lastIndex = this->index(item->row(), this->columnCount(parentIndex) - 1, parentIndex);
    emit dataChanged(firstIndex, lastIndex, { SelectedRole });
}

 


main.cpp文件内容如下:


#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <displayfilesystemmodel.h>
#include <QQmlContext>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    DisplayFileSystemModel * fsm = new DisplayFileSystemModel(&engine);
    fsm->setRootPath("D:/QT");
    engine.rootContext()->setContextProperty("fileSystemModel", fsm);
    qmlRegisterType<DisplayFileSystemModel>("org.qgis", 1, 0, "DisplayFileSystemModel");

    const QUrl url(u"qrc:/qml_test4/main.qml"_qs);
    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();
}

 


MyRectangle.qml文件内容如下:


import QtQuick
import QtQml.Models 2.2
import Qt.labs.folderlistmodel 2.2
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
import Qt.labs.qmlmodels

Item {
    id: fileExplorer
    visible: false
    width: parent.width
    height: parent.height

    ScrollView {
        id: id_scro
        anchors.fill: parent
        width: parent.width
        height: parent.height
        clip: true

        TreeView {
            id: fileTreeView
            anchors.fill: parent
            anchors.bottomMargin: 10

            // The model needs to be a QAbstractItemModel
            model: fileSystemModel

            selectionModel: ItemSelectionModel {id: fileItemSelectionModel}

            delegate: Item {
                id: treeDelegate
                implicitWidth: parent.width
                implicitHeight: label.implicitHeight * 1.5

                readonly property real indent: 20
                readonly property real padding: 5

                // Assigned to by TreeView:
                required property TreeView treeView
                required property bool expanded
                required property bool isTreeNode
                required property int hasChildren
                property bool isPressed: false

                TapHandler {
                    onTapped: {
                        treeView.toggleExpanded(row);
                        treeView.model.updateChecked(model.filepath, !checked);
                        treeView.model.updateSelected(model.filepath, !model.selected);
                    }
                }

                Rectangle {
                    id: dirRectangle
                    anchors.fill: parent
                    anchors.leftMargin: 10
                    anchors.rightMargin: 10
                    color: model.selected ? "#DDE8FB" : "white"
                    clip: true

                    Row {
                        width: parent.width
                        height: parent.height
                        x: padding + (model.depth * indent)
                        spacing: 10 // 设置图标和文字之间的间距为10个单位

                        Rectangle {
                            id: expandCollapseIcon
                            width: 13
                            height: 13
                            color: "#447DD1"
                            anchors.verticalCenter: parent.verticalCenter
                            visible: treeDelegate.hasChildren

                            Text {
                                id: expandCollapseText
                                text: treeDelegate.expanded ? "-" : "+"
                                anchors.centerIn: parent
                                color: "white"
                            }

                            MouseArea {
                                anchors.fill: parent
                                onClicked: {
                                    treeDelegate.expanded = !treeDelegate.expanded;
                                    treeView.toggleExpanded(index);
                                    treeView.model.updateSelected(model.filepath, !model.selected);
                                }
                            }
                        }

                        Rectangle {
                            width: 13
                            height: 13
                            visible: !treeDelegate.hasChildren
                            anchors.verticalCenter: parent.verticalCenter
                            anchors.leftMargin: 10

                            CheckBox {
                                id: fileCheckBox
                                anchors.horizontalCenter: parent.horizontalCenter
                                anchors.verticalCenter: parent.verticalCenter
//                                scale: 0.7

                                onPressed: {
                                    isPressed = true;
                                }

                                onReleased: {
                                    isPressed = false;
                                }

                                checked: model.checked

                                onCheckedChanged: {
                                    if (isPressed) {
                                        // 更新模型中的勾选状态
                                        treeView.model.updateChecked(model.filepath, checked);
                                        treeView.model.updateSelected(model.filepath, !model.selected);
                                    }
                                }
                            }
                        }

                        Image {
                            source: model.embellish
                            width: 15
                            height: 15
                            anchors.verticalCenter: parent.verticalCenter
                        }

                        Label {
                            id: label
                            text: model.filename
                            font.pixelSize: 12
                            anchors.verticalCenter: parent.verticalCenter
                            color: "black"
                        }
                    }
                }
            }
        }
    }
}

 


main.qml文件内容如下:

import QtQuick 2.9
import QtQuick.Controls 2.5

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        anchors.fill: parent

        MyRectangle {
            id: fileExp
            anchors.fill: parent
            visible: true
        }
    }
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值