界面编程之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对线程提供了支持,它引入了一些基本与平台无关的线程类 ...
随机推荐
- Exp8 web基础
20155332<网络对抗>Exp5 MSF基础应用 1.实验环境搭建 1.apache的安装与配置 安装:sudo apt-get install apache2 开启:service ...
- proftpd启动失败提示unable to determine IP address of “xxx.com”
proftpd启动失败提示unable to determine IP address of “xxx.com”这种proftpd启动失败的原因是无法解析后面主机的IP地址,解决方法是在DNS列表中增 ...
- Linux中tty、pty、pts的概念区别 转载
基本概念: > tty(终端设备的统称): tty一词源于Teletypes,或teletypewriters,原来指的是电传打字机,是通过串行线用打印机键盘通过阅读和发送信息的东西,后来这东西 ...
- git和github使用教程
看官请移步git和github简单教程, 本文是上述链接的截图,担心哪天作者不小心删除了,备一份在自己这里,仅为自己看着方便.侵权请告知
- SpringBoot日记——信息修改PUT篇
我们常用的功能,除了post和get,还有put和delete,这篇文章就介绍一下这个put的基本用法. 页面跳转和回显 1. 首先,我们之前的页面已经将添加和修改的按钮都做好了,那么如何实现这些按钮 ...
- VMware在Centos7上配置静态IP的方法
使用NAT模式 在这里记下192.168.161.2 进入系统,为系统自动分配一个ip 记录下 192.168.161.129 进入网络管理器配置文件目录 cd /etc/sysconfig/netw ...
- leetcode-递增的三元子序列
给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列. 数学表达式如下: 如果存在这样的 i, j, k, 且满足 0 ≤ i < j < k ≤ n-1,使得 arr[ ...
- Hyperldeger Fabric踩过的坑
给参与者颁发身份时错误 错误信息: fabric-ca request register failed with errors [[{"code":400,"messag ...
- ping命令使用及其常用参数
PING (Packet Internet Groper),因特网包探索器,用于测试网络连接量检查网络是否连通,可以很好地帮助我们分析和判定网络故障.Ping发送一个ICMP(Internet Con ...
- Intellij IDEA 文件修改提示星号
https://www.cnblogs.com/zheting/p/7594073.html