统信消息机制和事件
统信消息机制和事件
消息机制的基本概念
Qt 的消息机制基于以下几个核心概念:
- 事件循环(Event Loop):
Qt 使用事件循环来管理事件的收发。事件循环是一个持续运行的循环,负责检测系统事件(如鼠标、键盘、定时器等)并将这些事件分发给相应的对象进行处理。 - 事件(Event):
事件是 Qt 中消息机制的核心单位,用于表示各种系统级的动作或状态变化。常见的事件包括鼠标点击、键盘输入、窗口重绘等。事件在 Qt 中由 QEvent 类及其子类表示。 - 消息队列(Event Queue):
Qt 应用程序中的所有事件都被放入事件队列中排队等待处理。事件循环会从队列中取出事件,依次处理并将其分发给目标对象。 - 事件处理函数(Event Handlers):
每个 Qt 对象都有一组预定义的事件处理函数,用来处理特定类型的事件。常见的事件处理函数包括 mousePressEvent()、keyPressEvent() 等。 - 信号和槽机制(Signals & Slots):
Qt 中的信号和槽机制是消息传递的一种方式,允许对象之间进行通信。信号表示某个对象的状态发生了变化,槽是对这种变化的响应。
事件驱动模型的工作流程
在 Qt 的事件驱动模型中,应用程序通常包含以下几个步骤来处理消息和事件:
- 启动事件循环:
当应用程序启动时,QApplication 类的 exec() 方法会启动事件循环。事件循环会不断监听系统事件,并将其放入消息队列。 - 事件的生成:
当系统检测到用户的交互(如鼠标点击、键盘输入等)或系统事件(如窗口重绘、定时器触发等)时,会生成对应的事件对象,并将其添加到消息队列中。 - 事件的分发和处理:
事件循环从消息队列中取出事件,并调用 QApplication 的 notify() 函数来将事件分发给相应的接收对象。然后,事件被传递给对象的 event() 函数。 - 事件的处理:
event() 函数根据事件的类型调用具体的事件处理函数,比如 mousePressEvent()、keyPressEvent() 等。如果事件未被处理,event() 函数将继续将事件传递给父类,直到找到合适的处理方式。 - 事件循环继续:
一旦事件被处理,事件循环继续从消息队列中获取下一个事件,直到应用程序退出。
事件处理函数
事件处理函数是QObject子类中重写的函数,用于处理特定类型的事件。例如,mousePressEvent()用于处理鼠标按下事件。如果子类没有处理事件,可以通过调用基类的相应函数,将事件传递给父类处理。
事件的接受与忽略
事件处理函数可以通过调用事件对象的accept()和ignore()函数,来表示事件是否被处理。这有助于控制事件的传播。通常,如果事件被处理,返回true;如果事件被忽略,返回false。
事件过滤器
事件对象创建完毕后,Qt 将这个事件对象传递给 QObject 的 event()函数。event()
函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的
事件处理器(event handler)。
event()函数主要用于事件的分发。所以,如果你希望在事件分发之前做一些操
作,就可以重写这个 event()函数了。例如,我们希望在一个 QWidget 组件中监听
tab 键的按下,那么就可以继承 QWidget,并重写它的 event()函数,来达到这个
目的:
bool CustomWidget::event(QEvent *e)
{
if (e->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
if (keyEvent->key() == Qt::Key_Tab) {
qDebug() << "You press tab.";
return true;
}
}
return QWidget::event(e);
}
CustomWidget 是一个普通的 QWidget 子类。我们重写了它的 event()函数,这个
函数有一个 QEvent 对象作为参数,也就是需要转发的事件对象。函数返回值是
bool 类型。
如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返
回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其
它对象,而是会继续处理事件队列中的下一事件。
在 event()函数中,调用事件对象的 accept()和 ignore()函数是没有作用的,不
会影响到事件的传播。
可以通过使用 QEvent::type()函数可以检查事件的实际类型,其返回值是
QEvent::Type 类型的枚举。我们处理过自己感兴趣的事件之后,可以直接返回
true,表示我们已经对此事件进行了处理;对于其它我们不关心的事件,则需要调
用父类的 event()函数继续转发,否则这个组件就只能处理我们定义的事件了。
有时候,对象需要查看、甚至要拦截发送到另外对象的事件。例如,对话框可能想
要拦截按键事件,不让别的组件接收到;或者要修改回车键的默认处理。
Qt 创建了 QEvent 事件对象之后,会调用 QObject 的 event()函数处理事件的分
发。显然,我们可以在 event()函数中实现拦截的操作。由于 event()函数是
protected 的,因此,需要继承已有类。如果组件很多,就需要重写很多个 event()
函数。这当然相当麻烦,更不用说重写 event()函数还得小心一堆问题。好在 Qt
提供了另外一种机制来达到这一目的:事件过滤器。
QObject 有一个 eventFilter()函数,用于建立事件过滤器。函数原型如下:
virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );
这个函数正如其名字显示的那样,是一个“事件过滤器”。所谓事件过滤器,可以
理解成一种过滤代码。事件过滤器会检查接收到的事件。如果这个事件是我们感兴
趣的类型,就进行我们自己的处理;如果不是,就继续转发。这个函数返回一个
bool 类型,如果你想将参数 event 过滤出来,比如,不想让它继续转发,就返回
true,否则返回 false。事件过滤器的调用时间是目标对象(也就是参数里面的
watched 对象)接收到事件对象之前。也就是说,如果你在事件过滤器中停止了
某个事件,那么,watched 对象以及以后所有的事件过滤器根本不会知道这么一
个事件。