QT Event 以及 Event Filter 事件处理是本文要介绍的内容,详细内容如下,先来看内容。Event 和 Event Filters:
1、手动发送事件流程:
(1)构造自己的事件对象:
- QEvent *evt = new QEvent( QEvent::Close );
(2)发送给指定的对象:
- 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 在典型应用程序中是如何发送及处理的.
- How Events are Delivered
- Event Types
- Event Handlers
- Event Filters
- 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() 的文档简洁的说明了整个机制:
- 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 类:
- void MyCheckBox::mousePressEvent(QMouseEvent *event)
- {
- if (event->button() == Qt::LeftButton) {
- // handle left mouse button here
- } else {
- // pass on other buttons to base class
- QCheckBox::mousePressEvent(event);
- }
- }
如果你需要替换基类的函数, 你应该自己实现所有相关的处理. 但是, 如果你只想扩展基类的功能, 那么你就只需实现需要实现的部分, 然后调用基类处理函数来处理你不想处理的情况.
偶尔, 你要处理没有相应处理函数的特定事件, 或遇到事件处理函数不够用情况. 最常见的例子是 Tab 键按下事件. 通常, QWidget 截获到 Tab 键按下事件后,会移动键盘焦点, 但是少数控件需要自己来处理这个事件. 这些对想可以重写 QObject::event() 函数, 通用的 event handler, 然后在通常处理过程之前或之后写自己的事件处理过程, 或完全替代原处理过程. 下面是这样一个很常见的控件:
一个既自己处理 Tab 事件又自己处理某些按键事件, 然后将其它不需自己处理的事件转发给基类处理:
- bool MyWidget::event(QEvent *event)
- {
- if (event->type() == QEvent::KeyPress) {
- QKeyEvent *ke = static_cast<QKeyEvent *>(event);
- if (ke->key() == Qt::Key_Tab) {
- // special tab handling here
- return true;
- }
- } else if (event->type() == MyCustomEventType) {
- MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
- // custom event handling here
- return true;
- }
- return QWidget::event(event);
- }
值得注意的是对没有处理的事件仍调用 QWidget::event(), 并返回该基类调用的返回值以指示事件是否被处理了; 若返回 true 值则将会禁止将该事件再发往其他对象.
Event Filters
有时候一个对象需要检查, 还可能截取发往其它对象的事件.例如, 对话框通常需要过滤发往某些控件的事件, 比如 更改 Enter 键按下的事件处理.
通过调用过滤器对象的 QObject::installEventFilter() 函数, 为目标对象设置一个 event filter, 就可在过滤器对象的QObject::eventFilter() 函数中处理发往目标对象的事件. 一个 event filter 在目标对象收到事件之前处理事件, 这使得过滤器对象在需要的时候可以检查并丢弃事件. 一个现有的 event filter 可以调用 QObject::removeEventFilter() 来移除已经安装的 event filter .
当过滤器的 eventFilter() 实现被调用的时候, 它就可以选择是处理该事件,还是转发该事件, 或禁止该事件继续被其它对象处理. 若所有的事件过滤器都允许一个事件可被继续处理( 每个过滤器处理后都返回 false ), 该事件最终将被发送到目标对象. 如果其中一个中止了这个流程(通过返回 TRUE),
则后面的过滤器对象以及目标对象将不会收到该事件.
- bool FilterObject::eventFilter(QObject *object, QEvent *event)
- {
- if (object == target && event->type() == QEvent::KeyPress) {
- QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
- if (keyEvent->key() == Qt::Key_Tab) {
- // Special tab handling
- return true;
- } else
- return false;
- }
- return false;
- }
上面代码演示了另外一种截取发往特定对象 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 文档以获取更多细节.
小结:关于详解 QT Event 以及 Event Filter 事件处理的内容介绍完了,希望本文对你有所帮助。更多请参考编辑推荐。