项目背景
能够管理本地和远程文件(例如通过网络共享路径)并执行常见操作的工具。这些操作包括文件复制、剪切、删除等,且操作后能动态更新用户界面中显示的文件系统。项目中需要使用 QTreeView
作为文件浏览的界面,通过多线程处理文件操作任务,并从配置文件中读取管理路径。
项目功能概述
- 文件及文件夹管理:通过
QTreeView
显示目录结构,递归遍历文件夹,并能进行操作后自动刷新。 - 多线程文件操作:为每个文件操作(复制、剪切、删除)创建了专门的线程,避免主线程阻塞。
- 配置文件读取:支持从
config.ini
中读取配置文件来获取需要管理的文件路径,支持网络共享路径和本地路径。 - 操作反馈:在执行文件操作时,使用信号-槽机制向用户界面反馈操作进度。
- 灵活性:通过添加根目录的机制,保持目标路径结构与源路径一致。
关键功能说明
1. 配置文件读取
项目需要从配置文件 config.ini
读取路径信息。以下是 config.ini
文件的示例结构:
[operpath]
1\val="E:\\Qice Images"
2\val="//DESKTOP-IU21M5R//Qice Images"
我们通过 QSettings
读取配置文件中的每个路径,并返回管理路径的列表。
关键代码:
#include <QSettings>
#include <QStringList>
QStringList getOperPaths(const QString &configFilePath = "config.ini") {
QSettings settings(configFilePath, QSettings::IniFormat);
settings.beginGroup("operpath");
QStringList operPaths;
QStringList groups = settings.childGroups();
foreach (const QString &group, groups) {
settings.beginGroup(group);
QString path = settings.value("val").toString();
operPaths << path;
settings.endGroup();
}
settings.endGroup();
return operPaths;
}
2. 递归加载文件目录到 QTreeView
为了展示文件系统结构,项目中递归遍历给定的文件路径,并将其加载到 QTreeView
中。该函数每次都会递归进入子目录,加载所有文件夹和文件。
关键代码:
void MainWindow::populateFileList(const QStringList &initialDirPaths) {
model->clear(); // 清除现有数据
foreach (QString path, initialDirPaths) {
QDir dir(path);
if (!dir.exists()) {
continue;
}
QStandardItem *rootItem = new QStandardItem(dir.dirName());
rootItem->setData(path, Qt::UserRole);
model->invisibleRootItem()->appendRow(rootItem);
findFilesInDirectories(path, rootItem); // 递归加载文件
}
ui->treeView->expandAll(); // 展开所有节点
}
void MainWindow::findFilesInDirectories(const QString &dirPath, QStandardItem *parentItem) {
QDir dir(dirPath);
QFileInfoList list = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
foreach (QFileInfo fileInfo, list) {
QStandardItem *item = new QStandardItem(fileInfo.fileName());
item->setData(fileInfo.absoluteFilePath(), Qt::UserRole);
parentItem->appendRow(item);
if (fileInfo.isDir()) {
findFilesInDirectories(fileInfo.absoluteFilePath(), item); // 递归进入子目录
}
}
}
3. 多线程文件操作(复制、剪切、删除)
文件操作任务(如复制、剪切、删除)是耗时的,因此使用了 QThread
来处理这些操作。每个操作完成后会触发信号,通知主线程进行刷新。
关键代码:
void ImageMoverThread::run() {
if (rootPath.isEmpty()) {
emit progress(0, tc("源路径为空"));
return;
}
QDir dir(rootPath);
if (!dir.exists()) {
emit progress(0, tc("源路径不存在"));
return;
}
if (m_operationType == Copy || m_operationType == Cut) {
if (targetPath.isEmpty()) {
emit progress(0, tc("目标路径为空"));
return;
}
QString rootFolderName = QDir(rootPath).dirName();
QString targetRootPath = targetPath + "/" + rootFolderName;
if (!QDir(targetRootPath).exists()) {
if (!QDir().mkdir(targetRootPath)) {
emit progress(0, tc("无法创建目标根目录文件夹"));
return;
}
}
processDirectory(rootPath, targetRootPath); // 递归处理复制/剪切
if (m_operationType == Cut) {
deleteDirectory(rootPath); // 剪切操作后删除源文件
}
} else if (m_operationType == Delete) {
deleteDirectory(rootPath); // 删除操作
}
emit operationCompleted(); // 操作完成,发出信号刷新视图
}
4. 删除操作
删除操作会递归删除文件夹中的文件,但保留文件夹本身。
关键代码:
void ImageMoverThread::deleteDirectory(const QString &dirPath) {
QDir dir(dirPath);
QFileInfoList list = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
foreach (QFileInfo fileInfo, list) {
if (fileInfo.isDir()) {
deleteDirectory(fileInfo.absoluteFilePath()); // 递归处理子文件夹
} else {
if (!QFile::remove(fileInfo.absoluteFilePath())) {
emit progress(0, tc("删除文件失败: ") + fileInfo.fileName());
}
}
}
emit progress(100, tc("目录已清理文件: ") + dirPath);
}
5. 刷新 QTreeView
当文件操作完成后,触发刷新文件树结构的操作,以便显示最新的文件状态。
关键代码:
void MainWindow::refreshTreeView() {
QStringList paths = getOperPaths(); // 重新获取管理路径
populateFileList(paths); // 重新填充文件树
}