00. 目录


文章目录


01. 概述

Qt在运行时会开启一个主线程,如果没有开启工作线程的话,所有界面上的操作都是在主线程,包括更新界面或者处理数据等操作。我们都知道如果处理数据比较多的话,最好是在单独开启一个线程来处理数据,这样就不会影响主线程的运行。

02. 开发环境

Windows系统​:Windows10

Qt版本​:Qt5.15或者Qt6

03. 实时更新UI(非信号与槽)

QT中不建议工作线程中更新界面。

workthread.h

#ifndef WORKTHREAD_H
#define WORKTHREAD_H

#include <QThread>

class MainWindow;
class QLabel;

class WorkThread : public QThread
{
Q_OBJECT
public:
WorkThread(QObject *parent);
~WorkThread();

void setObject(MainWindow *obj);
void setLabel(QLabel *l);
void stop();

protected:
void run();


private:
MainWindow *mainObject = nullptr;
QLabel *label = nullptr;
int i = 0;

volatile bool stopped = false;
};

#endif // WORKTHREAD_H

workthread.cpp

#include "workthread.h"
#include "mainwindow.h"

#include <QLabel>
#include <QDebug>

WorkThread::WorkThread(QObject *parent):QThread(parent)
{
stopped = false;
}


WorkThread::~WorkThread()
{

}


void WorkThread::setObject(MainWindow *obj)
{
mainObject = obj;
}

void WorkThread::setLabel(QLabel *l)
{
label = l;
}


void WorkThread::run()
{
qDebug() << "线程开始运行";

while(!stopped)
{
msleep(1);
i++;
//mainObject->runInThread();
label->setText(QString("当前计数为:%1").arg(i));
}
//下次启动
stopped = false;
}

//暂停线程
void WorkThread::stop()
{
stopped = true;
qDebug() << "线程已经暂停";
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include "workthread.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class WorkThread;

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();

void runInThread();

private slots:
void on_pushButton_clicked();

void on_pushButton_2_clicked();

private:
Ui::MainWindow *ui;

WorkThread *thread = nullptr;

int count = 0;
};
#endif // MAINWINDOW_H

mainwindow.cpp

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

#include "workthread.h"

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

thread = new WorkThread(this);
thread->setObject(this);
thread->setLabel(ui->label);

ui->pushButton->setEnabled(true);
ui->pushButton_2->setEnabled(false);
}

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

void MainWindow::runInThread()
{
count++;

ui->label->setText(QString("当前计数为: %1").arg(count));
}

//启动线程
void MainWindow::on_pushButton_clicked()
{
if (nullptr != thread)
{
thread->start();
}

ui->pushButton->setEnabled(false);
ui->pushButton_2->setEnabled(true);
}

//暂停线程
void MainWindow::on_pushButton_2_clicked()
{
if (nullptr != thread)
{
thread->stop();
}

ui->pushButton->setDisabled(false);
ui->pushButton_2->setDisabled(true);
}

主界面

【Qt】 Qt中实时更新UI程序示例_#include

04. 实时更新UI(信号与槽)

workthread.h

#ifndef WORKTHREAD_H
#define WORKTHREAD_H

#include <QThread>

class MainWindow;
class QLabel;


struct Student
{
int id;
char sex;
QString name;
};

//声明元类型
Q_DECLARE_METATYPE(Student)

class WorkThread : public QThread
{
Q_OBJECT
public:
WorkThread(QObject *parent);
~WorkThread();

void stop();

protected:
void run();

signals:
void sigCount(Student s);

private:

int i = 0;

volatile bool stopped = false;
};

#endif // WORKTHREAD_H

workthread.cpp

#include "workthread.h"
#include "mainwindow.h"

#include <QLabel>
#include <QDebug>

WorkThread::WorkThread(QObject *parent):QThread(parent)
{
//注册自定义类型
qRegisterMetaType<Student>();

stopped = false;
}

WorkThread::~WorkThread()
{

}

void WorkThread::run()
{
qDebug() << "线程开始运行";

while(!stopped)
{
i++;

//发送信号
Student s;
s.id = i;
s.sex = 'M';
s.name = "lily";
emit sigCount(s);

msleep(1);
}

//下次启动
stopped = false;
}

//暂停线程
void WorkThread::stop()
{
stopped = true;
qDebug() << "线程已经暂停";
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include "workthread.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class WorkThread;

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();

private slots:
void on_pushButton_clicked();

void on_pushButton_2_clicked();

void slotSetValue(Student s);

private:
Ui::MainWindow *ui;

WorkThread *thread = nullptr;
};
#endif // MAINWINDOW_H

mainwindow.cpp

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

#include "workthread.h"

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

thread = new WorkThread(this);

ui->pushButton->setEnabled(true);
ui->pushButton_2->setEnabled(false);

connect(thread, &WorkThread::sigCount, this, &MainWindow::slotSetValue);
}

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


//启动线程
void MainWindow::on_pushButton_clicked()
{
if (nullptr != thread)
{
thread->start();
}

ui->pushButton->setEnabled(false);
ui->pushButton_2->setEnabled(true);
}

//暂停线程
void MainWindow::on_pushButton_2_clicked()
{
if (nullptr != thread)
{
thread->stop();
}

ui->pushButton->setDisabled(false);
ui->pushButton_2->setDisabled(true);
}

//槽函数
void MainWindow::slotSetValue(Student( s))
{
ui->label->setText(QString("当前值为: %1 %2 %3").arg(s.id).arg(s.sex).arg(s.name));
}

主界面

【Qt】 Qt中实时更新UI程序示例_#include_02

【温馨提示】

如果要在Qt信号槽中使用自定义类型,需要注意使用qRegisterMetaType对自定义类型进行注册,当然在不跨线程时使用自定义类型signal/slot来传递,可能不会出现什么问题;一旦涉及跨线程就很容易出错,回想下信号槽的作用就是用来对象与对象之间通信的,难免会跨线程,建议在使用自定义类型利用信号槽通信时,最好先通过qRegisterMetaType()将自定义类型进行注册,以免出错。

总结qRegisterMetaType使用方法如下:

1、注册位置:在第一次使用此类链接跨线程的signal/slot之前,一般在当前类的构造函数中进行注册;

2、注册方法:在当前类的顶部包含:#include ,构造函数中加入代码:qRegisterMetaType(“Myclass”);

3、Myclass的引用类型需单独注册:qRegisterMetaType(“Myclass&”);

05. 源码下载

下载:​​实时更新UI(信号与槽方式).rar​

06. 附录

6.1 Qt教程汇总