处理监控系统的时候遇到问题,在MainWidget中创建多个子Widget的时候,原意是想鼠标点击先让MainWidget截获处理后再分派给子Widget去处理,但调试后发现如果子Widget重新实现了事件方法,就直接处理掉事件了,没有进到MainWidget的处理方法中去,如果子Widget没有accept或ignore该事件,则该事件就会被传递给其父亲,在子Widget存在accept或ignore事件的时候,想要经过一下MainWidget的处理方法,就得用到事件处理器,因此网上找了一下,发现QT的事件处理器可以处理。
  QT将事件封装为QEvent实例以后,会呼叫QObject的event()方法,并且将QEvent实例传送给它,在某些情况下,希望在执行event()之前,先对一些事件进行处理或过滤,然后再决定是否呼叫event()方法,这时候可以使用事件过滤器。
  可以重新定义一个继承自QObject(或其子类)的类的eventFilter()方法,
bool FilterObject::eventFilter(QObject *object, QEvent *event)
{    
  if(event->type() == QEvent::KeyPress)
  {        
    QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);        
    if (keyEvent->key() == Qt::Key_Tab) 
    {
      // 处理Tab键           
      return true;       
    }
  }    
  return false;
}
eventFilter()的object参数表示事件发生的来源物件,eventFilter()若返回false,则安装该事件过滤器的对象的event()会继续执行,若返回true,则安装事件过滤器的对象后event()方法就不会被执行,由此进行事件的拦截处理。给本对象安装事件过滤器:
this->installEventFilter(this);
 
Qt事件的类型很多, 常见的qt的事件如下:
键盘事件: 按键按下和松开.
鼠标事件: 鼠标移动,鼠标按键的按下和松开.
拖放事件: 用鼠标进行拖放.
滚轮事件: 鼠标滚轮滚动.
绘屏事件: 重绘屏幕的某些部分.
定时事件: 定时器到时.
焦点事件: 键盘焦点移动.
进入和离开事件: 鼠标移入widget之内,或是移出.
移动事件: widget的位置改变.
大小改变事件: widget的大小改变.
显示和隐藏事件: widget显示和隐藏.
窗口事件: 窗口是否为当前窗口.
还有一些非常见的qt事件,比如socket事件,剪贴板事件,字体改变,布局改变等等.
Qt的事件和Qt中的signal不一样. 后者通常用来"使用"widget, 而前者用来"实现" widget. 比如一个按钮, 我们使用这个按钮的时候, 我们只关心他clicked()的signal, 至于这个按钮如何接收处理鼠标事件,再发射这个信号,我们是不用关心的. 但是如果我们要重载一个按钮的时候,我们就要面对event了. 比如我们可以改变它的行为,在鼠标按键按下的时候(mouse press event) 就触发clicked()的signal而不是通常在释放的( mouse release event)时候.
 
事件的产生
事件的两种来源:
       一种是系统产生的;通常是window system把从系统得到的消息,比如鼠标按键,键盘按键等, 放入系统的消息队列中. Qt事件循环的时候读取这些事件,转化为QEvent,再依次处理.
       一种是由Qt应用程序程序自身产生的.程序产生事件有两种方式, 一种是调用QApplication::postEvent(). 例如QWidget::update()函数,当需要重新绘制屏幕时,程序调用update()函数,new出来一个paintEvent,调用QApplication::postEvent(),将其放入Qt的消息队列中,等待依次被处理. 另一种方式是调用sendEvent()函数. 这时候事件不会放入队列, 而是直接被派发和处理, QWidget::repaint()函数用的就是这种方式.
 
事件的调度
两种调度方式,一种是同步的, 一种是异步.
Qt的事件循环是异步的,当调用QApplication::exec()时,就进入了事件循环. 该循环可以简化的描述为如下的代码:
while ( !app_exit_loop ) {
       while( !postedEvents ) {             processPostedEvents()       }
       while( !qwsEvnts ){            qwsProcessEvents();   }
       while( !postedEvents ) {             processPostedEvents()       }
}
先处理Qt事件队列中的事件, 直至为空. 再处理系统消息队列中的消息, 直至为空, 在处理系统消息的时候会产生新的Qt事件, 需要对其再次进行处理.
调用QApplication::sendEvent的时候, 消息会立即被处理,是同步的. 实际上QApplication::sendEvent()是通过调用QApplication::notify(), 直接进入了事件的派发和处理环节.

事件的派发和处理
首先说明Qt中事件过滤器的概念. 事件过滤器是Qt中一个独特的事件处理机制, 功能强大而且使用起来灵活方便. 通过它, 可以让一个对象侦听拦截另外一个对象的事件. 事件过滤器是这样实现的: 在所有Qt对象的基类: QObject中有一个类型为QObjectList的成员变量,名字为eventFilters,当某个QObjec (qobjA)给另一个QObject (qobjB)安装了事件过滤器之后, qobjB会把qobjA的指针保存在eventFilters中. 在qobjB处理事件之前,会先去检查eventFilters列表, 如果非空, 就先调用列表中对象的eventFilter()函数. 一个对象可以给多个对象安装过滤器. 同样, 一个对象能同时被安装多个过滤器, 在事件到达之后, 这些过滤器以安装次序的反序被调用. 事件过滤器函数( eventFilter() ) 返回值是bool型, 如果返回true, 则表示该事件已经被处理完毕, Qt将直接返回, 进行下一事件的处理; 如果返回false, 事件将接着被送往剩下的事件过滤器或是目标对象进行处理.
Qt中,事件的派发是从QApplication::notify() 开始的, 因为QAppliction也是继承自QObject, 所以先检查QAppliation对象, 如果有事件过滤器安装在qApp上, 先调用这些事件过滤器. 接下来QApplication::notify() 会过滤或合并一些事件(比如失效widget的鼠标事件会被过滤掉, 而同一区域重复的绘图事件会被合并). 之后,事件被送到reciver::event() 处理.
同样, 在reciver::event()中, 先检查有无事件过滤器安装在reciever上. 若有, 则调用之. 接下来,根据QEvent的类型, 调用相应的特定事件处理函数. 一些常见的事件都有特定事件处理函数, 比如:mousePressEvent(), focusOutEvent(),  resizeEvent(), paintEvent(), resizeEvent()等等. 在实际应用中, 经常需要重载这些特定事件处理函数在处理事件. 但对于那些不常见的事件, 是没有相对应的特定事件处理函数的. 如果要处理这些事件, 就需要使用别的办法, 比如重载event() 函数, 或是安装事件过滤器.

事件的转发
 对于某些类别的事件, 如果在整个事件的派发过程结束后还没有被处理, 那么这个事件将会向上转发给它的父widget, 直到最顶层窗口. 如图所示, 事件最先发送给QCheckBox, 如果QCheckBox没有处理, 那么由QGroupBox接着处理, 如果QGroupBox没有处理, 再送到QDialog, 因为QDialog已经是最顶层widget, 所以如果QDialog不处理, QEvent将停止转发.
如何判断一个事件是否被处理了呢? Qt中和事件相关的函数通过两种方式相互通信. QApplication::notify(), QObject::eventFilter(), QObject::event() 通过返回bool值来表示是否已处理. “真”表示已经处理, “假”表示事件需要继续传递. 另一种是调用QEvent::ignore() 或 QEvent::accept() 对事件进行标识. 这种方式只用于event() 函数和特定事件处理函数之间的沟通. 而且只有用在某些类别事件上是有意义的, 这些事件就是上面提到的那些会被转发的事件, 包括: 鼠标, 滚轮, 按键等事件.

实际应用
1.重载特定事件处理函数
最常见的事件处理办法就是重载象mousePressEvent(), keyPressEvent(), paintEvent() 这样的特定事件处理函数. 以按键事件为例, 一个典型的处理函数如下:
void imageView::keyPressEvent(QKeyEvent * event)
{
switch (event->key()) {
case Key_Plus:
zoomIn();
break;
case Key_Minus:
zoomOut();
break;
case Key_Left:
// …
default:
QWidget::keyPressEvent(event);
}
}

2.重载event()函数
通过重载event()函数,我们可以在事件被特定的事件处理函数处理之前(象keyPressEvent())处理它. 比如, 当我们想改变tab键的默认动作时,一般要重载这个函数. 在处理一些不常见的事件(比如:LayoutDirectionChange)时,evnet()也很有用,因为这些函数没有相应的特定事件处理函数. 当我们重载event()函数时, 需要调用父类的event()函数来处理我们不需要处理或是不清楚如何处理的事件.
下面这个例子演示了如何重载event()函数, 改变Tab键的默认动作: (默认的是键盘焦点移动到下一个控件上. )
bool CodeEditor::event(QEvent * event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = (QKeyEvent *) event;
if (keyEvent->key() == Key_Tab) {
insertAtCurrentPosition('\t');
return true;
}
}
return QWidget::event(event);
}

3.在QT对象上安装事件过滤器
安装事件过滤器有两个步骤: (假设要用A来监视过滤B的事件)
首先调用B的installEventFilter( const QOject *obj ), 以A的指针作为参数. 这样所有发往B的事件都将先由A的eventFilter()处理.
 然后, A要重载QObject::eventFilter()函数, 在eventFilter() 中书写对事件进行处理的代码.
用这种方法改写上面的例子: (假设我们将CodeEditor 放在MainWidget中)
MainWidget::MainWidget()
{
       // …
CodeEditor * ce = new CodeEditor( this, “code editor”);
ce->installEventFilter( this );
// …
}
bool MainWidget::eventFilter( QOject * target , QEvent * event )
{
       if( target == ce ){
              if( event->type() == QEvent::KeyPress ) {
                     QKeyEvent *ke = (QKeyEvent *) event;
                     if( ke->key() == Key_Tab ){
ce->insertAtCurrentPosition('\t');
return true;
                     }
              }
       }
       return false;
}

4.给QAppliction对象安装事件过滤器
一旦我们给qApp(每个程序中唯一的QApplication对象)装上过滤器,那么所有的事件在发往任何其他的过滤器时,都要先经过当前这个eventFilter(). 在debug的时候,这个办法就非常有用, 也常常被用来处理失效了的widget的鼠标事件,通常这些事件会被QApplication::notify()丢掉. ( 在QApplication::notify() 中, 是先调用qApp的过滤器, 再对事件进行分析, 以决定是否合并或丢弃)

5.继承QApplication类,并重载notify()函数
Qt是用QApplication::notify()函数来分发事件的.想要在任何事件过滤器查看任何事件之前先得到这些事件,重载这个函数是唯一的办法. 通常来说事件过滤器更好用一些, 因为不需要去继承QApplication类. 而且可以给QApplication对象安装任意个数的事件过滤器, 相比之下, notify()函数只有一个.

总结:

Qt中,事件的派发是从QApplication::notify() 开始的, 因为QAppliction也是继承自QObject, 所以先检查QAppliation对象, 如果有事件过滤器安装在qApp上, 先调用这些事件过滤器. 接下来QApplication::notify() 会过滤或合并一些事件(比如失效widget的鼠标事件会被过滤掉, 而同一区域重复的绘图事件会被合并). 之后,事件被送到reciver::event() 处理.

同样, 在reciver::event()中, 先检查有无事件过滤器安装在reciever上. 若有, 则调用之. 接下来,根据QEvent的类型, 调用相应的特定事件处理函数.

在QApplication::notify() 中, 是先调用qApp的过滤器, 再对事件进行分析;Qt是用QApplication::notify()函数来分发事件的.想要在任何事件过滤器查看任何事件之前先得到这些事件,重载这个函数是唯一的办法.

所以,notify先调用qApp的过滤器, 再对事件进行分析;而reciver::event()中,也会先检查有无事件过滤器安装在reciever上.证据如下(网上搜索“Qt中事件分发源代码剖析“,网址是http://blog.csdn.net/chenlong12580/article/details/25009095):

notify_helper传递的顺序是:首先传递给全局的事件过滤器,再传递给目标对象的事件过滤器,最终传递给目标对象。

还有一个类似的证据:网上搜索“Qt事件处理机制整个流程--以鼠标在一个窗口中点击为例“,网址是http://blog.csdn.net/zgrjkflmkyc/article/details/44240729,内容如下:

1.QT_TRY {

2.        //哇,终于来到大名鼎鼎的函数QCoreApplication::nofity()了 ==> Section 2-6

3.        returnValue = notify(receiver, event);

4.    } QT_CATCH (...) {

5.        --threadData->loopLevel;

6.        QT_RETHROW;

7.    }

8.}

9.// Section 2-6:  $QTDIR\gui\kernel\qapplication.cpp

10.  // QCoreApplication::notify和它的重载函数QApplication::notify在Qt的派发过程中起到核心的作用,Qt的官方文档时这样说的:

11. 任何线程的任何对象的所有事件在发送时都会调用notify函数。

12. bool QApplication::notify(QObject *receiver, QEvent *e)

13.   {

14.    //代码很长,最主要的是一个大大的Switch,Case

15.   ..

16. switch ( e->type())

17. {

18.  ...

19.                                                                                                      case QEvent::MouseButtonPress:

20.                                                                                                      case QEvent::MouseButtonRelease:

21.                                                                                                      case QEvent::MouseButtonDblClick:

22.                                                                                                      case QEvent::MouseMove:

23.                                                                                                       ...

24.                                                                                                          //让自己私有类(d是私有类的句柄)来进一步处理 ==> Section 2-7

25.                                                                                                          res = d->notify_helper(w, w == receiver ? mouse : &me);

26.                                                                                                          e->spont = false;

27.                                                                                                          break;

28.                                                                                                      }

29.                                                                                                      ...

30.                                                                                                  }

31.                                                                                                  // Section 2-7:  $QTDIR\gui\kernel\qapplication.cpp

32.                                                                                                  bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)

33.                                                                                                  {

34.                                                                                                      ...

35.                                                                                                      // 向事件过滤器发送该事件,这里介绍一下Event Filters. 事件过滤器是一个接受即将发送给目标对象所有事件的对象。

36.                                                                                                     //如代码所示它开始处理事件在目标对象行动之前。过滤器的QObject::eventFilter()实现被调用,能接受或者丢弃过滤,

37.                                                                                                  允许或者拒绝事件的更进一步的处理。如果所有的事件过滤器允许更进一步的事件处理,事件将被发送到目标对象本身。

38.                                                                                                  如果他们中的一个停止处理,目标和任何后来的事件过滤器不能看到任何事件。

39.                                                                                                      if (sendThroughObjectEventFilters(receiver, e))

40.                                                                                                          return true;

41.                                                                                                       // 递交事件给receiver  => Section 2-8

42.                                                                                                      bool consumed = receiver->event(e);

43.                                                                                                      e->spont = false;

44.                                                                                                  }

45.                                                                                                  // Section 2-8  $QTDIR\gui\kernel\qwidget.cpp

46.                                                                                                  // QApplication通过notify及其私有类notify_helper,将事件最终派发给了QObject的子类- QWidget.

47.                                                                                                  bool QWidget::event(QEvent *event)

48.                                                                                                  {

49.                                                                                                      ...

50.                                                                                                      switch(event->type()) {

51.                                                                                                      case QEvent::MouseButtonPress:

52.                                                                                                          // Don't reset input context here. Whether reset or not is

53.                                                                                                          // a responsibility of input method. reset() will be

54.                                                                                                          // called by mouseHandler() of input method if necessary

55.                                                                                                          // via mousePressEvent() of text widgets.

56.                                                                                                  #if 0

57.                                                                                                          resetInputContext();

58.                                                                                                  #endif

59.                                                                                                          //mousePressEvent是虚函数,QWidget的子类可以通过重载重新定义mousePress事件的行为

60.                                                                                                          mousePressEvent((QMouseEvent*)event);

61.                                                                                                          break;

}

事件的转发

对于某些类别的事件, 如果在整个事件的派发过程结束后还没有被处理, 那么这个事件将会向上转发给它的父widget, 直到最顶层窗口.

总结:(QT学习之路p55)

事件的调用最终都会调用 QCoreApplication的 notify()函数,因此,最大的控制权实际上是重写QCoreApplication的 notify()函数。由此可以看出, Qt的事件处理实际上是分层五个层次:重定义事件处理函数,重定义 event()函数,为单个组件安装事件过滤器(这里应该指的是上面的notify_helper源码中目标对象的事件过滤器),为 QApplication 安装事件过滤器(这里应该指的是上面的notify_helper源码中全局的事件过滤器),重定义QCoreApplication的 notify()函数。这几个层次的控制权是逐层增大的。

http://blog.csdn.net/u013281495/article/details/51049577

qt事件传递过程和处理的更多相关文章

  1. 【转】QT事件传递与事件过滤器

        [概览] 1.重载特定事件函数.    比如: mousePressEvent(),keyPressEvent(),  paintEvent() .     2.重新实现QObject::ev ...

  2. View的绘制、事件传递过程

    View绘制过程 onMeasure() onLayout() onDraw() 过程详解 onMeasure() 计算尺寸 onLayout() 为viewGroup类型布局子视图用的. onDra ...

  3. iOS中事件传递过程

    iOS中,UIApplication管理着一个事件的队列,当系统获取用户的点击或滑动等事件后,就会将这些事件按顺序插入UIApplication管理的这个队里中,UIApplication再从这个队列 ...

  4. Android webkit keyevent 事件传递过程

    前言:基于android webview 上定制自己使用的可移植浏览器apk,遇到好多按键处理的问题.所以索性研究了一下keyevent 事件的传递流程. frameworks 层 keyevent ...

  5. iOS事件传递->处理->响应

    前言: 按照时间顺序,事件的生命周期是这样的: 事件的产生和传递(事件如何从父控件传递到子控件并寻找到最合适的view.寻找最合适的view的底层实现.拦截事件的处理)->找到最合适的view后 ...

  6. iOS 事件传递(Touch事件)

    先总说如下: 1.当手指触摸到屏幕时,会产生UITouch对象和UIEvent对象. 2.这两个对象产生后会被传递到UIApplication管理的一个事件队列中. 3.再有UIApplication ...

  7. Android touch 事件传递机制

    前言: (1)在自定义view的时候经常会遇到事件拦截处理,比如在侧滑菜单的时候,我们希望在侧滑菜单里面有listview控件,但是我们希望既能左右滑动又能上下滑动,这个时候就需要对触摸的touch事 ...

  8. Android 手机卫士--事件传递&响应规则

    问题的提出: 本文地址:http://www.cnblogs.com/wuyudong/p/5911187.html ,转载请注明源地址. 前面的文章实现了点击SettingItemView条目的时候 ...

  9. Android事件传递机制(转)

    Android事件构成 在Android中,事件主要包括点按.长按.拖拽.滑动等,点按又包括单击和双击,另外还包括单指操作和多指操作.所有这些都构成了Android中的事件响应.总的来说,所有的事件都 ...

随机推荐

  1. Eclipse3.6 添加JUnit源代码

    Eclipse中无法查看JUnit源代码,也无法设置源代码的jar. 解决方法: 1.  下载org.junit.source_4.8.1.v4_8_1_v20100427-1100.jar,放到ec ...

  2. JavaScript和JQuery获取DIV的值

    1.设计源代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www ...

  3. ExecuteReader: CommandText 属性尚未初始化

    没有对sqlcommand对象的commandtext属性赋值说白了就是没写SQL语句 -.- 无语死了.

  4. Git 文件状态的转换

    很好低使用git 文件的状态转换的了解是非常重要的. 文件转换状态其实可以分为四种: untracked:未跟踪,此文件在工作区中,但并没有加入git库,不参与版本控制. 通过”git add”,”g ...

  5. Qt国际化(Q_DECLARE_TR_FUNCTIONS() 宏给非Qt类添加翻译支持,以前没见过QTextEncoder和QTextDecoder和QLibraryInfo::location()和QEvent::LanguageChange)

    Internationalization with Qt 应用程序的国际化就是使得程序能在国际间可用而不仅仅是在本国可用的过程. Relevant Qt Classes andAPIs 以下的类支持Q ...

  6. C# 仿金山毒霸启动和关闭淡入淡出效果

    原文 C# 仿金山毒霸启动和关闭淡入淡出效果 01 #region 窗体关闭效果 02   03 #region 私有方法 04 [DllImportAttribute("user32.dl ...

  7. Js内存泄露问题总结

    最近接受了一个Js职位的面试,问了很多Js的高级特性,才发现长时间使用已知的特性进行开发而忽略了对这门语言循序渐进的理解,包括Java我想也是一样,偶尔在Sun官方看到JDK6.0列举出来的new f ...

  8. Android-onInterceptTouchEvent()和onTouchEvent()总结

    老实说,这两个小东东实在是太麻烦了,很不好懂,我自己那api文档都头晕,在网上找到很多资料,才知道是怎么回事,这里总结一下,记住这个原则就会很清楚了: 1.onInterceptTouchEvent( ...

  9. c语言,数组和指针

    概要: 1.普通数组与指针 2.数组指针 3.指针的数组 数组是一个由(同一类型)连续元素组成的预先分配的内存块:指针是一个对任何位置的元素的引用. 数组自动分配空间,但不能重分配或改变大小:指针必须 ...

  10. Dynamic Pivot table wizard SQL Server

    原文 http://www.gyurcit.hu/pivot.html Dynamic Pivot table wizard This stored procedure generate dynami ...