一、线程的创建方法一:
1、自定义一个类(MyThread) ,继承于 QThread;
2、重写 QThread 的 run() 方法(run() 方法就是线程处理函数,将耗时的操作放在该方法中执行);
3、调用线程对象的 start() 方法启动线程,执行线程处理函数;注意:不能直接调用 run() 方法;
效果图:
子线程类:
mythread.h:
#include <QThread>
// 将父类改成 QThread
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
signals:
void threadOver(); // 自定义一个线程结束信号
public slots:
protected:
// 重写父类的 run() 方法;
// run() 方法就是线程处理函数,耗时的操作都放在这个方法中执行;
// 不能直接调用,要通过 start() 方法触发;
// 如果直接调用,就不是线程处理函数了,而是一个普通函数;
void run();
};
mythread.cpp:
#include "mythread.h"
#include <QDebug>
MyThread::MyThread(QObject *parent) : QThread(parent)
{
}
// 线程处理函数
void MyThread::run()
{
// 处理耗时的操作
for (int i = 0; i < 10; i++)
{
qDebug() << i;
QThread::sleep(1);
}
// 线程结束之后发送自定义的 threadOver 信号
emit threadOver();
}
在主界面中调用:
widget.h:
#include <QWidget>
#include <QTimer>
#include <mythread.h>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_btnStart_clicked();
void on_btnStop_clicked();
private:
Ui::Widget *ui;
QTimer *timer; // 定时器对象
MyThread *thread; // 线程对象
};
widget.cpp:
#include "widget.h"
#include "ui_widget.h"
#include <QThread>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
timer = new QTimer(this);
thread = new MyThread(this);
// 当定时器超时时触发(即间隔指定时间到了就会触发)
connect(timer, &QTimer::timeout, [=](){
// 在 LCD Number 控件上递加显示数据
static int i = 0;
ui->lcdNumber->display(i++);
});
// 线程结束时发出 threadOver 信号
connect(thread, &MyThread::threadOver, [=](){
qDebug() << "thread is over";
// 停止定时器
timer->stop();
});
}
Widget::~Widget()
{
delete ui;
}
// 开始
void Widget::on_btnStart_clicked()
{
if (timer->isActive() == false)
{
// 启动定时器,间隔指定时间触发一次 timeout 信号
timer->start(500);
// 模拟耗时操作:如果在 ui 线程中执行,那么在循环期间,主界面无响应!!
// for (int i = 0; i < 10; i++)
// {
// qDebug() << i;
// QThread::sleep(1);
// }
// 将耗时操作放在线程中;
// start() 方法用于启动线程,执行线程处理函数 run() 方法;
thread->start();
}
}
// 停止
void Widget::on_btnStop_clicked()
{
if (timer->isActive())
{
// 停止定时器
timer->stop();
}
// 停止线程
// 不建议使用 terminate() 方法停止线程;因为 terminate() 方法被调用的时候,会立即停止线程,
// 此时如果线程正在操作内存空间,就会导致内存泄漏;因为内存还没有被释放,线程就停止了。
// thread->terminate();
// 建议使用 quit() 方法停止线程,该方法会等待线程执行完毕再停止;
thread->quit();
}
二、线程的创建方法二:
1、自定义一个类(MyThread),直接或间接继承于 QObject(不需要继承于 QThread);
2、在类中定义一个线程处理函数,函数名随便起,不是必须为 run();但是线程处理函数只能有一个;
3、创建自定义类的对象:MyThread *myThread = new MyThread;注意:不能指定父对象;
4、创建一个子线程对象:QThread *thread = new QThread(this);
5、把自定义线程类,加入到子线程对象中:myThread->moveToThread(thread);
6、启动子线程对象:thread->start(); 注意:此时只是把子线程开启了,并没有启动我们自定义线程类中的线程处理函数;
7、启动自定义的线程处理函数,只能通过 信号和槽 的方法启动;把线程处理函数当做一个槽函数;
效果图:
mythread.h:
#include <QObject>
// 自定义类继承于 QObject
class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
// 声明一个线程处理函数(线程处理函数只能有一个)
void MyTimer();
// 私有的属性要通过一个公有的方法来操作
void setFlag(bool flag);
signals:
void mySignal(); // 定时器发送的信号
public slots:
private:
bool isStop; // 设置一个标志位,标志是否停止线程
};
mythread.cpp:
#include "mythread.h"
#include <QDebug>
#include <QThread>
MyThread::MyThread(QObject *parent) : QObject(parent)
{
}
// 线程处理函数:模拟定时器,每隔一秒发送一次信号
void MyThread::MyTimer()
{
// 死循环,每隔 1 秒发送一次信号
while (!isStop)
{
// 发送信号
emit mySignal();
QThread::sleep(1);
qDebug() << "子线程号:" << QThread::currentThread();
// 退出循环,停止线程
if (isStop)
{
break;
}
}
}
// 公有的方法操作私有的属性
void MyThread::setFlag(bool flag)
{
this->isStop = flag;
}
widget.h:
#include <QWidget>
#include <mythread.h>
#include <QThread>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_btnStart_clicked();
void on_btnStop_clicked();
signals:
void startThread(); // 启动子线程的信号
private:
Ui::Widget *ui;
MyThread *myThread; // 自定义线程对象
QThread *thread; // 子线程对象
};
widget.cpp:
#include "widget.h"
#include "ui_widget.h"
#include <QThread>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建自定义线程对象;不能指定父对象;
// 因为指定父对象之后,调用 moveToThread() 加入到子线程会报错;
myThread = new MyThread();
// 创建子线程对象;可以指定父对象
thread = new QThread(this);
// 把自定义线程加入到子线程中
myThread->moveToThread(thread);
qDebug() << "主线程号:" << QThread::currentThread();
// 处理自定义线程中 每隔一秒发送的信号
connect(myThread, &MyThread::mySignal, [=](){
static int i = 0;
ui->lcdNumber->display(i++);
});
// 当发出 startThread 信号的时候,执行自定义线程中的 线程处理函数(MyTimer)
connect(this, &Widget::startThread, myThread, &MyThread::MyTimer);
// 退出程序的时候也要停止线程
connect(this, &Widget::destroyed, [=](){
on_btnStop_clicked();
});
// 注意:线程处理函数内部,不允许操作图形界面。
}
Widget::~Widget()
{
delete ui;
// 因为 myThread 创建的时候没有指定父对象,
// 所以在程序退出的时候,需要手动释放。
delete myThread;
}
// 开始
void Widget::on_btnStart_clicked()
{
if (!thread->isRunning())
{
// 启动线程:
// 此时只是把子线程开启了,并没有启动我们自定义线程类中的线程处理函数;
thread->start();
// 启动线程设置标志位
myThread->setFlag(false);
// 不能直接调用线程处理函数;如果直接调用,线程处理函数就变成了一个普通函数,
// 一个在主线程中执行的普通函数,就会导致主线程卡死;
// myThread->MyTimer();
// 只能通过信号和槽的方式调用线程处理函数
emit startThread();
}
}
// 停止
void Widget::on_btnStop_clicked()
{
if (thread->isRunning())
{
// 停止线程设置标志位
myThread->setFlag(true);
// 停止线程:会等待线程执行结束后,再退出线程;
// 如果线程是个死循环,那么线程就会一直运行,不会退出;
// 所以:退出线程最好的方法是在线程中设置一个标志位;
thread->quit();
thread->wait();
}
}
三、线程画图案例:使用线程创建的第二种方法;
1、自定义一个类(MyThread),直接或间接继承于 QObject(不需要继承于 QThread);
2、在类中定义一个线程处理函数,函数名随便起,不是必须为 run();但是线程处理函数只能有一个;
3、创建自定义类的对象:MyThread *myThread = new MyThread;注意:不能指定父对象;
4、创建一个子线程对象:QThread *thread = new QThread(this);
5、把自定义线程类,加入到子线程对象中:myThread->moveToThread(thread);
6、启动子线程对象:thread->start(); 注意:此时只是把子线程开启了,并没有启动我们自定义线程类中的线程处理函数;
7、启动自定义的线程处理函数,只能通过 信号和槽 的方法启动;把线程处理函数当做一个槽函数;
效果图:点击按钮时,随机画图五角形;
mythread.h:
#include <QObject>
#include <QImage>
// 自定义类继承于 QObject
class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
// 声明一个线程处理函数(线程处理函数只能有一个)
void drawImage();
signals:
void updateImage(QImage); // 声明一个信号
};
mythread.cpp:
#include "mythread.h"
#include <QPainter>
#include <QPen>
#include <QBrush>
#include <QPoint>
MyThread::MyThread(QObject *parent) : QObject(parent)
{
}
// 线程处理函数:画图
void MyThread::drawImage()
{
// 声明一个 QImage 绘图设备
QImage image(300, 300, QImage::Format_ARGB32);
// 声明一个画家,指定绘图设备
QPainter painter(&image);
// 声明一个画笔
QPen pen;
pen.setWidth(5); // 设置画笔宽度
// 把画笔交给画家
painter.setPen(pen);
// 声明一个画刷
QBrush brush;
brush.setStyle(Qt::SolidPattern); // 设置画刷样式
brush.setColor(Qt::red); // 设置画刷颜色
// 把画刷交给画家
painter.setBrush(brush);
// 随机定义 5 个点
QPoint p[] = {
// qrand() 取随机数;对 300 取余目的是保证随机数在 300 以内;
QPoint(qrand() % 300, qrand() % 300),
QPoint(qrand() % 300, qrand() % 300),
QPoint(qrand() % 300, qrand() % 300),
QPoint(qrand() % 300, qrand() % 300),
QPoint(qrand() % 300, qrand() % 300)
};
// 画一个多边形。参数1表示点的数组指针;参数2表示点的个数;
painter.drawPolygon(p, 5);
// 通过信号将图片发送出去
emit updateImage(image);
}
widget.h:
#include <QWidget>
#include <mythread.h>
#include <QThread>
#include <QImage>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
// 重写绘图事件
void paintEvent(QPaintEvent *);
// 声明一个槽函数,获取线程绘制的图片
void getImage(QImage);
private:
Ui::Widget *ui;
QImage image; // 图片对象
MyThread *myThread; // 自定义线程对象
QThread *thread; // 子线程对象
};
widget.cpp:
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QPainter>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->resize(350, 350);
// 创建自定义线程对象;不能指定父对象;
// 因为指定父对象之后,调用 moveToThread() 加入到子线程会报错;
myThread = new MyThread();
// 创建子线程对象;可以指定父对象
thread = new QThread(this);
// 把自定义线程加入到子线程中
myThread->moveToThread(thread);
// 启动子线程(但是没有启动线程处理函数)
thread->start();
// 点击按钮时启用线程处理函数,开始画图
connect(ui->pushButton, &QPushButton::clicked, myThread, &MyThread::drawImage);
// 当子线程发送 updateImage 信号将绘制好的图片传出时,主线程使用 getImage 槽函数接收
connect(myThread, &MyThread::updateImage, this, &Widget::getImage);
// 程序退出的时候,退出子线程,回收资源
connect(this, &Widget::destroyed, [=](){
thread->quit(); // 退出线程
thread->wait(); // 等待回收资源
});
}
Widget::~Widget()
{
delete ui;
// 因为 myThread 创建的时候没有指定父对象,
// 所以在程序退出的时候,需要手动释放。
delete myThread;
}
// 绘图事件
void Widget::paintEvent(QPaintEvent *)
{
// 声明一个画家
QPainter painter(this);
// 画一张图片
painter.drawImage(20, 20, image);
}
// 接收子线程发送过来的图片
void Widget::getImage(QImage img)
{
this->image = img;
// 更新窗口,触发 paintEvent 绘图事件
update();
}