简体   繁体   中英

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

I applied an C++ example of working with threads in Qt 5.7. All things are good except two things:

1- I used Signal & Slot to update my label in the main form. the problem is that is no effect. Really, I don't know where is the issue.

2- The loop works fine, but when I exit my program I see (throught the "Application Output") that the loop still work (I think that's related with the started thread).

This my little example:


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

How can I fix this?

Thanks.

In your code, doWork() function is called from the Worker 's constructor, the constructor is invoked in the main thread (that is done in the line Worker* worker= new Worker; ).

Of course, that is not what you meant to do, since it will cause the main thread to execute the for loop in doWork() before even reaching into the connect call.

Instead of calling doWork() from the Worker 's constructor, you should connect the thread's started() signal to doWork() slot, then call thread->start() after moving the Worker object to the new thread. This will leverage Qt cross-thread signals to invoke doWork() in the new thread as soon as it starts.

Here is how your code should look like:

#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"

I solved my problem by following the comment of Mr @Mike.

Simply, I removed the doWork() function from the instructor. Then, I added new connection between the thread's started() SIGNAL and worker's doWork() SLOT.

So thes are the changes:


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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM