Unable to run a function on a concurrent thread.
-
I have a class named
myClass
. It has a sloton_button_clicked()
. There is a separate function calleddevKit_funct()
which is not a part of thismyClass
.I want to run
devKit_funct()
when the button is clicked. In other words, I want to rundevKit_funct()
in theon_button_clicked()
function. I am able to do this successfully and achieve my tasks. However, thisdevKit_funct()
takes a few seconds to complete. In the time it takes to complete, I want to execute a function which loads a gif in a label and has a loading icon video. This function is calledloading()
.loading()
works fine independently, however when I runloading()
insideon_button_clicked()
followed bydevKit_funct()
, theloading()
function does not run at all, and only thedevKit_funct()
runs.So, I guessed that I need to use a different thread to execute the
loading()
function while thedevKit_funct()
is being executed, so that they both run simultaneously.Given this, my program became the following, when I attempted implementing this:
myClass.h -
class myClass : public QDialog { Q_OBJECT public: myClass(QWidget *parent = nullptr); void loading(); private slots: void on_button_clicked(); private: Ui::myClass *ui; QMovie *movie; int a, b; };
myClass.cpp -
myClass::myClass(QWidget *parent) : QDialog(parent), ui(new Ui::myClass) { ui->setupUi(this); a=1; b=1; } void myClass::on_button_clicked(){ QFuture<void> future = QtConcurrent::run([this] () -> void { return loading(); }); if(devKit_funct(a, b)==32) { qDebug()<<"Error in devkit_funct()\n"; return; } } void myClass::loading{ movie= new QMovie(":/picture/images/watermelonLoad.gif"); ui->loadLabel->setMovie(movie); ui->loadLabel->show(); movie->start(); }
However, even with this, I am not able to see the loading gif when I click the button. It still only executes the
devKit_funct()
, and after it is done executing, I see just a flicker/glimpse of the loading icon, and then it disappears.I don't understand what I am doing wrong here. Would be grateful for some guidance.
-
@BigBen said in Unable to run a function on a concurrent thread.:
So, I guessed that I need to use a different thread to execute the loading() function while the devKit_funct() is being executed, so that they both run simultaneously.
Qt requires all UI operations to run only in the main thread.
However, this devKit_funct() takes a few seconds to complete. In the time it takes to complete,
You could do computation work in another thread, that is fine. But show your movie in main UI thread.
-
- Only call
loading()
, which does theQMovie
, from the main thread. Do that immediately before: - Use
QtConcurrent::run()
, or other thread worker, to do what time-consuming, computation, non-UI stuff you need to do. - When the thread finishes, stop the movie.
- Only call
-
I tried the below code, but here, the
devKit_funct()
gets executed first, and when it is done, the movie starts playing. There is still something that I am doing wrong.myClass::myClass(QWidget *parent) : QDialog(parent), ui(new Ui::myClass) { ui->setupUi(this); a=1; b=1; } void myClass::on_button_clicked(){ loading() QFuture<void> future = QtConcurrent::run([this] () -> int { return devKit_funct(a, b); }); if(future.result()==32) { qDebug()<<"Error in devkit_funct()\n"; return; } } void myClass::loading{ movie= new QMovie(":/picture/images/watermelonLoad.gif"); ui->loadLabel->setMovie(movie); ui->loadLabel->show(); movie->start(); }
-
@BigBen said in Unable to run a function on a concurrent thread.:
if(future.result()==32)
As soon as you ask for
QFuture.result()
your program blocks at this point, waiting for the thread to finish and return a result:If a result is not available at the time of calling the result(), resultAt(), or results() functions, QFuture will wait until the result becomes available.
You must not do this until the
QFuture
signals that it has finished:To interact with running tasks using signals and slots, use QFutureWatcher.
You will need to create a
QFutureWatcher
and it will send you aQFutureWatcher::resultReadyAt()
signal when the thread has finished, see https://doc.qt.io/qt-5/qfuturewatcher.html#details. YourQFuture<void> future
&QFutureWatcher
variables will need to be class member variables, to persist from thread start to finish. -
@JonB Thank you for the clarification. I understand what you're saying, but having slight difficulty in converting it to code. Need some guidance filling up some gaps in the code below.
--------- In the code are areas that I want to fill or want to replace with appropriate code, but not able to understand how to.myClass.h -
class myClass : public QDialog { Q_OBJECT public: myClass(QWidget *parent = nullptr); void loading(); QFuture<int> res; QFutureWatcher<int> futWatch; private slots: void on_button_clicked(); private: Ui::myClass *ui; QMovie *movie; int a, b; };
myClass.cpp -
myClass::myClass(QWidget *parent) : QDialog(parent), ui(new Ui::myClass) { ui->setupUi(this); a=1; b=1; } void myClass::on_button_clicked(){ loading(); res= QtConcurrent::run([this] ()->int {return devKit_funct(a, b);}); futWatch.setFuture(res); QObject::connect(futWatch, SIGNAL(resultReadyAt(int)), --------,---------); if(devKit_funct(a, b)==32 -------------) { qDebug()<<"Error in devkit_funct()\n"; return; } } void myClass::loading{ movie= new QMovie(":/picture/images/watermelonLoad.gif"); ui->loadLabel->setMovie(movie); ui->loadLabel->show(); movie->start(); }
-
@BigBen
Bearing in mind that I have never usedQFuture
/QFutureWatcher
...:QObject::connect(futWatch, &QFutureWatcher::finished, this, &myClass::onFinished); void myClass::onFinished() { move->stop(); }
if(devKit_funct(a, b)==32 -------------)
No idea what you want to do here. This line executes (in your main UI thread) immediately after you have set off the
res= QtConcurrent::run([this] ()->int {return devKit_funct(a, b);});
Guessing that you want to do something with the result (
return devKit_funct(a, b);
), get rid of theif(devKit_funct(a, b)==32 ...
inon_button_clicked()
, you must move it to themyClass::onFinished()
slot which be called when the thread finishes, e.g.void myClass::onFinished() { move->stop(); int result = futWatch.result(); qDebug() << "devKit_funct(a, b) returned:" << result; if (result == 32) { qDebug()<<"Error in devkit_funct()\n"; return; } }
-
Thank you for your guidance.
I was able to solve this issue with your help.For others facing this issue:
The
connect()
function that worked for me was:QObject::connect(&futWatch, &QFutureWatcher<int>::finished, this, &myClass::onFinished);
And the
onFinished()
function was the same as suggested by Jon