Qt 5:通过信号在QThread工作期间更新QProgressBar

时间:2016-02-27 17:46:18

标签: c++ multithreading qt qt5 qt-signals

我正在尝试更新QProgressDialog(由QMainWindow类拥有)以及执行处理一些耗时操作的QThread。线程在操作期间发出一些信号,以通知调用应用程序有关进展。我希望将线程发出的进度信号连接到QProgressDialog的setValue插槽,以便更新进度条。

它不起作用!不显示进度对话框。如果我在QMainWindow中添加一个插槽并将其连接到工作进度信号以显示线程通过qDebug输出给出的值,我看到信号似乎在线程操作期间被堆叠并且仅在结束时被取消堆叠。线程。

我尝试过DirectConnection连接选项但没有成功。

这是我的代码: 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

此代码的输出,其中k <&lt; 100是:

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

如果我重新加入 worker.wait(); 由

 int k=0;
    while(worker.isRunning())
    {
        qDebug() << "main " << k;
        k++;
    }

我得到线程的输出和调用方法的输出交错。它确认我的线程与调用方法无关。

对我做错了什么的想法?

2 个答案:

答案 0 :(得分:7)

使用QThread绝对错误。见what is the correct way to implement a QThread... (example please...)。你需要学习线程的基础知识。

你的错误:
1.在本地范围内创建静态线程对象;
2.等待主线程完成;
3.不要启动线程;
4.主线程中的直接调用方法doHeavyCaclulations();
5.发出没有工作事件循环的信号,以便传送...

为了您的目的,您需要:
不要继承QThread。只需使用必要的函数创建简单的Work类:

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

答案 1 :(得分:3)

QThread有一个非常重要的事情,你在使用它时必须始终记住 - 只有run()实际上在一个单独的线程中运行。

每当你创建一个QThread实例时,这个实例的线程关联(它所属的线程)就是你创建它的同一个线程。什么是最重要的你可能会问我的插槽和信号有什么关系?嗯,这与这些事情有很大关系。因为只有run()在单独的线程内运行,所以您必须考虑以下事项:

  1. 属于实例的信号ergo信号具有与run()
  2. 不同的线程关联
  3. 插槽属于实例ergo插槽具有不同的线程亲和力,然后run() - 访问在插槽内部和run()内部处理的共享数据需要明确使用线程安全机制,如互斥锁和信号量
  4. 如果你在插槽中做了很多东西,你仍然会冻结你的用户界面,好像你没有使用你的QThread
  5. 也就是说,在某些情况下,您可能需要/必须在QThread中使用插槽和信号,但这样的实现必须针对控制QThread的实例而不是它的& #39;实际上是在一个单独的线程中运行(使用run())。

    Here is a small demo我作为演示如何实现插槽和信号以及使用QObject与单独的线程进行交互的演示。它采用插槽和信号。请注意,实际上没有必要使用QThread。您也可以使用QRunnable作为示例(尽管您必须明确告诉它继承QObject或使用由您创建的QObject的单独子类,因为QRunnable不支持插槽和信号(它不是QObject的子类)。

    使用QObject的优点是,您可以将其实例移动到更改其线程关联性的线程,以便它完全在该单独的线程中运行(包括插槽)。如果您愿意,还可以将多个QObject实例放在单个QThread中。继承QThread并使用它代替此模型时,您将限制您的选项。

    所以我的建议是转储QThread实现并转到QThread + QObject(也称为工作者设计模式)的做事方式(针对此特定情况,是)。