Qt源码解析-QFileSystemWatcher-polling.md
公众号:Qt那些事儿前言上一篇讲了QFileSystemWatcher来检测文件夹的变化,里边的实现有两个// 这个用于检测文件类型的变化class QInotifyFileSystemWatcherEngine : public QFileSystemWatcherEngine// 这个用于监控Dir的变化class QDnotifyFileSystemWatcherEngine : publi
·
公众号:Qt那些事儿

前言
上一篇讲了QFileSystemWatcher来检测文件夹的变化,里边的实现有两个
// 这个用于检测文件类型的变化
class QInotifyFileSystemWatcherEngine : public QFileSystemWatcherEngine
// 这个用于监控Dir的变化
class QDnotifyFileSystemWatcherEngine : public QFileSystemWatcherEngine
QFileSystemWatcher中这两个类监视文件夹的变化还有一个小小的缺点,就是无法监视连接到服务器的位置,一般挂载到服务器到Linux本地的路径是这里
/run/user/1000/gvfs
这个目录下的。没有办法只能自己暂时实现了。
多线程 + 定时器
实际上一开始准备自己实现的,但是发现QFileSystemWatcher里边已经有一个类是对应的实现了。
enum { PollingInterval = 1000 };
class QPollingFileSystemWatcherEngine : public QFileSystemWatcherEngine
{
Q_OBJECT
class FileInfo
{
uint ownerId;
uint groupId;
QFile::Permissions permissions;
QDateTime lastModified;
QStringList entries;
public:
FileInfo(const QFileInfo &fileInfo)
: ownerId(fileInfo.ownerId()),
groupId(fileInfo.groupId()),
permissions(fileInfo.permissions()),
lastModified(fileInfo.lastModified())
{
if (fileInfo.isDir()) {
entries = fileInfo.absoluteDir().entryList(QDir::AllEntries);
}
}
FileInfo &operator=(const QFileInfo &fileInfo)
{
*this = FileInfo(fileInfo);
return *this;
}
bool operator!=(const QFileInfo &fileInfo) const
{
if (fileInfo.isDir() && entries != fileInfo.absoluteDir().entryList(QDir::AllEntries))
return true;
return (ownerId != fileInfo.ownerId()
|| groupId != fileInfo.groupId()
|| permissions != fileInfo.permissions()
|| lastModified != fileInfo.lastModified());
}
};
mutable QMutex mutex;
QHash<QString, FileInfo> files, directories;
public:
QPollingFileSystemWatcherEngine();
void run();
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
void stop();
private Q_SLOTS:
void timeout();
};
//--cpp
#include "filewatcher.h"
QPollingFileSystemWatcherEngine::QPollingFileSystemWatcherEngine()
{
#ifndef QT_NO_THREAD
moveToThread(this);
#endif
}
void QPollingFileSystemWatcherEngine::run()
{
QTimer timer;
connect(&timer, SIGNAL(timeout()), SLOT(timeout()));
timer.start(PollingInterval);
(void) exec();
}
QStringList QPollingFileSystemWatcherEngine::addPaths(const QStringList &paths,
QStringList *files,
QStringList *directories)
{
QMutexLocker locker(&mutex);
QStringList p = paths;
QMutableListIterator<QString> it(p);
while (it.hasNext()) {
QString path = it.next();
QFileInfo fi(path);
if (!fi.exists())
continue;
if (fi.isDir()) {
if (!directories->contains(path))
directories->append(path);
if (!path.endsWith(QLatin1Char('/')))
fi = QFileInfo(path + QLatin1Char('/'));
this->directories.insert(path, fi);
} else {
if (!files->contains(path))
files->append(path);
this->files.insert(path, fi);
}
it.remove();
}
start();
return p;
}
QStringList QPollingFileSystemWatcherEngine::removePaths(const QStringList &paths,
QStringList *files,
QStringList *directories)
{
QMutexLocker locker(&mutex);
QStringList p = paths;
QMutableListIterator<QString> it(p);
while (it.hasNext()) {
QString path = it.next();
if (this->directories.remove(path)) {
directories->removeAll(path);
it.remove();
} else if (this->files.remove(path)) {
files->removeAll(path);
it.remove();
}
}
if (this->files.isEmpty() && this->directories.isEmpty()) {
locker.unlock();
stop();
wait();
}
return p;
}
void QPollingFileSystemWatcherEngine::stop()
{
QMetaObject::invokeMethod(this, "quit");
}
void QPollingFileSystemWatcherEngine::timeout()
{
QMutexLocker locker(&mutex);
QMutableHashIterator<QString, FileInfo> fit(files);
while (fit.hasNext()) {
QHash<QString, FileInfo>::iterator x = fit.next();
QString path = x.key();
QFileInfo fi(path);
if (!fi.exists()) {
fit.remove();
emit fileChanged(path, true);
} else if (x.value() != fi) {
x.value() = fi;
emit fileChanged(path, false);
}
}
QMutableHashIterator<QString, FileInfo> dit(directories);
while (dit.hasNext()) {
QHash<QString, FileInfo>::iterator x = dit.next();
QString path = x.key();
QFileInfo fi(path);
if (!path.endsWith(QLatin1Char('/')))
fi = QFileInfo(path + QLatin1Char('/'));
if (!fi.exists()) {
dit.remove();
emit directoryChanged(path, true);
} else if (x.value() != fi) {
fi.refresh();
if (!fi.exists()) {
dit.remove();
emit directoryChanged(path, true);
} else {
x.value() = fi;
emit directoryChanged(path, false);
}
}
}
}
// 这个外部没有暴露对应的变化接口,但是检测其它类型的目录变化时我们会用到
class QPollingFileSystemWatcherEngine : public QFileSystemWatcherEngine
这个类Qt没有对外接口暴露,实际上看QFileSystemWatcher的实现
//poller在这里初始化
void QFileSystemWatcherPrivate::initPollerEngine()
{
if(poller)
return;
Q_Q(QFileSystemWatcher);
poller = new QPollingFileSystemWatcherEngine; // that was a mouthful
QObject::connect(poller,
SIGNAL(fileChanged(QString,bool)),
q,
SLOT(_q_fileChanged(QString,bool)));
QObject::connect(poller,
SIGNAL(directoryChanged(QString,bool)),
q,
SLOT(_q_directoryChanged(QString,bool)));
}
//在这里调用
if(!objectName().startsWith(QLatin1String("_qt_autotest_force_engine_"))) { //这里也就是
// Normal runtime case - search intelligently for best engine
if(d->native) {
engine = d->native;
} else {
d_func()->initPollerEngine(); //这里初始化,否则就走上边两个无法监视服务器的实现
engine = d->poller;
}
发现QFileSystemWatcher设置objectName为_qt_autotest_force_engine_ 就可以使用了。
然后接着
QFileSystemWatcher* fileSystemWatcher = new QFileSystemWatcher(this);
fileSystemWatcher->setObjectName(QLatin1String("_qt_autotest_force_engine_"));
这样就可以监视
/run/user/1000/gvfs
目录下的文件,也就是服务器挂载到Linux本地下的文件变化了。
公众号:Qt那些事儿

更多推荐


所有评论(0)