Qt:事件
目录
处理事件
鼠标事件
键盘事件
定时器事件
窗口事件
虽然 Qt 是跨平台的 C++ 开发框架,Qt 的很多能力其实是操作系统提供的
只不过 Qt 封装了系统的 API
事件
前面学习过信号槽:
用户进行的各种操作,就可能会产生出信号,可以给某个信号指定槽函数,当信号触发时,就能够自动的执行到对应的槽函数
事件和信号槽非常类似:
用户进行的各种操作,也会产生事件,程序员同样可以给事件关联上处理函数(处理的逻辑),当事件触发的时候,就能够执行到对应的代码
事件本身是操作系统提供的机制,Qt 也同样把操作系统事件机制进行了封装,拿到了 Qt 中
但是由于事件对应的代码编写起来不是很方便,所以 Qt 对于事件机制又进行了进一步的封装,就得到了信号槽
信号槽就是对于事件的进一步封装,事件是信号槽的底层机制
实际 Qt 开发程序过程中,绝大部分和用户之间进行的交互都是通过"信号槽"来完成的
有些特殊情况下,信号槽不一定能搞定(某个用户的动作行为,Qt 没有提供对应的信号)
此时就需要通过重写事件处理函数的形式,来手动处理事件的啊应逻辑
开发事件机制给咱们程序员,咱们就可以根据实际的需要进行更深度的定制化 diy 操作了
用户进行了很多操作,就会产生很多的事件(当然也会产生很多的信号)
下面就是开发中比较典型的事件:
不同场景下,要关注的点是不一样,这些事件的子类中就会包含一些对应的不同的属性
处理事件
让一段代码和某个事件关联起来,当事件触发的时候,就能指定到这段代码
之前信号槽这里通过 connect 来完成上述关联的
对于事件来说,还不太一样:
让当前的类,重写某个事件处理函数,这里用到的是"多态”机制
创建子类, 继承自 Qt 已有的类,在子类中重写父类的事件处理函数
后续事件触发过程中,就会通过多态这样的机制,执行到咱们自己写的子类的函数中
鼠标事件
下面都是创建 QWidget 的,当然也可以创建 QMainWindow,因为用不到工具栏,所以选择的 QWidget
鼠标进入和鼠标离开事件
下面使用上述方式,处理一下鼠标进入(enterEvent)和鼠标离开(leaveEvent)事件:
enterEvent 和 leaveEvent 函数都是虚函数,所以可以被子类重写:
图形化界面的方式创建一个 Label,鼠标进入 Label 时提示 enterEvent,离开时提示 leaveEvent:
为了能清楚看到 Label 的边框,将边框选为 Box
效果为:
这里需要创建 QLabel 的子类,重写 enterEvent 和 leaveEvent:
类名就叫 Label,父类叫 QLabel:
按照以往的习惯,创建的 Label 类需要有一个父控件,所以在 label.h 中添加:
label.cpp 中添加:
接着在 label.h 中声明两个需要重写的函数:
注意:
要想重写父类的函数,就需要确保你这边写的函数名字和函数的参数列表都完全一致 (形参名无所谓),谨防单词拼写错误
label.cpp 实现:
(void)event 是为了消除警告,因为暂时还没用到 event 这个形参
此时运行程序,鼠标进入和移出 label 时并没有执行上述逻辑,因为:
当前在界面上创建的这个 label 其实是 QLabel,不是咱们自己写的 Label
必须要确保界面上的这个 label 是一个咱们自己定义的 Label 类的实例,才会执行到
右键图形化界面的 label,点击提升为:
输入提升的类名 Label,点击添加,再点击提升:
一定要确保你的类名以及头文件的名字,和上述自定义的类名头文件都匹配
此时右边对象树上面就是 Label 了,没提升前显示是 QLabel:
通过"提升为"这样的方式,就可以把 Qt Designer 中拖上去的控件的类型转换成自定义的控件类型
此时再运行程序,鼠标移入移出 Label 时,就会打印下面的内容了:
此时就说明当前的 enterEvent 和 leaveEvent 这两个事件就被咱们给捕获到了
通过事件获取到鼠标点击的位置
与上面的操作一样,创建一个 Label,再创建一个 Label 类,父类定为 QLabel,并对生成的 Label 的构造函数做一个调整,添加一个 QWidget* 的参数,以便于能够指定父窗口
接着再右键 Label 点击提升为,输入类名后点击提升,此时就完成了提升操作:
下面就是 mousePressEvent 函数,当鼠标按下时就会触发这个函数调用:
左键、右键、滚轮、侧键都能触发
在 label.cpp 中实现 mousePressEvent 函数:
此时鼠标在 Label 范围内点击就会打印 鼠标点击的位置坐标:
上述是以 Label 左上角位置为原点的
下面则是以屏幕左上角为原点获取坐标:
也可以加上下面的代码,来判断按下的是左键还是右键:
通过事件获取到鼠标点击释放按键
与上面的鼠标点击事件一样,下面是重写的 mouseReleaseEvent 函数:
此时就能做到获取鼠标点击释放按键:
clicked 这样的信号,就相当于是一次鼠标按下事件和一次鼠标释放事件
通过事件获取到鼠标双击按键
重写的 mouseDoubleEvent 函数如下:
鼠标第二次按下的时候,才能够识别到是"双击:
注意:
有的程序,可能是单击有一些逻辑,双击有另一些逻辑,如果我们没注意,可能双击操作就能触发单击的逻辑,可能就有 bug
通过事件获取到鼠标移动
刚才重写鼠标事件的操作,都是在自定义的 Labe| 中完成的,此时鼠标只有在 Label 范围内进行动作的时候,才能捕获到
也可以把这些操作直接放到 Widget (QWidqet 子类) 来完成,这样的话,鼠标在整个窗口中进行的各种动作都能获取到了
所以直接在 widget.h widget.cpp 中重写 mouseMoveEvent 函数:
此时运行程序并没有效果
鼠标移动不同于鼠标按下
随便移动一下鼠标,就会产生出大量的鼠标移动事件,当你进行捕获事件的时候,尤其是在这里再进行一些复杂逻辑的时候,程序负担就很重,很容易产生卡顿之类的情况
Qt 为了保证程序的流畅性,默认情况下不会对鼠标移动进行追踪,鼠标移动的时候不会调用mouseMoveEvent,除非显式告诉 Qt 就要追踪鼠标位置
所以需要在 Widget 的构造函数中设置:
此时稍微一动鼠标,就会一直打印,如果移动的比较快,就会明显出现打印卡顿的情况:
通过事件获取到鼠标滚轮的滚动动作
在 QWheelEvent 中 通过 delta() 获取到这次事件鼠标滚轮滚动了多远
同样在 widget.h widget.cpp 中重写 wheelEvent 函数:
滚轮往下滚动就打印 -120,往上滚动就打印 120:
我们也可以在 Widget 类中新增 int total,初始化为0,就能在 wheelEvent 函数中实现统计滚轮滚动的距离了:
效果为:
就可以根据滚轮滚动的操作实现特定的功能,比如可以通过滚轮去缩放字体大小,可以把滚轮滚动的距离映射到具体的数值上,就可以实现类似的效果了
键盘事件
处理键盘按键事件
我们前面学习过的 QShortCut,这是信号槽机制封装过的,获取键盘按键的方式
站在更底层的角度,也可以通过事件获取到当前用户键盘按下的情况
依旧是在 widget.h widget.cpp 中重写 keyPressEvent 函数:
按下 ABCDEF 的效果为,可以发现每一个按钮都对应一个数字:
如果想得知是否按下了具体的某一个键,以 A 为例,代码改为:
也有些场景是组合键 Ctrl + A :
定时器事件
前面学习了QTimer 实现定时器功能
在 QTimer 背后是 QTimerEvent 定时器事件进行支撑的
QObject 提供了一个 timerEvent 这个函数,可以通过定时器,周期性的触发一些操作
里面需要搭配 startTimer 启动定时器,killTimer 关闭定时器 使用
下面通过图形化的方式,拖动一个 LCD Number,初始值改为 10 :
此处 startTimer 的返回值 timerld 类似于 Linux 中的文件描述符,起到的是身份标识的效果
因为后面程序可能还会用到 timerld,所以在 widget.h 中的构造函数定义为函数成员:
在 widget.h widget.cpp 中重写 timerEvent 函数:
运行程序,每隔一秒-1,直到0就停止:
使用 timerEvent 比 QTimer 还是要更复杂一点,手动管理 timerld,还需要区分这次函数调用是哪个 timer 引起的
后续实际开发中,使用 QTimer 即可
窗口事件
- moveEvent 窗口移动时触发的事件
- resizeEvent 窗口大小改变时触发的事件
moveEvent
QMoveEvent 中有下面两个常用的方法:
resizeEvent
QResizeEvent 中有下面两个常用的方法:
在 widget.h widget.cpp 中重写 moveEvent 和 resizeEvent函数:
如果移动 widget 窗口或调整 widget 窗口大小,下面就会打印:
Qt:事件相关知识到此结束