I've some code to monitor a folder containing .xml files.
I used a QFileSystemWatcher and connected the signal directoryChanged(const QString &path) to a slot where I implemented a small routine.
When I delete a file which is located in this monitored folder, the signal is emitted twice, and I cannot figure out why.
I've read other posts on stackoverflow, but all those I found mentioned the same issue when editing a file and not deleting it. Since I'm deleting files and not editing them, the file is not first removed and then written again by the editor.
Anyone knows why this happens and how to fix it ? I can add some code if needed.
Thanks !
EDIT : After reading this question, I added Qt::UniqueConnection to my connect to make sure it was only done once (even though I'm sure the connection is made only once with an auto connection since it's called in the constructor of the mainwindow), but the result is the same.
Related
My problem is relatively simple. I have an application where I need to monitor a particular folder (the downloads folder, in my case) for added files. Whenever a file is added to that folder, I want to move that file to a completely different directory. I have been looking at QFileSystemWatcher; however, none of the signals it provides seems to be ideal for my situation.
Here is my current code:
connect(&m_fileWatcher, &QFileSystemWatcher::directoryChanged, this, &FileHandler::directoryChanged);
void FileHandler::directoryChanged(const QString &dir)
{
qDebug() << "File changed...." << string;
// Some other logic
}
This signal only gives me a string to work with which is the directory that witnessed a change. I don't know what kind of change took place (add, rename, or delete), and I also have no idea which file has changed.
I understand that I could store all of the files in the directory in some sort of data structure and do some other logic when this signal is emitted, but that doesn't seem very performant in this case since I'm dealing with the user's downloads folder (which could contain thousands of files).
How can I make this work? Should I refer to a different helper class provided by QT, or is there some other way I can do this while utilizing QFileSystemWatcher? I'm simply just looking for ideas.
Thank you.
You’ve hit the limit of what the underlying OS provides: notification of change to the content of a directory.
If you wish to identify the file:
deleted you must have a prior list of files available for compare
added same as deleted
modified loop through the directory for the file with the most recent last modified date
IDK if you wish to use any specific filename container class from Qt or just a std::vector <std::filesystem::path> or the like for your cached folder contents.
QFileSystemWatcher only notifies you that a change happened, but not the details of what was changed. So you will have to resort to OS-specific APIs to get the details. For instance, on Windows, you can use ReadDirectoryChangesW() instead of QFileSystemWatcher.
One of the QIODevice reimplemented open() methods in QFile has a QFileDevice::FileHandleFlag argument. Taking a look at the documentation for it there are two options with contradicting descriptions.
From the QFileDevice documentation:
QFileDevice::AutoCloseHandle – The file handle passed into open() should be closed by close(), the default behavior is that close just flushes the file and the application is responsible for closing the file handle. When opening a file by name, this flag is ignored as Qt always owns the file handle and must close it.
QFileDevice::DontCloseHandle – If not explicitly closed, the underlying file handle is left open when the QFile object is destroyed.
So does Qt auto close files or not and does setting this option actually change anything?
After looking up the Qt source I found the line in QFSFileEngine.cpp:378* that ultimately uses the flag.
QFile::open() can be passed an existing (stdio.h) FILE handler which was not created by Qt and shouldn't be automatically closed by Qt. In contrast files opened by Qt are automatically closed by Qt.
The QFileDevice::FileHandleFlag flag is for the former case and allows the programmer to specify if QFile should auto-close a file ignoring the fact that it was not opened by Qt.
* Search for closeFileHandle if the line number doesn't match.
I have an application which uses inotify and std::thread to check if file is modified outside the application. The problem is that when the file is modified inside the application, inotify works and it is considered as updated outside. My question is, how can I check if file is modified only outside the application. Almost every text editor works that way (when file is modified outside, it asks to reload. But when you modify a text inside, it does not ask anything like that). How are those applications designed?
I don't KNOW how the editors do this, but I expect that when the application writes to the file itself, it "knows" that it did the writing, so either turns of the notifications whilst doing this, or just says "Sure, I know I wrote to the file, so I'll just ignore it".
FINAL EDIT: The code I've written below works, so disregard everything I've written. It seems that when I copied my input text file to the build directory, the file was somehow corrupted in process, which caused my external executable "prog" to break. Sorry for wasting your time and thanks to all of you who tried to help!
I've just started messing around with Qt and have a project called test_tiny. In the build folder of my project (where executable test_tiny is located), I have moved another little C++ executable called "prog" which reads from a file, does its thing, and outputs to a different file. The input file is also in the build directory.
I also have a window with a couple of text boxes and a few buttons. I would like to run my external program "prog" by pressing one of these buttons. This is what I've got so far:
void MainWindow::load2() {
QProcess *process = new QProcess(this);
process->start("./prog");
qDebug() << process->exitCode();
ui->textBrowser_2->clear();
ui->textBrowser_2->insertPlainText(read(":/File/out.txt"));
}
The second part works just fine - it reads from the out.txt file and loads it into the text browser. However, my process doesn't seem to run, and exitCode() always returns zero (I have changed it to 100 in "prog").
From what I've understood, the QProcess' working directory (unless otherwise specified) is set to its build folder, so calling process->start("./prog"); should work, but it doesn't. I've also tried calling it by referencing a QResource as well as giving the full path, but to no avail.
Any help would be appreciated, thanks!
I'm using Qt Creator 2.81 based on Qt 5.1.1 running on x64 Ubuntu 12.04.
EDIT: I forgot to mention that the executable "prog" only parses a few lines of text and outputs them to a file, which is then read and output to a text box. The external program "prog" doesn't actually seem to run, and I've already tried using process->waitForFinished().
You must wait till the process is finished before you check the exit code. You can wait by using the finised() signal or waitForFinished(). After waitForFinished succeeds or the finished signal is emitted it is safe to check the exit code. I almost always will use the finshed signal. However you should also make sure that the process started in the first place. Using the error() signal is how I detect if there is a problem starting the process. QProcess will emit this with a code describing the error. QProcess::FailedToStart will tell you that your application did not start in the first place.
You have two problems:
Are you on the correct path? while debugging using a full path, it make life easier.
You need to call QProcess::waitForFinished(LARGE_TIME) or connect to the finished() signal before you can check the error (the app starts asynchronously).
Here is the problem: I monitor a directory using Win32 API ReadDirectoryChangesW function. And I need to distinguish between newly created files and modified files. But there are problems... as always :(
Cases:
I monitor directory for new/modify (FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE). Problem: After file creation, new file event + modify file event is triggered. But i need only one. How can I avoid that? When file is modified I get what I want :).
I monitor directory only for new file (FILE_NOTIFY_CHANGE_FILE_NAME) - NO PROBLEM.
I monitor directory only for modify file (FILE_NOTIFY_CHANGE_SIZE). Problem: When a new file is, modify action is fired along with file creation event. How can I avoid that?
Of course, I implemented some workarounds. But, I want to know if there any elegant way of handling the problems I described.
You should be catching FILE_NOTIFY_CHANGE_LAST_WRITE, not FILE_NOTIFY_CHANGE_SIZE, for a modified file. Files may be modified without the size changing.
You should also keep a queue of changes and the time they happened and only process the queue after there have been no changes in the past 1-2 seconds. Some applications can do very strange things when creating or modifying files, and you'll most likely want to special case for popular applications if you plan on using this code in the wild.
ReadDirectoryChanges isn't one of the friendliest winapi functions. You probably can't get around receiving two events on file creation; I'm not completely sure whether you'll get an extra modify for FILE_NOTIFY_CHANGE_LAST_WRITE on creation, but I think you probably will. Using the queue approach will allow you to easily throw out the extra event if it has the same time stamp as the creation event.