如何将 QtConcurrent::run 函数(或类似函数)中的 progressText 传递给 QFutureWatcher?

Posted

技术标签:

【中文标题】如何将 QtConcurrent::run 函数(或类似函数)中的 progressText 传递给 QFutureWatcher?【英文标题】:How to communicate a progressText from a QtConcurrent::run function (or similar) to a QFutureWatcher? 【发布时间】:2014-05-02 22:45:00 【问题描述】:

如果我使用QtConcurrent::run 启动某个异步执行函数,并使用QFutureWatcher 监视返回的未来,如果我可以在该异步执行函数中执行什么操作来传达一些进度文本,这将导致QFutureWatcher 发射其progressTextChanged 信号?

即我想做的是某事,比如:

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);

然而大问题,QtConcurrent::run documentation 明确说明

请注意,QtConcurrent::run() 返回的 QFuture 确实不支持 取消、暂停或进度报告。返回的 QFuture 可以 只用于查询运行/结束状态和返回 函数的值。

那么我能做的最简单的事情是什么,它可以让我在功能上与上述尝试做的事情相同?我必须放弃QtConcurrent::run吗? QFuture?两个都? (然后返回QThread 并排队连接?)

【问题讨论】:

【参考方案1】:

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

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

【讨论】:

好的,但是我实际上在fn() 中调用了什么来获取要更新的进度信息?显然,它需要传递一些东西……但是什么?似乎有一个 QFutureInterfaceBase 具有进度信息设置方法,但 QFuture 实际上并没有继承它。嗯...显然下一步是查看QtConcurrent::mappedReduced() 的实现,看看它是如何实现的... 我认为使用信号报告进度是使用QtConcurrent::run()时最方便的方式。 是的,这就是我最终做的事情:使用带有 QueuedConnection 的 QMetaObject::invokeMethod 从并发函数中发送状态消息到监视其进度的插槽(实际上, BlockingQueuedConnection 似乎导致显示更流畅......我发送了很多消息)。更多关于qt-project.org/forums/viewthread/41012/#174738的上下文@【参考方案2】:

您仍然可以将QFutureWatcherQProgressDialog 一起使用,就像在我的示例中一样:

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();

【讨论】:

以上是关于如何将 QtConcurrent::run 函数(或类似函数)中的 progressText 传递给 QFutureWatcher?的主要内容,如果未能解决你的问题,请参考以下文章

QtConcurrent::run 如何停止后台任务

如何将字符串数组传递给 QtConcurrent::run?

qt 创建线程

QtConcurrent::run 发出信号

如何正确使用QtConcurrent运行类成员函数

使用 QtConcurrent::run() 修改成员变量?