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:
#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
#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);
}
#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
#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"