目录
【前情提示】
信号槽:
- 用户进行的各种操作,就可能会产生出信号.可以给某个信号指定槽函数. 当信号触发时,就能够自动的执行到对应的槽函数.
事件非常类似:
- 用户进行的各种操作,也会产生事件.程序员同样可以给事件关联上处理函数(处理的逻辑),当事件触发的时候,就能够执行到对应的代码。
事件本身是操作系统提供的机制.Qt 也同样把操作系统事件机制进行了封装.但是由于事件对应的代码编写起来不是很方便.Qt 对于事件机制又进行了进一步的封装,就得到了信号槽。
⭐️⭐️ 信号槽就是对于事件的进一步封装;事件是信号槽的底层机制.
实际 Qt 开发程序过程中,绝大部分和用户之问进行的交互都是通过“信号槽”来完成的。
有些特殊情况下,信号槽不一定能搞定.(某个用户的动作行为,Qt 没有提供对应的信号….)此时就需要通过重写事件处理函数的形式,来手动处理事件的响应逻辑。
(一)事件介绍
事件是应用程序内部或者外部产生的事情或者动作的统称。在Qt中使用⼀个对象来表示⼀个事件。所有的Qt事件均继承于抽象类QEvent。事件是由系统或者Qt平台本⾝在不同的时刻发出的。当用户 按下⿏标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出⼀个相应的事件。⼀些事件是在用户操作时发出,如键盘事件、⿏标事件等,另⼀些事件则是由系统本⾝⾃动发出,如定时器事件。常见的Qt事件如下:
常见事件描述:
事件名称 | 描述 |
⿏标事件 | ⿏标左键、⿏标右键、⿏标滚轮,⿏标的移动,⿏标按键的按下和松开 |
键盘事件 | 按键类型、按键按下、按键松开 |
定时器事件 | 定时时间到达 |
进入离开事件 | ⿏标的进入和离开 |
滚轮事件 | ⿏标滚轮滚动 |
绘屏事件 | 重绘屏幕的某些部分 |
显示隐藏事件 | 窗口的显示和隐藏 |
移动事件 | 窗口位置的变化 |
窗口事件 | 是否为当前窗口 |
大小改变事件 | 窗口大小改变 |
焦点事件 | 键盘焦点移动 |
拖拽事件 | 用⿏标进⾏拖拽 |
(二)事件的处理
事件处理⼀般常用的方法为:重写相关的Event函数。
在Qt中,几乎所有的Event函数都是虚函数,所以可以重新实现。如:在实现⿏标的进⼊和离开事件 时,直接重新实现enterEvent()和 leaveEvent()即可。enterEvent()和 leaveEvent()函数原型如 下 :
示例如下:
- 1、新建Qt项⽬,基类选择QWidget,同时勾选UI界⾯⽂件,如下图示:
- 2、设计UI⽂件,如下图示;
- 3、在项目中新添加⼀个类:MyLabel; 先选中项目名称QEvent,点击⿏标右键,选择addnew...,弹出如下对话框:
- 4、选择:Choose....,弹出如下界面:
- 5、此时项目中会新添加以下两个文件:
- 6、在帮助文档中查找对应的内容;
- 7、点击"显示"之后,出现如下内容:
- 8、复制enterEvent(),粘贴在项目文件mylabel.h中;
- 9、重写enterEvent()⽅法;
- 10、在UI文件中选中Label,右键------>提升为...
- 11、当点击"提升为..."之后,弹出如下对话框:
- 12、修改基类:
- 13、执行效果如下:当鼠标进入设计好的标签之后,就会在应用程序输出栏中打印:鼠标进入(同理鼠标离开也是一样)
示例2:鼠标左键点击时,打印对应的坐标值;鼠标右键点击时,打印基于屏幕的坐标,点击完毕显示释放操作
- 1、在上述示例的基础上,在mylabel.h中声明mousePressEvent()⽅法;
//当⿏标点击时,获取对应的坐标值
void mousePressEvent(QMouseEvent *event);
//鼠标释放操作
void mouseReleaseEvent(QMouseEvent *event);
- 2、在mylabel.cpp中重写mousePressEvent()⽅法;
void mylabel::mousePressEvent(QMouseEvent *event)
{
//按下右键
if(event->button() == Qt::RightButton){
//基于global
qDebug() << "鼠标右键已经按下 x = "<<event->globalX();
qDebug() << "鼠标右键已经按下 x = "<<event->globalY();
}
//按下左键
if(event->button() == Qt::LeftButton){
//基于窗口的坐标
qDebug() << "鼠标左键已经按下 x = "<<event->x();
qDebug() << "鼠标左键已经按下 x = "<<event->y();
}
//按下中间键
if(event->button() == Qt::MidButton){
qDebug() << "鼠标已经按下 x = "<<event->x();
qDebug() << "鼠标已经按下 x = "<<event->y();
}
}
void mylabel::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton){
qDebug() << "左键被释放";
}
else if(event->button() == Qt::RightButton){
qDebug() << "右键被释放";
}else if(event->button() == Qt::MidButton){
qDebug() << "中键被释放";
}
}
实现效果如下:
(三)按键事件
在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框架中,处理鼠标事件是创建交互式图形用户界面的关键部分。鼠标事件包括鼠标点击、双击、移动、滚轮滚动等。这些事件由QMouseEvent类表示,它提供了关于鼠标事件的详细信息,如鼠标位置、按钮状态等。
利⽤QMouseEvent类可以获取⿏标的哪个键被按下了以及⿏标的当前位置等信息。在Qt帮助⽂档中 查找QMouseEvent类如下:
鼠标单击和释放操作代码在上述已经完成,下述就不在展示代码!!
4.1 鼠标单击事件
在Qt中,⿏标按下是通过虚函数mousePressEvent()来捕获的。mousePressEvent()函数原型如 下:
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event)
⿏标左右键及滚的表示如下:
- Qt::LeftButton ⿏标左键
- Qt::RightButton ⿏标右键
- Qt::MidButton ⿏标滚轮
4.2 鼠标释放事件
⿏标释放事件是通过虚函数mouseReleaseEvent()来捕获的。mouseReleaseEvent()函数原型如 下:
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event)
4.3 鼠标双击事件
⿏标双击事件是通过虚函数:mouseDoubleClickEvent()来实现的。mouseDoubleClickEvent() 函数原型如下:
[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event)
void mylabel::mouseDoubleClickEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton){
qDebug() << "左键被双击";
}else if(event->button() == Qt::RightButton){
qDebug() << "右键被双击";
}else if(event->button() == Qt::MidButton){
qDebug() << "中键被双击";
}
}
4.4 鼠标移动事件
⿏标移动事件是通过虚函数:mouseMoveEvent()来实现的。同时为了实时捕获⿏标位置信息,需要 通过函数setMouseTracking()来追踪⿏标的位置。mouseMoveEvent()函数原型如下:
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent*event)
///
//setMouseTracking()函数原型如下:
void setMouseTracking(bool enable)
说明:
setMouseTracking() 函数默认是false,需要设置为true,才能实时捕获⿏标位置信息;
否则只有当⿏标按下时才能捕获其位置信息。
示例如下:
4.5 滚轮事件
在Qt中,⿏标滚轮事件是通过QWheelEvent类来实现的。滚轮滑动的距离可以通过delta()函数获 取。delta()函数原型如下:
int QGraphicsSceneWheelEvent::delta() const
- 其中返回值代表滚轮滑动的距离。正数表示滚轮相对于用户向前滑动,负数表示滚轮相对于用户向后滑动。
void Widget::wheelEvent(QWheelEvent *event)
{
//滚轮事件
static int x = 0;
x += event->delta();
if(event->delta() > 0){
qDebug() << "滚轮向前移动了" << x;
}
else if(event->delta() < 0){
qDebug() << "滚轮向后移动了" << x;
}
}
执行效果如下:
(五) 定时器
Qt 中在进行窗口程序的处理过程中,经常要周期性的执行某些操作,或者制作⼀些动画效果,使用定时器就可以实现。所谓定时器就是在间隔⼀定时间后,去执行某⼀个任务。定时器在很多场景下都会使用到,如弹窗自动关闭之类的功能等。
Qt中的定时器分为QTimerEvent和QTimer这2个类。
- QTimerEvent类⽤来描述⼀个定时器事件。在使⽤时需要通过startTimer()函数来开启⼀个定时器,这个函数需要输⼊⼀个以毫秒为单位的整数作为参数来表明设定的时间,它返回的整型值代表 这个定时器。当定时器溢出时(即定时时间到达)就可以在timerEvent()函数中获取该定时器的编号来进行相关操作。
- QTimer类来实现⼀个定时器,它提供了更⾼层次的编程接⼝,如:可以使⽤信号和槽,还可以设置只运⾏⼀次的定时器。
5.1 QTimerEvent类
示例:在UI界⾯上放置控件,程序启动开始倒计时操作
void Widget::timerEvent(QTimerEvent *event)
{
//先判定触发的是否为想要触发的
if(event->timerId() != this->timerid){
return;
}
int value = ui->lcdNumber->intValue();
if(value <= 0){
//停止定时器
this->killTimer(this->timerid);
return;
}
value -= 1;
ui->lcdNumber->display(value);
}
实现效果如下:
5.2 QTimer类
示例:在UI界面放置⼀个Label标签,两个按钮,分别是"开始"和"停止",当点击"开始"按钮时, 开始每隔1秒计数⼀次,点击"停止"按钮时,暂停计数。
ui->setupUi(this);
QTimer *time = new QTimer(this);
connect(ui->btn1 , &QPushButton::clicked,[=](){
time->start(1000);
});
connect(time , &QTimer::timeout,[=](){
static int num = 1;
ui->label->setText(QString::number (num++));
});
connect(ui->btn2 , &QPushButton::clicked,[=](){
time->stop();
});
实现效果如下:
(六)事件分发器
6.1 概述
在Qt中,事件分发器(EventDispatcher)是⼀个核心概念,⽤于处理GUI应用程序中的事件。事件分发器负责将事件从⼀个对象传递到另⼀个对象,直到事件被处理或被取消。每个继承自QObject类或 QObject类本⾝都可以在本类中重写boolevent(QEvent*e)函数,来实现相关事件的捕获和拦截。
6.2 事件分发器工作原理
在Qt中,我们发送的事件都是传给了QObject对象,更具体点是传给了QObject对象的event()函 数。所有的事件都会进入到这个函数里面,那么我们处理事件就要重写这个event()函数。event()函数本身不会去处理事件,是而根据事件类型(type值)调用不同的事件处理函数。事件分发器就是工作在应用程序向下分发事件的过程中,如下图:
如上图,事件分发器用于分发事件。在此过程中,事件分发器也可以做拦截操作。事件分发器主要是 通过boolevent(QEvent*e)函数来实现。其返回值为布尔类型,若为ture,代表拦截,不向下分发。
Qt 中的事件是封装在QEvent类中,在Qt助手中输入QEvent可以查看其所包括的事件类型,如下图示:
示例:
- 1、在widget.h头文件中声明鼠标点击事件和事件分发器;如下图示:
- 2、在widget.cpp文件中实现鼠标点击事件和拦截事件;
执行结果如下:
(七)事件过滤器
在Qt中,⼀个对象可能经常要查看或拦截另外⼀个对象的事件,如对话框想要拦截按键事件,不让别的组件接收到,或者修改按键的默认值等。通过上面的学习,我们已经知道,Qt创建了QEvent事件对象之后,会调用QObject的event()函数处理事件的分发。显然,我们可以在event()函数中实现拦 截的操作。由于event()函数是protected的,因此,需要继承已有类。如果组件很多,就需要重写很多个event()函数。这当然相当麻烦,更不用说重写event()函数还得小心⼀堆问题。好在Qt提供了另外⼀种机制来达到这⼀目的:事件过滤器。
事件过滤器是在应用程序分发到event事件分发器之前,再做⼀次更高级的拦截。如下图示:
事件过滤器的⼀般使⽤步骤:
- 1、安装事件过滤器;
- 2、重写事件过滤器函数:eventfilter()。
示例如下:
- 1、新建Qt项目,基类选择QWidget,同时勾选UI界面⽂件,如下图;
- 2、设计UI⽂件,如下图;
- 3、在项目新添加⼀个类:MyLabel; 先选中项名目称QEvent,点击⿏标右键,选择addnew...,弹出如下对话框:
- 4、选择:Choose....,弹出如下界面:
- 5、此时项目中会新添加以下两个文件;在UI文件中选中Label,右键------>提升为...
- 6、当点击"提升为..."之后,弹出如下对话框:
- 7、在mylabel.h中声明鼠标点击事件和事件分发器;
- 8、在mylabel.cpp⽂件中实现鼠标点击事件和事件分发器;
- 9、在widget.h头文件中声明事件过滤器函数;
- 10、在widget.cpp⽂件中实现事件过滤器的两个步骤;
执行结果如下:
总结
Qt的事件处理机制非常灵活,通过事件循环、事件对象、信号与槽机制以及事件过滤器,可以高效地处理各种用户交互和系统事件。理解这些概念对于开发复杂的Qt应用程序至关重要。