Qt 5 : update QProgressBar during QThread work via signal
Solution 1
Absolutely wrong using of QThread
). See what is the correct way to implement a QThread... (example please...). You need to learn thread's basics.
Your mistakes:
1. Create a static thread object in a local scope;
2. Wait for its finish in the main thread;
3. Don't start the thread;
4. Direct call method doHeavyCaclulations()
in the main thread;
5. emit signal without working event loop for its deliver...
For your purpose you need:
Don't inherit QThread
. Just create simple Work
class with the necessary function:
class Work: public QObject
{
Q_OBJECT
public:
Work(){};
virtual ~Work(){};
public slots:
void doHeavyCaclulations() { /* do what you need and emit progress signal */ };
signals:
void progress(int);
}
// Then:
void QApp::doSomeWork()
{
//...
QThread* thread = new QThread(parent);
Work* worker = new Work; // Do not set a parent. The object cannot be moved if it has a parent.
worker->moveToThread(thread);
connect(thread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(started()), worker, SLOT(doHeavyCaclulations()));
connect(worker, SIGNAL(progress(int)), &progressDialog, SLOT(setValue(int)));
thread->start();
//...
}
Solution 2
QThread
has one very important thing you have to always remember when working with it - only the run()
actually runs in a separate thread.
Whenever you create an instance of QThread
this instance's thread affinity (the thread it belongs to) is the same thread where you have created it in. What's the big deal with that and what does it have to do with my slots and signals you may ask? Well, it has a lot to do with these things. Because only run()
runs inside a separate thread you have to consider the following:
- Signals belong to the instance ergo signals have a different thread affinity then the
run()
- Slots belong to the instance ergo slots have a different thread affinity then the
run()
- accessing shared data that is processed both inside a slot and insiderun()
requires explicitly employing thread-safety mechanisms such as mutexes and semaphores - If you do a lot of stuff inside your slots you will still freeze your UI as if you are not using your
QThread
That said there are some scenarios where you may want to/have to employ slots and signals in a QThread
but such implementation would have to be directed towards controlling the instance of QThread
and not what it's actually running in a separate thread (using run()
).
Here is a small demo I have written as a demonstration of how to implement slots and signals and interact with a separate thread using QObject
. It employs slots and signals. Note that the usage of QThread
is actually not necessary. You can also use a QRunnable
for example (though you have to explicitly tell it to inherit from QObject
too or to use a separate subclass of QObject
created by you because QRunnable
doesn't support slots and signals (it's not a subclass of QObject
).
The advantage of using a QObject
is that you can move its instance to the thread that is change it's thread affinity so that it completely runs in that separate thread (slots included). You can also put multiple QObject
instances inside a single QThread
if you want to. When inheriting a QThread
and using it instead of this model you are limiting your options quite a bit.
So my advice here is dump the QThread
implementation and go for the QThread + QObject
(also know as Worker design pattern) way of doing things (for this particular scenario that is).
remove before flight
Updated on June 18, 2022Comments
-
remove before flight almost 2 years
I'm trying to update a QProgressDialog (owned by a QMainWindow class) along the execution of a QThread who process some time consuming operations. The thread emit some signals during operation in order to inform the calling app about progression. I'm looking to connect the progress signal emitted by the thread to the setValue slot of the QProgressDialog in order to update the progress bar.
It doesn't work ! The progress dialog is not displayed. If I add a slot in my QMainWindow and connect it to the worker progress signal in order to display the value given by the thread throught qDebug output, I see that signals seems to be stacked during the threaded operation and unstacked only at the end of the thread.
I have tryed the DirectConnection connect's option without any success.
Here is my code : qapp.cpp
#include "qapp.h" #include <threaded.h> #include <QVBoxLayout> #include <QPushButton> #include <QDebug> #include <QProgressDialog> QApp::QApp(QWidget *parent) : QMainWindow(parent) { QVBoxLayout *mainLayout = new QVBoxLayout(this); QWidget *window = new QWidget(this); window->setLayout(mainLayout); setCentralWidget(window); QPushButton *button = new QPushButton("Run"); mainLayout->addWidget(button); connect(button, SIGNAL(clicked(bool)), this, SLOT(doSomeWork())); } void QApp::doSomeWork() { qDebug() << "do some work"; Threaded worker; worker.doHeavyCaclulations(); QProgressDialog progressDialog("Copying files...", "Abort Copy", 0, 10000, this); progressDialog.setWindowModality(Qt::WindowModal); progressDialog.setMinimumDuration(0); progressDialog.setValue(0); connect(&worker, SIGNAL(progress(int)), &progressDialog, SLOT(setValue(int))); connect(&worker, SIGNAL(progress(int)), this, SLOT(displayProgress(int))); worker.wait(); qDebug() << "end of thread"; } void QApp::displayProgress(int value) { qDebug() << "data received" << value; } QApp::~QApp() { }
threaded.cpp :
#include "threaded.h" #include <QDebug> Threaded::Threaded(QObject *parent) : QThread(parent) { } void Threaded::doHeavyCaclulations() { if (!isRunning()) { qDebug() << "start thread" ; start(); } } void Threaded::run() { qDebug() << "running big loop"; for(double k = 0 ; k < 10000 ; k++) { qDebug() << k; emit progress(k); } }
qapp.h
#ifndef QAPP_H #define QAPP_H #include <QMainWindow> class QApp : public QMainWindow { Q_OBJECT public: explicit QApp(QWidget *parent = 0); ~QApp(); private: private slots: void doSomeWork(); void displayProgress(int value); }; #endif // QAPP_H
threaded.h
#ifndef THREADED_H #define THREADED_H #include <QObject> #include <QThread> class Threaded : public QThread { Q_OBJECT public: explicit Threaded(QObject *parent = 0); void doHeavyCaclulations(); void run(); private: signals: void progress(int value); public slots: }; #endif // THREADED_H
The output of this code with k < 100 is :
do some work start thread running big loop 0 1 2 3 [...] 97 98 99 end of big loop end of thread data received 17 data received 18 data received 19 [...] data received 99
If I remplace worker.wait(); by
int k=0; while(worker.isRunning()) { qDebug() << "main " << k; k++; }
I get outputs of the thread and output of the calling method interleaved. It confirm that my thread is independant of the calling method.
Any idea about what I'm doing wrong ?
-
remove before flight about 8 yearsThank you for your answer ! Works fine this way !
-
remove before flight about 8 yearsThank you for your answer ! Good complement with Vladimir Bershov answer ! Works fine now !
-
rbaleksandar about 8 years@removebeforeflight You're welcome. To his implementation don't forget the
deleteLater()
slots for the thread itself and the worker. This ensures proper deletion of both and also stopping the thread in a decent manner.