Qt - 如何将QtConcurrent和QThreadPool组合成QProgressBar?

时间:2017-02-20 14:12:35

标签: c++ qt qthread qtconcurrent qprogressbar

mainwindow.ui我创建了一个名为QProgressBar的{​​{1}}和一个名为progressBar的{​​{1}},它开始了繁重的计算。

QPushButton内,我有一个适当的speckle按钮和一个代表繁重计算的私有函数。 mainwindow.h

private slot

mainwindow.h应该观看class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_speckle_clicked(); ... private: Ui::MainWindow *ui; QFutureWatcher<std::vector<cv::Mat>> futureWatcher; std::vector<cv::Mat> progressSpecle();//heavy computation }; 返回的futureWatcher对象:

QFuture

但是应用程序不能像那样工作。编译并点击 on speckle 后,QtConcurrent没有响应。以下是MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); ... connect(&this->futureWatcher, SIGNAL(progressValueChanged(int)), ui->progressBar, SLOT(setValue(int))); ... } ... void MainWindow::on_speckle_clicked() { //Start the computation. QFuture<std::vector<cv::Mat>> future; future = QtConcurrent::run(this, &MainWindow::progressSpecle); this->futureWatcher.setFuture(future); QThreadPool::globalInstance()->waitForDone(); vector<cv::Mat> result = future.result(); specklevisualization *s = new specklevisualization; s-> setAttribute(Qt::WA_DeleteOnClose); s-> start(result); s-> show(); } 成员函数,其中创建了x Threads:

mainwindow

应用程序没有错误,但mainWindow不负责,因为(我认为)progressSpecle。但是我不明白为什么这会阻塞mainWindow,因为整个void MainWindow::progressSpecle(){ vector<cv::Mat> input; ...//do something with input vector<cv::Mat> result; vector<cv::Mat> *all; all = &result; QThreadPool *threadPool = QThreadPool::globalInstance(); for(unsigned int i = 1; i<input.size(); i++) { cv_speckle_algorithm *work = new cv_speckle_algorithm(input.at(i-1), input.at(i), all, i-1); work->setAutoDelete(false); threadPool->start(work); } while(true){ if(threadPool->activeThreadCount() == 1) return result; } } 函数在一个单独的线程中工作,并以while(true)开始。

为什么progressSpecle功能会阻止mainWindow? 那么如何让QtConcurrent工作?

1 个答案:

答案 0 :(得分:2)

QFutureWatcher信号从池内线程中发出。这意味着QProgressBar插槽将通过&#34;排队连接调用&#34;:一个事件将排队到主线程的事件循环,并且当此事件时将调用该插槽处理完毕。

QThreadPool::waitForDone的调用阻塞了主线程,因此事件循环未运行,并且不会调用排队的插槽。您需要在等待并发任务完成时保持主线程的事件循环运行。

我可以通过两种方式来实现这一目标。第一种是将回调连接到QFutureWatcher::finished信号并将控制返回到主事件循环:

void MainWindow::on_speckle_clicked()
{
    //Start the computation.
    QFuture<std::vector<cv::Mat>> future;
    future = QtConcurrent::run(this, &MainWindow::progressSpecle);

    connect(&futureWatcher, &QFutureWatcherBase::finished, this, [result] {
        vector<cv::Mat> result = future.result();
        specklevisualization *s = new specklevisualization;
        s->setAttribute(Qt::WA_DeleteOnClose);
        s->start(result);
        s->show();
    });
    this->futureWatcher.setFuture(future);

    // return control to event loop
}

如果您愿意,可以使用命名方法而不是lambda。

第二种方法是在函数内部运行嵌套事件循环,并将其quit插槽连接到QFutureWatcher::finished信号:

void MainWindow::on_speckle_clicked()
{
    QEventLoop localLoop;

    //Start the computation.
    QFuture<std::vector<cv::Mat>> future;
    future = QtConcurrent::run(this, &MainWindow::progressSpecle);

    connect(futureWatcher, &QFutureWatcherBase::finished, &localLoop, &QEventLoop::quit);
    this->futureWatcher.setFuture(future);

    localLoop.exec(); // wait for done

    vector<cv::Mat> result = future.result();
    specklevisualization *s = new specklevisualization;
    s->setAttribute(Qt::WA_DeleteOnClose);
    s->start(result);
    s->show();
}