界面编程之QT的线程20180731
/*******************************************************************************************/
一、为什么需要使用线程
图形界面中一旦使用了线程休眠,图形界面就不会刷新(不会动),呈现卡住无响应的状态。
这是由于图形界面中是单线程的
所以 很复杂的数据处理 耗时长的,就需要创建线程。
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的更多相关文章
- 界面编程之QT的Socket通信20180730
/*******************************************************************************************/ 一.linu ...
- 界面编程之QT的数据库操作20180801
/*******************************************************************************************/ 一.数据库连 ...
- 界面编程之QT绘图和绘图设备20180728
/*******************************************************************************************/ 一.绘图 整 ...
- 界面编程之QT的事件20180727
/*******************************************************************************************/ 一.事件 1 ...
- 界面编程之QT窗口系统20180726
/*******************************************************************************************/ 一.坐标系统 ...
- 界面编程之QT的信号与槽20180725
/*******************************************************************************************/ 一.指定父对 ...
- 界面编程之QT的基本介绍与使用20180722
/*******************************************************************************************/ 一.qt介绍 ...
- 界面编程之QT的文件操作20180729
/*******************************************************************************************/ 一.QT文件 ...
- QT核心编程之Qt线程 (c)
QT核心编程之Qt线程是本节要介绍的内容,QT核心编程我们要分几个部分来介绍,想参考更多内容,请看末尾的编辑推荐进行详细阅读,先来看本篇内容. Qt对线程提供了支持,它引入了一些基本与平台无关的线程类 ...
随机推荐
- OLEDB 命令转换组件的用法
在数据流任务组件中,OLEDB 命令转换组件对输入的每行数据调用TSQL,该组件能够把输入的数据作为参数,因此,该转换组件主要用于运行参数化的查询. 命令转换组件的配置十分简单,只有三个可编辑属性,位 ...
- js获取浏览器对象的信息
js中有一个对象叫 navigator,navigator 对象包含有关浏览器的信息.所有的浏览器都支持该对象. 其中经常用到的是 navigator.userAgent 属性,通常,它是在 navi ...
- Linux Socket 编程简介
在 TCP/IP 协议中,"IP地址 + TCP或UDP端口号" 可以唯一标识网络通讯中的一个进程,"IP地址+端口号" 就称为 socket.本文以一个简单的 ...
- Hyperledger Fabric网络节点架构
Fabric区块链网络的组成  区块链网络结构图 区块链网络组成 组成区块链网络相关的节点 节点是区块链的通信主体,和区块链网络相关的节点有多种类型:客户端(应用).Peer节点.排序服务(Orde ...
- Java 8 新特性---------Stream
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据. Stream使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象 ...
- C# 词频统计 东北师范大学 软件项目管理 第一次作业
一.作为杨老师的学生第一次听杨老师讲课,印象最深的就是:工程中所有步骤之间是乘法,如果任何一步为0,工程就做不出来了.以前所有老师讲到的都是不要太在乎结果,努力的过程很重要,但是这在软件工程中不合适了 ...
- Notes of Daily Scrum Meeting(12.21)
今天的燃尽图把周六的进度加了进来,由于我的失误没有及时更新TFS,所以出现了一些错误,向大家道歉. 下面是今天的任务总结: 团队成员 今日团队工作 陈少杰 继续进行网络连接的调试 王迪 测试搜索的功能 ...
- 【SE】Week3 : 个人博客作业(必应词典)
关于 微软必应词典客户端 的案例分析 [第一部分] 调研,评测 一.用户采访 1) 介绍采访对象的背景和需求: 被采访同学是马来西亚华裔叶能端同学,由于此前在马来西亚英语是第二语言,因此经常需要 ...
- 第五次Scrum meeting
第五次Scrum meeting 会议内容: 连接方面:确定封装成json的文本格式,尽量在满足在线组和手机客户端两组的情况下,降低自身的难度 测试方面:进行新一轮测试,主要测试程序的稳定性和可靠性, ...
- 2-Eleventh Scrum Meeting20151211
第二阶段任务分工整理会议 1.会议任务: (1)明晰第二阶段的开发内容,统计未完成的功能留需完善开发. (2)安排任务分工,每个人的工作安排. (3)PM职位担任. (4)博客内容负责. 2.会议记录 ...