Qt 实战(6)事件 | 6.1、事件机制

发布于:2024-06-29 ⋅ 阅读:(13) ⋅ 点赞:(0)


前言:

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