QT疑难杂症之如何使用自定义模型实现文件系统模型?类似QFileSystemModel,却比QFileSystemModel更好用
简介
本文讨论了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更好用的更多相关文章
- 使用WebGL 自定义 3D 摄像头监控模型
前言 随着视频监控联网系统的不断普及和发展, 网络摄像机更多的应用于监控系统中,尤其是高清时代的来临,更加快了网络摄像机的发展和应用. 在监控摄像机数量的不断庞大的同时,在监控系统中面临着严峻的现状问 ...
- 基于 HTML5 的 WebGL 自定义 3D 摄像头监控模型
前言 随着视频监控联网系统的不断普及和发展, 网络摄像机更多的应用于监控系统中,尤其是高清时代的来临,更加快了网络摄像机的发展和应用. 在监控摄像机数量的不断庞大的同时,在监控系统中面临着严峻的现状问 ...
- 第13章 TCP编程(4)_基于自定义协议的多线程模型
7. 基于自定义协议的多线程模型 (1)服务端编程 ①主线程负责调用accept与客户端连接 ②当接受客户端连接后,创建子线程来服务客户端,以处理多客户端的并发访问. ③服务端接到的客户端信息后,回显 ...
- 第13章 TCP编程(3)_基于自定义协议的多进程模型
5. 自定义协议编程 (1)自定义协议:MSG //自定义的协议(TLV:Type length Value) typedef struct{ //协议头部 ];//TLV中的T unsigned i ...
- dedecms自定义模型之独立模型在首页、列表页、内容调用内容
dedecms关于自定义模型(独立模型)的首页.列表页.内容怎么调用?在后台自定义模型(独立模型)的建立及自定义字段的添加比较简单,需要注意两点: (1)如果某个字段需要在前台列表页显示,则在前台参数 ...
- Qt 学习之路 2(45):模型
Home / Qt 学习之路 2 / Qt 学习之路 2(45):模型 Qt 学习之路 2(45):模型 豆子 2013年2月26日 Qt 学习之路 2 23条评论 在前面两章的基础之上,我们 ...
- Asp.net管道模型(管线模型)
Asp.net管道模型(管线模型) 前言 为什么我会起这样的一个标题,其实我原本只想了解asp.net的管道模型而已,但在查看资料的时候遇到不明白的地方又横向地查阅了其他相关的资料,而收获比当初预 ...
- ThinkPHP 学习笔记 ( 三 ) 数据库操作之数据表模型和基础模型 ( Model )
//TP 恶补ing... 一.定义数据表模型 1.模型映射 要测试数据库是否正常连接,最直接的办法就是在当前控制器中实例化数据表,然后使用 dump 函数输出,查看数据库的链接状态.代码: publ ...
- 二 Djano模型层之模型字段选项
字段选项 以下参数是全部字段类型都可用的,而且是可选的 null 如果为True,Django将在数据库中将空值存储为NULL.默认值为False 对于字符串字段,如果设置了null=True意味着& ...
- ThinkPHP 数据库操作之数据表模型和基础模型 ( Model )
一.定义数据表模型 1.模型映射 要测试数据库是否正常连接,最直接的办法就是在当前控制器中实例化数据表,然后使用 dump 函数输出,查看数据库的链接状态.代码: public function te ...
随机推荐
- [oeasy]python0127_中文系统_gbk_BIG5_南极星_内码转化
中文系统bgk 回忆上次内容 汉字字形通过 点阵式打字机 像素级寻址的屏幕 进入了计算机的世界 添加图片注释,不超过 140 字(可选) 在海峡对岸的台湾同胞 也进入了汉字时代 他 ...
- .NET科普:.NET简史、.NET Standard以及C#和.NET Framework之间的关系
最近在不少自媒体上看到有关.NET与C#的资讯与评价,感觉大家对.NET与C#还是不太了解,尤其是对2016年6月发布的跨平台.NET Core 1.0,更是知之甚少.在考虑一番之后,还是决定写点东西 ...
- Linux安装 JDK (CentOS 7)
Linux安装 JDK 一.Linux安装软件的方式 第一种:二进制发布包安装: 软件已经针对具体平台编译打包发布,只要解压,修改配置即可 第二种: rpm安装 : 软件已经按照redhat的包管理规 ...
- SQL Server 图解备份(完全备份、差异备份、增量备份)和还原
常用的数据备份方式有完全备份.差异备份以及增量备份,那么这三种备份方式有什么区别,在具体应用中又该如何选择呢? 1.三种备份方式 完全备份(Full Backup):备份全部选中的文件夹,并不依赖文件 ...
- python json.loads()字符串转json
python json.loads()字符串转json import jsonimport requestsres = '''{"code":200,"message& ...
- 基于 TrueLicense 项目证书生成
一.创建公钥私钥 1.首先要用 KeyTool 工具来生成私匙库:(-alias别名 -validity 3650 表示10年有效,这个时间不是License的过期时间) keytool -genke ...
- 【DataBase】MySQL 13 分组查询
视频参考自:P59 - P68 https://www.bilibili.com/video/BV1xW411u7ax 分组查询 GROUP BY -- group by 子句 -- 要注意!grou ...
- 【SpringMVC】02 流程分析
首先是web.xml配置SpringMVC最核心的东西,DispatcherServlet 也称为[请求分发器][前端控制器][分发Servlet] <!--1.注册DispatcherServ ...
- 【JS】01 JavaScript概述
感觉上来谈这个前端,结构的部分是使用了标签语言定义了页面的文档内容 但是XML和HTML的功能完全相反,XML被用于存储信息,而HTML则用于定义网页结构,辅助其他内容渲染 然后C3又可以通过选择器这 ...
- 【Vue】Re12 Webpack 第三部分(插件、热部署、配置分离)
一.HtmlWebpackPlugin webpack插件安装: npm install html-webpack-plugin --save-dev // 版本太高构建报错原因换这个 npm ins ...