Qt事件的处理:
1.事件有两种调度方式: 同步和异步
Qt的事件循环是异步的,当调用QApplication::exec()时,就进入了事件循环,先处理Qt事件队列中的事件, 直至为空,再处理系统消息队列中的消息, 直至为空, 处理系统消息的时候会产生新的Qt事件, 需要对其再次进行处理。
调用QApplication::sendEvent的时候, 消息会立即被处理,是同步的。实际上 QApplication::sendEvent()是通过调用QApplication::notify(), 直接进入了事件的派发和处理环节。
2.事件的派发和处理
事件处理
event()是一个集中处理不同类型的事件的地方
event()将具体事件的处理委托给具体的事件处理器,这些事件处理器是protected virtual,因此我们可以重写某一个事件处理器,让event调用
bool QObject::event(QEvent *e)
{
switch (e->type()) {
case QEvent::Timer:
timerEvent((QTimerEvent*)e);
break;
case QEvent::ChildAdded:
case QEvent::ChildPolished:
case QEvent::ChildRemoved:
childEvent((QChildEvent*)e);
break;
// ...
default:
if (e->type() >= QEvent::User) {
customEvent(e);
break;
}
return false;
}
return true;
}
这是 Qt 5 中 QObject::event() 函数的源代码(Qt 4 的版本也是类似的)。我们可以看到,同前面我们所说的一样,Qt 也是使用QEvent::type() 判断事件类型,然后调用了特定的事件处理器。比如,如果 event->type() 返回值是 QEvent::Timer, 则调用timerEvent()函数。
事件过滤器的实现:
在所有的Qt对象的基类QObject中有一个类型为QObjectList的成员变量,名字为eventFilters,当某个QObject(A)给另一个QObject(B)安装事件过滤器,B会把A的指针保存在eventFilters中。当B处理事件前,会检查eventFilters列表,如果非空,就会先调用列表中对象的eventFilter()函数。
事件过滤器以安装次序的反序被调用,eventFilter()返回值是bool,返回true表示事件处理完毕,Qt将之间返回,进行下一事件的处理
事件的派发
事件的派发是从QApplication::notify()开始的,因为QAppliction也是继承自QObject, 所以先检查QAppliation对象, 如果有事件过滤器安装在QAppliation上, 先调用事件过滤器,接下来QApplication::notify() 会过滤或合并一些事件(比如失效widget的鼠标事件会被过滤掉, 而同一区域重复的绘图事件会被合并),事件被送到reciver::event()处理。
在reciver::event()中, 先检查有无事件过滤器安装在reciever上。若有, 则调用之。然后根据QEvent的类型, 调用相应的特定事件处理函数。
3.事件的转发
如果某些事件在事件派发中没有被处理,这个事件会发发送到它的父widget,知道最顶层窗口。
Qt中和事件相关的函数通过两种方式相互通信,一种是QApplication::notify(), QObject::eventFilter(), QObject::event()通过返回bool值来表示是否已处理;另一种是调用QEvent::ignore() 或 QEvent::accept() 对事件进行标识,只用于event()函数和特定事件处理函数之间的沟通,而且只有用在某些类别事件上是有意义的, 这些事件就是上面提到的那些会被转发的事件, 包括: 鼠标, 滚轮, 按键等事件。
4.事件的处理、过滤
**重写特定事件处理函数:**重新实现部件的paintEvent(),mousePressEvent()等事件处理函数。这是最常用也的一种方法,不过它只能用来处理特定部件的特定事件。
继承QApplication类,并重载notify()函数:notify()函数来分发事件,要在任何事件过滤器查看任何事件之前先得到这些事件,重写notify()函数是唯一的办法。这个函数功能强大,提供了完全的控制,可以在事件过滤器得到事件之前就获得它们。但是,它一次只能处理一个事件。
**向QApplication对象上安装事件过滤器。**因为一个程序只有一个QApplication对象,所以这样实现的功能与使用notify()函数是相同的,优点是可以同时处理多个事件。
重新实现event()函数:QObject类的event()函数可以在事件到达默认的事件处理函数之前获得该事件。重写event()函数时, 需要调用父类的event()函数来处理不需要处理或是不清楚如何处理的事件。
bool MyLineEdit::event(QEvent *event)
{
if(event->type() == QEvent::KeyPress) //使用type()获取事件
{
qDebug() << tr("MyLineEdit的event()函数");
}
return QLineEdit::event(event); //调用父类的event()函数来处理不需要处理或者不清楚如何处理的事件
}
在对象上安装事件过滤器: 使用事件过滤器可以在一个界面类中同时处理不同子部件的不同事件。
ui->lineEdit->installEventFilter(this);// 为lineEdit安装事件过滤器 如果一个组件安装了多个过滤器,则最后一个安装的最先调用
bool Widget::eventFilter(QObject *obj, QEvent *event)
{
if(obj == ui->lineEdit) // 如果是lineEdit部件上的事件
{
if(event->type() == QEvent::KeyPress)
{
qDebug() << tr("Widget的事件过滤器");
}
}
return QWidget::eventFilter(obj, event);
}
///调用的时候会先调用事件过滤器再调用event()重新实现