目录
【一】类的架构设计
【二】通过QObject实现多线程
【三】界面的显示和按钮事件
先放一篇写的很好理解的文章~~~
https://blog.csdn.net/czyt1988/article/details/71194457
本文的代码的功能: 画一个界面,显示界面的时候,同时开启几个子线程, 子线程返回自己的线程ID号,并且在界面上显示出来。
一开始接触多线程,完全不知道怎么设计类。
我看了一些相关的代码之后,大家的程序都差不多,大概的程序构架是这样的:
|-----------------------------------------VS2017工程--------------------------------------------
|----------------程序入口 main.cpp
|---------------UI类头文件 UI.h
|---------------UI类成员函数实现 UI.cpp
|---------------线程类头文件 thread.h
|---------------线程类成员函数实现 thread.cpp
它们之间的关系,或者说我的代码实现流程大体是这样的:
(1)调用main函数。
main函数就干了一件事儿:构造了一个UI类的对象出来,然后显示。
========================备注备注备注备注====================================
在使用QT自动生成代码的时候,自动生成cpp文件或者h文件里面的实际上是一个类。有时候我又想改界面,再重新生成一个全新的cpp文件。所以我希望我实现一些功能的时候(比如添加某个按钮的信号和槽),不是在这个cpp文件里改动,否则一旦界面改动,我之前实现的功能就被QT自动生成的代码改掉了。
【实现方式】我自己写了一个UI类,QT生成的类是我的UI类的成员变量,也就是我在我的UI类里构造了一个对象出来。
详情见代码。
========================备注完了备注完了备注完了=============================
(2)开启多个线程。
线程在哪里实现嘞?main函数构造UI对象。这个过程调用了UI类。UI类有一个【开启线程】的成员函数。
这个【开启线程】函数会通过【线程数组】实例化一大堆线程出来,也就是开启多个线程。(这里涉及到一些槽和信号的连接,在后面中详细说。)
(3)线程开始工作。
实例化线程。主线程,也就是我的UI类,会通过emit方法让子线程开始工作。子线程开始干活,干完活儿再通过emit方法把结果给主线程。
放上两个类的定义的代码:
===线程类=====
#ifndef GGTHREAD_H
#define GGTHREAD_H
#include
class ggClassThread: public QObject
{
Q_OBJECT
public:
ggClassThread(QObject* parent = NULL);
~ggClassThread();
signals:
void message(const QString& info);
public slots:
void runSomeBigWork1();
};
#endif // !GGTHREAD_H
====UI类===
#ifndef GGCLASSUI_H
#define GGCLASSUI_H
# include "ggUI_V1.h"
# include "ggThread.h"
#include
class ggClassUi: public QWidget {
Q_OBJECT
public:
ggClassUi();
ggClassUi(QMainWindow *win);
~ggClassUi();
void outPutInfo(QString info);
void startThread();
private:
Ui_ggUI_V1Class ggUi;
ggClassThread* ggThread[4];
QThread* ggQthread[4];
signals:
void startThreadWork();
public slots:
void on_startSever_clicked();
};
#endif // GGCLASSUI_H
结合代码,流程大概就是:
(1)【main】------(实例化界面)-----》(2)【UI】---------(按钮按下的信号触发函数on_startServer_clicked,调用startThread()实例化线程)------》(3)【thread】-----------(通过信号startThreadWork,触发线程工作函数runSomeBigWork1, 线程工作结束,message函数触发outPutInfo显示结果)------》(4)【UI】显示结果
实现多线程的方式挺多的,这里主要是通过QObject实现多线程。
最关键的函数是:moveToThread()。
要了解这个函数,首先从多线程说起,QThread类是Qt多线程实现的基础。我们先一个实例化出来一个Qthread对象,在实例化我们自己定义的一个【实现某个功能】的类,这个时候,使用moveToThread函数,就能让这个功能在Qthread创建的线程里运行了。实际上是把一个普通的类的函数换了个地方执行,从而实现了多线程。
void ggClassUi::startThread() {
for (int i = 0; i < 4; i++) {
ggQthread[i] = new QThread(); //Qthread
ggThread[i] = new ggClassThread(); //普通的类
ggThread[i]->moveToThread(ggQthread[i]); //移到线程里执行
connect(ggQthread[i], &QThread::finished, ggQthread[i], &QObject::deleteLater);
connect(ggQthread[i], &QThread::finished, ggThread[i], &QObject::deleteLater);// 【1】
connect(this, &ggClassUi::startThreadWork, ggThread[i], &ggClassThread::runSomeBigWork1);//【2】
connect(ggThread[i], &ggClassThread::message, this, &ggClassUi::outPutInfo);//【3】
ggQthread[i]->start();
}
}
为了换地方,需要建立一些联系,通过connect函数来完成。
注释【1】Qthread结束, 类要delete
注释【2】startThreadWork被调用(这个函数里创建了一些Qthread对象出来),实际上是线程开始执行,触发自定义的线程类开始工作。
注释【3】工作结束,message函数会触发界面的outPutInfo函数。
初始化界面的时候,把界面上【开启线程】的按钮,通过clicked信号,连接到自己写的槽函数上,来开启线程。
ggClassUi::ggClassUi(QMainWindow *win) {
ggUi.setupUi(win);
connect(ggUi.startServerButton, &QPushButton::clicked, this, &ggClassUi::on_startSever_clicked);
}
void ggClassUi::on_startSever_clicked() {
startThread();
emit startThreadWork();
ggUi.startServerButton->setEnabled(false);
}
在通过startThread开启线程后,emit发出信号startThreadWork,线程就开始工作了。