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

一、为什么需要使用线程

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

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

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

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. Luogu P4322 [JSOI2016]最佳团体

    JZdalao昨天上课讲的题目,话说JSOI的题目是真的不难,ZJOI的题目真的是虐啊! 题意很简单,抽象一下就是:有一棵树,一次只能选从根到某个节点上的链上的所有点,问从中取出k个节点所得到的总价值 ...

  2. 【转载】C++引用详解

    原文:http://www.cnblogs.com/gw811/archive/2012/10/20/2732687.html 引用的概念 引用:就是某一变量(目标)的一个别名,对引用的操作与对变量直 ...

  3. 面向对象第一次练手-------ArrayList集合、类、对象、冒泡排序、类型转换

    思维转不过弯儿来  怎么做都是错      哪怕差一个()就成功的事情,也是千差万别 忽然想到一句话:差一步就成功的距离  =  差几万米就成功的距离 部分的理解和都体现在代码和注释里 using S ...

  4. IIS充当反向代理转发请求到Kestrel

    接着上篇博文为ASP.NetCore程序启用SSL的code,这篇将介绍如何用IIS充当反向代理的角色转发请求到Kestrel服务器 介绍 与ASP.NET不同,ASP.netCore使用的是自托管w ...

  5. win10引导错误的修复(内容系转载)

    #!尊重原作者,再此声明此内容属于网络转载,只是为了能保留下来方便日后查阅!!! win10误删引导文件,0xc0000098的解决方案,bcd引导文件受损情况分析 一.※相对简单的解决方法,对应的情 ...

  6. 关于go v1.11安装后出现不能正常运行测试程序的问题

    本人最近安装go1.11后出现上述问题,没有找到原因,可能之前安装过的旧的版本在windows下环境变量设置出现了问题,修改后仍然无效,后来删除所有安装版本,及go环境变量,重新下载1.10版本进行安 ...

  7. PHP学习 流程控制和数组

    flow control 流程控制decision structure 判断结构loop structure 循环结构 if(condition){statement1;} if(){}else{} ...

  8. Day Seven

    站立式会议 站立式会议内容总结 331 今天完成: 1.主页面 toolbar 菜单修改为点击弹出两个选项:新增计划和书籍 2.点击新增书籍跳转到文件管理器 home按钮为回退至上级目录,后退按钮为c ...

  9. 使用git进行代码的推送

    首先是对于锐捷墙的问题,登陆github有时可以有时又连不上,网络又非常慢,所以用了十足的耐心才fork完了代码库.链接https://github.com/niconiconiconi/hellow ...

  10. 03-java学习-基本数据类型-运算符-键盘接收用户输入

    java的八大基本数据类型: 类型转换的基本原则: java整数的默认类型是int,小数的默认类型是double 运算符: 算术运算符.连接.赋值.关系.逻辑.三目运算符等…… 键盘接收用户输入: j ...