Skip to content

事件系统

Qt 事件系统中的所有事件类型都是通过 QEvent 类来管理的,且所有事件类型都继承自 QEvent。Qt 中的事件种类繁多,根据事件的不同用途和触发场景,可以对事件进行分类整理。

事件系统概述

  • 事件 (Event):事件是描述用户输入或其他操作的对象。Qt 提供了多种事件类型,例如:
    • 鼠标事件 (QMouseEvent):描述鼠标的移动、点击、释放等操作。
    • 键盘事件 (QKeyEvent):描述键盘的按键按下、释放等操作。
    • 触摸事件 (QTouchEvent):描述触摸屏的触摸、移动、释放等操作。
    • 定时器事件 (QTimerEvent):描述定时器的计时器事件。
  • 事件循环 (Event Loop):Qt 应用程序通过事件循环接收并处理事件。事件循环不断从事件队列中获取事件,并将其分派给相应的对象进行处理。
  • 事件处理 (Event Handling):对象通过事件处理数据来响应事件。每个事件类型都有一个对应的事件处理函数,例如:
    • mousePressEvent(QMouseEvent):处理鼠标按下事件。
    • keyPressEvent(QKeyEvent):处理键盘按键按下事件。
    • touchEvent(QTouchEvent):处理触摸事件。
  • 事件分发器 (Event Dispatcher)
    • 事件分发器在 Qt 事件系统中起着关键的协调和调度作用。它维护着事件类型与事件处理器之间的映射关系,当事件发生时,它依据事件的类型在已建立的映射中查找对应的事件处理器,并将事件传递给这些处理器。例如,在一个复杂的图形界面应用中,可能存在多个不同类型的组件对鼠标点击事件感兴趣,事件分发器能够准确地将鼠标点击事件路由到相应组件的事件处理器中。
  • 事件处理器 (Event Handler)
    • 事件处理器负责对事件进行实际的处理操作。它包含一系列针对不同事件类型的处理方法或函数,这些逻辑定义了接收到特定事件时应采取的具体行动。
    • 比如在处理鼠标点击事件时,事件处理器可能会获取鼠标点击的位置、点击的按键信息等,然后根据这些信息执行相应的业务逻辑,如在图形界面中更新显示内容、触发特定的计算或数据操作等。

事件传递机制

  • 事件冒泡 (Event Bubbling):当事件发生时,事件首先被发送给最顶层的对象,然后逐级向下传递,直到找到能够处理该事件的对象。
  • 事件捕获 (Event Capturing):事件捕获机制与事件冒泡相反,事件首先被发送给最底层的对象,然后逐级向上传递。

事件处理函数

  • QObject::event(QEvent):这是所有事件处理函数的基类,它可以处理所有类型的事件。
  • QWidget::event(QEvent):这是 QWidget 类的事件处理函数,它可以处理 QWidget 对象的事件。
  • 其他事件处理函数:Qt 提供了针对不同事件类型的事件处理函数,例如 mousePressEventkeyPressEvent 等。

事件类型

输入事件

与用户输入设备(如鼠标、键盘、触摸等)交互相关的事件。

  • QKeyEvent:键盘事件,处理键盘按键的按下和释放。
    • QEvent::KeyPress:键盘按下。
    • QEvent::KeyRelease:键盘释放。
  • QMouseEvent:鼠标事件,处理鼠标按钮的按下、释放、移动等。
    • QEvent::MouseButtonPress:鼠标按下。
    • QEvent::MouseButtonRelease:鼠标释放。
    • QEvent::MouseButtonDblClick:鼠标双击。
    • QEvent::MouseMove:鼠标移动。
  • QWheelEvent:滚轮事件,处理鼠标滚轮滚动。
    • QEvent::Wheel:鼠标滚轮滚动。
  • QTouchEvent:触摸事件,处理触摸屏上的多点触摸操作。
    • QEvent::TouchBegin:开始触摸。
    • QEvent::TouchEnd:触摸结束。
    • QEvent::TouchUpdate:触摸更新。
  • QTabletEvent:绘图板事件,处理在绘图板上操作的事件。
    • QEvent::TabletPress:绘图板笔按下。
    • QEvent::TabletRelease:绘图板笔释放。
    • QEvent::TabletMove:绘图板笔移动。
  • QInputMethodEvent:输入法事件,处理输入法的输入。
    • QEvent::InputMethod:输入法事件。

窗口事件

与窗口状态、属性和显示相关的事件。

  • QResizeEvent:窗口调整大小事件。
    • QEvent::Resize:窗口大小改变。
  • QMoveEvent:窗口移动事件。
    • QEvent::Move:窗口移动。
  • QCloseEvent:窗口关闭事件。
    • QEvent::Close:窗口关闭。
  • QShowEvent:窗口显示事件。
    • QEvent::Show:窗口显示。
  • QHideEvent:窗口隐藏事件。
    • QEvent::Hide:窗口隐藏。
  • QFocusEvent:窗口或部件获得或失去焦点。
    • QEvent::FocusIn:获得焦点。
    • QEvent::FocusOut:失去焦点。
  • QEnterEvent:鼠标进入窗口事件。
    • QEvent::Enter:鼠标进入窗口或部件。
  • QLeaveEvent:鼠标离开窗口事件。
    • QEvent::Leave:鼠标离开窗口或部件。
  • QWindowStateChangeEvent:窗口状态改变事件。
    • QEvent::WindowStateChange:窗口状态改变(如最小化、最大化)。

定时器事件

处理定时器触发的事件。

  • QTimerEvent:定时器事件。
    • QEvent::Timer:定时器到期。

绘制和更新事件

与窗口或控件的绘制、重绘相关的事件。

  • QPaintEvent:绘制事件,窗口或部件需要重绘时触发。
    • QEvent::Paint:需要绘制。
  • QUpdateLaterEvent:更新事件,用于延迟更新。
    • QEvent::UpdateLater:延迟更新事件。
  • QUpdateRequestEvent:请求更新事件。
    • QEvent::UpdateRequest:请求更新事件。

拖放事件

与拖放操作相关的事件。

  • QDragEnterEvent:拖动进入事件。
    • QEvent::DragEnter:拖动进入。
  • QDragMoveEvent:拖动移动事件。
    • QEvent::DragMove:拖动时鼠标移动。
  • QDropEvent:拖动释放事件,处理放下的动作。
    • QEvent::Drop:释放被拖动的内容。
  • QDragLeaveEvent:拖动离开事件。
    • QEvent::DragLeave:拖动离开目标区域。

上下文菜单和快捷键事件

处理上下文菜单、快捷键和其他类似操作。

  • QContextMenuEvent:上下文菜单事件,通常是鼠标右键点击触发。
    • QEvent::ContextMenu:上下文菜单触发。
  • QShortcutEvent:快捷键事件,处理全局或本地的快捷键操作。
    • QEvent::Shortcut:快捷键按下。
    • QEvent::ShortcutOverride:快捷键覆盖事件。

系统事件

与系统相关的事件,如设备、平台相关操作。

  • QFileOpenEvent:文件打开事件,通常在双击文件时触发(在 macOS 上常用)。
    • QEvent::FileOpen:文件打开事件。
  • QPlatformSurfaceEvent:平台表面事件,处理底层平台表面(surface)创建或销毁的事件。
    • QEvent::PlatformSurface:平台表面事件。

对象事件

与对象状态相关的事件。

  • QChildEvent:子对象事件,处理对象的子对象添加或删除。
    • QEvent::ChildAdded:子对象添加。
    • QEvent::ChildRemoved:子对象删除。
  • QEvent::ParentChange:对象的父对象发生变化时触发。
    • QEvent::ParentChange:父对象改变。
  • QEvent::ThreadChange:对象的线程改变时触发。
    • QEvent::ThreadChange:对象从一个线程移到另一个线程。
  • QEvent::DynamicPropertyChange:动态属性变化时触发。
    • QEvent::DynamicPropertyChange:动态属性改变。

布局事件

与布局相关的事件,通常是布局管理器处理的事件。

  • QEvent::LayoutRequest:布局请求事件,通常用于请求重新布局。
    • QEvent::LayoutRequest:布局请求。
  • QEvent::GeometryChange:几何形状发生变化的事件。
    • QEvent::GeometryChange:几何形状改变。

其他事件

一些不常用或特殊的事件类型。

  • QEvent::Quit:程序退出事件,通常在应用程序关闭时触发。
    • QEvent::Quit:应用程序退出。
  • QEvent::StyleChange:样式改变事件,部件的视觉样式发生变化时触发。
    • QEvent::StyleChange:样式改变。
  • QEvent::LocaleChange:区域设置变化事件,系统语言或区域发生变化时触发。
    • QEvent::LocaleChange:区域设置变化。
  • QEvent::LanguageChange:语言变化事件,通常在多语言应用程序中,语言切换时触发。
    • QEvent::LanguageChange:语言改变。
  • QEvent::WindowActivate / QEvent::WindowDeactivate:窗口激活/失活事件。
    • QEvent::WindowActivate:窗口激活。
    • QEvent::WindowDeactivate:窗口失活。
  • QEvent::ApplicationActivate / QEvent::ApplicationDeactivate:应用程序激活/失活事件。
    • QEvent::ApplicationActivate:应用程序激活。
    • QEvent::ApplicationDeactivate:应用程序失活。
  • QEvent::Clipboard:剪贴板事件,处理剪贴板内容的变化。
    • QEvent::Clipboard:剪贴板改变。

自定义事件

用户可以通过继承 QEvent 创建自定义事件。通过 QEvent::registerEventType() 方法可以为自定义事件注册一个唯一的类型。


信号与槽

在 Qt 框架中,信号与槽(Signals and Slots)机制是一种强大的事件处理方式,它提供了一种类型安全的回调机制,使得不同的对象之间可以进行高效的通信。与传统的回调函数相比,信号与槽机制更加灵活、安全且易于维护。

信号与槽的基本概念

信号 (Signals)
信号是一种特殊的函数声明,它可以在特定的事件发生时被发射(emitted)。信号可以携带任意数量和类型的参数,用于传递事件相关的信息。
例如,一个按钮被点击时,可以发射一个信号,表示按钮被点击了。信号的声明通常使用关键字 signals,并且只能在类的声明中进行。

槽 (Slots)
槽是普通的成员函数,可以被连接到一个或多个信号上。当信号被发射时,与之连接的槽函数将被自动调用。
槽函数可以有不同的参数类型和返回值类型,但必须与连接的信号的参数类型相匹配。槽函数可以在任何 Qt 对象中定义,包括自定义的类。

声明信号和槽

在 Qt 中,信号和槽的声明使用特定的关键字。在类的声明中,使用 signals 关键字声明信号,使用 slots 和普通的成员函数声明槽。

cpp
class MyClass : public QObject  
{  
    Q_OBJECT  
public:  
    MyClass(QObject *parent = nullptr);  
    // 声明信号  
signals:  
    void mySignal(int param1, std::string param2);  
    // 声明槽函数  
public slots:  
    void mySlot(int param1, std::string param2);  
};

发射信号

cpp
void MyClass::sendSignalFunction()  
{  
    emit mySignal(42, "hello Qt"); // 发射信号,并传递了参数  
}

连接信号和槽

在 Qt 中,可以使用 connect 函数将信号和槽连接起来。连接时,需要指定信号的发送者、信号、槽的接收者和槽函数。

cpp
QMetaObject::Connection  
    connect(const typename QtPrivate::FunctionPointer<Func1>::object *sender, Func1 signal,  
        const typename QtPrivate::ContextTypeForFunctor<Func2>::ContextType *context, Func2 &&slot,  
        Qt::ConnectionType type = Qt::AutoConnection)  
/*  
@描述:  
    连接信号与槽的  
@sender:  
    信号发送者对象。  
@signal:  
    信号函数。  
@context:  
    类似于接受者  
@slot:  
    槽函数,接收到信号之后的处理函数  
@type:  
    指定信号和槽之间的连接方式,决定信号发出之后槽函数如何被调用  
    Qt::AutoConnection     会根据信号发送者和接收者是否在同一线程自动选择;  
    Qt::DirectConnection    信号发出之前调用    非阻塞  
    Qt::QueuedConnection    信号发出放入接收的事件队列中等待处理    阻塞  
*/

使用示例

cpp
MyClass *obj1 = new MyClass();  
MyClass *obj2 = new MyClass();  
connect(obj1, SIGNAL(mySignal(int, std::string)), obj2, SLOT(mySlot(int, std::string))); // 完成信号与槽的绑定

高级特性

多信号连接到一个槽
多个信号可以连接到一个槽函数。

cpp
connect(obj1, SIGNAL(mySignal1(int)), obj2, SLOT(mySlot(int)));  
connect(obj1, SIGNAL(mySignal2(QString)), obj2, SLOT(mySlot(int)));

一个信号连接到多个槽
一个信号可以连接到多个不同的槽函数。当信号被发射时,所有连接的槽函数将按照连接的顺序依次被调用。

cpp
connect(obj1, SIGNAL(mySignal(int)), obj2, SLOT(mySlot1(int)));  
connect(obj1, SIGNAL(mySignal(int)), obj2, SLOT(mySlot2(int)));

断开信号和槽的连接
可以使用 disconnect 函数断开信号和槽的连接。断开连接后,当信号被发射时,与之连接的槽函数将不再被调用。

cpp
disconnect(obj1, SIGNAL(mySignal(int)), obj2, SLOT(mySlot(int)));

自定义信号和槽的参数类型
Qt 支持自定义信号和槽的参数类型。可以使用 Qt 的元对象系统来注册自定义的类型,以便在信号和槽中使用。

cpp
class MyCustomType  
{  
public:  
    int value;  
};  

Q_DECLARE_METATYPE(MyCustomType)  

class MyClass : public QObject  
{  
    Q_OBJECT  
public:  
    explicit MyClass(QObject *parent = nullptr);  
signals:  
    void mySignal(MyCustomType param); // 自定义参数类型的信号  
public slots:  
    void mySlot(MyCustomType param); // 自定义参数类型的槽  
};

作业

  • 通过对象树模拟文件系统结构。
    • 使用元对象系统,事件系统。
    • 当增加一个派生类的时候,打印一句话,删除的时候打印一句话。

知识如风,常伴吾身