C++ Qt開發:QFileSystemWatcher檔案監視元件

lyshark發表於2024-03-08

Qt 是一個跨平臺C++圖形介面開發庫,利用Qt可以快速開發跨平臺窗體應用程式,在Qt中我們可以透過拖拽的方式將不同元件放到指定的位置,實現圖形化開發極大的方便了開發效率,本章將重點介紹如何運用QFileSystemWatcher元件實現對檔案或目錄的監視功能。

QFileSystemWatcher 是 Qt 框架中提供的一個類,用於監視檔案系統中的檔案和目錄的變化。它允許你在檔案或目錄發生變化時接收通知,並可以用於監視檔案的建立、刪除、重新命名以及內容修改等操作。這對於需要實時監控檔案系統變化的應用程式是非常有用的。

下面是關於 QFileSystemWatcher 類的一些常用函式的解釋:

函式 描述
QFileSystemWatcher(QObject *parent = nullptr) 建構函式,建立一個檔案系統監視器物件。
void addPath(const QString &path) 新增要監視的檔案或目錄路徑。
void addPaths(const QStringList &paths) 新增要監視的多個檔案或目錄路徑。
bool removePath(const QString &path) 移除要監視的檔案或目錄路徑。
void removePaths(const QStringList &paths) 移除要監視的多個檔案或目錄路徑。
bool contains(const QString &path) const 檢查監視器是否包含指定的檔案或目錄路徑。
QStringList files() const 返回當前監視的檔案路徑列表。
QStringList directories() const 返回當前監視的目錄路徑列表。
void setFilter(QFileSystemWatcher::Filter filter) 設定監視器的過濾器,用於指定要監視的事件型別。
QFileSystemWatcher::Filter filter() const 返回監視器當前的過濾器設定。
void fileChanged(const QString &path) 訊號,當監視的檔案發生變化時發出。
void directoryChanged(const QString &path) 訊號,當監視的目錄發生變化時發出。

這些函式允許你動態地新增或移除要監視的檔案或目錄,設定過濾器以確定要監視的事件型別,並連線相應的訊號以處理檔案系統的變化事件。

首先我們需要新增一個filesystem.h標頭檔案,該類主要用於實現對檔案訪問的封裝,其中addWatchPath用於增加一個被監控目錄,當目錄被更新世則呼叫directoryUpdated,檔案被修改呼叫fileUpdated

#ifndef FILESYSTEM_H
#define FILESYSTEM_H
#include <QObject>
#include <QMap>
#include <QString>
#include <QMap>
#include <QFileSystemWatcher>

class FileSystemWatcher : public QObject
{
    Q_OBJECT

public:
    static void addWatchPath(QString path);

public slots:

    // 目錄更新時呼叫
    void directoryUpdated(const QString &path);

    // 檔案被修改時呼叫
    void fileUpdated(const QString &path);

private:
    explicit FileSystemWatcher(QObject *parent = 0);

private:
    // 單例
    static FileSystemWatcher *m_pInstance;

    // QFileSystemWatcher變數
    QFileSystemWatcher *m_pSystemWatcher;

    // 當前每個監控的內容目錄列表
    QMap<QString, QStringList> m_currentContentsMap;
};

#endif // FILESYSTEM_H

接著是filesystem.cpp主函式部分,首先FileSystemWatcher::addWatchPath用於增加一個監控目錄。這裡的重點在於建立兩個訊號,當m_pSystemWatcher收到監控資料時,我們讓其分別去觸發directoryChangedfileChanged兩個訊號,在訊號中分別攜帶一個引數傳遞給directoryUpdatedfileUpdated槽函式上進行處理,如果是目錄則儲存目錄中的內容。

void FileSystemWatcher::addWatchPath(QString path)
{
   qDebug() << QString("新增監控目錄: %1").arg(path);

    if (m_pInstance == NULL)
    {
        m_pInstance = new FileSystemWatcher();
        m_pInstance->m_pSystemWatcher = new QFileSystemWatcher();

        // 連線QFileSystemWatcher的directoryChanged和fileChanged訊號到相應的槽
        connect(m_pInstance->m_pSystemWatcher, SIGNAL(directoryChanged(QString)), m_pInstance, SLOT(directoryUpdated(QString)));
        connect(m_pInstance->m_pSystemWatcher, SIGNAL(fileChanged(QString)), m_pInstance, SLOT(fileUpdated(QString)));
    }

    // 新增監控路徑
    m_pInstance->m_pSystemWatcher->addPath(path);

    // 如果新增路徑是一個目錄,儲存當前內容列表
    QFileInfo file(path);
    if (file.isDir())
    {
        const QDir dirw(path);
        m_pInstance->m_currentContentsMap[path] = dirw.entryList(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files, QDir::DirsFirst);
    }
}

接著是FileSystemWatcher::directoryUpdated函式的實現部分,如下所示程式碼,透過QFileSystemWatcher來監聽指定目錄下檔案和子目錄的變化。當目錄發生變化時,呼叫directoryUpdated槽函式,比較最新的目錄內容和之前儲存的內容,找出新增檔案、刪除檔案以及檔案重新命名等變化。

  • 功能概述
    1. 新增監控路徑:透過addWatchPath函式新增監控路徑,建立QFileSystemWatcher物件並連線相關訊號和槽。
    2. 目錄更新處理:當監控的目錄發生變化時,呼叫directoryUpdated槽函式。
    3. 內容變化比較:比較最新的目錄內容和之前儲存的內容,找出新增檔案、刪除檔案和檔案重新命名等變化。
    4. 檔案重新命名處理:如果有檔案重新命名,輸出檔案重新命名的資訊。
    5. 新增檔案處理:輸出新建檔案的資訊,並可以在相應的邏輯中處理每個新檔案。
    6. 刪除檔案處理:輸出刪除檔案的資訊,並可以在相應的邏輯中處理每個被刪除的檔案。

程式碼對檔案系統的變化進行了細緻的監控和處理,可以用於實時監控目錄下檔案的變動情況,例如新增檔案、刪除檔案和檔案重新命名等操作。當使用者需要自定義功能時可以在資訊輸出前對特定目錄做進一步處理以達到監視並控制特定檔案的功能。

// 任何監控的目錄更新(新增、刪除、重新命名)則呼叫
void FileSystemWatcher::directoryUpdated(const QString &path)
{
    qDebug() << QString("目錄更新: %1").arg(path);

    // 比較最新的內容和儲存的內容找出區別(變化)
    QStringList currEntryList = m_currentContentsMap[path];
    const QDir dir(path);

    QStringList newEntryList = dir.entryList(QDir::NoDotAndDotDot  | QDir::AllDirs | QDir::Files, QDir::DirsFirst);

    QSet<QString> newDirSet = QSet<QString>::fromList(newEntryList);
    QSet<QString> currentDirSet = QSet<QString>::fromList(currEntryList);

    // 新增了檔案
    QSet<QString> newFiles = newDirSet - currentDirSet;
    QStringList newFile = newFiles.toList();

    // 檔案已被移除
    QSet<QString> deletedFiles = currentDirSet - newDirSet;
    QStringList deleteFile = deletedFiles.toList();

    // 更新當前設定
    m_currentContentsMap[path] = newEntryList;

    if (!newFile.isEmpty() && !deleteFile.isEmpty())
    {
        // 檔案/目錄重新命名
        if ((newFile.count() == 1) && (deleteFile.count() == 1))
        {
           qDebug() << QString("檔案重新命名 %1 到 %2").arg(deleteFile.first()).arg(newFile.first());
        }
    }
    else
    {
        // 新增新檔案/目錄至Dir
        if (!newFile.isEmpty())
        {
           qDebug() << "新建檔案或目錄: " << newFile;

            foreach (QString file, newFile)
            {
                // 處理操作每個新檔案....
            }
        }

        // 從Dir中刪除檔案/目錄
        if (!deleteFile.isEmpty())
        {
            qDebug() << "刪除檔案或目錄: " << deleteFile;

            foreach(QString file, deleteFile)
            {
                // 處理操作每個被刪除的檔案....
            }
        }
    }
}

同理,當檔案被修改時則呼叫fileUpdated函式,只需要去除絕對路徑與檔名即可,如下程式碼所示;

void FileSystemWatcher::fileUpdated(const QString &path)
{
    QFileInfo file(path);
    QString strPath = file.absolutePath();
    QString strName = file.fileName();

   qDebug() << QString("檔案 %1 路徑 %2 修改").arg(strName).arg(strPath);
}

你可以自行執行課件FileSystemWatcher.zip來觀察監控效果,如下圖;

相關文章