You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt实现:QListWidget前端+QTreeWidget后端的虚拟文件系统网格视图

实现虚拟文件系统的网格层级视图方案

要实现兼具QListWidget网格展示风格和QTreeWidget层级结构的虚拟文件系统容器,最直接的方案是基于Qt的QTreeView+自定义QAbstractItemModel+自定义委托来实现,以下是具体步骤:

1. 自定义虚拟文件系统模型

因为你的文件系统是虚拟的,无法使用QFileSystemModel,所以需要自己实现继承自QAbstractItemModel的模型,用来维护虚拟的文件/文件夹层级数据。

首先定义数据节点类,存储每个文件/文件夹的核心信息:

class FileNode
{
public:
    FileNode(const QString& name, bool isFolder, FileNode* parent = nullptr)
        : m_name(name), m_isFolder(isFolder), m_parent(parent) {}
    ~FileNode() { qDeleteAll(m_children); }

    QString name() const { return m_name; }
    bool isFolder() const { return m_isFolder; }
    FileNode* parent() const { return m_parent; }
    QList<FileNode*> children() const { return m_children; }
    void addChild(FileNode* child) { m_children.append(child); }

private:
    QString m_name;
    bool m_isFolder;
    FileNode* m_parent;
    QList<FileNode*> m_children;
};

接着实现自定义模型,核心重写QAbstractItemModel的关键方法:

class VirtualFileSystemModel : public QAbstractItemModel
{
    Q_OBJECT
public:
    explicit VirtualFileSystemModel(QObject* parent = nullptr) : QAbstractItemModel(parent)
    {
        // 初始化根节点,可替换为你的虚拟系统根目录逻辑
        m_rootNode = new FileNode("/", true);
        // 示例测试数据,实际从虚拟系统加载
        m_rootNode->addChild(new FileNode("Documents", true, m_rootNode));
        m_rootNode->addChild(new FileNode("Pictures", true, m_rootNode));
        m_rootNode->addChild(new FileNode("readme.txt", false, m_rootNode));
    }

    ~VirtualFileSystemModel() { delete m_rootNode; }

    QVariant data(const QModelIndex& index, int role) const override
    {
        if (!index.isValid()) return QVariant();
        FileNode* node = static_cast<FileNode*>(index.internalPointer());
        switch(role)
        {
        case Qt::DisplayRole:
            return node->name();
        case Qt::DecorationRole:
            return node->isFolder() ? QIcon::fromTheme("folder") : QIcon::fromTheme("text-x-generic");
        default:
            return QVariant();
        }
    }

    QModelIndex parent(const QModelIndex& index) const override
    {
        if (!index.isValid()) return QModelIndex();
        FileNode* childNode = static_cast<FileNode*>(index.internalPointer());
        FileNode* parentNode = childNode->parent();
        if (parentNode == m_rootNode) return QModelIndex();
        return createIndex(parentNode->children().indexOf(childNode), 0, parentNode);
    }

    QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override
    {
        if (!hasIndex(row, column, parent)) return QModelIndex();
        FileNode* parentNode = parent.isValid() ? static_cast<FileNode*>(parent.internalPointer()) : m_rootNode;
        FileNode* childNode = parentNode->children().at(row);
        return createIndex(row, column, childNode);
    }

    int rowCount(const QModelIndex& parent = QModelIndex()) const override
    {
        FileNode* parentNode = parent.isValid() ? static_cast<FileNode*>(parent.internalPointer()) : m_rootNode;
        return parentNode->children().size();
    }

    int columnCount(const QModelIndex& parent = QModelIndex()) const override
    {
        Q_UNUSED(parent);
        return 1;
    }

private:
    FileNode* m_rootNode;
};

2. 自定义委托实现网格布局

QTreeView默认是垂直列表样式,要改成网格布局,需要自定义QStyledItemDelegate控制每个项的大小和绘制逻辑:

class GridItemDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    explicit GridItemDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {}

    // 设置每个项的固定尺寸
    QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override
    {
        Q_UNUSED(index);
        return QSize(120, 100); // 可根据需求调整尺寸
    }

    // 自定义绘制,让文件名居中显示
    void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override
    {
        // 先调用默认绘制逻辑渲染图标
        QStyleOptionViewItem opt = option;
        opt.text = ""; // 隐藏默认文本,自定义绘制
        QStyledItemDelegate::paint(painter, opt, index);
        
        // 绘制居中的文件名
        painter->save();
        painter->setPen(option.palette.text().color());
        QString text = index.data(Qt::DisplayRole).toString();
        QRect textRect = option.rect.adjusted(5, option.rect.height() - 30, -5, -5);
        painter->drawText(textRect, Qt::AlignCenter | Qt::TextWordWrap, text);
        painter->restore();
    }
};

3. 组装视图并配置

在主窗口中整合模型、委托和QTreeView,完成最终效果:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    QTreeView* treeView = new QTreeView(this);
    setCentralWidget(treeView);

    // 设置自定义模型
    VirtualFileSystemModel* model = new VirtualFileSystemModel(this);
    treeView->setModel(model);

    // 设置自定义委托
    GridItemDelegate* delegate = new GridItemDelegate(this);
    treeView->setItemDelegate(delegate);

    // 关键配置:开启图标网格模式
    treeView->setViewMode(QListView::IconMode);
    treeView->setUniformRowHeights(true);
    treeView->setRootIsDecorated(true); // 保留文件夹折叠/展开箭头
    treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); // 支持多选
    treeView->setWordWrap(true);

    // 隐藏多余表头
    treeView->setHeaderHidden(true);
}

额外优化点

  • 懒加载子节点:如果虚拟文件系统数据量大,重写模型的fetchMore()和canFetchMore()方法,在用户展开文件夹时动态加载子节点,避免一次性加载全部数据。
  • 样式定制:通过QSS样式表调整项的选中状态、边框、背景色等,强化网格视觉效果。
  • 右键菜单:重写视图的contextMenuEvent方法,实现文件/文件夹的新建、删除等操作逻辑。

内容的提问来源于stack exchange,提问作者RoyA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 01:35:24