QFileSystemWatcher: Detect if file has been flushed

1.3k Views Asked by At

I have a QT app that needs to know when new data is available in a specific file. So I used QFileSystemWatcher and connected the fileChanged signal to a function that will send a message in case of changes.

The probelm is that the fileChanged signal is not emitted when another application flushes this file, but only after it closes the file.

However, the QFileSystemWatcher documentation says that this signal is emitted "when the file at the specified path is modified, renamed or removed from disk". Maybe I'm missing something; What changes are included in modified? If flushing is not included, How can I detect when new data has been written to the file?

Here is the source code:

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.h

#include <QFileSystemWatcher>
#include <QMainWindow>

class MainWindow : public QMainWindow
{
  Q_OBJECT

public:
  explicit MainWindow(QWidget *parent = 0);
  ~MainWindow();

public slots:
  void fileChangedEvent(const QString & path);

private:
  QFileSystemWatcher * watcher;
};

mainwindow.cpp

#include "mainwindow.h"

MainWindow::MainWindow()
{
  watcher = new QFileSystemWatcher();
  connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(fileChangedEvent(QString)));
  watcher->addPath("path to file");
}

void MainWindow::fileChangedEvent(const QString & path)
{
  qDebug() << path;
}

MainWindow::~MainWindow()
{
    if(watcher!=NULL)
    {
        delete watcher;
        watcher=NULL;
    }
}

This is the code of the another application which changes the file (this is a 3rd party app, so I can't change it to be synchronized with):

#include <fstream>

int main () {

  std::ofstream outfile ("path to file");

  for (int n=0; n<100; ++n)
  {
    outfile << n;
    outfile.flush();
  }
  outfile.close();

  return 0;
}

The fileChanged() signal is emitted only after std::ofstream outfile ("path to file");and outfile.close(); are called and not after outfile.flush();

2

There are 2 best solutions below

6
Aaron On

That loop of write and flush is pretty fast (microseconds!?). You can not expect that the QFileSytemWatcher get's notice of all of that actions as it may well be implemented using timers .. and everything is over so fast that you are informed only unreliably about what's going on.

I just testet it and found out this assumption is correct. Consider the following code. It is writer which writes either timed or everything in a row without delay. Using a delay will give your watcher time to get notice about every single flush. But without delay it rarely reports more than two file system changes.

#include "mainwindow.h"

#include <QDebug>
#include <QTimer>

MainWindow::MainWindow()
    : m_count(10), m_file("./file")
{

  m_file.open(QFile::Truncate | QFile::ReadWrite);

// CHECK THIS:

  QTimer * t = new QTimer(this);
  connect(t,SIGNAL(timeout()), SLOT(writeChunk()));
  t->start(100);

// AGAINST THIS:

  //for(int i = 0; i < 10; i++) writeChunk();
}

void MainWindow::writeChunk()
{
  qDebug() << "wrinteChunk()";
  m_file.write("Hallo Spencer!");
  m_file.flush();

  if( ! --m_count ) {
      m_file.close();
      qDebug() << "Wrote 10 chunks";
      exit(0);
  }
}

MainWindow::~MainWindow()
{

}
0
Sajal On

On Windows, it seems that fileChanged signal is emitted when the time-stamp of the file changes, which happens when the file is closed (std::ofstream::close()) but not (at least on Windows) when it is flushed (std::ofstream::flush()). In order test it I explicitly updated the timestamp of the file each time after writing into it (after calling std::ofstream::flush()) using below function:

#include <ctime>
#include <sys/stat.h>
#ifdef _WIN32
#include <sys/utime.h>
#else
#include <utime.h>
#endif

bool UpdateFileTimestamp(std::string fileName) {
    struct stat fstat;
    struct utimbuf new_time;

    if (0 != stat(fileName.c_str(), &fstat))
        return false;

    new_time.actime = fstat.st_atime;
    new_time.modtime = time(NULL);
    if (0 != utime(fileName.c_str(), &new_time))
        return false;

    return true;
}

And it worked. fileChanged signal was emitted as expected.