PDA

View Full Version : Getting last modified file from QFileSystemWatcher



rawfool
17th March 2014, 10:05
I'm getting last modified file in a directory using a QFileSystemWatcher with QDir & QFileInfoList. I create a slot for directoryChanged(QString) signal in my class inherited from QFileSystemWatcher. So when ever a file is created/modified, the slot stateChanged(QString str) is getting emitted 4 times. I'm unable to understand the reason why the slot is being called 4 times.
Here's my implementation -
// cfilesystemwatcher.cpp
#include "cfilesystemwatcher.h"
#include <QDebug>
#include <QDir>
#include <QFileInfoList>
#include <QDateTime>

CFileSystemWatcher::CFileSystemWatcher(QObject *parent) :
QFileSystemWatcher(parent)
{
// connections
connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(stateChanged(QString)));
connect(this, SIGNAL(fileChanged(QString)), this, SLOT(stateChanged(QString)));
}

void CFileSystemWatcher::stateChanged(QString str) // <<--- slot
{
QDir dir;
dir.setPath(str);
QFileInfoList list = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files, QDir::Time);

// Here I'm getting the last modified file.
qDebug() << list.first().baseName(); // <<--- This debug is printing the name 4 times to the console for 1 modification
}



// main.cpp
#include <QCoreApplication>
#include "cfilesystemwatcher.h"

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

CFileSystemWatcher fileWatcher;
fileWatcher.addPath("/home/Rahul/Desktop/");

return a.exec();
}
Now why is the slot with my qDebug() statement getting called 4 times ?
Also this is not giving the files modified inside the subs directories of the specified path. How do I make it emit signal for changes specified to a directory recursively ?

Thank you.

anda_skoa
17th March 2014, 10:30
I am not sure this can work correctly, you are connecting the same slot to two different signals with two different arguments.

In the case of directoryChanged the string is the path of the directory, in case of fileChanged it is the path of the file.

I guess it works in your case because you are not watching any files, only a directory.

As for the slot being called multiple times:
is the file the only file being modified? is the file atomically changed, e.g. renamed or truncated?

As for recursively monitoring: I think QFileSystemWatcher only monitors the directories you tell it to monitor, not its subdirectories.
You'll have to add them to the watched set to do this.

Cheers,
_

rawfool
17th March 2014, 10:40
is the file the only file being modified? is the file atomically changed, e.g. renamed or truncated?I'm modifying a text file named file1 in the specified directory just by adding some text to the file. And after clicking save, immediately this slot is getting called but the output is printed 4 times on the console.

What I want to achieve is to create a class with a function that gives me the last activity in the specified directory. And I need to update the last activity into a log file.
Last activity = New file created (or) Existing file modified/deleted (or) new directory created.

Any hints to implement this ?

Thank you.

anda_skoa
17th March 2014, 13:03
The saving in the text editor might not be just one file change, e.g. the file system could modify multiple blocks in a sequence of operations and notify you about each individual change.

You could start a timer when you get the change notification and log the modification when it runs out, etc.

Cheers,
_

rawfool
17th March 2014, 14:46
Sorry for this, but I still didn't understand why it is generating the same signal 4 time upon modification of a file once.



You could start a timer when you get the change notification and log the modification when it runs out, etcSo if I start a timer on change notification (directoryChanged/fileChanged), what is the mark for me to stop the timer ? And you mean to print to console or enter the details in the log only after stopping the timer?

anda_skoa
17th March 2014, 15:36
Sorry for this, but I still didn't understand why it is generating the same signal 4 time upon modification of a file once.

Well, the underlying system, whatever that is in your platform, triggers more than once. Maybe once for the file being opened for write, once for data being added, once for file writing being complete and once for file entry in directory being updated.

The operation on the file (writing) can always trigger multiple changes, it can be an ongoing operation until the file is being closed.
There are only very few atomic file manipulations, e.g. rename.



So if I start a timer on change notification (directoryChanged/fileChanged), what is the mark for me to stop the timer ?

The timer would not be stopped, it would timeout, telling you that there had been no further change since it had been started.


And you mean to print to console or enter the details in the log only after stopping the timer?
That is obviously up to you program.

Cheers,
_

rawfool
18th March 2014, 12:24
Thanks for the replies anda_skoa.
I'm looking for something like inotify equivalent in Qt. Though I'm trying to get things from QFileSystemWatcher, but it's getting cumbersome.

Thank you.

wysota
18th March 2014, 12:39
QFileSystemWatcher is using inotify under the hood.

rawfool
18th March 2014, 12:49
Is there any way to filter results for IN_CREATE | IN_DELETE | IN_MODIFY modes only? As anda_skoa suggested to use timer to suppress multiple signals that gets generated, I did it. But now the results are not accurate.


#include "cfilesystemwatcher.h"
#include <QDebug>
#include <QDir>
#include <QFileInfoList>
#include <QDateTime>
#include <QTimer>

CFileSystemWatcher::CFileSystemWatcher(QObject *parent) :
QFileSystemWatcher(parent)
{
timer = new QTimer;
timer->setInterval(500);

// connections
connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(stateChanged(QString)));
connect(timer, SIGNAL(timeout()), this, SLOT(changedFileName()));
}

void CFileSystemWatcher::stateChanged(QString str) // slot
{
if(!timer->isActive())
{
timer->start();
QDir dir;
dir.setPath(str);
QFileInfoList list = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs, QDir::Time);
fileName = list.first().baseName();
}
}

QString CFileSystemWatcher::changedFileName(void) // slot
{
timer->stop();

if(QString("untitled folder").mid(0, 8) == fileName.toLower().mid(0, 8) ||
QString("new file").mid(0, 8) == fileName.toLower().mid(0, 8) || fileName.isEmpty())
return QString();

qDebug() << fileName;
return fileName;
}

wysota
18th March 2014, 12:53
I don't think there is. You can only use inotify directly instead of going through the Qt wrapper. Remember that Qt implements the least common denominator of what platforms have to offer.

BTW. Your implementation is not correct. The timer should be restarted each time the slot is fired. It should also be a single shot timer. And certainly it should have a much smaller interval (c.a. 10-50ms, depending on how inotify reports the events).