最近在重新学习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 的事件传送机制,这里就没有怎么讲了,不过还是建议读者好好去了解一下的好。