remove before flight remove before flight - 3 months ago 36
C++ Question

Qt 5 : update QProgressBar during QThread work via signal

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 ?

Answer

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();        
    //...
}