I want to call a function to delete files and folders from the system in a parallel thread called by QtConcurrent::run()
(Qt for Embedded Linux 4.8). Using only a QFuture<bool>
with QFuture::waitForFinished()
to grab the result (I need to run some code right after the operation), I was able to make the system work.
But I want to display the ongoing result of the operation in a QProgressBar
derived class with its setValue(int)
called via signals and slots mechanism from inside the delete function, and what I get with the above method is a freezed main thread while the operation isn't finished, and that is inadmissible.
So I though about using QFutureWatcher<bool>
and connecting it's finished()
signal to another slot containing the remaining code to be run after the delete operation is finished.
The problem I'm facing is that, when I do this, the delete function is simply not run by QtConcurrent::run()
! I checked that with printed messages to Terminal. Everything that occurs is that the QFutureWatcher
calls its finished()
signal without any execution of the delete files function (and that also happens if I use QFutureWatcher::waitForFinished()
).
Is this some bug from Qt?
Regarding code, it's pretty exactly as in Qt Assistant: create the QFuture and QFutureWatcher globally, connect the finished() signal with the slot, call QtConcurrent::run()
, and setFuture()
to the future. Nothing special.
Any help appreciated.
EDIT
Following the request of Kuba, here is the relevant part of the code:
//Declared globally in the .cpp
QFuture<bool> future;
QFutureWatcher<bool> watcher;
//
void SelectRecordToDeleteWidget::slotDeleteRecordStateMachine()
{
switch (deleteRecordStateMachine)
{
case PrepareToDelete:
{
//...
connect(&watcher,SIGNAL(finished()),this,SLOT(slotDeleteRecordStateMachine()),Qt::UniqueConnection);
//...
}
break;
case DeleteRecords:
{
//...
future = QtConcurrent::run(removeFiles, QString(DEFAULT_RECORD_DIR) + "/" + recordList.at(aaa).second.second, poProgressDialog, &itemCounter);
watcher.setFuture(future);
qApp->processEvents();
//...
}
break;
case FinishDelete:
{
//Run code after deleting files
}
break;
default:
break;
}
}
This is all the code using QFuture and QFutureWatcher. The removeFiles
is as follows (not forgetting that it works well without QFutureWatcher):
bool removeFiles(const QString dirName, Interface::ProgressDialog* const poProgressDialog, qint32* const itemDeletedCounter)
{
bool result = true;
try
{
QDir dir(dirName);
if (dir.exists())
{
Q_FOREACH (QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst))
{
// if (Q_UNLIKELY(poProgressDialog->wasCanceled()))
// break;
if (info.isDir())
{
result = removeFiles(info.absoluteFilePath(),poProgressDialog,itemDeletedCounter);
if (!result)
return result;
}
else
{
result = QFile::remove(info.absoluteFilePath());
if (!result)
return result;
if (!QMetaObject::invokeMethod(poProgressDialog, "setValue",
Qt::BlockingQueuedConnection,
Q_ARG(qint32, *itemDeletedCounter)))
{
mDebugS(QString("removeFiles: %1QMetaObject::invokeMethod(poProgressDialog, \"setValue\"... failed!"));
}
++(*itemDeletedCounter);
// mDebugS(QString("removeFiles: %1").arg(*itemDeletedCounter));
}
}
result = dir.rmdir(dirName);
}
}
catch (...)
{
const QString strTemp = QString("An error in a call to removeFiles");
mDebugS(strTemp);
mLog(strTemp);
}
return result;
}