Qt - SIGNAL& SLOT没有从工人类更新主窗口中的QLabel

时间:2016-12-16 20:20:15

标签: c++ multithreading qt loops signals-slots

我在Qt 5.7中应用了一个使用线程的C ++示例。除了两件事之外,一切都很好:

1-我使用过Signal&插槽以更新主窗体中的标签。问题是没有效果。真的,我不知道问题出在哪里。

2-循环工作正常,但是当我退出程序时,我看到(通过“应用程序输出”)循环仍然有效(我认为这与启动的线程有关)。

这是我的小例子:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();  

private slots:
    void on_pushButton_clicked();
    void updateLabelText(const QString TheString);

private:
    Ui::MainWindow *ui;

};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "worker.h"

//#include <QDebug>
#include <QThread>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    QThread *workerThread = new QThread;
    Worker *worker  = new Worker;
    worker->moveToThread(workerThread);
    connect(worker, SIGNAL(sendText(const QString)), this, SLOT(updateLabelText(QString)));
    workerThread->start();
}

void  MainWindow::updateLabelText(const QString TheString)
{
    ui->label->setText(TheString);
}

worker.h

#ifndef WORKER_H
#define WORKER_H

#include <QObject>

class Worker : public QObject
{
    Q_OBJECT

public:
    explicit Worker();

public slots:
    void doWork();

signals:
  void sendText(const QString);

};

#endif // WORKER_H

worker.cpp

#include "worker.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>
#include <QThread>
#include <QMessageBox>
#include <QApplication>

Worker::Worker()
{
    doWork();
}

void Worker::doWork()
{
    for (int i = 0; i<999999; i++) {
        emit sendText(QString::number(i));
        qDebug() << "The number is : " + QString::number(i);
        qApp->processEvents();
        //QThread::msleep(5);
    }
}

我该如何解决这个问题?

感谢。

2 个答案:

答案 0 :(得分:0)

在你的代码中,从doWork()的构造函数调用Worker函数,在主线程中调用构造函数(在行Worker* worker= new Worker;中完成)

当然,这不是你想要做的,因为它会导致主线程在for之前执行doWork()循环,直到进入connect调用。< / p>

您应该将线程的started()信号连接到doWork()广告位,然后拨打电话,而不是从Worker的构造函数中调用doWork()。将thread->start()对象移动到新线程后Worker。这将利用Qt跨线程信号在新线程启动时立即调用doWork()

以下是您的代码的外观:

#include <QtWidgets>

//QThread wrapper for safe destruction
//see http://stackoverflow.com/a/19666329
class Thread : public QThread{
    using QThread::run; //final
public:
    Thread(QObject* parent= nullptr): QThread(parent){}
    ~Thread(){ quit(); wait();}

};

class Worker : public QObject{
    Q_OBJECT
public:
    explicit Worker(QObject* parent= nullptr): QObject(parent){}
    ~Worker()= default;

    Q_SIGNAL void sendText(QString text);
    Q_SIGNAL void workFinished();

    Q_SLOT void doWork(){
        for (int i = 0; i<1000; i++) {
            emit sendText(QString::number(i));
            QThread::msleep(5);
        }
        emit workFinished();
    }
};

class Widget : public QWidget{
    Q_OBJECT
public:
    explicit Widget(QWidget* parent= nullptr): QWidget(parent){
        layout.addWidget(&buttonWork);
        layout.addWidget(&label);

        connect(&buttonWork, &QPushButton::clicked,
                this, &Widget::buttonWorkClicked);

    }
    ~Widget()= default;

    Q_SLOT void buttonWorkClicked(){
        Thread* thread= new Thread(this);
        Worker* worker= new Worker;
        worker->moveToThread(thread);
        //invoke doWork as soon as the thread is started
        connect(thread, &QThread::started, worker, &Worker::doWork);
        connect(worker, &Worker::sendText, this, &Widget::updateLabelText);
        //quit the thread when work is finished
        connect(worker, &Worker::workFinished, thread, &QThread::quit);
        //destroy thread and worker object when work is finished
        connect(thread, &QThread::finished, thread, &QObject::deleteLater);
        connect(thread, &QThread::finished, worker, &QObject::deleteLater);
        //start the thread
        thread->start();
    }

    Q_SLOT void updateLabelText(QString text){
        label.setText(text);
    }

private:
    QVBoxLayout layout{this};
    QPushButton buttonWork{"Work"};
    QLabel label{"No work yet"};
};

int main(int argc, char* argv[]){
    QApplication a(argc, argv);

    Widget w;
    w.show();

    return a.exec();
}

#include "main.moc"

答案 1 :(得分:0)

我按照@Mike先生的评论解决了我的问题。

简单地说,我从教师那里删除了doWork()函数。然后,我在线程的started()SIGNAL和worker的doWork()SLOT之间添加了新的连接。

所以这些是变化:

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "worker.h"

#include <QThread>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    QThread *workerThread = new QThread;
    Worker *worker  = new Worker;
    connect(workerThread, SIGNAL(started()), worker, SLOT(doWork()),Qt::QueuedConnection);
    connect(worker, SIGNAL(sendText(const QString)), this, SLOT(updateLabelText(const QString)),Qt::QueuedConnection);
    worker->moveToThread(workerThread);
    workerThread->start();
}
void  MainWindow::updateLabelText(const QString TheString)
{
    ui->label->setText(TheString);
}

worker.cpp

#include "worker.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>
#include <QThread>
#include <QMessageBox>
#include <QApplication>

Worker::Worker()
{
    doWork();
}

void Worker::doWork()
{
    for (int i = 0; i<999999; i++) {
        emit sendText(QString::number(i));
        qDebug() << "The number is : " + QString::number(i);
        qApp->processEvents();
        QThread::msleep(5);
    }
}