QFileSystemWatcher源码剖析(Linux)_1

前言

上一篇讲了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本地下的文件变化了。


文章作者: 张小飞
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 张小飞 !
 上一篇
Ubuntu16.04 安装 kubuntu桌面 Ubuntu16.04 安装 kubuntu桌面
简述最近想看看kde下的文件管理器是怎么实现的,因为发现Qt自带的文件管理器是有性能bug的,而且并没有ui线程上的交互处理,如果改的话,只能去更改Qt源码(但是这样改也太蛋疼了,哪有一言不合就去改Qt源码的)。所以想看看同样是Qt写的kd
2020-06-27 张小飞
下一篇 
QFileSystemWatcher源码剖析(Linux) QFileSystemWatcher源码剖析(Linux)
简介QFileSystemWatcher的作用是监视本地文件夹的变化以及文件的变化。 概述QFileSystemWatcher的实现类是QFileSystemWatcherPrivate。 其中QFileSystemWatcherPriva
2020-06-27 张小飞
  目录