Qt学习笔记(四·上):鼠标事件与事件分发器
导读:本文是 Qt 事件系统系列的上篇。信号与槽虽然好用,但它只是 Qt 交互的"上层接口",底层真正驱动一切的是事件系统。本文从事件流程全貌讲起,重点掌握鼠标事件的用法和
event()事件分发器的拦截机制,为下篇的事件过滤器打好基础。
前言
在前面的文章中,我们通过信号与槽实现了控件之间的通信。但信号与槽并不是 Qt 处理用户交互的唯一方式——在更底层,Qt 使用事件(Event)系统来处理所有的用户操作和系统通知。鼠标点击、键盘输入、窗口缩放、定时器触发,本质上都是事件。理解事件系统,能帮助你在需要更精细控制的场景下实现复杂交互逻辑。
一、Qt 事件系统概述
Qt 中的每一个事件都是QEvent类或其子类的对象。当用户操作或系统状态发生变化时,Qt 会创建一个事件对象,并将其传递给目标控件进行处理。
事件处理的完整流程
一个事件从产生到被处理,会依次经过三个关卡:
事件产生(用户操作 / 系统通知) │ ▼ ┌─────────────────────┐ │ QApplication │ 事件循环(Event Loop)接收事件 └─────────┬───────────┘ │ ▼ ┌─────────────────────┐ │ 事件过滤器 │ eventFilter() —— "小区大门" │ (eventFilter) │ 可在此拦截/过滤事件 └─────────┬───────────┘ │ (未拦截) ▼ ┌─────────────────────┐ │ 事件分发器 │ event() —— "家中大门" │ (event) │ 可在此分发/拦截事件 └─────────┬───────────┘ │ (未拦截) ▼ ┌─────────────────────┐ │ 具体事件处理函数 │ mousePressEvent() / keyPressEvent() 等 │ (handler) │ 最终处理事件的地方 └─────────────────────┘- 事件过滤器是"小区大门"——在事件到达目标对象之前,统一拦截
- 事件分发器是"家中大门"——在事件到达具体处理函数之前,统一分发
- 具体事件处理函数是"房间"——最终真正处理事件的地方
本篇重点讲解"家中大门"及之前的流程,下篇将讲解"小区大门"(事件过滤器)和定时器。
二、鼠标事件
鼠标事件是最常用的事件类型之一。Qt 提供了多个虚函数来处理不同的鼠标操作,只需在头文件中声明并重写即可。
2.1 鼠标事件函数声明
protected:// 鼠标进入控件区域virtualvoidenterEvent(QEvent*event)override;// 鼠标离开控件区域virtualvoidleaveEvent(QEvent*event)override;// 鼠标按下virtualvoidmousePressEvent(QMouseEvent*event)override;// 鼠标释放virtualvoidmouseReleaseEvent(QMouseEvent*event)override;// 鼠标移动virtualvoidmouseMoveEvent(QMouseEvent*event)override;2.2 获取鼠标坐标
在鼠标事件处理函数中,可以通过QMouseEvent对象获取鼠标的详细位置信息:
voidMyWidget::mousePressEvent(QMouseEvent*event){// 获取相对于当前控件的坐标QPoint pos=event->pos();qDebug()<<"鼠标按下位置:"<<pos.x()<<pos.y();// 获取全局屏幕坐标QPoint globalPos=event->globalPos();qDebug()<<"全局坐标:"<<globalPos.x()<<globalPos.y();}2.3 区分鼠标按键
通过event->button()可以判断是哪个按键触发了事件:
voidMyWidget::mousePressEvent(QMouseEvent*event){if(event->button()==Qt::LeftButton){qDebug()<<"左键按下";}elseif(event->button()==Qt::RightButton){qDebug()<<"右键按下";}elseif(event->button()==Qt::MiddleButton){qDebug()<<"中键按下";}}💡小贴士:默认情况下,
mouseMoveEvent只有在按住鼠标按键移动时才会触发。如果希望鼠标移动事件在不按下按键时也能触发,需要调用setMouseTracking(true)开启鼠标追踪。
三、事件分发器(event 函数)
事件分发器event()是每个QObject派生类都有的虚函数,它是事件到达具体处理函数之前的总闸门。所有事件都会先经过event()函数,再由它决定分发到哪个具体的处理函数。
3.1 工作原理
在QWidget::event()的默认实现中,会根据事件类型调用对应的处理函数:
| 事件类型 | 调用的处理函数 |
|---|---|
QEvent::MouseButtonPress | mousePressEvent() |
QEvent::MouseButtonRelease | mouseReleaseEvent() |
QEvent::MouseMove | mouseMoveEvent() |
QEvent::KeyPress | keyPressEvent() |
QEvent::Enter | enterEvent() |
QEvent::Leave | leaveEvent() |
3.2 拦截事件
通过重写event()函数,我们可以拦截特定事件,阻止其传递到具体的处理函数:
boolMyEvent::event(QEvent*event){if(event->type()==QEvent::MouseMove){// 返回 true 表示事件已被处理,不再向下传递// 这样 mouseMoveEvent() 就不会被调用returntrue;// 屏蔽鼠标移动事件}// 其他事件交给父类处理returnQWidget::event(event);}返回值含义:
| 返回值 | 含义 |
|---|---|
true | 事件已被处理,停止向下传递 |
false | 事件未被处理,继续向下传递给父类的event() |
⚠️注意:拦截事件时要谨慎,过度拦截可能导致控件无法正常响应用户操作。通常只拦截你明确需要屏蔽的事件类型,其余一律交给父类处理。
四、本篇小结
| 知识点 | 关键内容 |
|---|---|
| 事件流程 | 产生 → 事件过滤器 → 事件分发器 → 具体处理函数 |
| 鼠标事件 | enter/leave/press/release/move 五种基本事件 |
| 坐标获取 | event->pos()相对坐标,event->globalPos()全局坐标 |
| 鼠标追踪 | setMouseTracking(true)开启后,不按按键也能触发移动事件 |
| 事件分发器 | event()函数,可拦截和分发事件,返回 true 阻断传递 |
本篇讲解了事件系统的整体流程和鼠标事件、事件分发器的用法。下篇将讲解更上层的事件过滤器(可以监控其他对象的事件)以及定时器的两种实现方式,敬请期待。