Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
401 views
in Technique[技术] by (71.8m points)

c++ - 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 ?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...