目录
[1. 事件的介绍](#1. 事件的介绍)
[2. 事件的处理](#2. 事件的处理)
[2.1 重写鼠标进入和鼠标离开](#2.1 重写鼠标进入和鼠标离开)
[2.2 鼠标按下事件](#2.2 鼠标按下事件)
[2.3 鼠标释放事件](#2.3 鼠标释放事件)
[2.4 鼠标双击事件](#2.4 鼠标双击事件)
[2.5 鼠标移动事件](#2.5 鼠标移动事件)
[2.6 鼠标滚轮的滚动动作](#2.6 鼠标滚轮的滚动动作)
[2.7 按键事件](#2.7 按键事件)
[2.8 定时器](#2.8 定时器)
[2.9 窗口移动和大小改变事件](#2.9 窗口移动和大小改变事件)
[3. 总结](#3. 总结)
虽然 Qt 是跨平台的 C++ 开发框架,Qt 的很多能力其实是操作系统提供的,只不过 Qt 封装了系统的API。程是运行在操作系统上的,需要系统给我们提供支撑。
- 事件
- 文件操作
- 多线程编程
- 网络编程
- 多媒体(音频、视频)
1. 事件的介绍
信号槽,用户进行的各种操作,就可能会产生出信号,可以给某个信号指定槽函数,当信号触发时,就能够自动执行到对应的槽函数
事件和信号槽非常相似,用户进行的各种操作,也会产生事件。程序员同样可以给事件关联上处理函数(处理的逻辑),当事件触发的时候,就能够执行到对应的代码。
事件本身是操作系统提供的机制。Qt 也同样把操作系统事件机制进行了封装,拿到了 Qt 中。但是由于事件对应的代码编写起来不是很方便。Qt 对于事件机制又进行了进一步的封装,就得到了信号槽。
信号槽和事件的关系:
信号槽就是对于事件的进一步封装,事件是信号槽的底层机制。
实际 Qt 开发程序过程中,绝大部分和用户之间进行的交互都是通过 "信号槽"来完成的。有些特殊情况下,信号槽不一定能搞定(某个用户的动作行为,Qt 没有提供对应的信号......此时就需要通过重写事件处理函数的形式,来手动处理事件的响应逻辑)
开放事件机制,我们就可以根据实际的需要进行更深度的定制化操作了。
用户进行了很多操作,就会产生很多的事件(当然也会产生很多的信号)

不同的场景下,要关注的点是不一样的,这些事件的子类中就会包含一些对应的不同的属性。
常见事件描述:
|--------|---------------------------------|
| 事件名称 | 描述 |
| ⿏标事件 | ⿏标左键、⿏标右键、⿏标滚轮,⿏标的移动,⿏标按键的按下和松开 |
| 键盘事件 | 按键类型、按键按下、按键松开 |
| 定时器事件 | 定时时间到达 |
| 进⼊离开事件 | ⿏标的进⼊和离开 |
| 滚轮事件 | ⿏标滚轮滚动 |
| 绘屏事件 | 重绘屏幕的某些部分 |
| 显⽰隐藏事件 | 窗⼝的显示和隐藏 |
| 移动事件 | 窗⼝位置的变化 |
| 窗⼝事件 | 是否为当前窗⼝ |
| 大小改变事件 | 窗⼝大小改变 |
| 焦点事件 | 键盘焦点移动 |
| 拖拽事件 | ⽤⿏标进⾏拖拽 |
2. 事件的处理
让一段代码和某个事件关联起来,当事件触发的时候,就能指定到这段代码,之前是通过 connect 来完成上述关联的,对于事件来说,还不太一样:让当前的类,重写某个事件处理函数。用到的是 "多态"机制,创建子类,继承自 Qt 已有的类,在子类中重写父类的书简处理函数,后续事件触发的过程中,就会通过多态这样的机制,执行到我们自己写的子类的函数中。
2.1 重写鼠标进入和鼠标离开
enterEvent:

leaveEvent:



这里需要创建 QLabel 的子类,重写 enterEvent 和 leaveEvent





要想重写父类的函数,就需要确保你这边写的函数名字和函数的参数列表都完全一致(形参名无所谓)

上述代码,虽然重写了这两函数,但是还有点问题:当前在界面上创建的这个 label 其实是 QLabel,不是我们自己写的 Label,还是父类,不是子类,自然执行不到我们写的那个方法

必须要确保界面上的这个 label 是一个我们自己定义的 label 实例,才会执行到。

一定要确保你的类名以及头文件的名字和上述自定义的类名头文件等等内容都要匹配上!!

点击添加:

此时,类型变为 Label 类型:

通过 "提升为" 这样的方式,就可以把 Qt Designer 中拖上去的控件的类型转换为自定义的控件类型
再次运行:

此时说明当前的 enterEvent 和 leaveEvent 这两个事件就被我们给捕获到了。
2.2 鼠标按下事件
在 Qt 中,⿏标按下是通过虚函数 mousePressEvent() 来捕获的。mousePressEvent() 函数原型如下:
virtual protected void QWidget::mousePressEvent(QMouseEvent *event)
⿏标左右键及滚的表⽰如下:
- Qt::LeftButton ⿏标左键
- Qt::RightButton ⿏标右键
- Qt::MidButton ⿏标滚轮






点击添加:

点击提升:

重写该函数:



运行:
鼠标的左键、右键、滚轮、侧键点击都可以获取到


运行:

区分按下鼠标的是左键还是右键:

运行:

2.3 鼠标释放事件
⿏标释放事件是通过虚函数 mouseReleaseEvent() 来捕获的。mouseReleaseEvent() 函数原型如下:
virtual protected void QWidget::mouseReleaseEvent(QMouseEvent *event)


运行:

clicked 这样的信号,就相当于是一次鼠标按下事件和一次鼠标释放事件。
2.4 鼠标双击事件
⿏标双击事件是通过虚函数:mouseDoubleClickEvent() 来实现的。mouseDoubleClickEvent()函数原型如下:
virtual protected void QWidget::mouseDoubleClickEvent(QMouseEvent *event)



运行:
双击左键

第二次按下的时候,才能识别到是双击。比如有的程序,可能是的单击有一些墨迹,双击又有另一些逻辑,如果我们没注意,可能双击操作就能触发单击的逻辑,可能就有 bug。
2.5 鼠标移动事件
以上重写鼠标事件的操作,都是在自定义的 Label 中完成的,此时鼠标只有在 Label 范围内进行动作的时候,才能捕获到。也可以把这些操作直接放到 Widget(QWidget 子类)来完成。这样的话,鼠标在整个窗口中进行的各种动作都能获取到了。

鼠标移动不同于鼠标按下,随便移动一下鼠标,就会产生大量的鼠标移动事件。当你进行捕获事件的时候,尤其是在这里在进行一些复杂逻辑的时候,程序负担就很重,很容易产生卡顿之类的情况。
Qt 为了保证程序的流畅性,默认情况下不会对鼠标移动进行追踪。鼠标移动的时候不会调用 mouseMoveEvent,除非显示告诉 Qt 就要追踪鼠标位置。

运行:

2.6 鼠标滚轮的滚动动作
在 Qt 中,⿏标滚轮事件是通过 QWheelEvent 类来实现的。滚轮滑动的距离可以通过 delta() 函数获取。delta() 函数原型如下:
int QGraphicsSceneWheelEvent::delta() const
其中返回值代表滚轮滑动的距离。正数表示滚轮相对于用户向前滑动,负数表示滚轮相对于用户向后滑动。


运行:

2.7 按键事件
Qt 中的按键事件是通过QKeyEvent 类来实现的。当键盘上的按键被按下或者被释放时,键盘事件便会触发。
之前的 QShortCut 这是信号槽机制封装过的,获取键盘按键的方式。
站在更底层的角度,也可以通过事件获取到当前用户键盘按下的情况。


运行:

此时只有按下 a ,才有反应,其余的键都没反应。
按下两个键,将这些搭配组合键的功能键,单独拎出来了,modifiers(修饰符)

运行,按下 ctrl + a:

添加快捷键更好的办法是 QShortCut。
2.8 定时器
Qt 中在进行窗⼝程序的处理过程中,经常要周期性的执⾏某些操作,或者制作⼀些动画效果,使⽤定时器就可以实现。所谓定时器就是在间隔⼀定时间后,去执⾏某⼀个任务。定时器在很多场景下都会使⽤到,如弹窗⾃动关闭之类的功能等。
Qt中的定时器分为 QTimerEvent 和 QTimer 这2个类。
- QTimerEvent类 ⽤来描述⼀个定时器事件。在使⽤时需要通过 startTimer() 函数来开启⼀个定时器,这个函数需要输⼊⼀个以毫秒为单位的整数作为参数来表明设定的时间,它返回的整型值代表这个定时器。当定时器溢出时(即定时时间到达)就可以在 timerEvent() 函数中获取该定时器的编号来进⾏相关操作。
startTimer 启动定时器
killTimer 关闭定时器 - QTimer类 来实现⼀个定时器,它提供了更⾼层次的编程接⼝,如:可以使⽤信号和槽,还可以设置只运⾏⼀次的定时器。



运行:

使用 timerEvent 比 QTimer 还是要复杂一点。手动管理timerId,还需要区分整个函数调用是哪个 timer 引起的。实际开发中,使用 QTimer 即可。
2.9 窗口移动和大小改变事件
- moveEvent 窗口移动时触发的事件


- resizeEvent 窗口大小改变时触发的事件




运行:

3. 总结
实际开发中,优先使用信号槽的方式来完成用户交互的功能