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

本文介绍的是详解 QT Event 以及 Event Filter 事件处理,不多说,先来看内容,更多相关内容参考文章末尾。

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 在典型应用程序中是如何发送及处理的.

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() 的文档简洁的说明了整个机制:

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 类型的特定信息.

原创文章,作者:365开发网,如若转载,请注明出处:https://www.365codes.com/615.html