Qt - SIGNAL & SLOT 没有从工作人员更新我在主窗口中的 QLabel class

Qt - SIGNAL & SLOT are not updating my QLabel in mainwindow from a worker class

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

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

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

这是我的小例子:


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);
    }
}

我该如何解决这个问题?

谢谢。

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

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

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

您的代码应如下所示:

#include <QtWidgets>

//QThread wrapper for safe destruction
//see 
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"

我按照@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);
    }
}