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]python0074_修改字体背景颜色_background_color_背景色
修改背景色 回忆上次内容 上次将asciiart和颜色一起来玩 7 种基本色 变化多端 不过到目前为止 改的 都是前景色 背景色可以修改吗? 重温参数 具体动手试试 print("\033[ ...
- 几个适合Java开发者的免费IDEA插件
今天,给大家推荐几个好用且免费的IntelliJ IDEA插件.如果你还没有用过,可以尝试一下,也许对你的日常工作会有一定的效率提升噢! RestFulTool 如果你是一个RESTful服务的开发者 ...
- Linux安装 JDK (CentOS 7)
Linux安装 JDK 一.Linux安装软件的方式 第一种:二进制发布包安装: 软件已经针对具体平台编译打包发布,只要解压,修改配置即可 第二种: rpm安装 : 软件已经按照redhat的包管理规 ...
- Known框架实战演练——进销存框架搭建
本文介绍如何使用Known开发框架搭建进销存管理系统的项目结构,以及开发前的一些配置和基础代码. 项目代码:JxcLite 开源地址: https://gitee.com/known/JxcLite ...
- Python项目批量管理第三方包(requirements.txt)
python项目中必须包含一个 requirements.txt 文件,用于记录所有依赖包及其精确的版本号,以便新环境部署. requirements.txt可以通过pip命令自动生成和安装 生成re ...
- c++ 快速复习第一部份
去年有事无事学过一c++ ,,由于工作用不上,学来没有用,所以学得乱七八的,最近需要复习一下,因为最近想学习一下硬 件相关 第一 引用头文件和自定义头 #include <iostream& ...
- activity向上/向下一个页面发送数据
向下一个活动发送数据 一般使用bundle(捆) 使用方法 创建并准备发送数据 //这里是上一个页面 Intent intent = new Intent(this,class); //新建意图 Bu ...
- 记一次seata启动错误日志ErrMsg:failed to req API:/nacos/v1/ns/instance after all servers....
错误日志如下: java.lang.RuntimeException: ErrCode:500, ErrMsg:failed to req API:/nacos/v1/ns/instance afte ...
- 【C】Re03
一.变量 变量存储了两个东西: 1.内存空间地址 2.内存空间存放的值 本质是给内存地址起了一个别名,允许我们通过别名对内存进行访问 void variable01() { int a = 100; ...
- 【转载】【技术杂谈】shell和terminal
分享视频: [技术杂谈]shell和terminal