今天学了什么
今天学习Qt事件系统。
Qt事件系统
① 定义:操作系统或Qt框架生成事件对象,按规则分发给对应控件处理,实现用户交互与状态响应。
② 事件分类(按触发来源):
- 用户操作触发:鼠标、键盘、拖放事件(人操作才产生);
- 系统自动触发:paintEvent、resizeEvent、closeEvent、timerEvent、焦点、鼠标进出等(状态变化自动发出,无需持续操作);
③ 事件的5种处理方式:
- 重写专属事件函数(最常用)
mousePressEvent、keyPressEvent、paintEvent等,在类内重写虚函数
- 重写event()总入口
在分到专属函数前拦截,经典场景:捕获Tab键;
- 局部事件过滤器
控件->installEventFilter(监视器),只监听指定控件;
必须重写eventFilter;
- 全局事件过滤器
qApp->installEventFilter(监视器),监听整个应用所有控件;
必须重写eventFilter,开销更大
- 重写QApplication::notify()
事件分发最顶层入口,权限最高,业务开发极少使用;
事件流经顺序(从先到后):
notify() → 全局过滤器 → 局部过滤器 → event() → 专属xxxEvent;
注:
- 过滤器返回值:return true 拦截事件;
- 返回父类eventFilter,事件继续分发;
- 重写键盘事件时,焦点在子控件,主窗口keyPressEvent收不到键盘事件,使用全局过滤器解决(局部也可以,但全局更通用);
补充:
setFocusPolicy:设置控件获取键盘焦点的策略(也可解决主窗口keyPressEvent收不到键盘事件);
绘图规范:拖拽橡皮筋画线,mouseMove更新终点+update(),绘图必须写在paintEvent
Qt常用事件
鼠标事件:
- mousePressEvent 鼠标按下
一般在这里记录起点,开启拖拽、橡皮筋绘图;
- mouseMoveEvent 鼠标移动
默认按住鼠标才会触发;
想鼠标不按触发,在构造函数里面写 setMouseTracking();
更新坐标,调用update()触发重绘;
- mouseReleaseEvent 鼠标松开
拖拽结束,保存最终图形,结束标记;
- mouseDoubleClickEvent 鼠标双击
区分单击双击,注意不要和单击冲突;
键盘事件:
- keyPressEvent 按键按下
焦点在哪,键盘事件给谁;
子控件抢焦点后主窗口收不到;
Tab默认切换焦点,需要在event()拦截;
- keyReleaseEvent 按键松开
少用,多用于组合键检测;
绘图事件:
- paintEvent 绘图事件
所有自定义绘图必须写在这里;
不能在mouseMove里直接new QPainter绘图;
调用update()触发,不要手动调用paintEvent;
大小、位置变化:
- resizeEvent 控件大小改变
窗口拉伸、控件缩放时自动触发,可重新布局;
- moveEvent 控件位置移动
控件坐标改变触发;
窗口相关:
- closeEvent 窗口关闭
用e->ignore()阻止关闭;e->accept()允许关闭;
- showEvent 窗口显示
窗口第一次弹出/从隐藏到显示触发;
- hideEvent 窗口隐藏
窗口被隐藏时触发;
焦点事件:
- focusInEvent 获取键盘焦点
控件拿到焦点,可以接收键盘输入;
- focusOutEvent 失去键盘焦点
焦点被别的控件抢走后触发;
鼠标进出控件:
- enterEvent 鼠标进入控件区域
鼠标一进控件就触发,不需要点击;
- leaveEvent 鼠标离开控件区域
鼠标移出控件触发,常用于样式变化;
定时器事件:
两种定时器方案:
① 重写timerEvent(基础原生,startTimer启动)
- timerEvent 定时器事件
基础定时器,区别QTimer;
需要startTimer开启;
② QTimer类(信号槽方式,项目更常用);
拖放事件:
Qt拖放(Drag & Drop):支持用鼠标拖拽数据,从一个控件拖动到另一个控件,实现数据传输的交互机制;
分为两个阶段:拖动发起(Drag)、放下接收(Drop);
- dragEnterEvent
当拖拽的东西刚进入目标控件区域时触发;
必须在这里判断能不能接收该类型数据;
调用event->acceptProposedAction()允许继续拖入,否则无法放下;
- dragMoveEvent
拖拽过程中,鼠标在目标控件内移动持续触发;
可控制鼠标样式、限定可放置区域;
- dragLeaveEvent
拖拽的鼠标移出目标控件区域触发。
- dropEvent
鼠标松开,完成放下的那一刻触发(核心,在这里取出拖拽的数据,完成业务处理);
发起拖拽(拖动源):一般在mousePressEvent或mouseMoveEvent中构造QDrag对象,启动拖拽。
基础使用步骤:
目标控件(接收拖拽的控件):
① 开启属性:setAcceptDrops(true); 【必须写!不写接收不到拖放事件】
② 重写4个拖放事件:dragEnterEvent、dragMoveEvent、dragLeaveEvent、dropEvent
拖拽源(被拖动的控件)在鼠标事件里创建QDrag,封装要传递的数据(QMimeData),调用
exec()启动拖拽。
注:
① 普通事件和closeEvent accept()/ignore()区别:
普通事件(鼠标、键盘等,控制事件冒泡传递)
- ignore():事件继续向上传递,交给父控件继续处理
- accept():事件到此处理完毕,不再继续往上传递(默认)
closeEvent(特例,专门控制窗口关闭是否放行,是特殊用法)
- accept():同意本次关闭,继续执行窗口关闭流程
- ignore():拒绝本次关闭,直接终止关闭,窗口不关闭
② 重写专属事件函数和重写event()使用时机:
- 有专属事件虚函数,就重写xxxEvent;
- 没有专属函数 / 要拦截Tab / 需要在分发前统一处理,才重写event();
③ 区分【事件】和【信号槽】:
信号槽是对象通信机制,不属于事件处理的5种方式;
事件是系统或应用状态变化的通知,触发源不同;