(1)操作系统检测到用户动作时,会产生一条系统消息,该消息被发送到Qt应用程序
(2)Qt应用程序收到系统消息后,将其转化为一个对应的QEvent事件对象,并调用QObject::event()将其分发出去。
(3)事件对应被分发到当前正在操作的窗口部件上,该窗口部件会调用QWidget::event()函数来处理,然后,在这个函数内部又会调用其他的子函数(如KeyPressEvent或mouseReleaseEvent)来进行具体的处理。
(4)event函数处理完后,可能会将当前事件传递给父组件(parent)对象。但这个过程只是一种可能,也就是有一部分会被传递,有些并不需要被传递。
2. QEvent及其子类对象涉及的关键成员函数:实际上只是在操作或判断一个标志位
(1)void ignore():事件的接收者忽略当前事件,事件可能传递给父组件
(2)void accept();事件的接收者期望处理当前事件,表明我们自己写了事件处理函数,一般不需要被父组件再处理
(3)bool isAccepted();判断当前事件是否被处理
void MyLineEdit::keyPressEvent(QKeyEvent *e)
{
qDebug()<<"MyLineEdit::keyPressEvent";
QLineEdit::keyPressEvent(e);// 执行QLineEdit类的默认事件处理
}
实例事件处理的顺序
//main.cpp
#include "Widget.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); } /*当在编辑框中按下按键时输出结果:(注意MyLineEdit的父组件,即w中的 的event事件并未被触发 MyLineEdit::event MyLineEdit::keyPressEvent */
//MyLineEdit.h
#ifndef MYLINEEDIT_H #define MYLINEEDIT_H #include <QLineEdit> class MyLineEdit : public QLineEdit { Q_OBJECT public: explicit MyLineEdit(QWidget* parent = 0); bool event(QEvent* e); void keyPressEvent(QKeyEvent* e); }; #endif // MYLINEEDIT_H
//MyLineEdit.cpp
#include "MyLineEdit.h" #include <QKeyEvent> #include <QDebug> MyLineEdit::MyLineEdit(QWidget* parent):QLineEdit(parent) { } bool MyLineEdit::event(QEvent* e) { if( e->type() == QEvent::KeyPress) { qDebug() << "MyLineEdit::event"; } return QLineEdit::event(e); } void MyLineEdit::keyPressEvent(QKeyEvent* e) { qDebug() << "MyLineEdit::keyPressEvent"; QLineEdit::keyPressEvent(e); // e->ignore(); //表示事件会继续传递给父组件,本例中为Widget对象 //如果注释或e-accept()表示不再传递,则父组件的 //event函数不会被触发 }
//Widget.h
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include "MyLineEdit.h" class Widget : public QWidget { Q_OBJECT private: MyLineEdit myLineEdit; public: Widget(QWidget *parent = 0); bool event(QEvent* e); void keyPressEvent(QKeyEvent* e); ~Widget(); }; #endif // WIDGET_H
//Widget.cpp
#include "Widget.h" #include <QEvent> #include <QDebug> Widget::Widget(QWidget *parent) : QWidget(parent),myLineEdit(this) { } //操作系统将消息转化为事件,并分发到了这个Widget,会首先调用 //这个Wiget的event函数,该函数可以收到多种的系统事件,同时其内部会根据event //事件的类型调用相应的事件处理函数,如keyPressEvent()。比如,当发生按键事件时 //会先调用event(),再调用后面的keyPressEvent() bool Widget::event(QEvent* e) { if(e->type() == QEvent::KeyPress) { qDebug() << "Widget::event"; } return QWidget::event(e); } void Widget::keyPressEvent(QKeyEvent *e) { qDebug() << "Widget::keyPressEvent"; QWidget::keyPressEvent(e); } Widget::~Widget() { }
3. Qt中的事件过滤器
(1)事件过滤器
①事件过滤器可以对其他组件接收到的事件进行监控
②任意的QObject对象都可以作为事件过滤器使用。
③事件过滤器对象需要重写eventFilter()函数。
(2)安装事件过滤器:installEventFilter()函数
①事件过滤器在组件之前接收到事件
②事件过滤器能够决定是否将事件转到组件对象
(3)Qt中的事件过滤器与dll中的勾子的不同
①Qt中事件的过滤,是由事件的目标对象自己,发起过滤请求的。即委托过滤器,在自己接收事件前,先叫过滤器过滤一下。这意味着目标对象是知道这个过滤器的存在的。如下面例子中的myLineEdit.installEventFilter(this),就是myLineEdit委托Widget对事件进行过滤。
②而dll中的勾子不管目标对象愿不愿意,消息都会被拦下,而且目标对象本身也并不知道勾子的存在。
(4)事件过滤器的典型实现
//场景:将所有发往obj的事件先经过指定的过滤函数处理一下,然后再发往obj //返回true表示事件己经处理,无需再传递给obj对象了 //返回false则正常传递到obj对象 bool Widget::eventFilter(QObject* obj, QEvent* e) { if(/*根据obj判断是否是所需的目标对象*/) { if(/*根据e->type()判断是否是感兴趣的事件*/) { /*事件处理逻辑*/ } } /*调用父类中的同名函数*/ return QWidget::eventFilter(obj, e); }
main.cpp
#include <QApplication> #include "widget.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); }
widget.h
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include "mylineedit.h" class Widget : public QWidget { Q_OBJECT MyLineEdit* lineEdit; protected: void keyPressEvent(QKeyEvent *); public: Widget(QWidget *parent = 0); ~Widget(); bool eventFilter(QObject* obj, QEvent *event); }; #endif // WIDGET_H
widget.cpp
#include "widget.h" #include <QDebug> Widget::Widget(QWidget *parent) : QWidget(parent) { lineEdit=new MyLineEdit(this); //安装事件过滤器,即让所有发往myLineEdit的事件先由经由eventFilter过滤,这里委派Widget的EventFilter来过滤 lineEdit->installEventFilter(this);//在Widget上为lineedit安装事件过滤器了 } Widget::~Widget() { } bool Widget::eventFilter(QObject *obj, QEvent *event)//事件过滤器 { //该过滤器只过滤发往myLineEdit对象的KeyPress事件 if(obj==lineEdit)//判断事件的对象是不是linEdit { if(event->type()==QEvent::KeyPress)//判断事件类型 { qDebug()<<"Widget::eventFilter"; } } return QWidget::eventFilter(obj, event);//最后返回QWidget类默认的事件过滤器的执行结果 } void Widget::keyPressEvent(QKeyEvent *) { qDebug()<<"Widget::keyPressEvent"; }
mylinedit.h
#ifndef MYLINEEDIT_H #define MYLINEEDIT_H #include <QLineEdit> #include <QEvent> class MyLineEdit : public QLineEdit { Q_OBJECT protected: void keyPressEvent(QKeyEvent *e); public: explicit MyLineEdit(QWidget *parent = 0); bool event(QEvent* event); signals: public slots: }; #endif // MYLINEEDIT_H
mylineedit.cpp
#include "mylineedit.h" #include <QDebug> #include <QkeyEvent> #include <QLineEdit> MyLineEdit::MyLineEdit(QWidget *parent) : QLineEdit(parent) { } bool MyLineEdit::event(QEvent *event) { if(event->type()==QEvent::KeyPress) qDebug()<<"MyLineEdit::event"; return QLineEdit::event(event);//执行QLineEdit类的event()函数的默认操作 因为event()函数是有一个bool型返回值所以在函数的最后要使用return语句,这里一般是返回父类的event()函数的操作结果 } void MyLineEdit::keyPressEvent(QKeyEvent *e) { qDebug()<<"MyLineEdit::keyPressEvent"; // QLineEdit::keyPressEvent(e); e->ignore(); }
当点击键盘按键时,执行结果为:
Widget::eventFilter MyLineEdit::event MyLineEdit::keyPressEvent Widget::keyPressEvent
可以看到事件的传递顺序是:事件过滤器----->该部件的event()函数------>该部件的事件处理函数------>有可能是父组件的event函数(如果子部件ignore())
3. 小结
(1)Qt应用程序有严格的事件处理顺序
(2)Qt事件在处理后可能传递给父组件对象
(3)可以通过installEventFilter()函数安装事件过滤器
(4)事件过滤器可以对其他组件接收到的事件进行监控
(5)事件过滤器能够决定是否将事件转发到组件对象