PDA

View Full Version : QFileSystemWatcher - Issue Removing Files



Escot
17th June 2017, 10:13
I wanted to remove all files from a QFileSystemWatcher so I wrote this:


if (fsWatcher.files().isEmpty() == false)
fsWatcher.removePaths(fsWatcher.files());
However, I found it didn't work, and checking the QStringList returned by removePaths() I found it was failing for every single file, so no files were ever being removed from the watcher list.

Since removePaths() wasn't working I thought I'd try the removePath() method with the code:


if (fsWatcher.files().isEmpty() == false)
{
QStringList fileList = fsWatcher.files();
QStringList::const_iterator filePath;
for (filePath = fileList.constBegin() ; filePath != fileList.constEnd() ; ++filePath)
if (fsWatcher.removePath(*filePath) == false)
qDebug() << "Failed to remove file: " << *filePath;
}
This too didn't work, and it failed for every file.

To investigate the matter further I wrote this program:


#include <QCoreApplication>
#include <QFileSystemWatcher>
#include <QDir>
#include <QDebug>

void AddFiles(QDir & dirReader, QFileSystemWatcher & fsWatcher);
void RemoveFilesQDir(QDir & dirReader, QFileSystemWatcher & fsWatcher);
void RemoveFilesQFSW(QFileSystemWatcher & fsWatcher);


int main()
{
QDir dirReader("G:/TestFiles/Test");
QFileSystemWatcher fsWatcher;

qDebug() << "Remove using RemoveFilesQDir()";
AddFiles(dirReader, fsWatcher);
RemoveFilesQDir(dirReader, fsWatcher);

qDebug() << "\nRemove using RemoveFilesQFSW()";
AddFiles(dirReader, fsWatcher);
RemoveFilesQFSW(fsWatcher);

return 0;
}


void AddFiles(QDir & dirReader, QFileSystemWatcher & fsWatcher)
{
QFileInfoList fileList = dirReader.entryInfoList();
QFileInfoList::const_iterator file;
for (file = fileList.constBegin() ; file != fileList.constEnd() ; ++file)
{
if (file->isFile())
{
if (fsWatcher.addPath(file->filePath()))
qDebug() << "Add Success\t" << file->filePath();
else
qDebug() << "Add Fail\t" << file->filePath();
}
}
}

void RemoveFilesQDir(QDir & dirReader, QFileSystemWatcher & fsWatcher)
{
QFileInfoList fileList = dirReader.entryInfoList();
QFileInfoList::const_iterator file;
for (file = fileList.constBegin() ; file != fileList.constEnd() ; ++file)
{
if (file->isFile())
{
if (fsWatcher.removePath(file->filePath()))
qDebug() << "Remove Success\t" << file->filePath();
else
qDebug() << "Remove Fail\t" << file->filePath();
}
}
}


void RemoveFilesQFSW(QFileSystemWatcher & fsWatcher)
{
if (fsWatcher.files().isEmpty() == false)
{
QStringList fileList = fsWatcher.files();
QStringList::const_iterator file;
for (file = fileList.constBegin() ; file != fileList.constEnd() ; ++file)
{
if (fsWatcher.removePath(*file) == false)
qDebug() << "Remove Successful\t" << *file;
else
qDebug() << "Remove Fail\t" << *file;
}
}
}
The output from the program is this:


Remove using RemoveFilesQDir()
Add Success "G:/TestFiles/Test/Test 01.txt"
Add Success "G:/TestFiles/Test/Test 02.txt"
Add Success "G:/TestFiles/Test/Test 03.txt"
Remove Success "G:/TestFiles/Test/Test 01.txt"
Remove Success "G:/TestFiles/Test/Test 02.txt"
Remove Success "G:/TestFiles/Test/Test 03.txt"

Remove using RemoveFilesQFSW()
Add Success "G:/TestFiles/Test/Test 01.txt"
Add Success "G:/TestFiles/Test/Test 02.txt"
Add Success "G:/TestFiles/Test/Test 03.txt"
Remove Fail "G:/TestFiles/Test/Test 01.txt"
Remove Fail "G:/TestFiles/Test/Test 02.txt"
Remove Fail "G:/TestFiles/Test/Test 03.txt"
Press <RETURN> to close this window...
So, removing the files using the file paths from the QDir::entryInfoList() works fine, but removing the files using the paths returned by QFileSystemWatcher::files() fails, even though the file paths are identical in both cases.

I was just wondering if somebody could explain why removing files using the paths returned by QFileSystemWatcher::files() fails?

Also, since the below code doesn't work, what's the fastest way to clear all the files from a QFileSystemWatcher?


if (fsWatcher.files().isEmpty() == false)
fsWatcher.removePaths(fsWatcher.files());
Thanks a lot.

d_stranz
17th June 2017, 23:09
What on earth are you trying to do with this code?



if (fsWatcher.removePath(*file) == false)
qDebug() << "Remove Successful\t" << *file;
else
qDebug() << "Remove Fail\t" << *file;


In the first place, entries in a QStringList are QString instances, not QString *, so the argument in the first line should be "file", not "*file".
Second, your logic is backwards. The if statement says, "If you can't remove the file (removePath() returned false), then report Remove Successful".

QFileSystemWatcher::addPath() will add the file to its list of things to watch only if the file exists. Because of the way you have constructed your tests, you are passing in a list of files obtained from QDir, which guarantees these are real files. So of course your "RemoveFilesQDir" works because you've added valid files to its list in "AddDir" and you are calling removePath() with the correct argument.

QFileSystemWatcher::removePath() will remove the file from the list of watched files, but if the file doesn't exist, then it can return false. But your logic is upside down anyway in RemoveFilesQFSW..

I don't know why your removePaths() code doesn't work.

Escot
18th June 2017, 00:11
Sorry, I was a bit out of it when I wrote that example after spending the day trying to work out what was going wrong.

Anyway, I've spent another day and I think I've found the problem. It seems that if you add a directory, and then add some files in that directory, you can successfully remove the files, but any attempt to remove the directory fails. If you do things the other way around and add the files first, you can successfully remove the directory, but any attempts to remove the files fail.

I'm once again a bit delirious (need to get to bed) but let me try and write an example. Here I add the directory first and then the files, then try to remove them:


#include <QCoreApplication>
#include <QFileSystemWatcher>
#include <QDebug>

void RemoveItems(QFileSystemWatcher & fsWatcher, char* desc, QStringList Items);

int main()
{
QFileSystemWatcher fsWatcher;

fsWatcher.addPath("G:/TestFiles/Test1");
fsWatcher.addPath("G:/TestFiles/Test1/File 1-1.txt");
fsWatcher.addPath("G:/TestFiles/Test1/File 1-2.txt");
fsWatcher.addPath("G:/TestFiles/Test1/File 1-3.txt");

RemoveItems(fsWatcher, "Files", fsWatcher.files());
RemoveItems(fsWatcher, "Directories", fsWatcher.directories());

fsWatcher.addPath("G:/TestFiles/Test2");
fsWatcher.addPath("G:/TestFiles/Test2/File 2-1.txt");
fsWatcher.addPath("G:/TestFiles/Test2/File 2-2.txt");
fsWatcher.addPath("G:/TestFiles/Test2/File 2-3.txt");

RemoveItems(fsWatcher, "Files", fsWatcher.files());
RemoveItems(fsWatcher, "Directories", fsWatcher.directories());

return 0;
}

void RemoveItems(QFileSystemWatcher & fsWatcher, char* desc, QStringList itemList)
{
printf("Removing %d %s\n", itemList.size(), desc);
QStringList failList = fsWatcher.removePaths(itemList);

if (failList.isEmpty())
{
printf("%s removed successfully\n", desc);
}
else
{
QStringList::const_iterator item;
for (item = failList.constBegin() ; item != failList.constEnd() ; ++item)
printf("Remove fail - %s\n", item->toLatin1().data());
}
printf("\n");
}

As you can see in the output, attempts to remove the directories fail:


Removing 3 Files
Files removed successfully

Removing 1 Directories
Remove fail - G:/TestFiles/Test1

Removing 3 Files
Files removed successfully

Removing 2 Directories
Remove fail - G:/TestFiles/Test1
Remove fail - G:/TestFiles/Test2
Below I've changed the main() function to first add the files then the directory:


#include <QCoreApplication>
#include <QFileSystemWatcher>
#include <QDebug>

void RemoveItems(QFileSystemWatcher & fsWatcher, char* desc, QStringList Items);

int main()
{
QFileSystemWatcher fsWatcher;

fsWatcher.addPath("G:/TestFiles/Test1/File 1-1.txt");
fsWatcher.addPath("G:/TestFiles/Test1/File 1-2.txt");
fsWatcher.addPath("G:/TestFiles/Test1/File 1-3.txt");
fsWatcher.addPath("G:/TestFiles/Test1");

RemoveItems(fsWatcher, "Files", fsWatcher.files());
RemoveItems(fsWatcher, "Directories", fsWatcher.directories());

fsWatcher.addPath("G:/TestFiles/Test2/File 2-1.txt");
fsWatcher.addPath("G:/TestFiles/Test2/File 2-2.txt");
fsWatcher.addPath("G:/TestFiles/Test2/File 2-3.txt");
fsWatcher.addPath("G:/TestFiles/Test2");

RemoveItems(fsWatcher, "Files", fsWatcher.files());
RemoveItems(fsWatcher, "Directories", fsWatcher.directories());

return 0;
}

void RemoveItems(QFileSystemWatcher & fsWatcher, char* desc, QStringList itemList)
{
printf("Removing %d %s\n", itemList.size(), desc);
QStringList failList = fsWatcher.removePaths(itemList);

if (failList.isEmpty())
{
printf("%s removed successfully\n", desc);
}
else
{
QStringList::const_iterator item;
for (item = failList.constBegin() ; item != failList.constEnd() ; ++item)
printf("Remove fail - %s\n", item->toLatin1().data());
}
printf("\n");
}

Now attempts to remove the files fail:


Removing 3 Files
Remove fail - G:/TestFiles/Test1/File 1-1.txt
Remove fail - G:/TestFiles/Test1/File 1-2.txt
Remove fail - G:/TestFiles/Test1/File 1-3.txt

Removing 1 Directories
Directories removed successfully

Removing 6 Files
Remove fail - G:/TestFiles/Test1/File 1-1.txt
Remove fail - G:/TestFiles/Test1/File 1-2.txt
Remove fail - G:/TestFiles/Test1/File 1-3.txt
Remove fail - G:/TestFiles/Test2/File 2-1.txt
Remove fail - G:/TestFiles/Test2/File 2-2.txt
Remove fail - G:/TestFiles/Test2/File 2-3.txt

Removing 1 Directories
Directories removed successfully

Press <RETURN> to close this window...

Hopefully those examples make sense this time :)

I'm not sure if this is a bug with QFileSystemWatcher or if I'm misunderstanding something. Anyway, I best take a shower and get to bed and I'll look into this more tomorrow morning.

Escot
18th June 2017, 19:23
Unless I'm missing something, this does appear to be a bug in QFileSystemWatcher. It appears that the only solution is to have two QFileSystemWatcher - one for files and another for directories. If you do things that way it works fine.

Here's the program from the previous post modified to use two QFileSystemWatchers:


#include <QCoreApplication>
#include <QFileSystemWatcher>
#include <QDebug>

void RemoveItems(QFileSystemWatcher & fsWatcher, char* desc, QStringList Items);

int main()
{
QFileSystemWatcher fsWatcherDir;
QFileSystemWatcher fsWatcherFile;

fsWatcherDir.addPath("G:/TestFiles/Test1");
fsWatcherFile.addPath("G:/TestFiles/Test1/File 1-1.txt");
fsWatcherFile.addPath("G:/TestFiles/Test1/File 1-2.txt");
fsWatcherFile.addPath("G:/TestFiles/Test1/File 1-3.txt");

RemoveItems(fsWatcherFile, "Files", fsWatcherFile.files());
RemoveItems(fsWatcherDir, "Directories", fsWatcherDir.directories());

fsWatcherDir.addPath("G:/TestFiles/Test2");
fsWatcherFile.addPath("G:/TestFiles/Test2/File 2-1.txt");
fsWatcherFile.addPath("G:/TestFiles/Test2/File 2-2.txt");
fsWatcherFile.addPath("G:/TestFiles/Test2/File 2-3.txt");

RemoveItems(fsWatcherFile, "Files", fsWatcherFile.files());
RemoveItems(fsWatcherDir, "Directories", fsWatcherDir.directories());

return 0;
}

void RemoveItems(QFileSystemWatcher & fsWatcher, char* desc, QStringList itemList)
{
printf("Removing %d %s\n", itemList.size(), desc);
QStringList failList = fsWatcher.removePaths(itemList);

if (failList.isEmpty())
{
printf("%s removed successfully\n", desc);
}
else
{
QStringList::const_iterator item;
for (item = failList.constBegin() ; item != failList.constEnd() ; ++item)
printf("Remove fail - %s\n", item->toLatin1().data());
}
printf("\n");
}

And the output:


Removing 3 Files
Files removed successfully

Removing 1 Directories
Directories removed successfully

Removing 3 Files
Files removed successfully

Removing 1 Directories
Directories removed successfully

I'd be grateful if somebody could clarify if this is a misunderstanding on my part or a bug that needs reporting.

Thanks.

d_stranz
18th June 2017, 20:15
Nice work in testing this. I have no idea if this is a bug or not.

A further test would be to see if you still get change events on the directories even after you have tried (and failed) to remove them from the QFSW. If you do, then I would definitely classify that as a bug.

Escot
19th June 2017, 23:47
After a failed attempt to remove a file from the watcher it does stop sending fileChanged() signals for the file. The problem is that if you need to add that file back to the QFileSystemWatcher the addPath() operation files.

This can be a significant issue, for example in my program:

User visits directory - all files get added to watcher and fileChanged() signals work correctly.
User leaves directory - program attempts to remove files for watcher, but removePath() operation fails for all files.
User returns to directory - program attempts to add files again to watcher, but addPath() operation fails. Program will therefore not received fileChanged() signals so will fail to work correctly.

This is actually how I noticed the issue to begin with. The program wasn't receiving fileChanged() signals when it should so I checked the QFileSystemWatcher files() list and found the files were there, so I was confused. I later noticed there were other files on the list that shouldn't have been there and that's when I found out that removePath() was failing.