In the mainwindow.ui
I created a QProgressBar
named progressBar
and a QPushButton
named speckle
which starts the heavy computation.
Inside the mainwindow.h
I have an appropriate private slot
for the button and a private function which represents the heavy computation. 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
};
The futureWatcher
is supposed to watch the the QFuture
object that gets returned from 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();
}
But the the application does not work like that. After compiling and clicking on speckle the mainwindow
is not responsive. Here is the progressSpecle
member function in which x Threads gets created:
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;
}
}
The application works without errors but the mainWindow is not responsible because (I think) of the while(true)
. But I do not understand why this should block the mainWindow, because the whole progressSpecle
function works in a seperate thread created and started with QtConcurrent
.
Why does the progressSpecle
function Blocks the mainWindow?
So how can I get the progressBar
to work?
The QFutureWatcher
signal is emitted from within the pooled thread. This means that the QProgressBar
slot will be called via a "queued connection": an event will be queued to the main thread's event loop, and the slot will be called when this event is processed.
The call to QThreadPool::waitForDone
is blocking the main thread, so the event loop is not running and the queued slot will not be called. You need to keep the main thread's event loop running while it waits for the concurrent task to finish.
There are two ways I can think to accomplish this. The first is to connect a callback to the QFutureWatcher::finished
signal and return control to the main event loop:
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
}
You can use a named method instead of a lambda if you prefer.
The second way is to run a nested event loop inside your function, and connect its quit
slot to the QFutureWatcher::finished
signal:
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();
}