Qt - 如何为 QProgressBar 组合 QtConcurrent 和 QThreadPool?
Qt - How to combine QtConcurrent and QThreadPool for a QProgressBar?
在 mainwindow.ui
中,我创建了一个名为 progressBar
的 QProgressBar
和一个名为 speckle
的 QPushButton
来启动繁重的计算。
在 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
应该 watch 从 QtConcurrent
:
返回的 QFuture
对象
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();
}
但是应用程序不是那样工作的。编译并单击 on speckle 后,mainwindow
没有响应。这是创建 x 线程的 progressSpecle
成员函数:
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;
}
}
应用程序运行没有错误,但 mainWindow 不负责,因为(我认为)while(true)
。但我不明白为什么这会阻塞主窗口,因为整个 progressSpecle
函数在一个单独的线程中工作,该线程以 QtConcurrent
.
创建并启动
为什么 progressSpecle
函数会阻塞主窗口?
那么我怎样才能让 progressBar
工作呢?
QFutureWatcher
信号是从线程池中发出的。这意味着 QProgressBar
插槽将通过 "queued connection" 调用:一个事件将排队到主线程的事件循环,并在处理此事件时调用插槽。
对QThreadPool::waitForDone
的调用阻塞了主线程,所以事件循环不是运行ning并且不会调用排队槽。在等待并发任务完成时,您需要保持主线程的事件循环 运行ning。
我认为有两种方法可以实现这一点。第一个是将回调连接到 QFutureWatcher::finished
信号和 return 控制到主事件循环:
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();
}
在 mainwindow.ui
中,我创建了一个名为 progressBar
的 QProgressBar
和一个名为 speckle
的 QPushButton
来启动繁重的计算。
在 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
应该 watch 从 QtConcurrent
:
QFuture
对象
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();
}
但是应用程序不是那样工作的。编译并单击 on speckle 后,mainwindow
没有响应。这是创建 x 线程的 progressSpecle
成员函数:
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;
}
}
应用程序运行没有错误,但 mainWindow 不负责,因为(我认为)while(true)
。但我不明白为什么这会阻塞主窗口,因为整个 progressSpecle
函数在一个单独的线程中工作,该线程以 QtConcurrent
.
为什么 progressSpecle
函数会阻塞主窗口?
那么我怎样才能让 progressBar
工作呢?
QFutureWatcher
信号是从线程池中发出的。这意味着 QProgressBar
插槽将通过 "queued connection" 调用:一个事件将排队到主线程的事件循环,并在处理此事件时调用插槽。
对QThreadPool::waitForDone
的调用阻塞了主线程,所以事件循环不是运行ning并且不会调用排队槽。在等待并发任务完成时,您需要保持主线程的事件循环 运行ning。
我认为有两种方法可以实现这一点。第一个是将回调连接到 QFutureWatcher::finished
信号和 return 控制到主事件循环:
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();
}