QFileSystemWatcher技术
QFileSystemWatcher 类概述
在 Qt 中,QFileSystemWatcher 类提供了一种方便的方式来监视文件和目录的变化。当被监视的文件或目录发生修改、重命名或删除时,该类会发出相应的信号,使应用程序能够及时响应这些变化。
核心功能与方法
1. 添加监视路径
· addPath(const QString &path):添加单个文件或目录到监视列表
· addPaths(const QStringList &paths):添加多个文件或目录到监视列表
2. 移除监视路径
· removePath(const QString &path):从监视列表中移除单个文件或目录
· removePaths(const QStringList &paths):从监视列表中移除多个文件或目录
3. 获取当前监视的路径
· files():返回正在监视的文件列表
· directories():返回正在监视的目录列表
4. 信号
· fileChanged(const QString &path):当监视的文件发生变化时发出
· directoryChanged(const QString &path):当监视的目录发生变化时发出
代码示例:文件系统监视器
下面是一个完整的示例,展示如何使用 QFileSystemWatcher 来监视文件和目录的变化:
#include <QApplication>
#include <QMainWindow>
#include <QFileSystemWatcher>
#include <QTextEdit>
#include <QListWidget>
#include <QPushButton>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QSplitter>
#include <QLabel>
#include <QDateTime>
#include <QMessageBox>
class FileSystemWatcherDemo : public QMainWindow {
Q_OBJECT
public:
FileSystemWatcherDemo(QWidget *parent = nullptr) : QMainWindow(parent) {
setupUI();
setupConnections();
// 初始化文件系统监视器
watcher = new QFileSystemWatcher(this);
connect(watcher, &QFileSystemWatcher::fileChanged, this, &FileSystemWatcherDemo::onFileChanged);
connect(watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemWatcherDemo::onDirectoryChanged);
setWindowTitle(“Qt文件系统监视器示例”);
resize(800, 600);
}
private slots:
void onAddFile() {
QString filePath = QFileDialog::getOpenFileName(this, “选择要监视的文件”);
if (!filePath.isEmpty()) {
if (watcher->addPath(filePath)) {
addToWatchList(filePath, true);
logMessage(“已添加文件监视: ” + filePath);
} else {
logMessage(“无法监视文件: ” + filePath);
}
}
}
void onAddDirectory() {
QString dirPath = QFileDialog::getExistingDirectory(this, “选择要监视的目录”);
if (!dirPath.isEmpty()) {
if (watcher->addPath(dirPath)) {
addToWatchList(dirPath, false);
logMessage(“已添加目录监视: ” + dirPath);
} else {
logMessage(“无法监视目录: ” + dirPath);
}
}
}
void onRemoveSelected() {
QList<QListWidgetItem*> selectedItems = watchList->selectedItems();
for (QListWidgetItem *item : selectedItems) {
QString path = item->text();
bool isFile = item->data(Qt::UserRole).toBool();
if (isFile ? watcher->removePath(path) : watcher->removePath(path)) {
logMessage(“已移除监视: ” + path);
delete item;
} else {
logMessage(“无法移除监视: ” + path);
}
}
}
void onClearAll() {
QStringList files = watcher->files();
QStringList directories = watcher->directories();
if (!files.isEmpty()) watcher->removePaths(files);
if (!directories.isEmpty()) watcher->removePaths(directories);
watchList->clear();
logMessage(“已清除所有监视路径”);
}
void onFileChanged(const QString &path) {
logMessage(“文件发生变化: ” + path + ” – ” + QDateTime::currentDateTime().toString());
// 如果文件被删除,自动从监视列表中移除
if (!QFile::exists(path)) {
watcher->removePath(path);
removeFromWatchList(path);
logMessage(“文件已被删除,自动移除监视: ” + path);
}
}
void onDirectoryChanged(const QString &path) {
logMessage(“目录发生变化: ” + path + ” – ” + QDateTime::currentDateTime().toString());
// 如果目录被删除,自动从监视列表中移除
if (!QDir(path).exists()) {
watcher->removePath(path);
removeFromWatchList(path);
logMessage(“目录已被删除,自动移除监视: ” + path);
}
}
private:
void setupUI() {
QWidget *centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
QSplitter *splitter = new QSplitter(Qt::Vertical, centralWidget);
// 上部区域:监视列表和控制按钮
QWidget *topWidget = new QWidget;
QVBoxLayout *topLayout = new QVBoxLayout(topWidget);
QLabel *listLabel = new QLabel(“监视列表:”);
watchList = new QListWidget;
QHBoxLayout *buttonLayout = new QHBoxLayout;
QPushButton *addFileBtn = new QPushButton(“添加文件”);
QPushButton *addDirBtn = new QPushButton(“添加目录”);
QPushButton *removeBtn = new QPushButton(“移除选中”);
QPushButton *clearBtn = new QPushButton(“清除所有”);
buttonLayout->addWidget(addFileBtn);
buttonLayout->addWidget(addDirBtn);
buttonLayout->addWidget(removeBtn);
buttonLayout->addWidget(clearBtn);
buttonLayout->addStretch();
topLayout->addWidget(listLabel);
topLayout->addWidget(watchList);
topLayout->addLayout(buttonLayout);
// 下部区域:日志显示
QWidget *bottomWidget = new QWidget;
QVBoxLayout *bottomLayout = new QVBoxLayout(bottomWidget);
QLabel *logLabel = new QLabel(“监视日志:”);
logEdit = new QTextEdit;
logEdit->setReadOnly(true);
bottomLayout->addWidget(logLabel);
bottomLayout->addWidget(logEdit);
splitter->addWidget(topWidget);
splitter->addWidget(bottomWidget);
splitter->setSizes(QList<int>() << 200 << 400);
QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);
mainLayout->addWidget(splitter);
// 连接按钮信号
connect(addFileBtn, &QPushButton::clicked, this, &FileSystemWatcherDemo::onAddFile);
connect(addDirBtn, &QPushButton::clicked, this, &FileSystemWatcherDemo::onAddDirectory);
connect(removeBtn, &QPushButton::clicked, this, &FileSystemWatcherDemo::onRemoveSelected);
connect(clearBtn, &QPushButton::clicked, this, &FileSystemWatcherDemo::onClearAll);
}
void setupConnections() {
// 连接列表项双击事件
connect(watchList, &QListWidget::itemDoubleClicked, [this](QListWidgetItem *item) {
QString path = item->text();
if (item->data(Qt::UserRole).toBool()) { // 是文件
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
} else { // 是目录
QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(path).path()));
}
});
}
void addToWatchList(const QString &path, bool isFile) {
QListWidgetItem *item = new QListWidgetItem(path);
item->setData(Qt::UserRole, isFile);
item->setIcon(isFile ? QIcon(“:/icons/file.png”) : QIcon(“:/icons/folder.png”));
watchList->addItem(item);
}
void removeFromWatchList(const QString &path) {
for (int i = 0; i < watchList->count(); ++i) {
QListWidgetItem *item = watchList->item(i);
if (item->text() == path) {
delete watchList->takeItem(i);
break;
}
}
}
void logMessage(const QString &message) {
logEdit->append(“[” + QDateTime::currentDateTime().toString(“hh:mm:ss”) + “] ” + message);
}
private:
QFileSystemWatcher *watcher;
QListWidget *watchList;
QTextEdit *logEdit;
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
FileSystemWatcherDemo demo;
demo.show();
return app.exec();
}
#include “main.moc”
关键技术点详解
1. 初始化文件系统监视器
watcher = new QFileSystemWatcher(this);
connect(watcher, &QFileSystemWatcher::fileChanged, this, &FileSystemWatcherDemo::onFileChanged);
connect(watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemWatcherDemo::onDirectoryChanged);
创建 QFileSystemWatcher 实例并连接其信号到相应的槽函数。
2. 添加监视路径
if (watcher->addPath(filePath)) {
addToWatchList(filePath, true);
logMessage(“已添加文件监视: ” + filePath);
} else {
logMessage(“无法监视文件: ” + filePath);
}
使用 addPath() 方法添加单个监视路径,并根据返回值判断是否成功。
3. 处理文件变化事件
void onFileChanged(const QString &path) {
logMessage(“文件发生变化: ” + path + ” – ” + QDateTime::currentDateTime().toString());
// 如果文件被删除,自动从监视列表中移除
if (!QFile::exists(path)) {
watcher->removePath(path);
removeFromWatchList(path);
logMessage(“文件已被删除,自动移除监视: ” + path);
}
}
当文件发生变化时,记录日志并检查文件是否依旧存在。如果文件被删除,自动从监视列表中移除。
4. 处理目录变化事件
void onDirectoryChanged(const QString &path) {
logMessage(“目录发生变化: ” + path + ” – ” + QDateTime::currentDateTime().toString());
// 如果目录被删除,自动从监视列表中移除
if (!QDir(path).exists()) {
watcher->removePath(path);
removeFromWatchList(path);
logMessage(“目录已被删除,自动移除监视: ” + path);
}
}
当目录发生变化时,记录日志并检查目录是否依旧存在。如果目录被删除,自动从监视列表中移除。
5. 移除监视路径
if (isFile ? watcher->removePath(path) : watcher->removePath(path)) {
logMessage(“已移除监视: ” + path);
delete item;
} else {
logMessage(“无法移除监视: ” + path);
}
使用 removePath() 方法移除单个监视路径,并根据返回值判断是否成功。
实际应用场景
1. 配置文件监视:当应用程序的配置文件被修改时,自动重新加载配置
2. 自动构建系统:当源代码文件发生变化时,自动触发构建过程
3. 文件同步工具:当源目录中的文件发生变化时,自动同步到目标目录
4. 日志文件监视:当日志文件被更新时,实时显示最新内容
5. 资源文件热重载:在开发过程中,当资源文件发生变化时,自动更新应用程序界面
注意事项
1. 性能思考:监视大量文件或目录可能会影响系统性能
2. 文件系统限制:某些文件系统可能不支持文件监视功能
3. 路径有效性:当监视的路径被删除时,需要及时从监视列表中移除
4. 符号链接:符号链接的行为可能因操作系统而异
5. 网络文件系统:网络文件系统的监视可能不可靠或有限制
总结
QFileSystemWatcher 是 Qt 提供的一个强劲工具,用于监视文件和目录的变化。通过简单的 API,开发者可以轻松实现文件系统变化的实时监控和响应。在实际应用中,合理使用 QFileSystemWatcher 可以大大增强应用程序的交互性和自动化能力。

