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

一、为什么需要使用线程

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

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

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

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

界面编程之QT的线程20180731的更多相关文章

  1. 界面编程之QT的Socket通信20180730

    /*******************************************************************************************/ 一.linu ...

  2. 界面编程之QT的数据库操作20180801

    /*******************************************************************************************/ 一.数据库连 ...

  3. 界面编程之QT绘图和绘图设备20180728

    /*******************************************************************************************/ 一.绘图 整 ...

  4. 界面编程之QT的事件20180727

    /*******************************************************************************************/ 一.事件 1 ...

  5. 界面编程之QT窗口系统20180726

    /*******************************************************************************************/ 一.坐标系统 ...

  6. 界面编程之QT的信号与槽20180725

    /*******************************************************************************************/ 一.指定父对 ...

  7. 界面编程之QT的基本介绍与使用20180722

    /*******************************************************************************************/ 一.qt介绍 ...

  8. 界面编程之QT的文件操作20180729

    /*******************************************************************************************/ 一.QT文件 ...

  9. QT核心编程之Qt线程 (c)

    QT核心编程之Qt线程是本节要介绍的内容,QT核心编程我们要分几个部分来介绍,想参考更多内容,请看末尾的编辑推荐进行详细阅读,先来看本篇内容. Qt对线程提供了支持,它引入了一些基本与平台无关的线程类 ...

随机推荐

  1. 20155306 白皎 免考实践总结——0day漏洞

    本次免考实践提纲及链接 第一部分 基础知识 1.1 0day漏洞概述 1.2二进制文件概述 1.3 必备工具 1.4 crack实验 第二部分 漏洞利用 2.1栈溢出利用 2.1.1 系统栈工作原理 ...

  2. 20155323刘威良 网络对抗《网络攻防》 Exp1 PC平台逆向破解(5)M

    实践目标 本次实践的对象是linux的可执行文件 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串. 该程序同时包含另一个代码片段,getShell,会返回一个可 ...

  3. 20155327 实验四 Android程序设计

    20155327 实验四 Android程序设计 任务一: 完成Hello World, 要求修改res目录中的内容,Hello World后要显示自己的学号 步骤: 将布局文件activity_ma ...

  4. SpingMVC的<context:component-scan>包扫描踩坑记录

        公司项目配置的Spring项目的包扫描有点问题,出现了一个被Spring容器管理的Bean被创建了2次的现象.在此记录下解决的过程,方便后续查阅. 改动前: 容器启动监听器中会扫描全部包,创建 ...

  5. 开源软件License汇总

    用到的open source code越多,遇到的开源License协议就越多.License是软件的授权许可,里面详尽表述了你获得代码后拥有的权利,可以对别人的作品进行何种操作,何种操作又是被禁止的 ...

  6. Unity接入监控摄像头

    公网RTSP测试地址: rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov RTSP测试软件: EasyPlayerRTSP: https:// ...

  7. 第十八次ScrumMeeting博客

    第十八次ScrumMeeting博客 本次会议于12月8日(五)22时整在3公寓725房间召开,持续20分钟. 与会人员:刘畅.辛德泰.张安澜.赵奕.方科栋. 1. 每个人的工作(有Issue的内容和 ...

  8. PAT甲题题解-1067. Sort with Swap(0,*) (25)-贪心算法

    贪心算法 次数最少的方法,即:1.每次都将0与应该放置在0位置的数字交换即可.2.如果0处在自己位置上,那么随便与一个不处在自己位置上的数交换,重复上一步即可.拿样例举例:   0 1 2 3 4 5 ...

  9. 结对&词频统计

    结对编程 Pathner 濮成林(博客:http://www.cnblogs.com/charliePU/) 1.词频统计 环境依赖: 开发环境.myeclipse 2013, jdk1.7.0_04 ...

  10. 《linux内核设计与实现》第二章

    第二章 从内核出发 一.获取内核源码 1.使用Git(linux创造的系统) 使用git来获取最新提交到linux版本树的一个副本: $ git clone git://git.kernel.org/ ...