简介

本文讨论了QT文件系统模型QFileSystemModel的不足之处,并且讨论了改进目标,如何实现自定义文件系统模型,以及进一步改进的空间。

目录

QFileSystemModel的不足之处

改进目标

自定义文件系统模型

进一步改进的空间

正文

QFileSystemModel的不足之处

QFileSystemModel文件系统模型在实际使用过程中,经常先调用setRootPath()函数设置 一个目录作为根路径。

QFileSystemModel的setRootPath函数
QFileSystemModel实际使用过程中遇到两个问题:

一个问题是在搭配QTreeView和QTreeWidget使用时, 即使已经调用了这个函数,还需进一步调用QTreeView的setRootIndex()函数,才能在控件中只显示根路径中的文件和目录;否则仍然是显示计算机中的所有驱动器的信息。

QTreeView的setRootIndex()函数
另一个问题是在搭配QML的TreeView使用时,在QML中TreeView没有setRootIndex之类的函数,因此总是显示计算机中的所有驱动器的信息。

改进目标

本次的改进目标只有一个,就是实现一个自定义的文件系统模型,只要调用了setRootPath()函数指定了根路径,那么不管是和 QTreeView或QTreeWidget搭配使用,还是和QML的TreeView搭配使用,总是只显示根路径中的文件和目录。比如在根路径设置为 d:\butianyun的情况下,效果如图所示:

ButianyunFileSystemModel和QTreeView搭配使用

ButianyunFileSystemModel和QML中的TreeView搭配使用

自定义文件系统模型

使用自定义模型实现的文件系统模型时,从QAbstractItemModel派生一个新的类型ButianyunFileSystemModel。

头文件如下所示:

/**********************************************************************************
****************** Butianyun QT Video Lesson V2 ***********************************
*********** BUTIANYUN, QT Programming Trainning Professional **********************
***********************************************************************************/ #ifndef BUTIANYUNFILESYSTEMMODEL_H
#define BUTIANYUNFILESYSTEMMODEL_H #include <QAbstractItemModel> struct ButianyunFileSystemModelPrivate; class ButianyunFileSystemModel : public QAbstractItemModel
{
Q_OBJECT
Q_DECLARE_PRIVATE(ButianyunFileSystemModel)
Q_PROPERTY(QString rootPath READ rootPath WRITE setRootPath NOTIFY rootPathChanged)
Q_PROPERTY(QModelIndex rootIndex READ rootIndex CONSTANT) public:
Q_INVOKABLE explicit ButianyunFileSystemModel(QObject* p = nullptr);
~ButianyunFileSystemModel(); void setRootPath(const QString& value);
QString rootPath() const;
QModelIndex rootIndex() const;
ButianyunFileInfo fileInfo(const QModelIndex& index) const;
bool isValid(const QModelIndex& index) const;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int,QByteArray> roleNames() const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QModelIndex index(const QString& filePath, const QModelIndex &parent = QModelIndex()) const; signals:
void rootPathChanged(); public slots:
void clearCache(); private:
QSharedPointer<ButianyunFileSystemModelPrivate> d_ptr;
Q_DISABLE_COPY(ButianyunFileSystemModel)
}; #endif // BUTIANYUNFILESYSTEMMODEL_H

C++实现文件如下:

/**********************************************************************************
****************** Butianyun QT Video Lesson V2 ***********************************
*********** BUTIANYUN, QT Programming Trainning Professional **********************
***********************************************************************************/ #include "butianyunfilesystemmodel.h" void ButianyunFileSystemModel::setRootPath(const QString& value)
{
Q_D(ButianyunFileSystemModel);
QFileInfo fi(value);
if (fi.absoluteFilePath() == d->_rootPath)
{
return;
} d->_id_to_info_cache.clear();
d->_path_to_id_cache.clear();
d->_rootPath = fi.absoluteFilePath();
quintptr id = d->_id_to_info_cache.size() + 1;
d->_rootIndex = createIndex(0, 0, reinterpret_cast<const void*>(id));
emit rootPathChanged();
} QString ButianyunFileSystemModel::rootPath() const
{
const ButianyunFileSystemModelPrivate* d = d_func();
return d->_rootPath;
} QModelIndex ButianyunFileSystemModel::rootIndex() const
{
const ButianyunFileSystemModelPrivate* d = d_func();
return d->_rootIndex;
} ButianyunFileInfo ButianyunFileSystemModel::fileInfo(const QModelIndex& index) const
{
const ButianyunFileSystemModelPrivate* d = d_func();
quintptr pid = index.internalId();
if (0 == pid)
{
if (index.parent().isValid())
{
return ButianyunFileInfo();
}
pid = 1;
}
auto it = d->_id_to_info_cache.find(pid);
if (it == d->_id_to_info_cache.end())
{
return ButianyunFileInfo();
}
return it.value();
} bool ButianyunFileSystemModel::isValid(const QModelIndex& index) const
{
const ButianyunFileSystemModelPrivate* d = d_func();
if (index.column() >= ButianyunFileInfoRoleCount)
{
return false;
} quintptr pid = index.internalId();
if (0 == pid)
{
if (index.parent().isValid())
{
return false;
}
pid = 1;
}
auto it = d->_id_to_info_cache.find(pid);
if (it == d->_id_to_info_cache.end())
{
return false;
}
return it.value().isValid(pid);
} static QFileInfoList butianyun_get_file_info_list(const QString& filePath)
{
QDir dir(filePath);
return dir.entryInfoList(QDir::Filter::NoDotAndDotDot |QDir::Drives | QDir::Dirs |QDir::Files,
QDir::DirsFirst |QDir::Name |QDir::Type |QDir::IgnoreCase);
} QModelIndex ButianyunFileSystemModel::index(int row, int column, const QModelIndex &parent) const
{
ButianyunFileSystemModelPrivate* d = const_cast<ButianyunFileSystemModel*>(this)->d_func();
if (row < 0 || column < 0 || column >= ButianyunFileInfoRoleCount)
{
return QModelIndex();
}
quintptr pid = parent.internalId();
auto parent_fileinfo = fileInfo(parent); if (0 == pid && !parent.parent().isValid())
{
pid = 1;
}
if (!parent_fileinfo.isValid(pid))
{
return QModelIndex();
} QFileInfoList list = butianyun_get_file_info_list(parent_fileinfo.filePath);
if (row >= list.length())
{
return QModelIndex();
} quintptr this_id = 0;
return createIndex(row, column, reinterpret_cast<const void *>(this_id));
} QModelIndex ButianyunFileSystemModel::parent(const QModelIndex &child) const
{
const ButianyunFileSystemModelPrivate* d = d_func();
if (!child.isValid())
{
return QModelIndex();
} quintptr id = child.internalId();
auto fileinfo = fileInfo(child);
if (!fileinfo.isValid(id) || 0 == fileinfo.level)
{
return QModelIndex();
} int index = fileinfo.filePath.lastIndexOf("/");
if (index <= 0)
{
return QModelIndex();
} QString filePath = fileinfo.filePath.left(index);
if (filePath.isEmpty())
{
return QModelIndex();
} auto it = d->_path_to_id_cache.find(filePath);
if (it == d->_path_to_id_cache.end())
{
return QModelIndex();
} if (it.value() != fileinfo.pid)
{
return QModelIndex();
} quintptr pid = fileinfo.pid;
auto it2 = d->_id_to_info_cache.find(pid);
if (it2 == d->_id_to_info_cache.end())
{
return QModelIndex();
}
fileinfo = it2.value();
if (pid != fileinfo.id)
{
return QModelIndex();
}
return createIndex(fileinfo.row, 0, pid);
} int ButianyunFileSystemModel::rowCount(const QModelIndex &parent) const
{
quintptr pid = parent.internalId();
auto parent_fileinfo = fileInfo(parent);
if (0 == pid && !parent.parent().isValid())
{
pid = 1;
}
if (!parent_fileinfo.isValid(pid))
{
return 0;
} QFileInfoList list = butianyun_get_file_info_list(parent_fileinfo.filePath);
return list.length();
} int ButianyunFileSystemModel::columnCount(const QModelIndex &parent) const
{
return ButianyunFileInfoRoleCount;
} bool ButianyunFileSystemModel::hasChildren(const QModelIndex &parent) const
{
return rowCount(parent) > 0;
} QVariant ButianyunFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (Qt::Horizontal == orientation)
{
if (Qt::DisplayRole == role)
{
switch (section + Qt::UserRole + 1)
{
case Role_ButianyunFileName:
return tr("Name");
case Role_ButianyunFileSize:
return tr("Size");
case Role_ButianyunFileType:
return tr("Type");
case Role_ButianyunFileLastModified:
return tr("Data Modified");
default:
return QVariant();
}
}
}
else
{
if (Qt::DisplayRole == role)
{
return QString::number(section);
}
}
return QVariant();
} QVariant ButianyunFileSystemModel::data(const QModelIndex &index, int role) const
{
if (Qt::DisplayRole != role && Qt::DecorationRole != role)
{
return QVariant();
} if (!index.isValid())
{
return QVariant();
}
if (index.column() >= ButianyunFileInfoRoleCount)
{
return false;
} quintptr pid = index.internalId();
if (0 == pid && !index.parent().isValid())
{
pid = 1;
}
auto fileinfo = fileInfo(index); if (!fileinfo.isValid(pid))
{
return QVariant();
} QFileInfo fi(fileinfo.filePath);
switch (index.column() + Qt::UserRole + 1)
{
case Role_ButianyunFileName:
if (Qt::DisplayRole == role)
{
return fi.fileName();
}
else if (Qt::DecorationRole == role)
{
return fileinfo.fileicon();
} case Role_ButianyunFileSize:
{
return fileinfo.filesize();
} case Role_ButianyunFileType:
return fileinfo.filetype(); case Role_ButianyunFileLastModified:
return fi.lastModified().toString("yyyy-MM-dd hh:mm"); default:
return QVariant();
}
return QVariant();
} QHash<int,QByteArray> ButianyunFileSystemModel::roleNames() const
{
QHash<int,QByteArray> roles;
roles = QAbstractItemModel::roleNames();
roles[Role_ButianyunFileName] = QStringLiteral("FileName").toUtf8();
roles[Role_ButianyunFileSize] = QStringLiteral("FileSize").toUtf8();
roles[Role_ButianyunFileType] = QStringLiteral("FileType").toUtf8();
roles[Role_ButianyunFileLastModified] = QStringLiteral("LastModified").toUtf8();
return roles;
} Qt::ItemFlags ButianyunFileSystemModel::flags(const QModelIndex &index) const
{
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
} QModelIndex ButianyunFileSystemModel::index(const QString& filePath, const QModelIndex &parent) const
{
ButianyunFileSystemModelPrivate* d = const_cast<ButianyunFileSystemModel*>(this)->d_func();
auto it = d->_path_to_id_cache.find(filePath);
if (it == d->_path_to_id_cache.end())
{
return QModelIndex();
}
quintptr pid = parent.internalId();
if (0 == pid && !parent.parent().isValid())
{
pid = 1;
}
auto it2 = d->_id_to_info_cache.find(it.value());
if (it2 == d->_id_to_info_cache.end())
{
return QModelIndex();
}
ButianyunFileInfo fileinfo = it2.value();
if (fileinfo.pid != pid && fileinfo.level > 0)
{
return QModelIndex();
}
return createIndex(fileinfo.row, 0, pid);
}

和QTreeView搭配使用的测试代码如下所示:

    ButianyunFileSystemModel* model = new ButianyunFileSystemModel(this);
model->setRootPath(R"(d:\butianyun)");
QTreeView* tree = new QTreeView();
tree->setModel(model);
tree->setRootIndex(model->index(model->rootPath()));

和 QML 的 TreeView搭配使用的测试代码如下所示:

TreeView {
id: tree
anchors.topMargin: header.height + button_reset.height
anchors.fill: parent
model: fsmodel
columnWidthProvider: get_column_width
columnSpacing: 10
delegate: TreeViewDelegate {}
}

进一步改进的空间

目前这个实现版本的文件系统模型 ButianyunFileSystemModel,初步实现了前面讨论的改进目标。这个类型还存在一些进一步的改进空间,比如:

根目录中的文件变化的处理:QFileSystemModel能够感知到文件变化通知,并能做出对应的响应,而ButianyunFileSystemModel还不能感知到文件变化通知。ButianyunFileSystemModel提供了resetCache()函数用于手动触发重置操作。

没有实现QT模型类型中的常见操作canFetchMore()和 fetchMore()函数: QFileSystemModel实现了这两个函数。ButianyunFileSystemModel在内部使用 QHash 做了一定的缓存,已经避免了在目录中有很多文件时一次查询过多文件。

本文相关的另一个文章:

QT QML:QT疑难杂症之QML程序中如何使用文件系统模型QFileSystemModel?

总结

本文讨论了QT文件系统模型QFileSystemModel的不足之处,并且讨论了改进目标,如何实现自定义文件系统模型,以及进一步改进的空间。

如果您认为这篇文章对您有所帮助,请您一定立即点赞+喜欢+收藏,本文作者将能从您的点赞+喜欢+收藏中获取到创作新的好文章的动力。如果您认为作者写的文章还有一些参考价值,您也可以关注这篇文章的作者。

QT疑难杂症之如何使用自定义模型实现文件系统模型?类似QFileSystemModel,却比QFileSystemModel更好用的更多相关文章

  1. 使用WebGL 自定义 3D 摄像头监控模型

    前言 随着视频监控联网系统的不断普及和发展, 网络摄像机更多的应用于监控系统中,尤其是高清时代的来临,更加快了网络摄像机的发展和应用. 在监控摄像机数量的不断庞大的同时,在监控系统中面临着严峻的现状问 ...

  2. 基于 HTML5 的 WebGL 自定义 3D 摄像头监控模型

    前言 随着视频监控联网系统的不断普及和发展, 网络摄像机更多的应用于监控系统中,尤其是高清时代的来临,更加快了网络摄像机的发展和应用. 在监控摄像机数量的不断庞大的同时,在监控系统中面临着严峻的现状问 ...

  3. 第13章 TCP编程(4)_基于自定义协议的多线程模型

    7. 基于自定义协议的多线程模型 (1)服务端编程 ①主线程负责调用accept与客户端连接 ②当接受客户端连接后,创建子线程来服务客户端,以处理多客户端的并发访问. ③服务端接到的客户端信息后,回显 ...

  4. 第13章 TCP编程(3)_基于自定义协议的多进程模型

    5. 自定义协议编程 (1)自定义协议:MSG //自定义的协议(TLV:Type length Value) typedef struct{ //协议头部 ];//TLV中的T unsigned i ...

  5. dedecms自定义模型之独立模型在首页、列表页、内容调用内容

    dedecms关于自定义模型(独立模型)的首页.列表页.内容怎么调用?在后台自定义模型(独立模型)的建立及自定义字段的添加比较简单,需要注意两点: (1)如果某个字段需要在前台列表页显示,则在前台参数 ...

  6. Qt 学习之路 2(45):模型

    Home / Qt 学习之路 2 / Qt 学习之路 2(45):模型 Qt 学习之路 2(45):模型  豆子  2013年2月26日  Qt 学习之路 2  23条评论 在前面两章的基础之上,我们 ...

  7. Asp.net管道模型(管线模型)

    Asp.net管道模型(管线模型)   前言 为什么我会起这样的一个标题,其实我原本只想了解asp.net的管道模型而已,但在查看资料的时候遇到不明白的地方又横向地查阅了其他相关的资料,而收获比当初预 ...

  8. ThinkPHP 学习笔记 ( 三 ) 数据库操作之数据表模型和基础模型 ( Model )

    //TP 恶补ing... 一.定义数据表模型 1.模型映射 要测试数据库是否正常连接,最直接的办法就是在当前控制器中实例化数据表,然后使用 dump 函数输出,查看数据库的链接状态.代码: publ ...

  9. 二 Djano模型层之模型字段选项

    字段选项 以下参数是全部字段类型都可用的,而且是可选的 null 如果为True,Django将在数据库中将空值存储为NULL.默认值为False 对于字符串字段,如果设置了null=True意味着& ...

  10. ThinkPHP 数据库操作之数据表模型和基础模型 ( Model )

    一.定义数据表模型 1.模型映射 要测试数据库是否正常连接,最直接的办法就是在当前控制器中实例化数据表,然后使用 dump 函数输出,查看数据库的链接状态.代码: public function te ...

随机推荐

  1. [oeasy]python0037_终端_terminal_电传打字机_tty_shell_控制台_console_发展历史

    换行回车 回忆上次内容 换行 和 回车 是两回事 换行 对应字节0x0A Line-Feed 水平 不动 垂直 向上喂纸 所以是 feed 回车 对应字节0x0D Carriage-Return 垂直 ...

  2. 手把手帮助你搭建属于自己的个人博客,使用cervel部署,无需后端

    1.项目简介 项目使用了vue+elementUI技术栈,通过读取本地md文件实现博客文章的展示,使用vercel实现自动化部署,纯前端项目,无需后端 第一步:下载源码 仓库地址: github:ht ...

  3. Javascript 转Date对象为字符串实现函数

    转Date对象为字符串实现函数 function formatDate(time, format = "Y-MM-dd HH:mm:ss") { /** 格式化字符说明 Y 年 四 ...

  4. java面试一日一题:垃圾回收器如何组合使用

    问题:请讲下java中垃圾回收器如何组合使用 分析:该问题主要考察对垃圾回收器的深度理解 回答要点: 主要从以下几点去考虑, 1.垃圾回收器有哪些种类,每种的特点 2.组合使用怎么理解 在上篇文章&l ...

  5. 快速将headers转字典

    使用Headers插件完成快捷操作 在pycharm的Preferences-Plugins-Marketplace下搜索Headers install安装.apply应用,ok确定 接下来只要复制相 ...

  6. 最新SEO自动外链蜘蛛池工具促进百度快速收录使用方法介绍

    此工具集成市面上所有自动外链网站的资源链接,经过合并.去重.筛选.验证 总结出最终的外链资源 ,软件实时更新 本软件将您繁杂的外链推广转为自动化进行,并且加入站群的支持,您只需要将你的站群域名粘贴到软 ...

  7. 【Java】关于获取注解的问题发现

    同事设置了个注解,想用Spring获取的Bean来找到Class获取注解 但是发现是空的,在查看的Spring返回Bean之后,发现这个Bean对象并不是原生的实例 而是被Spring代理增强的代理对 ...

  8. 汽车模具设计软件 —— 达索集团的Catia

    相关: https://www.3ds.com/zh/products-services/catia/ Catia是Dassault Systems公司推出的产品造型软件,广泛应用于汽车.航空.机械等 ...

  9. 为什么我要弃用华为的软件产品——mindspore从入门到放弃之感想

    从本博主前段时间的博文就可以看到博主写了好多关于华为软件mindspore的入门资料和编译方法以及一些bug的修复,但是无奈之下发现这些简单而且显而易见的bug不仅长期存在与软件中而且在提交PR后而一 ...

  10. 如何在AWS上构建Apache DolphinScheduler

    引言 随着云计算技术的发展,Amazon Web Services (AWS) 作为一个开放的平台,一直在帮助开发者更好的在云上构建和使用开源软件,同时也与开源社区紧密合作,推动开源项目的发展. 本文 ...