⭐️我叫忆_恒心,一名喜欢书写博客的在读研究生。
如果觉得本文能帮到您,麻烦点个赞
呗!
近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧,喜欢的小伙伴给个三连支持一下呗。⭐️❤️
最近刚好重新学习了一下Qt的知识,觉得知识点之间还是比较散乱,网上大部份书籍的例子也比较零碎,想找个一个实战的项目将一些知识点穿起来,同时项目本身又比较有趣,可以进行不断地优化,发现文件查找器系统是个很有趣的实践项目。
作为Qt的入门项目,我个人觉得这个项目真的是太棒了,涵盖了许多知识点,同时还可以对此不断地进行优化。
本文将对Qt文件查找系统进行技术总结。该系统包含了许多技术细节,包括文件管理器的打开、记忆下拉框内容、filetable
的使用、文件信息显示、文件图标获取、自定义排序、QFileSystemWatch
监视文件目录、变化后重新加载目录、库的制作、qmake使用、makefile生成外部库后引入到项目中、Qt调用第三方库的方法、表格激活状态下选中右键、右键菜单、删除功能实现、启动文件、复制文件名以及检测剪切板、快捷键使用、快捷键冲突、窗口焦点捕获、托盘、系统消息设置、正则化表达式的书写、正则化表达式的应用、模糊匹配中按照顺序进行匹配、模糊匹配中遇到空格的处理、模糊匹配算法的优化、以及如何提高模糊查询的效率(QtConcurrent)。
如果你觉得这篇文章帮到了你,欢迎
点赞+收藏+评论
, 对系统感兴趣的,欢迎订阅这个专栏欧,后面代码
会放到专栏中,并在这个专栏里进行更新讲解
欧!感谢支持⭐️❤️
将作业要求分为了七个功能点,每个功能点下会列出需要地知识点,
功能点的实现是这些知识点的最佳实践
这个项目个人觉得非常有意思,所以花了一些时间去设计,也遇到了挺多Bug的,最近在忙着毕业设计,在论文写完后,后面会尝试对这个项目进行优化地,如果有兴趣地小伙伴可以关注一下专栏后期会一直在上面进行更新地,一周至少更新一篇。
- 打开文件管理器进行目录选择
- 记忆下拉框的内容
filetable
使用- 文件信息显示
- 文件图标的获取
- 自定义排序
要想实现文件查找功能大致分为以下几步:
要使用Qt实现一个类似Everything文件搜索工具,可以按照以下步骤进行:
Qt GUI
应用程序,并添加一个窗口作为搜索工具的主界面。Qt
的QFileSystemModel
类来遍历文件系统中的文件和目录。QFileSystemModel
类是一个模型-视图类,可以用来访问本地文件系统中的文件和目录。QRegExp
类来匹配搜索关键字。QRegExp
类是Qt中的正则表达式类,可以用来进行高级字符串匹配操作。QTableView
类来显示搜索结果。QTableView
类是一个表格视图类,可以用来显示数据。可以将搜索结果显示为表格,并在表格中显示文件名、文件路径、文件大小和文件修改日期等信息。关于显示地结果:
注:
a. 文件夹无需展示大小
b. 搜索结果无需包含桌面文件夹中的子文件和子文件夹;
文件夹无需显示大小:
// 大小
QTableWidgetItem *sizeItem = nullptr;
// 如果是目录则sizeItem值为空
QFileInfo fileInfo(filePath);
if(fileInfo.isDir()){
// 如果是目录,直接设置为nullptr, 不显示大小
sizeItem = nullptr;
}else{
// 如果是目录,计算文件大小并设置为sizeItem
sizeItem = new QTableWidgetItem(tr("%1 KB")
.arg(int((size + 1023) / 1024)));
sizeItem->setData(absoluteFileNameRole, QVariant(filePath));
sizeItem->setToolTip(toolTip);
sizeItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
sizeItem->setFlags(sizeItem->flags() ^ Qt::ItemIsEditable);
}
如果是只通过编写代码的话,就直接通过connect
函数进行槽的绑定就可以啦。
这里主要尝试ui下的槽函数,可视化添加槽。
具体的槽函数实现功能:
// 当查找的按钮被触碰时触发的槽函数
void MainWindow::on_findButton_clicked()
{
// 注意这里用的filesTable 是wiget类型的
ui->filesTable->setRowCount(0);
QString fileName = ui->fileComboBox->currentText();
QString text = ui->textComboBox->currentText();
QString path = QDir::cleanPath(ui->directoryCombox->currentText());
// 查找并显示文件
currentDir = QDir(path);
// 更新目录
watcher.removePath(currentDir.path());
watcher.addPath(path);
findFilesInDirectory(path, ui->fileComboBox->currentText(), ui->textComboBox->currentText());
QStringList files;
// 文件名进行查找文件
findRecursion(path, fileName.isEmpty() ? QStringLiteral("*") : fileName, &files);
// 在已经查找出的文件进一步查找哪些包含了text
if(!text.isEmpty())
files = findFiles(files, text);
files.sort();
showFiles(files);
}
目录选择
void MainWindow::on_broweButton_clicked()
{
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this,
tr("Find Files"),
QDir::currentPath()));
if(!directory.isEmpty()){
if(ui->directoryCombox->findText(directory) == -1)
ui->directoryCombox->addItem(directory);
ui->directoryCombox->setCurrentIndex(ui->directoryCombox->findText(directory));
}
}
// 表头排序指示器
ui->filesTable->setSortingEnabled(true); // 允许排序
ui->filesTable->horizontalHeader()->setSortIndicatorShown(true); // 显示排序指示器
具体实现:
void MainWindow::createFilesTable()
{
ui->filesTable = new QTableWidget(0,3);
ui->filesTable->setSelectionBehavior(QAbstractItemView::SelectRows);
QStringList labels;
labels << tr("Filename") <<tr("Path")<< tr("Size");
ui->filesTable->setHorizontalHeaderLabels(labels);
ui->filesTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
ui->filesTable->verticalHeader()->hide();
ui->filesTable->setShowGrid(false);
// 表头排序指示器
// ui->filesTable->setSortingEnabled(true); // 允许排序
// ui->filesTable->horizontalHeader()->setSortIndicatorShown(true); // 显示排序指示器
// 菜单
ui->filesTable->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->filesTable, &QTableWidget::customContextMenuRequested,
this, &MainWindow::contextMenu);
connect(ui->filesTable, &QTableWidget::cellActivated,
this, &MainWindow::openFileOfItem);
}
不过有点问题:
因为size
字段带了单位后是字符,字符的大小比较会有点问题,因此在实现中先关闭。
解决办法:
1、通过文本排序QTextListFormat
的style
和index
进行排序。
2、lamb表达式对排序规则进行自定义。
QIcon icon = iconProvider.icon(filePath);
具体实现:
void MainWindow::showFiles(const QStringList &paths)
{
QFileIconProvider iconProvider;
for(const QString &filePath : paths){
// 获取图标
const QString toolTip = QDir::toNativeSeparators(filePath);
const QString relativePath = QDir::toNativeSeparators(currentDir.relativeFilePath((filePath)));
const qint64 size = QFileInfo(filePath).size();
QIcon icon = iconProvider.icon(filePath);
QTableWidgetItem *fileNameItem = new QTableWidgetItem(relativePath);
fileNameItem->setData(absoluteFileNameRole, QVariant(filePath));
fileNameItem->setToolTip(toolTip);
fileNameItem->setFlags(fileNameItem->flags() ^ Qt::ItemIsEditable);
// 添加图标进入名称的位置
fileNameItem->setIcon(icon);
// 添加路径
QTableWidgetItem *pathItem = new QTableWidgetItem(filePath);
pathItem->setData(absoluteFileNameRole, QVariant(filePath));
pathItem->setToolTip(toolTip);
pathItem->setFlags(fileNameItem->flags() ^ Qt::ItemIsEditable);
// 大小
QTableWidgetItem *sizeItem = nullptr;
// 如果是目录则sizeItem值为空
QFileInfo fileInfo(filePath);
if(fileInfo.isDir()){
// 如果是目录,直接设置为nullptr, 不显示大小
sizeItem = nullptr;
}else{
// 如果是目录,计算文件大小并设置为sizeItem
sizeItem = new QTableWidgetItem(tr("%1 KB")
.arg(int((size + 1023) / 1024)));
sizeItem->setData(absoluteFileNameRole, QVariant(filePath));
sizeItem->setToolTip(toolTip);
sizeItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
sizeItem->setFlags(sizeItem->flags() ^ Qt::ItemIsEditable);
}
int row = ui->filesTable->rowCount();
ui->filesTable->insertRow(row);
ui->filesTable->setItem(row, 0, fileNameItem);
ui->filesTable->setItem(row, 1, pathItem);
ui->filesTable->setItem(row, 2, sizeItem);
}
ui->fileFoundLabel->setText(tr("%n file(s) found (Double click on a file to open it)", nullptr, paths.size()));
ui->fileFoundLabel->setWordWrap(true);
}
- 表格激活状态下选中右键
- 右键菜单
- 删除功能实现
- 启动文件
- 复制文件名以及检测剪切板
在createFilesTable
函数中实现
// 菜单
ui->filesTable->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->filesTable, &QTableWidget::customContextMenuRequested,
this, &MainWindow::contextMenu);
connect(ui->filesTable, &QTableWidget::cellActivated,
this, &MainWindow::openFileOfItem);
void MainWindow::contextMenu(const QPoint &pos)
{
const QTableWidgetItem *item = ui->filesTable->itemAt(pos);
if(!item)
return;
QMenu menu;
#ifndef QT_NO_CLIPBOARD
QAction *copyAction = menu.addAction("Copy Name");
#endif
QAction *openAction = menu.addAction("Open");
deleteAction = menu.addAction("delete");
QAction *action = menu.exec(ui->filesTable->mapToGlobal(pos));
if(!action)
return;
const QString fileName = fileNameOfItem(item);
if(action == openAction)
openFile(fileName);
else if (action == deleteAction){
qDebug()<<"deleteAction"<<endl;
deleteFileFromContextMenu();
// connect(deleteAction, &QAction::triggered, this, &MainWindow::deleteFileFromContextMenu);
}
#ifndef QT_NO_CLIPBOARD
else if (action == copyAction)
QGuiApplication::clipboard()->setText(QDir::toNativeSeparators(fileName));
#endif
}
具体的实现思路:
- 需要定义一个
QAction
- 根据QAction 唤醒对应的功能
void MainWindow::deleteFileFromContextMenu()
{
QList<QTableWidgetItem *> selectedItems = ui->filesTable->selectedItems();
QMessageBox::StandardButton reply = QMessageBox::question(this, tr("Delete"), tr("Are you sure to delete the selected files?"), QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
for (QTableWidgetItem *item : selectedItems) {
QString filePath = item->data(absoluteFileNameRole).toString();
QFile file(filePath);
file.remove();
ui->filesTable->removeRow(item->row());
}
}
}
oid MainWindow::openFile(const QString &fileName)
{
QDesktopServices::openUrl(QUrl::fromLocalFile(fileName));
}
- 快捷键使用
- 快捷键冲突
- 窗口焦点捕获
- 托盘
- 系统消息设置
详细内容将更新在专栏中,有兴趣的小伙伴可以关注这个专栏
- 正则化表达式的书写
- 正则化表达式的应用
- 模糊匹配中按照顺序进行匹配
- 模糊匹配中遇到空格的处理
- 模糊匹配算法的优化
- 如何提高模糊查询的效率(QtConcurrent)
要实现模糊查询,您可以使用正则化表达式来匹配文件名。正则表达式可以匹配类似于通配符的模式,并可以通过捕获组提取有用的信息。
这个功能真是饶了太久了,一开始没弄清楚需求。
先说一下需求是这样的:
支持模糊匹配搜索,例如输入“wps”,能够搜到 “wps.exe”也能搜到 “windows powsershell.exe";
需要定位到需要修改的函数。
详细内容将更新在专栏中,有兴趣的小伙伴可以关注这个专栏
支持模糊匹配搜索,例如输入“wps”,能够搜到 “wps.exe”也能搜到 “windows powsershell.exe"; 中文搜索问题。
可以按照以下步骤进行实现:
- 库的制作
- qmake使用
- makefile生成外部库后引入到项目中
- Qt调用第三方库的方法
pinyin4cpp 有点坑啊。。。 很多词都对不上的!!!
比如微信
生成的居然是wei wei
早知道这么垃圾就不用了。。。
留下一个坑待修复:
详细内容将更新在专栏中,有兴趣的小伙伴可以关注这个专栏
// some bug in here
HanyuPinyinOutputFormat *outputFormat;
PinyinHelper::toHanyuPinyinStringArray(firstChar, outputFormat, &pinyinList); // 获取文件名对应的汉语拼音
QString firstLetters; // 存储汉语拼音的首字母
foreach(const QString& pinyin, pinyinList) {
qDebug()<<"pinyin "<<pinyin<<endl;
if (!pinyin.isEmpty() && pinyin.at(0).isLower()) {
firstLetters += pinyin.at(0);
qDebug()<<"firstLetters "<<firstLetters<<endl;
if (fileName.contains(firstLetters, Qt::CaseInsensitive))
{
result->append(filePath);
}
break;
}
}
只匹配到第一个字母就返回中文的。
QFileSystemWatch
监视文件目录- 变化后重新加载目录
详细内容将更新在专栏中,有兴趣的小伙伴可以关注这个专栏
s
接下来测试一下:
当我们选定了需要监控的目录,在改目录下更新的所有操作都会被
项目Ui优化
知识点:
简单地为每个目录创建多个线程,容易因为迭代深度太高二导致性能问题。 糟糕的设计方法
QList> futures;
foreach (const QString& dir, currentDir.entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot))
{
SearchParam param;
param.path = prefix + dir;
param.fileName = fileName;
futures.append(QtConcurrent::run(this, &MainWindow::searchDirectory, param, result));
}
QFutureSynchronizer sync;
sync.waitForFinished();
解决思路:用线程池进行管理。
详细内容将更新在专栏中,有兴趣的小伙伴可以关注这个专栏
本文将对Qt文件查找系统进行技术总结。该系统包含了许多技术细节,包括文件管理器的打开、记忆下拉框内容、
filetable
的使用、文件信息显示、文件图标获取、自定义排序、QFileSystemWatch
监视文件目录、变化后重新加载目录、库的制作、qmake使用、makefile生成外部库后引入到项目中、Qt调用第三方库的方法、表格激活状态下选中右键、右键菜单、删除功能实现、启动文件、复制文件名以及检测剪切板、快捷键使用、快捷键冲突、窗口焦点捕获、托盘、系统消息设置、正则化表达式的书写、正则化表达式的应用、模糊匹配中按照顺序进行匹配、模糊匹配中遇到空格的处理、模糊匹配算法的优化、以及如何提高模糊查询的效率(QtConcurrent)。
filetable
作为文件管理的核心组件,可以用于显示文件信息以及进行自定义排序。通过获取文件图标,系统可以使用户对文件进行更加直观的辨认。QFileSystemWatch
这个项目仅仅是作为新手学习的时候的学习笔记,在实现的地方可能会存在一些不合理的地方,烦请大佬批评与指正,⭐️:⭐️ ⭐️
在实现文件系统时,总有些疑惑就是:Everything这种本地文件搜索器是如何这么快检索到需要的文件呢?
非常期待各位大佬留言解惑!
最后,最后
如果觉得有用,麻烦三连⭐️❤️支持一下呀,希望这篇文章可以帮到你,你的点赞是我持续更新的动力