QFileSystemWatcher gives notification if the watched directory is modified. But it does not give any information about the type of change. So here I am going to explain that how you can find the change (rename, add, delete, modify) and the name of the file/dir (inside the watched dir) that is modified.
First of all add a class to your project. Lets name it “MyFileSystemWatcher”.
Now add a class variable of type QFileSystemWatcher to this class and a function to add paths to watch.
We will add two slots directoryUpdated(QString path) and fileUpdated(QString path) that will be connected to corresponding signals that are emitted when some change occurs in the watched dir/file.
Also we need a variable to maintain the list of current contents in a directory, and that can be compared to the directory contents after a change is notified to find out the name of file changed and the type of change. We’ll use a QMap having a QString as key and QStringList as its value for this purpose. The key will be the path of some directory and the value will be the list of names of all files/directories it contains.
Hence moving on, your header file MyFileSystemWatcher.h will look like:
#ifndef MYFILESYSTEMWATCHER_H #define MYFILESYSTEMWATCHER_H #include <QObject> #include <QStringList> #include <QMap> #include <QFileSystemWatcher> class MyFileSystemWatcher : public QObject { Q_OBJECT public: static MyFileSystemWatcher* _instance; //singleton class instance QMap<QString, QStringList> _currContents; //maintain list of current contents of each watched directory QFileSystemWatcher* _sysWatcher; //QFileSystemWatcher variable static void addWatchPath(QString path); public slots: void directoryUpdated(const QString & path); //called when a watched directory is updated. "path" is the path of watched directory in which change occured void fileUpdated(const QString & path); //called when a watched file is modified."path" is the path of watched file which is modified };
In the implementation file, we will allocate memory to the singleton instance of class and the QFileSystemWatcher variable “_sysWatcher”, also we will maintain the currents contents of each directory path that is watched(added to watcher) and compare the contents in the directoryUpdated() method called when a watched directory is modified.
So here is what the implementation file MyFileSystemWatcher.cpp will look like:
#include "MyFileSystemWatcher.h" MyFileSystemWatcher* MyFileSystemWatcher::_instance = 0; // Add a file or directory path to be watched void MyFileSystemWatcher::addWatchPath(QString path) { qDebug()<<"Add to watch: "<<path; // Allocate memory to instance if not already allocated if(_instance == 0) { _instance = new MyFileSystemWatcher; _instance->_sysWatcher = new QFileSystemWatcher(); // Connect the directoryChanged and fileChanged signals of QFileSystemWatcher to corresponding slots connect(_instance->_sysWatcher,SIGNAL(directoryChanged( QString )),_instance,SLOT(directoryUpdated(QString))); connect(_instance->_sysWatcher,SIGNAL(fileChanged( QString )),_instance,SLOT(fileUpdated(QString))); } _instance->_sysWatcher->addPath(path); //add path to watch // Save the list of current contents if the added path is a directory QFileInfo f(path); if(f.isDir()) { const QDir dirw(path); _instance->_currContents[path] = dirw.entryList(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files, QDir::DirsFirst); } } // Slot invoked whenever any of the watched directory is updated (some file in the watched dir is added, deleted or renamed) void MyFileSystemWatcher::directoryUpdated(const QString & path) { qDebug()<<"Directory updated: "<<path; // Compare the latest contents to saved contents for the dir updated to find out the difference(change) QStringList currEntryList = _instance->_currContents[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 ); // Files that have been added QSet<QString> newFiles = newDirSet - currentDirSet; QStringList newFile = newFiles.toList(); // Files that have been removed QSet<QString> deletedFiles = currentDirSet - newDirSet; QStringList deleteFile = deletedFiles.toList(); // Update the current set _instance->_currContents[path] = newEntryList; if(!newFile.isEmpty() && !deleteFile.isEmpty()) { // File/Dir is renamed if(newFile.count() == 1 && deleteFile.count() == 1) { qDebug()<<"File Renamed from "<<newFile.first()<<" to "<<deleteFile.first(); } } else { // New File/Dir Added to Dir if(!newFile.isEmpty()) { qDebug()<<"New Files/Dirs added: "<<newFile; foreach(QString file, newFile) { //Handle Operation on new files..... } } // File/Dir is deleted from Dir if(!deleteFile.isEmpty()) { qDebug()<<"Files/Dirs deleted: "<<deleteFile; foreach(QString file, deleteFile) { // Handle operation of each deleted file.... } } } } // Slot invoked whenever the watched file is modified void MyFileSystemWatcher::fileUpdated(const QString & path) { QFileInfo file(path); QString path1 = file.absolutePath(); QString name = file.fileName(); qDebug()<<"The file "<<name<<" at path "<<path1<<" is updated"; }
This way you may add any no. of paths to the watcher by calling the static method “addWatchPath:” and the path can be either a file or directory.
If you want to watch a directory as well as all its sub directories, you need to add the path of parent directory and path of all its sub directories recursively to the watcher.
For Example:
If you want to watch a directory “ABC” in the C drive, just call the method
MyFileSystemwatcher::addWatchPath("C:/ABC");
This is all about QFileSystemWatcher. However there is a known bug in using it for recursive monitoring on Windows. To know more about this check out my other post.
Written By: Neha Gupta