文章目录
前言:
Qt 框架中的事件机制(Event Mechanism)是一种核心功能,它允许应用程序以事件驱动的方式响应各种外部和内部发生的动作。事件机制在 Qt 中扮演着至关重要的角色,无论是在用户界面的交互还是在后台处理数据时。它不仅确保了程序的响应性和灵活性,还大大降低了编程的复杂性,使开发者能够以更直观的方式来处理异步事件。
一、事件
1、基本概念
事件是由程序的内部或外部产生的事情或某种操作的统称。例如:用户按下鼠标或键盘就会产生一个鼠标或键盘事件,当窗口第一次绘制时会产生一个绘制事件。Qt中的事件主要来源于以下几个方面:
- 用户输入:如鼠标点击、键盘按键、触摸屏操作等。
- 系统事件:如窗口大小改变、系统时间变化、网络状态改变等。
- 定时器事件:通过QTimer类创建的定时器事件。
- 其他事件:如自定义事件、信号与槽机制触发的事件等
事件的作用:事件在 Qt 应用程序中扮演着至关重要的角色。它们是应用程序与用户或系统交互的基础,使得程序能够以非阻塞的方式响应外部的变化。通过事件,Qt 应用程序能够保持响应性和灵活性,同时简化复杂交互的处理逻辑。
2、事件描述
在Qt中使用抽象类
QEvent
类来描述事件,所有事件都是QEvent
的派生类。基本的QEvent
类只包含一个事件类型参数,QEvent
的子类包含额外的参数用来描述事件信息。事件类型使用QEvent::type
来表示,这个枚举类定义了Qt中有效的事件类型。Qt中常见的事件有
- 键盘事件(QKeyEvent)
- 鼠标事件(QMouseEvent)
- 拖放事件(QDragEvent 和 QDropEvent)
- 绘图事件(QPaintEvent)
- 焦点事件(QFocusEvent)
3、事件循环
通过
QApplication::exec()
开启事件循环,事件循环运行在一个无限循环中,不断检查是否有新的事件发生。当事件发生时,它们被加入到一个队列中。事件循环按照事件发生的顺序,从队列中取出并分发这些事件到相应的对象进行处理。这个过程是异步的,意味着事件的产生和处理是分离的,确保了应用程序的高效运行。事件循环是保持 Qt 应用程序响应性的关键。没有事件循环,应用程序将无法适时地响应用户输入、网络通信等事件。
4、事件分发
4.1、QApplication::notify()
在 Qt 的事件系统中,所有的事件(如鼠标点击、键盘输入、定时器事件等)都会经过
notify
方法进行分发。notify
方法原型如下:
bool notify(QObject *, QEvent *) Q_DECL_OVERRIDE;
默认情况下,
notify
方法会调用相应对象(接受事件的对象)的QObject::event
方法来处理这些事件。但可以通过重写notify
方法来定制事件的处理方式。具体来说,QApplication::notify
的作用包括:
- 事件分发:当 Qt 检测到一个事件(如鼠标点击、键盘输入等)时,它会调用 QApplication 的 notify 方法来分发这个事件。
- 事件过滤:在事件被发送到目标对象之前,你可以通过重写 notify 方法来过滤或修改事件。例如,你可以阻止某个特定类型的事件被发送到某个对象。
一般来说,直接重写 QApplication 的 notify 方法并不常见,因为这样做会影响整个应用程序的事件处理。但在某些特殊情况下,如实现全局的事件过滤器或自定义事件分发策略时,重写 notify 方法可能会非常有用。
4.2、QObject::event()
QObject::event
在 Qt 框架中是一个重要的成员函数,用于处理接收到的QEvent
对象,并将这些事件按照不同的类型,分发给不同的事件处理器。Qt中QObject::event
的源码如下:
bool QObject::event(QEvent *e)
{
switch (e->type()) {
case QEvent::Timer:
timerEvent((QTimerEvent*)e);
break;
case QEvent::ChildAdded:
case QEvent::ChildPolished:
case QEvent::ChildRemoved:
childEvent((QChildEvent*)e);
break;
// ...
default:
if (e->type() >= QEvent::User) {
customEvent(e);
break;
}
return false;
}
return true;
}
5、事件传递
如果对象没有处理该事件(即没有重写对应的事件处理函数或函数中没有调用event->accept()),Qt会尝试将该事件传递给其父对象。这个过程会一直持续到某个对象处理了事件或事件到达了顶级对象(如应用程序窗口)为止。这就是所谓的“事件冒泡”(event bubbling)。
6、事件处理器
如果Qt找到了一个与事件相关的对象,它会调用该对象的事件处理器(也称为事件处理函数),这些事件处理器都是虚函数,直接重写对应的虚函数就可以处理自己的业务。下面是常见的事件及事件处理器,如下:
- 鼠标事件(QMouseEvent)
- mousePressEvent(QMouseEvent *event): 处理鼠标按键的按下事件
- mouseReleaseEvent(QMouseEvent *event): 处理鼠标按键的释放事件。
- mouseMoveEvent(QMouseEvent *event): 处理鼠标移动事件。
- wheelEvent(QWheelEvent *event): 处理鼠标滚轮的滚动事件。
- 键盘事件(QKeyEvent)
- keyPressEvent(QKeyEvent *event): 处理键盘按键的按下事件。
- keyReleaseEvent(QKeyEvent *event): 处理键盘按键的释放事件。
7、事件与信号的区别
Qt中的事件(Event)与信号(Signal)在Qt框架中扮演着不同的角色,用于处理不同的编程场景。以下是它们之间的主要区别:
7.1、事件的定义及来源
- 系统消息的转换:在Qt中,事件通常源自操作系统传递的底层系统消息,例如用户交互、网络操作等。这些系统消息被Qt转化为QEvent对象,然后分发给相应的QWidget对象进行处理。
- 局部影响:每个事件(如鼠标点击、键盘按键等)通常只在其发生的位置或组件内部被处理,并且可以通过重写相应事件的处理函数来自定义行为。
7.2、信号的定义及来源
- 编程逻辑产生:与事件不同,信号是在Qt编程逻辑中产生的,用于在不同对象之间进行通信。当某个特定的条件或动作发生时,一个对象会发出一个信号,零个、一个或多个槽函数可以响应这个信号。
- 全局通信:信号和槽机制是一种实现观察者模式的方法,允许对象之间的松耦合通信。信号的发出并不关心接收者是谁,而是通过槽自动连接和处理。
7.3、处理方式
- 事件队列:事件被存储在一个队列中,按照发生顺序逐个处理。这种方式确保了事件能够有序地被分发和处理。
- 即时调用:相对于事件的队列处理,信号在发出后会立即调用所有连接的槽函数,无需等待其他操作,这可以实现实时响应。
7.4、应用场景
- 局部事件处理:事件通常用于处理用户界面相关的局部行为,如鼠标点击、键盘输入等。开发者可以通过重写各类事件处理函数来实现自定义的控制逻辑。
- 对象间通信:信号和槽则广泛应用于对象之间的状态通知和响应。例如,一个按钮的点击信号可以被任何数量的槽函数接收并做出响应,这使得设计复杂的交互逻辑成为可能。
参考文档:https://developer.aliyun.com/article/1469054