详解 QT Event 以及 Event Filter 事件处理

QT Event 以及 Event Filter 事件处理是本文要介绍的内容,详细内容如下,先来看内容。Event 和 Event Filters:

1、手动发送事件流程:

(1)构造自己的事件对象:


 
  1. QEvent *evt = new QEvent( QEvent::Close  );  

(2)发送给指定的对象:


 
  1. QApplication::sendEvent(this, evt );  

2、定制某个控件的事件处理:

(1)确定需要对哪些控件的哪些事件, 通常的 close以及 key 和 keyboard 事件;

(2)重写该对象的 event() 函数; 
       
3、事件过滤流程:  
  
(1)确定自己需要过滤处理哪些对象的那些事件;    
     
(2)构造自己的事件过滤类: 重写该类的 eventFilter 函数;

(3)在主程序中实例化一个过滤类对象; 
   
(4)调用该过滤类对象的 installEventFilter( receiver, QEvent *event), 
   
以在目标对象上安装该过滤器。

在 Qt 中, event 被建模成派生自abstract QEvent 类的对象, 用来表示在应用程序中发生的事件,或是应用程序需要处理的外部活动产生的事件.

Events 可以被任一 QObject 派生的子类实例对象接收和处理, 但他们是关联到特定控件的. 本文档描述 event 在典型应用程序中是如何发送及处理的.


 
  1. How Events are Delivered   
  2. Event Types   
  3. Event Handlers   
  4. Event Filters   
  5. Sending Events 

event 如何发送

通常情况下,当一个事件发生的时候, Qt 通过构造一个合适的 QEvent子类对象来表示事件的发生, 然后将该事件对象发送给特定的 QObject对象( 或其子类实例对象), 通过调用该 QObject 的 event() 函数. 这个 event() 函数不会对事件本身进行处理, 而是首先检查所接受到event 的类型, 然后根据 event 的类型来调用相应的 event handler, event handler 在处理完 event 之后会返回一个bool值表示 该 event是被接受了,还是被忽略了。

某些事件, 例如 QMouseEvent 和 QKeyEvent, 来自于窗口系统; 某些, 例如 QTimerEvent, 来自于其他的事件源; 某些, 来自于应用程序本身.

Event 类型

Qt为多数 Event 类型建立了相应的类, 常见有QResizeEvent, QPaintEvent, QMouseEvent, QKeyEvent, QCloseEvent.

每一个特定的 event 类都是继承自 QEvent 基类,添加特定的事件函数. 例如, QResizeEvent 添加了 size() 和 oldSize() 让控件可以发现他们的尺度,发生了的怎么改变.

某些类实际支持不止一种事件类型. QMouseEvent 就支持鼠标按键按下事件,双击事件, 移动事件, 以及其他相关操作所引发的事件.

每一个事件都有它的类型, 由 QEvent::Type 定义, 运行时可以很方便的检测每个事件对象的事件类型,以快速的判断该事件对象构造自哪个事件类.

由于程序需要和又多样又复杂的事件进行交互, 所以 Qt 的 event 发送机制设计非常有弹性.

QCoreApplication::notify() 的文档简洁的说明了整个机制:


 
  1. bool QCoreApplication::notify ( QObject * receiver, QEvent * event )   [virtual]  

发送 event 给 接收者: receiver->event(event). 返回从 receiver 的 event handler 返回的值. 注意这个函数适用于该应用程序中的任何线程中的任何对象. 对于特定类型的事件 (例如, 鼠标和键盘事件), 该事件将被传送到 receiver 的 parent 并这样逐级上传

直到传到 top-level object, 如果这些 receiver 都没有对该事件进行处理的话(比如, 它返回 false ).

共五种处理 event 的方法; 重写(重实现) QCoreApplication::notify() 这个 virtual 函数只是其 中的一种. 以下列出了这五种方法:

1、重写 paintEvent(), mousePressEvent() 等. 这是最常用, 最简单但也是最有限的方式.

2、重写 QCoreApplication::notify(). 这非常强大, 可以完全控制事件处理; 但一次只可用于一个子类.

3、给 QCoreApplication::instance() 安装一个 event filter .这个 event filter 就能处理所有控件的所有事件, 因此这与重写 notify() 一样强大; 此外, 可以有不止一个应用程序全局级的 event filter. 应用程序全局级 event filter 甚至可以收到已禁用控件的鼠标事件.

注意:  应用程序级 event filter 仅能用于存活在主线程中的对象.

4、 重写 QObject::event()( 像 QWidget 那样 ). 如果你重写了 QObject::event(), 当 Tab 键按下时, 你就可以在任何控件级 event filter 捕获这个 Tab 键按下事件之前处理这个事件.

5、给相应的接收对象安装一个 event filter. 例如一个捕获所有事件的 event filter, 包含 Tab 和Shift+Tab 键按下事件, 在它们没有改变焦点控件之前.

另请参考 QObject::event() 以及 installEventFilter().

Event Handlers

处理 event 的标准方式是调用一个 virtual 函数. 例如, QPaintEvent 是通过调用 QWidget::paintEvent() 来处理的. 这个 virtual 函数负责进行相应的处理, 通常就是重画该控件. 如果你在自己实现的 virtual 函数中没有做所有必要的工作, 你就有必要调用它的基类实现.

例如, 下面的代码处理一个定制 checkbox 控件的鼠标左键点击事件, 并将所有其他点击事件转发给它的基类 QCheckBox 类:


 
  1. void MyCheckBox::mousePressEvent(QMouseEvent *event)   
  2. {   
  3.     if (event->button() == Qt::LeftButton) {   
  4.         // handle left mouse button here   
  5.     } else {   
  6.         // pass on other buttons to base class   
  7.         QCheckBox::mousePressEvent(event);   
  8.     }   
  9. }  

如果你需要替换基类的函数, 你应该自己实现所有相关的处理. 但是, 如果你只想扩展基类的功能, 那么你就只需实现需要实现的部分, 然后调用基类处理函数来处理你不想处理的情况.

偶尔, 你要处理没有相应处理函数的特定事件, 或遇到事件处理函数不够用情况. 最常见的例子是 Tab 键按下事件. 通常, QWidget 截获到 Tab 键按下事件后,会移动键盘焦点, 但是少数控件需要自己来处理这个事件. 这些对想可以重写 QObject::event() 函数, 通用的 event handler, 然后在通常处理过程之前或之后写自己的事件处理过程, 或完全替代原处理过程. 下面是这样一个很常见的控件:

一个既自己处理 Tab 事件又自己处理某些按键事件, 然后将其它不需自己处理的事件转发给基类处理:


 
  1. bool MyWidget::event(QEvent *event)   
  2. {   
  3.     if (event->type() == QEvent::KeyPress) {   
  4.         QKeyEvent *ke = static_cast<QKeyEvent *>(event);   
  5.         if (ke->key() == Qt::Key_Tab) {   
  6.             // special tab handling here   
  7.             return true;   
  8.         }   
  9.     } else if (event->type() == MyCustomEventType) {   
  10.         MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);   
  11.         // custom event handling here   
  12.         return true;   
  13.     }   
  14.  
  15.     return QWidget::event(event);   
  16. }  

值得注意的是对没有处理的事件仍调用 QWidget::event(), 并返回该基类调用的返回值以指示事件是否被处理了; 若返回 true 值则将会禁止将该事件再发往其他对象.

Event Filters

有时候一个对象需要检查, 还可能截取发往其它对象的事件.例如, 对话框通常需要过滤发往某些控件的事件, 比如 更改 Enter 键按下的事件处理.

通过调用过滤器对象的 QObject::installEventFilter() 函数, 为目标对象设置一个 event filter, 就可在过滤器对象的QObject::eventFilter() 函数中处理发往目标对象的事件. 一个 event filter 在目标对象收到事件之前处理事件, 这使得过滤器对象在需要的时候可以检查并丢弃事件. 一个现有的 event filter 可以调用 QObject::removeEventFilter() 来移除已经安装的 event filter .

当过滤器的 eventFilter() 实现被调用的时候, 它就可以选择是处理该事件,还是转发该事件, 或禁止该事件继续被其它对象处理. 若所有的事件过滤器都允许一个事件可被继续处理( 每个过滤器处理后都返回 false ), 该事件最终将被发送到目标对象. 如果其中一个中止了这个流程(通过返回 TRUE),

则后面的过滤器对象以及目标对象将不会收到该事件.


 
  1. bool FilterObject::eventFilter(QObject *object, QEvent *event)   
  2. {   
  3.     if (object == target && event->type() == QEvent::KeyPress) {   
  4.         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);   
  5.         if (keyEvent->key() == Qt::Key_Tab) {   
  6.             // Special tab handling   
  7.             return true;   
  8.         } else   
  9.             return false;   
  10.     }   
  11.     return false;   
  12. }  

上面代码演示了另外一种截取发往特定对象 Tab 键事件的方法. 在这个例子里, 该过滤器处理 Tab 事件后返回 true 来阻止它们被继续处理. 所有其他的按键事件将被忽略掉,然后过滤器返回 false 来允许该事件被已安装的后续过滤器处理, 最终发往目标控件.

当然也可以过滤整个应用程序的所有事件, 只需将过滤器对象安装到 QApplication 对象或QCoreApplication 对象上. 这样的全局事件过滤器会在任何对象级过滤器()调用之前调用.

这是非常强大的, 但它也拖慢了整个应用程序范围内每个事件的每次处理过程; 通常使用其他的技术来实现应用程序全局的事件过滤.

发送事件

许多应用程序需要创建并发送自己的事件. 你完全可以模仿 Qt 自有的 event loop 机制, 先构造合适的事件对象, 然调用 QCoreApplication::sendEvent() QCoreApplication::postEvent() 来把这些构造好的事件发送给指定的接收者.

sendEvent() 立即同步处理要发送的 event . 当它返回的时候, 表示相关的事件过滤器 和/或目标对象就处理完了该 event. 对于多数的 event 类, 有一个成员函数 isAccepted() 可以用来判别该事件是已被接受处理了,还是被拒绝处理了.

postEvent() 将 event 提交到一个队列中等待调度. 在下一次 Qt 的主 event loop 运行的时候,主 event loop 就会调度所有提交到队列中的 event, 以某种优化的方式. 例如, 如果有几个 resize event, 他们就会被压缩成一个事件. 同样适用于 paint events: QWidget::update() 调用postEvent(), 以避免多次重画来避免闪烁以及提高速度.

postEvent() 也被用于对象的初始化过程, 因为提交过的 event 通常在相应对象初始化完毕后极短的 时间内就会被调度. 在实现一个控件的时候, 在自定义控件的 constructor 中尽早支持事件机制是非常重要的, 在可能接受到任何事件之前,确保尽早初始化成员变量.

要创建一个定制类型的 event, 你需要定义一个事件号( event number ), 这个事件号应该大于QEvent::User, 并且你可能需要继承 QEvent 以传递关于你定制的 event 类型的特定信息.  
参考 QEvent 文档以获取更多细节.

发布了80 篇原创文章 · 获赞 109 · 访问量 22万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览