Qt The Event System 事件系统

Qt The Event System 事件系统

前言

在Qt中,事件就是对象,继承自QEvent抽象类。事件可能发生在应用内部,也可能发生在应用外部,都需要被应用捕获。QObject子类的任何实例可以接受事件、处理事件,经常用在widgets中。

事件和信号是有所不同的,比如对于一个鼠标单击动作,按钮可以发送一个clicked()信号,此时我们只是关注鼠标单击这一个信号,而不去考虑鼠标如何点击的。

在这里插入图片描述

图3-1 事件循环

上图就是一个完整的事件处理流程,当我们处理一个事件时会跳出事件循环,等到事件处理完成之后回到事件循环。

在这里插入图片描述

图3-2 事件循环与信号槽机制

但是信号槽不同,当处理信号时不会跳出事件循环,实际上信号槽是另一种机制,它更像是一种函数调用,当发出信号时接收信号的槽会立即执行,执行完毕后返回到原位置。

事件的递送

当有一个事件发生时,Qt将会创建一个合适的QEvent子类实例,通过QObject或其子类的event()函数将它传送给特定的QObject或其子类实例。

event()不会处理这个事件,而是根据特定类型的事件调用不同的事件处理器,并返回一个状态,表明是否被接受。

事件类型

大部分事件类型都有特殊的类,比如QResizeEventQPaintEventQMouseEventQKeyEventQCloseEvent等。每个类都继承自QEvent并添加了特定的函数用于处理特定事件。

一些类支持多于一个实际事件操作,比如QMouseEvent支持鼠标单击,双击,移动以及其他操作。

每一个事件还有一个关联类型,定义为QEvent::Type,它可以用作一个运行时类型信息(RTTI)源,能够快速确定给定事件是从哪个子类构造的。

事件处理器

一般事件处理方式是通过event()分发给不同的事件处理器。

比如QPaintEvent就通过调用QWidget::paintEvent()进行传递。这个虚函数负责做出适当的反应,通常是重画界面。如果不想讨论这么多的情况,则可以通过调用基类的实现来完成。

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);
    }
}

上面这个例子就通过mousePressEvent处理左键单击事件,而其他按钮事件传递给QCheckBox基类去完成。其中mousePressEvent就可以看成一个事件处理器。

如果我们希望替换基类的函数,就必须实现每一个事件(一般不会这么做),一般做法就是只处理我们需要的,其他部分交给基类的函数去完成。

上面的例子中我们已经知道事件是QMouseEvent中的一种,但是大部分情况下我们都不知道具体事件是什么,此时可以重新实现QObject::event()函数,和上面类似,我们可以只处理我们需要的事件,其他事件交给QWidget::event()去处理。


其实对于事件处理器可以单纯理解为处理事件的函数或者代码块,一般来说event是分发事件的函数,具体的mousePressEvent是处理事件的函数,但有时也可以直接在event中处理事件,这时对应我们重写event的情况。


Event Filters 事件过滤器

有时一个对象需要查看或者拦截某个传递给另一个对象的事件,这时候需要用到事件过滤器。

事件过滤器在event()分发之前拦截。

事件过滤器的特点

通过QObject::installEventFilter()函数就可以为某个对象设置一个事件过滤器,在这个对象中接收目标对象的事件,这个函数的参数是一个QObject对象(过滤器对象),这个过滤器对象必须重写eventFilter()成员函数。

事件过滤器在目标对象之前处理事件,可以根据需要检查和丢弃事件。

bool QObject::eventFilter(QObject *watched, QEvent *event);

这是过滤器对象的eventFilter()成员函数的参数要求,为了能够识别目标对象,在这个过滤器对象中必须设置一个target变量保存目标对象。

当一个过滤器对象的eventFilter()被调用了,它可以接收或丢弃这个事件,并可以允许或拒绝事件的进一步处理。如果所有的事件过滤器都允许某个事件的进一步处理(每一个过滤器都返回false),这个事件将会被发送给目标对象。如果其中有一个过滤器阻止了事件的进一步传播,则目标对象以及之后的过滤器都将看不到这个事件。

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;
}

上面这个示例先比较事件的目标是否是我们规定的目标对象,然后对其进行相应处理。

我们通过返回true阻止事件进一步传递,通过返回false允许事件继续传递给其他事件过滤器或者目标对象。

可以通过QObject::removeEventFilter()函数删除事件过滤器。

我们也可以在整个应用上添加一个事件过滤器,这样当我们传递某个事件给目标对象时,就会优先调用这个全局过滤器先进行处理。

发送事件

许多应用也想创建并发送它们自己的事件。我们可以通过QCoreApplication::sendEvent()andQCoreApplication::postEvent()

sendEvent()将会立即处理事件,当它返回时,能够通过调用事件类的isAccepted()来判断这个事件是否被接收。

postEvent()会把事件放到队列中,下一次运行Qt主事件循环时,它会根据一些优化策略调度所有被提交的事件。这种优化的例子有很多,比如有多个调整大小的事件,则会将他们压缩成一个。

postEvent()也经常用在对象初始化期间,因为被提交的事件在对象初始化完成后很快就会被调度。

如果想要创建自定义类型的事件可以到官网查阅QEvent文档。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值