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 posevent-pos();qDebug()鼠标按下位置pos.x()pos.y();// 获取全局屏幕坐标QPoint globalPosevent-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::MouseButtonPressmousePressEvent()QEvent::MouseButtonReleasemouseReleaseEvent()QEvent::MouseMovemouseMoveEvent()QEvent::KeyPresskeyPressEvent()QEvent::EnterenterEvent()QEvent::LeaveleaveEvent()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 阻断传递本篇讲解了事件系统的整体流程和鼠标事件、事件分发器的用法。下篇将讲解更上层的事件过滤器可以监控其他对象的事件以及定时器的两种实现方式敬请期待。