简介

本文讨论了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. 第十节 JMeter基础-初级购物车【接口关联-鉴权】

    声明:本文所记录的仅本次操作学习到的知识点,其中商城IP错误,请自行更改. 背景:商城购物车可以添加数据,也可以删除数据. 思路: 登录后添加购物车,加入成功后查看购物车列表. 购物车列表,随机删除, ...

  2. DSCL:已开源,北京大学提出解耦对比损失 | AAAI 2024

    监督对比损失(SCL)在视觉表征学习中很流行.但在长尾识别场景中,由于每类样本数量不平衡,对两类正样本进行同等对待会导致类内距离的优化出现偏差.此外,SCL忽略了负样本之间的相似关系的语义线索.为了提 ...

  3. 【MySQL】全库调整表大小写语句

    统一修改字段成小写+下划线的命名规则: V1上线后,重新看SQL调整的较可行的写法: # = = = = = = = = = = = = = = = 统一更改全库所有字段大小写脚本SQL(会删除字段原 ...

  4. 目前国内全地形能力最强的双足机器人 —— 逐际动力 —— 提出迭代式预训练(Iterative Pre-training)方法的强化学习算法

    相关: https://weibo.com/1255595687/O5k4Aj8l2 该公司对其产品的强化学习训练算法给出了较少的描述: 提出迭代式预训练(Iterative Pre-training ...

  5. Google的TPU的向量化内存的读取规格——单次读取/写入的数据量

    异构加速设备: GPU.TPU.NPU 这几种设备,除了GPU公开了部分硬件设计原理和软件编程范式以外,所有的TPU和NPU的资料都是不公开的,都是被其所属公司保留的,然后这些公司会自己在这些硬件之上 ...

  6. 【转载】 HTTP中的响应协议及302、304的含义

    原文地址: https://www.cnblogs.com/chenyablog/p/9197305.html ============================== 响应协议 HTTP/1.1 ...

  7. 记录一次实验室linux系统的GPU服务器死机故障的排查——Linux系统的Power States

    实验室的ubuntu服务器不知怎么的突然又崩溃了,死机重启,然后查看日志,发现了下面的情况: 由于从其他的日志中知道是显卡的问题引起的死机,而这个显卡的地址正好是D9:00,这部分的日志就不给出了.结 ...

  8. DeepMind Lab的一些python例子—————(Ubuntu22.04系统安装DeepMind Lab)后续

    相关资料: Ubuntu22.04系统安装DeepMind Lab ====================================================== 关于DeepMind ...

  9. [工具分享]ClipX超级粘贴板,超级好用

    1.背景 话说粘贴.复制是码农们的必备核心技能, 普通码农们当然已经熟练的掌握了普通的粘贴复制.... 但是,你不知道的是,牛逼的架构师已经会使用超级粘贴板了,功能非常强大 ............. ...

  10. css 优惠券波浪线效果

    <ul> <li> <i class="left"></i><span class="center"> ...