zoukankan      html  css  js  c++  java
  • 界面编程之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》

     1 #ifndef MYWIDGET_H
     2 #define MYWIDGET_H
     3 
     4 #include <QWidget>
     5 #include <QTimer> //定时器头文件
     6 #include "mythread.h" //线程头文件
     7 
     8 namespace Ui {
     9 class MyWidget;
    10 }
    11 
    12 class MyWidget : public QWidget
    13 {
    14     Q_OBJECT
    15 
    16 public:
    17     explicit MyWidget(QWidget *parent = 0);
    18     ~MyWidget();
    19 
    20     void dealTimeout(); //定时器槽函数
    21     void dealDone(); //线程结束槽函数
    22     void stopThread(); //停止线程槽函数
    23 
    24 private slots:
    25     void on_pushButton_clicked();
    26 
    27 private:
    28     Ui::MyWidget *ui;
    29 
    30     QTimer *myTimer; //声明变量
    31     MyThread *thread; //线程对象
    32 };
    33 
    34 #endif // MYWIDGET_H
    mywidget.h
     1 #include "mywidget.h"
     2 #include "ui_mywidget.h"
     3 #include <QThread>
     4 #include <QDebug>
     5 
     6 MyWidget::MyWidget(QWidget *parent) :
     7     QWidget(parent),
     8     ui(new Ui::MyWidget)
     9 {
    10     ui->setupUi(this);
    11 
    12     myTimer = new QTimer(this);
    13 
    14     //只要定时器启动,自动触发timeout()
    15     connect(myTimer, &QTimer::timeout, this, &MyWidget::dealTimeout);
    16 
    17     //分配空间
    18     thread = new MyThread(this);
    19 
    20 
    21     connect(thread, &MyThread::isDone, this, &MyWidget::dealDone);
    22 
    23     //当按窗口右上角x时,窗口触发destroyed()
    24     connect(this, &MyWidget::destroyed, this, &MyWidget::stopThread);
    25 
    26 }
    27 
    28 void MyWidget::stopThread()
    29 {
    30     //停止线程
    31     thread->quit();
    32     //等待线程处理完手头动作
    33     thread->wait();
    34 }
    35 
    36 void MyWidget::dealDone()
    37 {
    38     qDebug() << "it is over";
    39     myTimer->stop(); //关闭定时器
    40 }
    41 
    42 void MyWidget::dealTimeout()
    43 {
    44     static int i = 0;
    45     i++;
    46     //设置lcd的值
    47     ui->lcdNumber->display(i);
    48 }
    49 
    50 MyWidget::~MyWidget()
    51 {
    52     delete ui;
    53 }
    54 
    55 void MyWidget::on_pushButton_clicked()
    56 {
    57     //如果定时器没有工作
    58     if(myTimer->isActive() == false)
    59     {
    60         myTimer->start(100);
    61     }
    62 
    63     //启动线程,处理数据
    64     thread->start();
    65 
    66 }
    mywidget.cpp
     1 #ifndef MYTHREAD_H
     2 #define MYTHREAD_H
     3 
     4 #include <QThread>
     5 
     6 
     7 class MyThread : public QThread
     8 {
     9     Q_OBJECT
    10 public:
    11     explicit MyThread(QObject *parent = 0);
    12 
    13 protected:
    14     //QThread的虚函数
    15     //线程处理函数
    16     //不能直接调用,通过start()间接调用
    17     void run();
    18 
    19 signals:
    20     void isDone();
    21 
    22 public slots:
    23 };
    24 
    25 #endif // MYTHREAD_H
    mythread.h
     1 #include "mythread.h"
     2 
     3 MyThread::MyThread(QObject *parent) : QThread(parent)
     4 {
     5 
     6 }
     7 
     8 void MyThread::run()
     9 {
    10     //很复杂的数据处理
    11     //需要耗时5s
    12     sleep(5);
    13 
    14     emit isDone();
    15 }
    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》

     1 #ifndef MYWIDGET_H
     2 #define MYWIDGET_H
     3 
     4 #include <QWidget>
     5 #include "mythread.h"
     6 #include <QThread>
     7 
     8 namespace Ui {
     9 class MyWidget;
    10 }
    11 
    12 class MyWidget : public QWidget
    13 {
    14     Q_OBJECT
    15 
    16 public:
    17     explicit MyWidget(QWidget *parent = 0);
    18     ~MyWidget();
    19 
    20     void dealSignal();
    21     void dealClose();
    22 
    23 signals:
    24     void startThread(); //启动子线程的信号
    25 
    26 private slots:
    27     void on_buttonStart_clicked();
    28 
    29     void on_buttonStop_clicked();
    30 
    31 private:
    32     Ui::MyWidget *ui;
    33     MyThread *myT;
    34     QThread *thread;
    35 
    36 };
    37 
    38 #endif // MYWIDGET_H
    mywidget.h
     1 #include "mywidget.h"
     2 #include "ui_mywidget.h"
     3 #include <QDebug>
     4 
     5 
     6 MyWidget::MyWidget(QWidget *parent) :
     7     QWidget(parent),
     8     ui(new Ui::MyWidget)
     9 {
    10     ui->setupUi(this);
    11 
    12     //动态分配空间,不能指定父对象
    13     myT = new MyThread;
    14 
    15     //创建子线程
    16     thread = new QThread(this);
    17 
    18     //把自定义线程加入到子线程中
    19     myT->moveToThread(thread);
    20 
    21     connect(myT, &MyThread::mySignal, this, &MyWidget::dealSignal);
    22 
    23     qDebug() << "主线程号:" << QThread::currentThread();
    24 
    25     connect(this, &MyWidget::startThread, myT, &MyThread::myTimeout);
    26 
    27 
    28     connect(this, &MyWidget::destroyed, this, &MyWidget::dealClose);
    29 
    30     //线程处理函数内部,不允许操作图形界面
    31 
    32 
    33     //connect()第五个参数的作用,连接方式:默认,队列,直接
    34     //多线程时才有意义
    35     //默认的时候
    36     //如果是多线程,默认使用队列
    37     //如果是单线程, 默认使用直接方式
    38     //队列: 槽函数所在的线程和接收者一样
    39     //直接:槽函数所在线程和发送者一样
    40 
    41 
    42 }
    43 
    44 MyWidget::~MyWidget()
    45 {
    46     delete ui;
    47 }
    48 
    49 void MyWidget::dealClose()
    50 {
    51     on_buttonStop_clicked();
    52     delete myT;
    53 }
    54 
    55 void MyWidget::dealSignal()
    56 {
    57     static int i = 0;
    58     i++;
    59     ui->lcdNumber->display(i);
    60 }
    61 
    62 void MyWidget::on_buttonStart_clicked()
    63 {
    64 
    65     if(thread->isRunning() == true)
    66     {
    67         return;
    68     }
    69 
    70     //启动线程,但是没有启动线程处理函数
    71     thread->start();
    72     myT->setFlag(false);
    73 
    74     //不能直接调用线程处理函数,
    75     //直接调用,导致,线程处理函数和主线程是在同一个线程
    76     //myT->myTimeout();
    77 
    78     //只能通过 signal - slot 方式调用
    79     emit startThread();
    80 
    81 
    82 }
    83 
    84 void MyWidget::on_buttonStop_clicked()
    85 {
    86     if(thread->isRunning() == false)
    87     {
    88         return;
    89     }
    90 
    91     myT->setFlag(true);
    92     thread->quit();
    93     thread->wait();
    94 }
    mywidget.cpp
     1 #ifndef MYTHREAD_H
     2 #define MYTHREAD_H
     3 
     4 #include <QObject>
     5 
     6 class MyThread : public QObject
     7 {
     8     Q_OBJECT
     9 public:
    10     explicit MyThread(QObject *parent = 0);
    11 
    12     //线程处理函数
    13     void myTimeout();
    14 
    15     void setFlag(bool flag = true);
    16 
    17 signals:
    18     void mySignal();
    19 
    20 public slots:
    21 
    22 private:
    23     bool isStop;
    24 };
    25 
    26 #endif // MYTHREAD_H
    mythread.h
     1 #include "mythread.h"
     2 #include <QThread>
     3 #include <QDebug>
     4 #include <QMessageBox>
     5 
     6 MyThread::MyThread(QObject *parent) : QObject(parent)
     7 {
     8     isStop = false;
     9 }
    10 
    11 void MyThread::myTimeout()
    12 {
    13     while( !isStop )
    14     {
    15 
    16         QThread::sleep(1);
    17         emit mySignal();
    18         //QMessageBox::aboutQt(NULL);
    19 
    20          qDebug() << "子线程号:" << QThread::currentThread();
    21 
    22          if(isStop)
    23          {
    24              break;
    25          }
    26     }
    27 }
    28 
    29 void MyThread::setFlag(bool flag)
    30 {
    31     isStop = flag;
    32 }
    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》

     1 #ifndef WIDGET_H
     2 #define WIDGET_H
     3 
     4 #include <QWidget>
     5 #include "mythread.h"
     6 #include <QThread>
     7 #include <QImage>
     8 
     9 namespace Ui {
    10 class Widget;
    11 }
    12 
    13 class Widget : public QWidget
    14 {
    15     Q_OBJECT
    16 
    17 public:
    18     explicit Widget(QWidget *parent = 0);
    19     ~Widget();
    20 
    21     //重写绘图事件
    22     void paintEvent(QPaintEvent *);
    23 
    24     void getImage(QImage); //槽函数
    25     void dealClose(); //窗口关闭槽函数
    26 
    27 private:
    28     Ui::Widget *ui;
    29     QImage image;
    30     MyThread *myT; //自定义线程对象
    31     QThread *thread; //子线程
    32 };
    33 
    34 #endif // WIDGET_H
    widget.h
     1 #include "widget.h"
     2 #include "ui_widget.h"
     3 #include <QPainter>
     4 #include <QThread>
     5 
     6 Widget::Widget(QWidget *parent) :
     7     QWidget(parent),
     8     ui(new Ui::Widget)
     9 {
    10     ui->setupUi(this);
    11 
    12     //自定义类对象,分配空间,不可以指定父对象
    13     myT = new MyThread;
    14 
    15     //创建子线程
    16     thread = new QThread(this);
    17 
    18     //把自定义模块添加到子线程
    19     myT->moveToThread(thread);
    20 
    21     //启动子线程,但是,并没有启动线程处理函数
    22     thread->start();
    23 
    24     //线程处理函数,必须通过signal - slot 调用
    25     connect(ui->pushButton, &QPushButton::pressed, myT, &MyThread::drawImage);
    26     connect(myT, &MyThread::updateImage, this, &Widget::getImage);
    27 
    28     connect(this, &Widget::destroyed, this, &Widget::dealClose);
    29 
    30 }
    31 
    32 Widget::~Widget()
    33 {
    34     delete ui;
    35 }
    36 
    37 void Widget::dealClose()
    38 {
    39     //退出子线程
    40     thread->quit();
    41     //回收资源
    42     thread->wait();
    43     delete myT;
    44 
    45 }
    46 
    47 void Widget::getImage(QImage temp)
    48 {
    49     image = temp;
    50     update(); //更新窗口,间接调用paintEvent()
    51 }
    52 
    53 void Widget::paintEvent(QPaintEvent *)
    54 {
    55     QPainter p(this); //创建画家,指定绘图设备为窗口
    56     p.drawImage(50, 50, image);
    57 }
    widget.cpp
     1 #ifndef MYTHREAD_H
     2 #define MYTHREAD_H
     3 
     4 #include <QObject>
     5 #include <QImage>
     6 
     7 class MyThread : public QObject
     8 {
     9     Q_OBJECT
    10 public:
    11     explicit MyThread(QObject *parent = 0);
    12 
    13     //线程处理函数
    14     void drawImage();
    15 
    16 signals:
    17     void updateImage(QImage temp);
    18 
    19 public slots:
    20 };
    21 
    22 #endif // MYTHREAD_H
    mythread.h
     1 #include "mythread.h"
     2 #include <QPainter>
     3 #include <QPen>
     4 #include <QBrush>
     5 #include <QImage>
     6 
     7 MyThread::MyThread(QObject *parent) : QObject(parent)
     8 {
     9 
    10 }
    11 
    12 void MyThread::drawImage()
    13 {
    14     //定义QImage绘图设备
    15     QImage image(500, 500, QImage::Format_ARGB32);
    16     //定义画家,指定绘图设备
    17     QPainter p(&image);
    18 
    19 
    20     //定义画笔对象
    21     QPen pen;
    22     pen.setWidth(5); //设置宽度
    23     //把画笔交给画家
    24     p.setPen(pen);
    25 
    26     //定义画刷
    27     QBrush brush;
    28     brush.setStyle(Qt::SolidPattern); //设置样式
    29     brush.setColor(Qt::red); //设置颜色
    30     //把画刷交给画家
    31     p.setBrush(brush);
    32 
    33     //定义5个点
    34     QPoint a[] =
    35     {
    36        QPoint(qrand()%500, qrand()%500),
    37        QPoint(qrand()%500, qrand()%500),
    38        QPoint(qrand()%500, qrand()%500),
    39        QPoint(qrand()%500, qrand()%500),
    40        QPoint(qrand()%500, qrand()%500)
    41     };
    42 
    43     p.drawPolygon(a, 5);
    44 
    45 
    46     //通过信号发送图片
    47     emit updateImage(image);
    48 
    49 }
    mythread.cpp
  • 相关阅读:
    008——MATLAB-xlswrite的使用方法
    006——转载-MATLAB数字与字符之间的转换
    013——C# chart控件时间作为X轴(附教程)
    012——C#打开ecxel修改数据(附教程)
    010——C#选择文件路径
    011——C#创建ecxel文件(附教程)
    009——C#全局变量定义
    008——转载——c#获取当前日期时间
    007——转载——C#将字符串转换为整型的三种方法的总结
    006——C#关闭窗口 添加FormClosing事件
  • 原文地址:https://www.cnblogs.com/yuweifeng/p/9384728.html
Copyright © 2011-2022 走看看