QFileSystemWatcher: directoryChanged signal not emitted when a file inside a watched directory is modified
From the "documentation ":http://qt-project.org/doc/qt-5/qfilesystemwatcher.html for QFileSystemWatcher::directoryChanged signal:
"This signal is emitted when the directory at a specified path, is modified (e.g., when a file is added, modified or deleted)".
So it seems that the signal is supposed to trigger when a file is modified, but that doesn't happen on Win 7 x64 with Qt 5.2.1. it is triggered when a file is added or removed, but doesn't trigger when a file is opened for writing, truncated and its contents is replaced. Is it expected behavior? Can I watch these changes without subscribing to every single file in a folder (there may be tons of files)?
Hmm...could be a bug.
Did you close() your file after modifying its contents?
I'm not sure, it was modified by the OS. But more likely than not it has been closed.
Try experimenting by creating + modifying your own file, and see when signals get emitted. If I'm not mistaken, a file needs to be flushed (or even closed) for the OS to know that it has been changed.
Example: When I create a text file and stream data into it over a long period of time, Windows Explorer says the file size is 0 kB even though lots of data has already been "written". It only reports the correct file size after the file is closed.
OS certainly knows the file's contents has changed, because I can see its size changing in Total Commander. But I don't know if it uses watcher, or if it queries directory contents on timer.
But if I simply open a text file with Notepad, add some text and close the file - QFileSystemWatcher does detect it.
Like I said, create your own file and check the signals.
Write your changes, then wait a while.
Flush your changes, then wait a while.
Close the file.
Which step (if any) causes the directoryChanged() signal to be emitted?
I've created a program that follows your steps.
No change is detected either by QFileSystemWatcher or other Windows programs (Explorer, Total commander).
Explorer / TC: the change is displayed a long long time after flushing (maybe sooner if you write and flush very often, not sure). QFileSystemWatcher : change not detected.
Explorer / TC: Change is displayed immediately once the file is closed. QFileSystemWatcher : change not detected. If I query QDir::entryInfoList, I get the actual updated file stats no problem.
I think it's a bug... QFileSystemWatcher isn't working as advertised, unfortunately :( I just tried the steps using Qt 5.3.0 beta (MSVC 2013, Windows 8 x64) and I don't get the directroyChanged() signals either.
EDIT: If I add the file itself to the watcher, I do get the fileChanged() signal when I call QFile::open() and QFile::close(), but not when I call QFile::write() or QFile::flush().
Would you like to submit a bug report at https://bugreports.qt-project.org/ ?
As a workaround, I guess you could periodically check the output of QDir::entryInfoList() and see if anything has changed.
Absolutely, I'll file a bug report.
Unfortunately, periodic checks is exactly what I'm trying to avoid, for directories with A LOT of files it becomes slow and the program becomes unresponsive while updating file list. I guess I'll have to live with this issue for now.
Maybe subscribing to fileChanged for every file would still be better than updating periodically (because it will not get called when nothing has changed).
Thanks. Please post a link to your report here, so that others with the same issue may find it.
I don't know if QDir::entryInfoList() or QFileSystemWatcher::fileChanged() performs better (note that the docs say "The act of monitoring files and directories for modifications consumes system resources"). I suggest trying both to see which works better.
I believe you could reduce the performance impact of QDir::entryInfoList() by doing the checks in a separate thread. Emit your own signal if a change is detected.
I could use a separate thread, but there's not much point: I need to display the folder's contents in the UI anyway. I think it's better to just do it in the main thread and block, than let user click everywhere while the refresher thread is already working.
There's my report: https://bugreports.qt-project.org/browse/QTBUG-38757