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

Posted

技术标签:

【中文标题】Qt - SIGNAL & SLOT 没有从工人阶级更新我在主窗口中的 QLabel【英文标题】:Qt - SIGNAL & SLOT are not updating my QLabel in mainwindow from a worker class 【发布时间】:2016-12-16 20:20:14 【问题描述】:

我应用了一个在 Qt 5.7 中使用线程的 C++ 示例。万事俱备,除了两件事:

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

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

这是我的小例子:


主窗口.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

主窗口.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);
    


我该如何解决这个问题?

谢谢。

【问题讨论】:

【参考方案1】:

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

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

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

你的代码应该是这样的:

#include <QtWidgets>

//QThread wrapper for safe destruction
//see http://***.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 layoutthis;
    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"

【讨论】:

【参考方案2】:

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

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

所以这些是变化:


主窗口.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);
    

【讨论】:

以上是关于Qt - SIGNAL & SLOT 没有从工人阶级更新我在主窗口中的 QLabel的主要内容,如果未能解决你的问题,请参考以下文章

QT 用比 SLOT 更少的参数连接一个 SIGNAL

关于QT中的signal 和 slot 中带参数问题

深入了解Qt之元signal和slot

golang-GUI开发qt之signal和slot

Qt Object::connect: No such slot Signal to Thread Slot

没有 SLOT 宏的 Qt 连接