/*******************************************************************************************/

一、为什么需要使用线程

图形界面中一旦使用了线程休眠,图形界面就不会刷新(不会动),呈现卡住无响应的状态。

这是由于图形界面中是单线程的

所以  很复杂的数据处理 耗时长的,就需要创建线程。

QThread 线程类,

qt中的线程睡眠函数:QThread::sleep();

void MyWidget::on_pushButton_clicked()

{

//如果定时器没有工作

if(myTimer->isActive() == false)

{

myTimer->start(100);

}

//很复杂的数据处理

//需要耗时5s

sleep(5);//图形界面中一旦使用了线程休眠,图形界面就不会刷新(不会动),呈现卡住无响应的状态。

//也就是说由于睡眠,导致前面启动了的定时器都不工作

myTimer->stop();//过了5s后图形界面才会有响应。但是

//此时是停了定时器,前面是睡眠 定时器也不工作,所以呈现出一直定时器一直不工作的状态

}

/*******************************************************************************************/

二、线程

1.Qt4.7以前 线程的使用方法:

1).自定义一个类,继承于QThread

class MyThread : public QThread

{

public:

void run();//只有这个是线程处理函数(和主线程不在同一个线程),虚函数。

}

2).使用自定义的类创建对象,并开启线程

MyThread myThread;

//启动线程

//注意不能直接调用run函数,否则还是在主线程里面运行,而不是在新线程里面

myThread.start();//间接调用run

3).返回处理结果,告诉处理完

可以在类中定义一个信号,然后在需要(处理好后)的时候发送这个信号,来告诉其他线程。

具体见图《线程1》

2.实现:

在项目中添加一个继承QThread的类,添加的时候注意由于这个不是控件,所以下拉框选择QObject,然后

再在文件中把基类改为QThread

class MyThread : public QThread

{

Q_OBJECT

public:

explicit MyThread(QObject *parent = 0);

protected:

//QThread的虚函数

//线程处理函数

//不能直接调用,通过start()间接调用

void run();

signals:

void isDone();

public slots:

};

void MyThread::run()

{

//很复杂的数据处理

//需要耗时5s

sleep(5);

emit isDone();

}

//分配空间

thread = new MyThread(this);

//处理线程中的信号,最好用这种传统的方式

connect(thread, &MyThread::isDone, this, &MyWidget::dealDone);

//当按窗口右上角x时,窗口触发destroyed(),否则关了窗口线程还会运行

connect(this, &MyWidget::destroyed, this, &MyWidget::stopThread);

void MyWidget::dealDone()

{

qDebug() << "it is over";

myTimer->stop(); //关闭定时器

}

void MyWidget::on_pushButton_clicked()

{

//如果定时器没有工作

if(myTimer->isActive() == false)

{

myTimer->start(100);

}

//启动线程,处理数据

thread->start();

}

void MyWidget::stopThread()

{

//停止线程,不是立马关闭,释放线程占用的内存,线程号等资源。和下面配合使用

thread->quit();//类似linux中,pthread_exit()

//等待线程处理完手头动作

thread->wait();//类似linux中,pthread_join , pthread_detach,

//terminate 强制结束,很暴力,往往会导致内存问题。所以一般不用

}

上述代码具体见《QThread》

 #ifndef MYWIDGET_H
#define MYWIDGET_H #include <QWidget>
#include <QTimer> //定时器头文件
#include "mythread.h" //线程头文件 namespace Ui {
class MyWidget;
} class MyWidget : public QWidget
{
Q_OBJECT public:
explicit MyWidget(QWidget *parent = );
~MyWidget(); void dealTimeout(); //定时器槽函数
void dealDone(); //线程结束槽函数
void stopThread(); //停止线程槽函数 private slots:
void on_pushButton_clicked(); private:
Ui::MyWidget *ui; QTimer *myTimer; //声明变量
MyThread *thread; //线程对象
}; #endif // MYWIDGET_H

mywidget.h

 #include "mywidget.h"
#include "ui_mywidget.h"
#include <QThread>
#include <QDebug> MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this); myTimer = new QTimer(this); //只要定时器启动,自动触发timeout()
connect(myTimer, &QTimer::timeout, this, &MyWidget::dealTimeout); //分配空间
thread = new MyThread(this); connect(thread, &MyThread::isDone, this, &MyWidget::dealDone); //当按窗口右上角x时,窗口触发destroyed()
connect(this, &MyWidget::destroyed, this, &MyWidget::stopThread); } void MyWidget::stopThread()
{
//停止线程
thread->quit();
//等待线程处理完手头动作
thread->wait();
} void MyWidget::dealDone()
{
qDebug() << "it is over";
myTimer->stop(); //关闭定时器
} void MyWidget::dealTimeout()
{
static int i = ;
i++;
//设置lcd的值
ui->lcdNumber->display(i);
} MyWidget::~MyWidget()
{
delete ui;
} void MyWidget::on_pushButton_clicked()
{
//如果定时器没有工作
if(myTimer->isActive() == false)
{
myTimer->start();
} //启动线程,处理数据
thread->start(); }

mywidget.cpp

 #ifndef MYTHREAD_H
#define MYTHREAD_H #include <QThread> class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = ); protected:
//QThread的虚函数
//线程处理函数
//不能直接调用,通过start()间接调用
void run(); signals:
void isDone(); public slots:
}; #endif // MYTHREAD_H

mythread.h

 #include "mythread.h"

 MyThread::MyThread(QObject *parent) : QThread(parent)
{ } void MyThread::run()
{
//很复杂的数据处理
//需要耗时5s
sleep(); emit isDone();
}

mythread.cpp

3.Qt4.7以后 线程的使用方法:

1).定义:

(1).设定一个类,继承与QObject

(2).类中设置一个线程函数(只有一个是线程函数,函数名可以任意)

class MyThread : public QObject

{

public:

void fun();//

}

2).使用:

(1).创建自定义线程对象(不能指定父对象),

否则后续就无法加入到QThread线程对象,因为指定后是已经给了窗口了就不能放到线程的地方了

或者说已经有父对象的则不能再移动了(不能移动到别的父对象)

myThread=new MyThread;

(2).创建QThread线程对象

QThread *thread = new QThread(this)

(3).把自定义线程类,加入到QThread线程对象,  关联起来

myThread->moveToThread(thread);

(4).启动线程对象的线程,

thread.start();//只是把线程开启了,并没有启动线程处理函数(线程是启动了,但是线程函数没有启动)

(5).线程处理函数(自定义线程函数)的启动,必须通过signal-slot 信号和槽的方式(不能直接调用这个函数):

主线程发送信号,并且信号的处理函数指定为线程处理函数,这样线程处理函数才启动了

(定义信号与槽必须要有Q_OBJECT宏)

connect(this, &MyWidget::startThread, myT, &MyThread::myTimeout);

(6).线程退出,直接退出是不行的,因为线程处理函数中的while(1)这样退出不了

//当按窗口右上角x时,窗口触发destroyed(),如果此时不关闭线程,则关了窗口线程还会运行

connect(this, &MyWidget::destroyed, this, &MyWidget::dealClose);//不像linux中进程关了则线程都关了,这里线程还会运行

注意使用前面:

thread->quit();//等待线程处理完,但是线程是死循环一致处理不完,所以线程无法退出

thread->wait();

这样线程不退出,必须先让线程处理函数退出循环,通过标志位的方法:

myT->setFlag(true);//设置退出标记,则循环条件不满足,则线程处理函数会退出

thread->quit();

thread->wait()

3).返回处理结果,

可以在类中定义一个信号,信号可以是带参数的,参数可以是处理后的数据

然后在需要(处理好后)的时候发送这个信号,来告诉其他线程。

具体见图《线程2》

4.实现:

void MyThread::myTimeout()

{

while( !isStop )

{

QThread::sleep(1);

emit mySignal();

//QMessageBox::aboutQt(NULL);

qDebug() << "子线程号:" << QThread::currentThread();

if(isStop)

{

break;

}

}

}

void MyWidget::on_buttonStart_clicked()

{

if(thread->isRunning() == true)

{

return;

}

//启动线程,但是没有启动线程处理函数

thread->start();

myT->setFlag(false);

//不能直接调用线程处理函数,

//直接调用,导致,线程处理函数和主线程是在同一个线程

//myT->myTimeout();

//只能通过 signal - slot 方式调用

emit startThread();

}

void MyWidget::on_buttonStop_clicked()

{

if(thread->isRunning() == false)

{

return;

}

myT->setFlag(true);

thread->quit();

thread->wait();

}

5.注意:

1).线程处理函数内部,不允许操作图形界面,也不允许创建图形界面,否则程序会奔溃。

线程处理函数内部一般是纯数据处理

2).connect()第五个参数的作用,第五个参数多线程时才有意义

connect()第五个参数表示的是连接方式,主要有三种:自动连接,队列连接,直接连接

默认的时候,用的是自动连接,自动连接时:

如果是多线程,默认使用队列连接 (通过(接收的)类来判断是否是子线程的)

如果是单线程, 默认使用直接连接方式

队列连接含义: 槽函数所在的线程和接收者一样

connect(this, &MyWidget::startThread, myT, &MyThread::myTimeout);

//槽函数所在的线程和myT一样,即在子线程中

直接连接含义:槽函数所在线程和发送者一样

connect(this, &MyWidget::startThread, myT, &MyThread::myTimeout,Qt::DirectConnection);

//槽函数所在的线程和this一样,即在主线程中,无法实现多任务

这就是为啥,启动线程处理函数使用connect的原因,默认是队列方式(多线程下)。

一般用默认就够了

上述代码具体见《ThreadPro》

 #ifndef MYWIDGET_H
#define MYWIDGET_H #include <QWidget>
#include "mythread.h"
#include <QThread> namespace Ui {
class MyWidget;
} class MyWidget : public QWidget
{
Q_OBJECT public:
explicit MyWidget(QWidget *parent = );
~MyWidget(); void dealSignal();
void dealClose(); signals:
void startThread(); //启动子线程的信号 private slots:
void on_buttonStart_clicked(); void on_buttonStop_clicked(); private:
Ui::MyWidget *ui;
MyThread *myT;
QThread *thread; }; #endif // MYWIDGET_H

mywidget.h

 #include "mywidget.h"
#include "ui_mywidget.h"
#include <QDebug> MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this); //动态分配空间,不能指定父对象
myT = new MyThread; //创建子线程
thread = new QThread(this); //把自定义线程加入到子线程中
myT->moveToThread(thread); connect(myT, &MyThread::mySignal, this, &MyWidget::dealSignal); qDebug() << "主线程号:" << QThread::currentThread(); connect(this, &MyWidget::startThread, myT, &MyThread::myTimeout); connect(this, &MyWidget::destroyed, this, &MyWidget::dealClose); //线程处理函数内部,不允许操作图形界面 //connect()第五个参数的作用,连接方式:默认,队列,直接
//多线程时才有意义
//默认的时候
//如果是多线程,默认使用队列
//如果是单线程, 默认使用直接方式
//队列: 槽函数所在的线程和接收者一样
//直接:槽函数所在线程和发送者一样 } MyWidget::~MyWidget()
{
delete ui;
} void MyWidget::dealClose()
{
on_buttonStop_clicked();
delete myT;
} void MyWidget::dealSignal()
{
static int i = ;
i++;
ui->lcdNumber->display(i);
} void MyWidget::on_buttonStart_clicked()
{ if(thread->isRunning() == true)
{
return;
} //启动线程,但是没有启动线程处理函数
thread->start();
myT->setFlag(false); //不能直接调用线程处理函数,
//直接调用,导致,线程处理函数和主线程是在同一个线程
//myT->myTimeout(); //只能通过 signal - slot 方式调用
emit startThread(); } void MyWidget::on_buttonStop_clicked()
{
if(thread->isRunning() == false)
{
return;
} myT->setFlag(true);
thread->quit();
thread->wait();
}

mywidget.cpp

 #ifndef MYTHREAD_H
#define MYTHREAD_H #include <QObject> class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = ); //线程处理函数
void myTimeout(); void setFlag(bool flag = true); signals:
void mySignal(); public slots: private:
bool isStop;
}; #endif // MYTHREAD_H

mythread.h

 #include "mythread.h"
#include <QThread>
#include <QDebug>
#include <QMessageBox> MyThread::MyThread(QObject *parent) : QObject(parent)
{
isStop = false;
} void MyThread::myTimeout()
{
while( !isStop )
{ QThread::sleep();
emit mySignal();
//QMessageBox::aboutQt(NULL); qDebug() << "子线程号:" << QThread::currentThread(); if(isStop)
{
break;
}
}
} void MyThread::setFlag(bool flag)
{
isStop = flag;
}

mythread.cpp

两种使用线程的方法,新方式是推荐的,但是老方式更好用。

/*******************************************************************************************/

三、线程画图

线程中可以绘图,可以使用绘图设备QImage

绘画完毕后可以把绘图结果即绘图设备,通过带参数(即绘图设备)的信号发送给主窗口

当绘图很复杂时,当然是放在线程中最合适。

void MyThread::drawImage()

{

//定义QImage绘图设备

QImage image(500, 500, QImage::Format_ARGB32);

//定义画家,指定绘图设备

QPainter p(&image);

//定义画笔对象

QPen pen;

pen.setWidth(5); //设置宽度

//把画笔交给画家

p.setPen(pen);

//定义画刷

QBrush brush;

brush.setStyle(Qt::SolidPattern); //设置样式

brush.setColor(Qt::red); //设置颜色

//把画刷交给画家

p.setBrush(brush);

//定义5个点

QPoint a[] =

{

QPoint(qrand()%500, qrand()%500),

QPoint(qrand()%500, qrand()%500),

QPoint(qrand()%500, qrand()%500),

QPoint(qrand()%500, qrand()%500),

QPoint(qrand()%500, qrand()%500)

};

p.drawPolygon(a, 5);

//通过信号发送图片

emit updateImage(image);

}

上述代码具体见《ThreadIamge》

 #ifndef WIDGET_H
#define 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 = );
~Widget(); //重写绘图事件
void paintEvent(QPaintEvent *); void getImage(QImage); //槽函数
void dealClose(); //窗口关闭槽函数 private:
Ui::Widget *ui;
QImage image;
MyThread *myT; //自定义线程对象
QThread *thread; //子线程
}; #endif // WIDGET_H

widget.h

 #include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QThread> Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this); //自定义类对象,分配空间,不可以指定父对象
myT = new MyThread; //创建子线程
thread = new QThread(this); //把自定义模块添加到子线程
myT->moveToThread(thread); //启动子线程,但是,并没有启动线程处理函数
thread->start(); //线程处理函数,必须通过signal - slot 调用
connect(ui->pushButton, &QPushButton::pressed, myT, &MyThread::drawImage);
connect(myT, &MyThread::updateImage, this, &Widget::getImage); connect(this, &Widget::destroyed, this, &Widget::dealClose); } Widget::~Widget()
{
delete ui;
} void Widget::dealClose()
{
//退出子线程
thread->quit();
//回收资源
thread->wait();
delete myT; } void Widget::getImage(QImage temp)
{
image = temp;
update(); //更新窗口,间接调用paintEvent()
} void Widget::paintEvent(QPaintEvent *)
{
QPainter p(this); //创建画家,指定绘图设备为窗口
p.drawImage(, , image);
}

widget.cpp

 #ifndef MYTHREAD_H
#define MYTHREAD_H #include <QObject>
#include <QImage> class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = ); //线程处理函数
void drawImage(); signals:
void updateImage(QImage temp); public slots:
}; #endif // MYTHREAD_H

mythread.h

 #include "mythread.h"
#include <QPainter>
#include <QPen>
#include <QBrush>
#include <QImage> MyThread::MyThread(QObject *parent) : QObject(parent)
{ } void MyThread::drawImage()
{
//定义QImage绘图设备
QImage image(, , QImage::Format_ARGB32);
//定义画家,指定绘图设备
QPainter p(&image); //定义画笔对象
QPen pen;
pen.setWidth(); //设置宽度
//把画笔交给画家
p.setPen(pen); //定义画刷
QBrush brush;
brush.setStyle(Qt::SolidPattern); //设置样式
brush.setColor(Qt::red); //设置颜色
//把画刷交给画家
p.setBrush(brush); //定义5个点
QPoint a[] =
{
QPoint(qrand()%, qrand()%),
QPoint(qrand()%, qrand()%),
QPoint(qrand()%, qrand()%),
QPoint(qrand()%, qrand()%),
QPoint(qrand()%, qrand()%)
}; p.drawPolygon(a, ); //通过信号发送图片
emit updateImage(image); }

mythread.cpp

最新文章

  1. 一鼓作气 博客--第五篇 note5
  2. Cassandra-几个基本测试常识
  3. Move to Github
  4. 【原】requirejs学习笔记
  5. 在opencv3中实现机器学习算法之:利用最近邻算法(knn)实现手写数字分类
  6. LightOJ1005 Rooks(DP/排列组合)
  7. Python 处理理时间超详细转的
  8. ios AudioQueueStart returns -50 录音失败问题
  9. js实现form表单提交,图片预览功能
  10. eclipse乱码
  11. oracle 游标简单示例
  12. .netcore2.0发送邮件
  13. nginx配置vue项目部署访问无问题,刷新出现404问题
  14. 1021. Deepest Root (25)
  15. redis常用性能分析命令
  16. CentOS与RedHat的关系
  17. Codeforces Round #394 (Div. 2) E. Dasha and Puzzle(dfs)
  18. ssh免密登录方法不生效?Authentication refused: bad ownership or modes for directory
  19. 【bzoj4551】TJOI2016&amp;HEOI2016树
  20. [APIO2018] New Home

热门文章

  1. 20155301 Exp4 恶意代码分析
  2. 20155307实验八 《网络对抗》 Web基础
  3. 20155334 《网络攻防》 Exp7 网络欺诈防范
  4. 设计模式 笔记 备忘录模式 Memento
  5. Scrapy持久化存储
  6. 重磅发布丨乐维监控:全面兼容云平台,助力企业DevOps转型升级!
  7. [T-ARA][내가 너무 아파][我很痛]
  8. error: Build input file cannot be found: &#39;*******/node_modules/react-native/Libraries/WebSocket/libfishhook.a&#39; 问题解决记录
  9. CSAPP lab2 二进制拆弹 binary bombs phase_2
  10. 团队博客 Week14