目录
1. 事件介绍
2. 事件的处理
示例1:处理鼠标进入和离开
示例2:当鼠标点击时,获取对应的坐标值;
3. 按键事件
3.1 单个按键
3.2 组合按键
4. 鼠标事件
4.1 鼠标单击事件
4.2 鼠标释放事件
4.3 鼠标双击事件
4.4 鼠标移动事件
4.5 滚轮事件
5. 定时器
5.1 QTimerEvent 类
5.2 QTimer 类
5.3 获取系统日期及时间
6. 事件分发器
6.1 概述
6.2 事件分发器工作原理
编辑7. 事件过滤器
1. 事件介绍
前面我们学习了信号槽,它是用户进行的各种操作,可能会产生出信号,程序员给某个信号指定槽函数,当信号触发时,就能够自动的执行到对应的槽函数。事件和信号槽非常类似,用户进行的各种操作,也会产生事件。程序员同样可以给事件关联上处理函数(处理的逻辑),当事件触发的时候,就能够执行到对应的代码。
事件本身是操作系统提供的机制,Qt 也同样把操作系统事件机制进行了封装,拿到了Qt 中,但是由于事件对应的代码编写起来不是很方便,所以 Qt 对于事件机制又进行了进一步的封装,就得到了信号槽。信号槽就是对于事件的进一步封装,事件是信号槽的底层机制。
事件是应用程序内部或者外部产生的事情或者动作的统称。在 Qt 中使用⼀个对象来表示一个事件。所有的 Qt 事件均继承于抽象类 QEvent。事件是由系统或者 Qt 平台本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件是在用户操作时发出,如键盘事件、鼠标事件等,另一些事件则是由系统本身自动发出,如定时器事件。常见的 Qt 事件如下:
常见事件描述:
事件名称 | 描述 |
鼠标事件 | 鼠标左键、鼠标右键、鼠标滚轮,鼠标的移动,鼠标按键的按下和松开 |
键盘事件 | 按键类型、按键按下、按键松开 |
定时器事件 | 定时时间到达 |
进入离开事件 | 鼠标的进入和离开 |
滚轮事件 | 鼠标滚轮滚动 |
绘屏事件 | 重绘屏幕的某些部分 |
显示隐藏事件 | 窗口的显示和隐藏 |
移动事件 | 窗口位置的变化 |
窗口事件 | 是否为当前窗口 |
大小改变事件 | 窗口大小改变 |
焦点事件 | 键盘焦点移动 |
拖拽事件 | 用鼠标进行拖拽 |
2. 事件的处理
事件处理一般常用的方法为:重写相关的 Event 函数。
在 Qt 中,几乎所有的 Event 函数都是虚函数,所以可以重新实现。如:在实现鼠标的进入和离开事件时,直接重新实现 enterEvent() 和 leaveEvent() 即可。enterEvent() 和 leaveEvent() 函数原型如下:
示例1:处理鼠标进入和离开
1、新建 Qt 项目,基类选择 QWidget,同时勾选 UI 界面文件,如下图示;
2、设计 UI 文件,如下图示;
3、这里需要创建 QLabel 的子类,重写 enterEvent 和 leaveEvent,在项目中新添加一个类:MyLabel;
- 先选中项目名称 enterEventAndleaveEvent,点击鼠标右键,选择 add new ... ,弹出如下对话框:
4、选择:Choose .... ,弹出如下界面:
5、此时项目中会新添加以下两个文件:
6、要想重写父类的函数,就需要确保你这边写的函数名字和函数的参数列表都完全一致(形参名无所谓),为了防止单词拼写错误,我们可以在帮助文档中查找对应的内容;
7、点击 "显示" 之后,出现如下内容:
8、复制 enterEvent() 和 leaveEvent() ,粘贴在项目文件 "mylabel.h" 中;
9、重写 enterEvent() 和 leaveEvent() 方法;
10、上述代码,虽然重写了这俩函数,但是还是有点问题。运行程序,我们会发现鼠标进入和移出界面上没有一点反应。这是因为当前在界面上创建的这个 label 其实是 QLabel,不是咋们自己写的 Label,咋们必须要确保界面上的这个 label 是一个咋们自己定义的 Label 类的实例,程序才会执行到。解决办法也很简单,在 UI 文件中选中 Label,右键 ------> 提升为...
11、当点击 "提升为... " 之后,弹出如下对话框:
12、修改基类:
13、执行效果如下:当鼠标进入设计好的标签之后,就会在应用程序输出栏中打印:鼠标进入
示例2:当鼠标点击时,获取对应的坐标值;
- 鼠标左键点击时,打印对应的坐标值,鼠标右键点击时,打印基于屏幕的坐标
mousePressEvent 这个函数,按下左键、右键、滚轮都能触发,有的鼠标还带有前进后退侧键,也是可以触发的;但是还有的鼠标,有着更多的按键,这就不一定能触发了~
1、在上述示例的基础上,在 mylabel.h 中声明 mousePressEvent() 方法;
2、在 mylabel.cpp 中重写 mousePressEvent() 方法;
实现效果如下:
3. 按键事件
Qt 中的按键事件是通过 QKeyEvent 类来实现的。当键盘上的按键被按下或者被释放时,键盘事件便会触发。在帮助文档中查找 QKeyEvent 类如下:
查找按键事件中所有的按键类型:在帮助文档中输入:Qt::Key,如下图:
3.1 单个按键
示例:当某个按键被按下时,输出:某个按键被按下了;
1、新建项目,在头文件 "widget.h" 中声明虚函数 keyPressEvent():
2、在 "widget.cpp" 文件中重写 keyPressEvent() 虚函数:
实现效果如下:
3.2 组合按键
在 Qt 助手中搜索:Qt::KeyboardModifier,如下图示:
Qt::KeyboardModifier 中定义了在处理键盘事件时对应的修改键。在 Qt 中,键盘事件可以与修改键一起使用,以实现一些复杂的交互操作。KeyboardModifier 中修改键的具体描述如下:
Qt::NoModifier | 无修改键 |
Qt::ShiftModifier | Shift 键 |
Qt::ControlModifier | Ctrl 键 |
Qt::AltModifier | Alt 键 |
Qt::MetaModifier | Meta键(在Windows上指Windows键,在macOS上指Command键) |
Qt::KeypadModifier | 使用键盘上的数字键盘进行输⼊时,Num Lock键处于打开状态 |
Qt::GroupSwitchModifier | 用于在输入法 组之间 切换 |
示例:
实现效果如下:
4. 鼠标事件
在 Qt 中,鼠标事件是用 QMouseEvent 类来实现的。当在窗口中按下鼠标或者移动鼠标时,都会产生鼠标事件。
利用 QMouseEvent 类可以获取鼠标的哪个键被按下了以及鼠标的当前位置等信息。在 Qt 帮助文档中查找 QMouseEvent类 如下图示:
4.1 鼠标单击事件
在 Qt 中,鼠标按下是通过虚函数 mousePressEvent() 来捕获的。mousePressEvent() 函数原型如下:
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event)
鼠标左右键及滚轮的表示如下:
- Qt::LeftButton 鼠标左键
- Qt::RightButton 鼠标右键
- Qt::MidButton 鼠标滚轮
示例:鼠标左键 + 右键 + 滚轮
1、在 "widget.h" 头文件中声明鼠标按下事件;
2、在 "widget.cpp" 文件中重新实现 mousePressEvent() 函数;
实现效果如下:
4.2 鼠标释放事件
鼠标释放事件是通过虚函数 mouseReleaseEvent() 来捕获的。mouseReleaseEvent() 函数原型如下:
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event)
示例:鼠标左键释放
1、在 "widget.h" 头文件中声明鼠标释放事件;
2、在 "widget.cpp" 文件中重新实现 mouseReleaseEvent() 函数;
实现效果如下:
4.3 鼠标双击事件
鼠标双击事件是通过虚函数:mouseDoubleClickEvent() 来实现的。mouseDoubleClickEvent() 函数原型如下:
[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event)
示例:鼠标左键双击
1、在 "widget.h" 头文件中声明鼠标双击事件;
2、在 "widget.cpp" 文件中重新实现 mouseDoubleClickEvent() 函数;
实现效果如下:
4.4 鼠标移动事件
鼠标移动事件是通过虚函数:mouseMoveEvent() 来实现的。同时为了实时捕获鼠标位置信息,需要通过函数 setMouseTracking() 来追踪鼠标的位置。
mouseMoveEvent()函数原型如下:
- [virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event)
setMouseTracking()函数原型如下:
- void setMouseTracking(bool enable)
说明:
鼠标移动不同于鼠标按下。随便移动一下鼠标,就会产生出大量的鼠标移动事件。当你进行捕获事件的时候,尤其是在这里在进行一些复杂逻辑的时候,程序负担就很重,很容易产生卡顿之类的情况。
Qt 为了保证程序的流畅性,默认情况下不会对鼠标移动进行追踪,鼠标移动的时候不会调用 mouseMoveEvent。除非显示告诉 Qt 要追踪鼠标位置。
setMouseTracking() 函数默认是 false,需要设置为 true,才能实时捕获鼠标位置信息。否则只有当鼠标按下时才能捕获其位置信息。
示例:
1、在 "widget.h" 头文件中声明鼠标移动事件;
2、在 "widget.cpp" 文件中重新实现 mouseMoveEvent() 函数;
执行效果:
4.5 滚轮事件
在 Qt 中,鼠标滚轮事件是通过 QWheelEvent 类来实现的。滚轮滑动的距离可以通过 delta() 函数获取。delta() 函数原型如下:
int QGraphicsSceneWheelEvent::delta() const
其中返回值代表滚轮滑动的距离。正数表示滚轮相对于用户向前滑动,负数表示滚轮相对于用户向后滑动。
示例:
1、在 "widget.h" 头文件中声明鼠标滚轮事件;
2、在 "widget.cpp" 文件中重新实现 wheelEvent() 函数;
执行效果:
5. 定时器
Qt 中在进行窗口程序的处理过程中,经常要周期性的执行某些操作,或者制作一些动画效果,使用定时器就可以实现。所谓定时器就是在间隔一定时间后,去执行某⼀个任务。定时器在很多场景下都会使用到,如弹窗自动关闭之类的功能等。
Qt中的定时器分为 QTimerEvent 和 QTimer 这2个类:
- QTimerEvent类 用来描述一个定时器事件。在使用时需要通过 startTimer() 函数来开启一个定时器,这个函数需要输入一个以毫秒为单位的整数作为参数来表明设定的时间,它返回的整型值代表这个定时器。当定时器溢出时(即定时时间到达)就可以在 timerEvent() 函数中获取该定时器的编号来进行相关操作。
- QTimer类 来实现一个定时器,它提供了更高层次的编程接口,如:可以使用信号和槽,还可以设置只运行一次的定时器。
5.1 QTimerEvent 类
示例1:在UI界面上放置两个 Label 控件,⼀个让其1秒数字累加⼀次,⼀个让其2秒数字累加⼀次。
1、新建项目,在UI界面文件放置两个 Label 控件;
2、在 "widget.h" 头文件中声明 timerEvent() 函数,并定义两个整型变量;
3、在 "widget.cpp" 文件中重写 timerEvent() 函数;
实现效果如下:
5.2 QTimer 类
示例:在UI界面放置一个 Label 标签,两个按钮,分别是 "开始" 和 "停止" ,当点击 "开始" 按钮时,开始每隔1秒计数⼀次,点击 "停止" 按钮时,暂停计数。
1、设计 UI 界面如下:
2、在 "widget.cpp" 文件中实现对应功能;
实现效果如下:
5.3 获取系统日期及时间
在 Qt 中,获取系统的日期及实时时间可以通过 QTimer 类 和 QDateTime类。
QDateTime类 提供了字符串格式的时间。字符串形式的时间输出格式由 toString() 方法中的 format 参数列表决定,可用的参数列表如下:
示例:获取系统日期及实时时间;
1、设计UI界面文件;放置⼀个 Label控件,用来显示日期及时间,放置两个按钮:"开始" 和 "停止" ;
2、在 "widget.h" 头文件中声明更新时间的槽函数;
3、在 "widget.cpp" 文件中实现对应功能;
实现效果如下:
6. 事件分发器
6.1 概述
在 Qt 中,事件分发器(Event Dispatcher) 是⼀个核心概念,用于处理 GUI 应用程序中的事件。事件分发器负责将事件从⼀个对象传递到另⼀个对象,直到事件被处理或被取消。每个继承自 QObject类 或 QObject类 本身都可以在本类中重写 bool event(QEvent *e) 函数,来实现相关事件的捕获和拦截。
6.2 事件分发器工作原理
在 Qt 中,我们发送的事件都是传给了 QObject 对象,更具体点是传给了 QObject 对象的 event() 函数。所有的事件都会进入到这个函数里面,那么我们处理事件就要重写这个 event() 函数。event() 函数本身不会去处理事件,而是根据 事件类型(type值)调用不同的事件处理函数。事件分发器就是工作在应用程序向下分发事件的过程中,如下图:
如上图,事件分发器用于分发事件。在此过程中,事件分发器也可以做拦截操作。事件分发器主要是通过 bool event(QEvent *e) 函数来实现。其返回值为布尔类型,若为 ture,代表拦截,不向下分发。
Qt 中的事件是封装在 QEvent类 中,在 Qt 助手中输入 QEvent 可以查看其所包括的事件类型,如下图示:
示例:
1、在 "widget.h" 头文件中声明 鼠标点击事件 和 事件分发器;如下图示:
2、在 "widget.cpp" 文件中实现 鼠标点击事件 和 拦截事件;
执行结果如下:
7. 事件过滤器
在 Qt 中,一个对象可能经常要查看或拦截另外一个对象的事件,如对话框想要拦截按键事件,不让别的组件接收到,或者修改按键的默认值等。通过前面学习过的知识,我们已经知道,Qt 创建了 QEvent事件 对象之后,会调用QObject 的 event()函数 处理事件的分发。显然,我们可以在 event()函数 中实现拦截的操作。由于 event()函数是 protected 的,因此,需要继承已有类。如果组件很多,就需要重写很多个event()函数。这当然相当麻烦,更不用说重写 event()函数还得小心一堆问题。好在 Qt 提供了另外一种机制来达到这一目的:事件过滤器。
事件过滤器是在应用程序分发到 event事件分发器 之前,再做⼀次更高级的拦截。如下图示:
事件过滤器的一般使用步骤:
- 安装事件过滤器;
- 重写事件过滤器函数:eventfilter() 。
示例:
1、新建 Qt 项目,基类选择 QWidget,同时勾选 UI 界面文件,如下图示;
2、设计 UI 文件,如下图示;
3、在项目新添加⼀个类:MyLabel;
先选中项目名称 QEvent,点击鼠标右键,选择 add new ... ,弹出如下对话框:
4、选择:Choose .... ,弹出如下界面:
5、此时项目中会新添加以下两个文件:
6、在 UI 文件中选中 Label,右键 ------> 提升为...
7、当点击 "提升为... " 之后,弹出如下对话框:
8、在 "mylabel.h" 中声明 鼠标点击事件 和 事件分发器;
9、在 "mylabel.cpp" 文件中实现鼠标点击事件和事件分发器;
10、在 "widget.h" 头文件中声明事件过滤器函数;
11、在 "widget.cpp" 文件中实现事件过滤器的两个步骤;
执行结果如下所示: