通过实现两个线程同时按指定顺序读取图片,并显示在label中来了解Qt多线程QThread的使用(为方便实现,所使用的图片以1-10的数字命名)
一、线程类QThread的重载
1:mythread.h文件
定义的线程类命名为myThread,变量threadID用来确顶是哪一个线程发出信号;在函数threadBegin(),threadPause()和threadStop()中通过该改变bool类型变量pause和stop的值来达到控制线程启动、暂停、停止的目的。
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
#include <QThread>
class myThread :public QThread
{
Q_OBJECT
public:
myThread();
void threadBegin();//开始线程
void threadPause();//暂停
void threadStop();//终止
void setThreadID(int id);
private:
int p_seq;//图片序号
bool pause=false;//暂停
bool stop=false;//停止
int threadID;//线程id
signals:
//通过信号发出消息
void pictureNumber(int p_seq,int threadID);
protected:
void run();
};
#endif // MYTHREAD_H
2:mythread.cpp文件
通过重载QThread类中的虚函数run()来实现线程的主要任务,run()函数一般都是事件循环过程。通过发射信号来传递数据。信号pictureNumber(int,int)的两个参数分别是图片序号和线程id用于主线程中显示图片和确认线程。setThreadID(int id)用于赋予线程id。
#include "mythread.h"
myThread::myThread()
{
}
void myThread::setThreadID(int id)
{
threadID=id;
}
void myThread::threadBegin()
{
stop=false;
pause=false;
}
void myThread::threadPause()
{
if(pause)
pause=false;
else
pause=true;
}
void myThread::threadStop()
{
stop=true;
p_seq=0;//线程终止后图片序号归为0
}
void myThread::run()
{//线程任务
stop=false;
p_seq=0;//线程启动时图片序号为0
while(!stop)
{
if(!pause)
{
if(p_seq>=10)
{
p_seq=0;
}
emit pictureNumber(p_seq,threadID);//发射信号
p_seq++;
}
msleep(500);
}
quit();//退出线程事件循环
}
二、主线程设计
1:主线程的界面布局
QLabel的变量label和label_2分别用于两个线程显示图片,三个QPushButton分别用于控制线程。
2:widget,h
槽函数getPicture(int num,int threadID)用于接收线程发出的信号,
#define threadNum 2表示使用的子线程数量为2(可根据需求自行定义)
重载closeEvent()事件,在关闭窗口时确保所有线程被停止
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPixmap>
#include <QCloseEvent>
#include "mythread.h"
#define threadNum 2//子线程数量
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
protected:
void closeEvent(QCloseEvent *event);
private slots:
void on_pBStart_clicked();//线程开始
void on_pBPause_clicked();//线程暂停
void on_pBEnd_clicked();//线程结束
private slots:
void getPicture(int num,int threadID);//线程信号槽函数
private:
myThread thread[threadNum];//线程
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
3:widget.cpp
子线程的id设置和线程信号和槽函数的连接都在构造函数中完成。
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->pBStart->setEnabled(true);
ui->pBPause->setEnabled(false);
ui->pBEnd->setEnabled(false);
ui->label->setScaledContents(true);
ui->label_2->setScaledContents(true);
//
for(int i=0;i<threadNum;i++)
{
thread[i].setThreadID(i);//
connect(&thread[i],SIGNAL(pictureNumber(int,int)),this,SLOT(getPicture(int ,int)));
}
}
Widget::~Widget()
{
delete ui;
}
void Widget::getPicture(int num,int threadID)
{//线程信号响应的槽函数
QPixmap temp;
QString path=QString("pictures/%1.png").arg(num+1);
temp.load(path);
if(threadID==0)
ui->label->setPixmap(temp);
if(threadID==1)
ui->label_2->setPixmap(temp);
}
void Widget::on_pBStart_clicked()
{//线程开始
qDebug()<<"the begin";
for(int i=0;i<threadNum;i++)
{
thread[i].start();
thread[i].threadBegin();
}
ui->pBStart->setEnabled(false);
ui->pBPause->setEnabled(true);
ui->pBEnd->setEnabled(true);
}
void Widget::on_pBPause_clicked()
{//线程暂停
for(int i=0;i<threadNum;i++)
{
thread[i].threadPause();
}
QString text;
if(ui->pBPause->text()==QString::fromLocal8Bit("暂停"))
text=QString::fromLocal8Bit("继续");
else if(ui->pBPause->text()==QString::fromLocal8Bit("继续"))
text=QString::fromLocal8Bit("暂停");
ui->pBPause->setText(text);
}
void Widget::on_pBEnd_clicked()
{//线程结束
for(int i=0;i<threadNum;i++)
{
thread[i].threadStop();
}
ui->pBStart->setEnabled(true);
ui->pBEnd->setEnabled(false);
ui->pBPause->setEnabled(false);
if(ui->pBPause->text()==QString::fromLocal8Bit("继续"))
ui->pBPause->setText(QString::fromLocal8Bit("暂停"));
}
void Widget::closeEvent(QCloseEvent *event)
{//窗口关闭,必须结束所有线程
for(int i=0;i<threadNum;i++)
{
thread[i].threadStop();
if(thread[i].isRunning())
{//结束所有线程
thread[i].threadStop();
thread[i].wait();
}
}
event->accept();
}
三、效果展示
Widget 2023-03-07 16-45-48