Qt的事件机制
事件过滤器:
可以让一个对象侦听拦截另外一个对象的事件。
实现原理:
在所有Qt对象的基类:QObject中有一个
类型为:QObjectList
名字为:eventFilters
的成员变量,当A给B安装了事件过滤器后
B的eventList中就会保存A对象的指针,
在B处理事件之前,会先检查eventList是
否为空,如果不为空,就会调用事件过滤器函数eventFilter(),如果eventFilter()返回true,表示事件已经被处理完毕,Qt将直接返回进行下一事件处理,如果为false,事件将接着被送往剩下的事件过滤器或者是目标对象进行处理。
按照事件的起源将事件分为三类:
Spontaneous事件-----自发事件
由窗口系统产生,被放到系统队列中,通过事件循环逐个处理。
Posted事件
由Qt或是应用程序产生,被Qt组成队列,再通过事件循环处理。
sent事件
由Qt或是应用程序产生,但他们被直接发送到目标对象。
Qt事件循环的过程
在调用QApplication::exec()时,程序进入了Qt的事件循环,
事件循环的大致示意
1 while (!exit_was_called) 2 { 3 while(!posted_event_queue_is_empty) 4 { 5 process_next_posted_event(); 6 } 7 while(!spontaneous_event_queue_is_empty) 8 { 9 process_next_spontaneous_event(); 10 } 11 while(!posted_event_queue_is_empty) 12 { 13 process_next_posted_event(); 14 } 15 }
可以看出,程序首先处理所有的posted事件,知道队列空,再处理Spontaneous事件,再处理因Spontaneous事件产生的posted事件。
send事件不在事件循环内,因为他们不进入事件队列而是直接发送给目标对象
实例paint()事件:
当一个widget第一次可见,或者是被遮挡后可见,窗口产生一个(Spontaneous)paint事件,要求程序重绘widget,事件循环最宗从事件队列中拣选这个事件并把他们分发到那个徐奥重画的widget对象。
并不是所有paint事件都是窗口系统产生,当你调用update()去强行重画widget,这个widget会post一个paint事件给自己,这个paint事件被放入队列,最终被事件循环分发。
而当你等不及事件循环时,本来应该调用paintEvent()强制立即重画,但是实际上不可行因为paintEvent()是受保护的函数,因此Qt提供了一个机制直接sending事件给对象,repaint()就使用了这个机制来进行立即重画。(这是update()更新和repaint()更新的区别)。
posting相对于sending的一个优势就是给了Qt一个压缩事件的机会,假如在一个widget上连续调用update()十次,因update()而产生的这十个事件,会被自动地合并成一个单独的事件,
人工合成的事件
Qt应用程序可以产生他们自己的事件,或是预定义类型,或是自定义类型。这可以通过创建QEvent类或它的子类的实例,并且调用QApplication::postEvent()或QApplication::sendEvent()来实现。
这两个函数需要一个QObject* 与一个QEvent作为参数,如果使用postEvent(),要使用new操作符来创建事件对象,如
QApplication::postEvent(mainWin,new QKeyEvent(QEvent::KeyPress,Key_X,\'X\',0));
如果使用sendEvent(),应该使用栈来创建事件
QKeyEvent event(QEvent::KeyPress,Key_X,\'X\',0);
QApplication::sendEvent(mainWin,&event);
定制事件类型
Qt允许创建自己的事件类型,可以作为对象间的一种通讯机制。是因为这个可以是异步的,函数调用或槽调用总是同步的。另一个好处是可以被过滤
post一个定制事件:
const QEvent::Type MyEvent = (QEvent::Type)1234;
...
QApplication::postEvent(obj,new QCustomEvent(MyEvent));
事件必须是QCustomEvent类型(或子类)的。构造函数的参数是事件的类型,
为了处理定制事件类型,要重新实现customEvent()函数:
void MyLineEdit::customEvent(QCustomEvent *event)
{
if(event->type() == MyEvent){
myEvent();
}else{
QLineEdit::customEvent(event);
}
}
可以子类化QCustomEvent,加上别的成员,但是需要在customEvent()中转换QCustomEvent到特有的类型
事件的处理与过滤
Qt的事件可以在五个不同的层次上被处理
1.重新实现一个特定的事件handler
QObject与QWidget提供了许多特定的事件handlers,分别对应于不同的事件类型。(如paintEvent()对应paint()事件)
2.重新实现QObject::event()
event()函数是所有对象事件的入口,QObject和QWidget中缺省的实现是简单地把事件推入特定的事件handlers。
3.在QObject上安装事件过滤器
事件过滤器是一个对象,它在事件到达指定目标之前接收这些事件。
4.在aApp上安装一个事件过滤器
它会监视程序中发送到所有对象的所有事件
5.重新实现QApplication::notify()
Qt的事件循环与sendEvent()调用这个函数来分发事件,
特定对象的事件处理
一些事件类型可以被传递。这意味着加入目标对象不处理一个事件,Qt会试着寻找另外的事件接收者。用新的目标来调用QApplication::notify()。举例来讲,key事件是传递的,假如拥有焦点的Widget不处理特定键,Qt会分发相同的事件给父widget,然后是父亲的父亲,直到最顶层widget。
什么时候该接收事件,什么时候该忽略
通过accept()函数和ignore()函数。可被传递的事件有一个accept()函数和一个ignore()函数,可以用他们来告诉Qt,你"接收"或是"忽略"这个事件。假如事件handler调用accept(),这个事件将不会再被传递。假如事件handler调用ignore(),Qt会试着查找另外的事件接收者。
来源:http://www.cnblogs.com/li-hao/archive/2011/11/13/2247662.html