1

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;
}
Momergil
  • 2,213
  • 5
  • 29
  • 59
  • 1
    Do not use `waitForXxx` in any code. It blocks the event loop, uses a page worth of stack (!!), and leads to horrible pseudo-synchronous code style. It's as simple as that. When you want to do something when the operation finishes, do it in a slot or a functor attached to the `finished` signal. – Kuba hasn't forgotten Monica Sep 17 '15 at 15:00
  • Your question is unanswerable without a code example. Nobody will recreate the code for you from a textual description. It's your job to post the question with the code included. *Nothing special* Are you serious? You've **just admitted** that it doesn't work. I can do it, and it works. I can't divine what you did wrong, sorry. – Kuba hasn't forgotten Monica Sep 17 '15 at 15:02
  • @KubaOber well I always avoid `waitForFinished()`; actually I just did it as part of trying to understand what was going on since I wanted to use `QFutureWatcher`'s signals from the beginning. Regarding code, I edited my question. – Momergil Sep 18 '15 at 11:29

0 Answers0