5

If I launch some function for asynchronous execution using QtConcurrent::run, and am monitoring the returned future using a QFutureWatcher, what if anything can I do in that asynchronously executing function to communicate some progress text back which will result in the QFutureWatcher firing its progressTextChanged signal?

ie what I want to do is something like:

void fn() {
  ???->setProgressText("Starting);
  ...
  ???->setProgressText("halfway");
  ...
  ???->setProgressText("done!");
}

QFutureWatcher watcher;
connect(&watcher, SIGNAL(progressTextChanged(const QString&)), &someGuiThing, SLOT(updateProgress(const QString&)));
connect(&watcher, SIGNAL(finished(), &someGuiThing, SLOT(doStuff()));
QFuture<void> future=QConcurrent::run(fn);
watcher.setFuture(future);

However, big problem, the QtConcurrent::run documentation clearly states

Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting. The QFuture returned can only be used to query for the running/finished status and the return value of the function.

So what's the simplest thing I can do which will get me something functionally equivalent to what the above is trying to do? Do I have to abandon QtConcurrent::run? QFuture? Both? (And go back to QThread and queued connections?)

4

2 回答 2

2

QtConcurrent 函数返回的 QFutureQtConcurrent::mappedReduced()具有由 progressValue()、progressMinimum()、progressMaximum() 和 progressText() 函数提供的进度信息。不像QtConcurrent::run()which 不会自动提供这样的东西。

QtConcurrent::run()不自动提供进度信息,如QtConcurrent::mappedReduced(). 但是您可以使用信号拥有自己的进度报告机制。我认为没有其他直接的方法。

于 2014-05-03T03:43:23.837 回答
0

在我的示例中,您仍然可以使用QFutureWatcherwith QProgressDialoglike:

void hole_mark::get_frames_with_progress(const QString& movie, const QString& output) {
    Ptr<cv::VideoCapture> source = makePtr<VideoCapture>(movie.toUtf8().constData());

    auto frames = (int)(source->get(CAP_PROP_FRAME_COUNT));

    QProgressDialog dialog(tr("Importing frames: %1...").arg(frames), tr("Cancel"), 0, frames, this);
    dialog.setWindowModality(Qt::WindowModal);

    QFutureWatcher<void> futureWatcherProgress;
    QFutureInterface<void> promise;
    QFuture<void> future = promise.future();
    promise.reportStarted();

    QObject::connect(&futureWatcherProgress, SIGNAL(finished()), &dialog, SLOT(reset()));
    QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcherProgress, SLOT(cancel()));
    QObject::connect(&futureWatcherProgress, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int)));
    QObject::connect(&futureWatcherProgress, SIGNAL(progressRangeChanged(int, int)), &dialog, SLOT(setRange(int, int)));
    QObject::connect(&futureWatcherProgress, SIGNAL(progressTextChanged(const QString&)), &dialog, SLOT(setLabelText(const QString&)));

    futureWatcherProgress.setFuture(future);
    promise.setThreadPool(QThreadPool::globalInstance());

    auto runnable = QRunnable::create([&, frames, source]() {

        promise.setProgressRange(0, frames);
        promise.setProgressValue(0);
        cv::Mat m;

        int frame = 0;

        while (!future.isCanceled()) {
            *source >> m;

            if (m.empty()) {
                break;
            }

            promise.setProgressValueAndText(++frame, tr("Importing %1 frame from: %2...").arg(frame).arg(frames));

            qDebug() << "frame: " << frame;
        }


        promise.reportFinished();
    });

    promise.setRunnable(runnable);

    QThreadPool::globalInstance()->start(runnable);

    // Display the dialog and start the event loop.
    dialog.exec();

    futureWatcherProgress.waitForFinished();

    // Query the future to check if was canceled.
    qDebug() << "Canceled?" << futureWatcherProgress.future().isCanceled();
}
于 2021-09-10T11:29:38.237 回答