Qt自定義事件實現及子線程向主線程傳送事件消息(理論+代碼)


最近在重新學習Qt的時候,因爲要涉及到子線程與主線程傳遞消息,所以便琢磨了一下,順便把實用的記錄下來,方便自己以後查詢及各位同仁的參考!

特此聲明,本篇博文主要講述實用的,也就是直接說明怎麼實現,就不打算陳述一大堆理論啦,不過,還是建議大家去查查相應的理論比較好,這樣能對Qt的消息傳送機制的理解更加深入!

根據網上大多數人的資料,要實現自定義消息,需要從QEvent 派生一個自定義的事件;其實也可以不需要,只要使用QEvent::Type自定義一個事件就行了。在這裏,本人把兩種實現方法都在這裏講述一下!

一、這裏先講述使用 QEvent::Type 定義一個自定義事件

1、新建一個新的工程 “myEvent” ,在 ui 界面添加一個按鈕,並未按鈕添加 onclick() 響應函數。

2、在 widget.h 頭文件使用 QEvent::Type 定義兩個自定義事件。

3、重新實現 event() 虛函數 

bool event(QEvent* e);

4、添加 #include<QEvent>

整個頭文件如下:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QEvent>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
  Q_OBJECT

public:
  explicit Widget(QWidget *parent = 0);
  ~Widget();

  bool event(QEvent* e);

private slots:
  void on_pushButton_clicked();

private:
  Ui::Widget *ui;

  QEvent::Type myEvent1;
  QEvent::Type myEvent2;

};

#endif // WIDGET_H

5、使用QEvent :: registerEventType ( ) 靜態函數爲剛纔兩個自定義事件註冊值。

6、重新實現 event()  函數 。

7、在 按鈕響應函數裏面發送時間消息。

widget.cpp 文件如下:

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QCoreApplication>
#include <QDebug>

Widget::Widget(QWidget *parent) :
  QWidget(parent),
  ui(new Ui::Widget)
{
  ui->setupUi(this);

  myEvent1 = static_cast<QEvent::Type>(QEvent::registerEventType(-1));
  myEvent2 = static_cast<QEvent::Type>(QEvent::registerEventType(-1));
}

Widget::~Widget()
{
  delete ui;
}

bool Widget::event(QEvent* e)
{
  if(e->type() == myEvent1){
    QMessageBox::warning(this, tr("event"), tr("myEvent1"), QMessageBox::Yes); return true;

  }else if(e->type() == myEvent2){
    QMessageBox::warning(this, tr("event"), tr("myEvent2"), QMessageBox::Yes); return true;

  }

  return QWidget::event(e);
}


void Widget::on_pushButton_clicked()
{
  QCoreApplication::sendEvent(this, &QEvent(myEvent1));
  QCoreApplication::sendEvent(this, &QEvent(myEvent2));

}

編譯運行後界面如下:

二、從 QEvent 派生一個自定義事件類,類名取爲 myEvent 。

1、myevent.h 頭文件如下,裏面自定義了三個自定義事件,分別爲 m_event1 ,m_event2 , m_event3 :

#ifndef MYEVENT_H
#define MYEVENT_H

#include <QEvent>

class myEvent : public QEvent
{
public:
  myEvent(Type e);

public:
  static Type m_event1;
  static Type m_event2;
  static Type m_event3;
};

#endif // MYEVENT_H

2 、在 myevent.cpp 文件 裏面使用 QEvent :: registerEventType () 爲自定義的事件註冊。 

myevent.cpp 文件如下:

#include "myevent.h"
#include <QEvent>

QEvent::Type myEvent::m_event1 = static_cast<QEvent::Type>(QEvent::registerEventType());
QEvent::Type myEvent::m_event2 = static_cast<QEvent::Type>(QEvent::registerEventType());
QEvent::Type myEvent::m_event3 = static_cast<QEvent::Type>(QEvent::registerEventType());

myEvent::myEvent(Type e):QEvent(e)
{
}

3、在 widget.cpp 文件 添加   myevent.h 頭文件 。

4、修改 widget.cpp 文件裏面的按鈕響應函數如下:

void Widget::on_pushButton_clicked()
{
    myEvent e(myEvent::m_event1);
    QCoreApplication::sendEvent(this, &e);
}

5、

修改 widget.cpp 文件裏面的 event() 函數如下:

bool Widget::event(QEvent* e)
{
    if(e->type() == myEvent1){
        QMessageBox::warning(this, tr("event"), tr("myEvent1"), QMessageBox::Yes); <pre name="code" class="cpp">        return true;

    }else if(e->type() == myEvent2){
        QMessageBox::warning(this, tr("event"), tr("myEvent2"), QMessageBox::Yes);
<pre name="code" class="cpp"><pre name="code" class="cpp">        return true;
}else if(e->type() == myEvent::m_event1){ QMessageBox::warning(this, tr("myEvent"), tr("m_event1"), QMessageBox::Yes); return true; } else if(e->type() == myEvent::m_event2){ QMessageBox::warning(this, tr("myEvent"), tr("m_event2"), QMessageBox::Yes); return true; } return QWidget::event(e);}

然後重新編譯運行,效果如下:

三 、 前面講的都是在主線程裏面傳遞事件消息,接下來講述如何 在子線程裏面 往主線程 傳遞事件消息。

1、 從 QThread 派生一個自定義事件類,類名取爲 myThread , 並 重新實現 run() 虛函數。mythread.h 頭文件如下:

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>

class myThread : public QThread
{
  Q_OBJECT
public:
  explicit myThread(QObject *parent = 0);

signals:

public slots:

protected:
  void run();
};

#endif // MYTHREAD_H
 

2、在mythread.cpp 裏面重新實現 run() 函數,在裏面實現向主線程發送事件消息,mythread.cpp 文件如下:

#include "mythread.h"
#include "myevent.h"
#include <QCoreApplication>

myThread::myThread(QObject *parent) :
  QThread(parent)
{
}

void myThread::run()
{
  myEvent e(myEvent::m_event2);
  QCoreApplication::postEvent(this->parent(), new myEvent(myEvent::m_event2));

  //this->exec();
}

3、在 widget.h        裏面添加

mythread.h 頭文件

, 然後定義一個子線程對象, 如下:

#include "mythread.h"

myThread * m_pThread ;

4、在堆內存裏面爲 m_pThread 開闢一個內存空間,如下:

<span style="color:#000000;">Widget::Widget(QWidget *parent) :
  QWidget(parent),
  ui(new Ui::Widget)
{
  ui->setupUi(this);

  m_pThread = new myThread(this);

   。。。。。。
}</span>

5、在 ui  界面 添加另一個按鈕,併爲它添加 onclick() 事件響應,然後在裏面運行子線程,如下:

void Widget::on_pushButton_2_clicked()
{
    m_pThread->start();
}

編譯運行程序,效果如下:

四、 QCoreApplication :: postEvent (); 和 QCoreApplication :: sendEvent ();     的區別。

在前面的程序中發送 事件消息 的時候用到了 QCoreApplication :: postEvent (); 和QCoreApplication :: sendEvent (); 兩個函數,這裏可不是隨便使用的,這兩個函數時又區別的!

1、 QCoreApplication :: sendEvent ();   根據Qt Asistant 裏面的講述,這個函數直接將事件消息直接發送給接受者進行處理,等到事件處理完畢後才返回;並且使用它所傳遞的消息事件是在 棧(stack) 上創建的,也就是說它的內存空間是有編譯器來自動管理的。

2、 QCoreApplication :: postEvent (); 根據Qt Asistant 裏面的講述, 使用這個函數來傳遞時間消息時,它將事件消息發送到接受者的的消息隊列裏面,然後立即返回,不需要等到事件處理完畢才返回;並且使用它所傳遞的消息事件是在 堆(heep) 上創建的,也就是說它的內存空間是又程序員自己管理的,如用 new 創建的變量!

這兩個函數對事件的處理方式就像使用 repaint()  和 paint() 這兩個函數對界面進行重畫一樣,前者直接對界面進行重畫操作;後者先將重畫事件放到消息隊列裏面,等到適當的時候在對界面進行重畫操作。

在上面的子線程給主線程傳遞消息的時候使用的就是 QCoreApplication :: postEvent();  函數,因爲這裏必須保證在子線程退出之前,若子線程所傳遞的事件消息還未被主線程處理的話,子線程所傳遞的消息仍然是可用的。

好了,主線程內事件的傳遞與子線程向主線程傳遞事件消息的方法就介紹到這裏了。至於Qt 的事件傳送機制,這裏就沒有怎麼講了,不過還是建議讀者好好去了解一下的好。

發佈了7 篇原創文章 · 獲贊 13 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章