目录
前置知识
事件概念
常见的事件描述
进入和离开事件
代码示例:
鼠标事件
鼠标点击事件
鼠标释放事件
鼠标双击事件
鼠标滚轮动作
键盘事件
定时器事件
开启定时器事件
窗口相关事件
窗口移动触发事件
窗口大小改变时触发的事件
扩展
前置知识
在前面的介绍中,我们知道信号槽就是:用户进行的各种操作,就可能会产生出信号,可以给某个信号指定槽函数,当信号触发时,就能够自动的执行对应的槽函数。
这里的事件也是类似的,用户进行的各种操作,也会产生事件,程序员同样可以给事件关联上处理函数(处理的逻辑),当事件触发的时候,就能够执行到对应的代码。
事件本身是操作系统提供的机制,Qt也同样把操作系统的事件机制进行了封装,拿到了Qt中,但是由于事件对应的代码编写起来不是很方便,Qt对于事件机制又进行了进一步的封装,就得到了信号槽。
也就是说,信号槽就是对于事件的进一步封装,事件是信号槽的底层机制。
实际上Qt开发过程中,绝大部分和用户之间进行的交互都是通过“信号槽”,来完成的,有些特殊情况下,信号槽不一定能搞定(某个用户的动作行为,Qt中没有提供对应的信号……)此时就需要通过重写事件处理函数的形式,来手动处理事件的响应逻辑。
事件概念
常见的事件描述
事件名称 | 描述 |
---|---|
⿏标事件 | ⿏标左键、⿏标右键、⿏标滚轮,⿏标的移动,⿏标按键的按下和松开 |
键盘事件 | 按键类型、按键按下、按键松开 |
定时器事件 | 定时时间到达 |
进⼊离开事件 | ⿏标的进⼊和离开 |
滚轮事件 | ⿏标滚轮滚动 |
绘屏事件 | 重绘屏幕的某些部分 |
显⽰隐藏事件 | 窗⼝的显⽰和隐藏 |
移动事件 | 窗⼝位置的变化 |
窗⼝事件 | 是否为当前窗⼝ |
⼤⼩改变事件 | 窗⼝⼤⼩改变 |
焦点事件 | 键盘焦点移动 |
拖拽事件 | ⽤⿏标进⾏拖拽 |
进入和离开事件
这里需要创建QLabel的子类,重写enterEvent和leaveEvent。
注意:要想重写父类的函数,就需要确保这里写的函数名字和函数的参数列表一致(形参名无所谓)
这里要注意,我们通过图形化创建的label是QLabel类的,不是我们创建的子类的实例,我们可以将他提升为Label,也就是我们创建的子类。
此时在看这个控件的类型,可以发现已经变为Label
代码示例:
label.h
#ifndef LABEL_H
#define LABEL_H
#include <QLabel>
#include <QWidget>class Label : public QLabel
{Q_OBJECT
public:Label(QWidget* parent);void enterEvent(QEvent* event);void leaveEvent(QEvent* event);
};#endif // LABEL_H
label.cpp
#include "label.h"#include <QDebug>
Label::Label(QWidget* parent):QLabel(parent)
{}void Label::enterEvent(QEvent *event)
{(void)event;qDebug()<<"enterEvent"<<endl;
}void Label::leaveEvent(QEvent *event)
{(void)event;qDebug()<<"leaveEvent"<<endl;
}
效果如下:
鼠标事件
鼠标点击事件
mousePressEvent()用法示例:
void Label::mousePressEvent(QMouseEvent *ev)
{//当前event对象就包含了鼠标点击位置的坐标qDebug()<<ev->x()<<','<<ev->y();qDebug()<<ev->globalX()<<','<<ev->globalY();
}
这个事件处理的是鼠标点击后,显示鼠标点击位置的坐标, 如下所示,返回两个坐标,但是这两个坐标并不相同,因为 ev->globalX()和ev->globalY()返回的是以屏幕左上角为原点的坐标,x()和y()是以label标签左上角为原点的坐标。
这里的mousePressEvent不仅是左键点击触发,右键和滚轮点击也可触发,有的鼠标还带有前进后退侧键也是可以触发的。
我们有时也要区分用户按了什么按钮,因此可以加上一层判断,如下:
if(ev->button()==Qt::LeftButton){qDebug()<<"按下左键";}else if(ev->button()==Qt::RightButton){qDebug()<<"按下右键";}
鼠标释放事件
mouseReleaseEvent()用法如下:
void Label::mouseReleaseEvent(QMouseEvent *ev)
{if(ev->button()==Qt::LeftButton){qDebug()<<"释放左键";}else if(ev->button()==Qt::RightButton){qDebug()<<"释放右键";}
}
鼠标双击事件
如果设置了鼠标单击事件,需要注意逻辑处理,只有第二次按下的时候,才能够识别是双击。双击的同时可能会触发单击事件。
void Label::mouseDoubleClickEvent(QMouseEvent *ev)
{if(ev->button()==Qt::LeftButton){qDebug()<<"双击左键";}else if(ev->button()==Qt::RightButton){qDebug()<<"双击右键";}
}
上述重写鼠标事件的操作,都是在自定义的label中完成的,此时鼠标只有在label范围进行动作,才能捕获到,也可以把这些操作直接放到Widget(QWidget子类)来完成,这样的话,鼠标在整个窗口中进行的各种动作都能获取到了。
其中鼠标移动不同于鼠标按下,随便移动下鼠标,就会产生大量的鼠标移动事件,当进行捕获事件的时候,尤其是在进行一些复杂逻辑的时候,程序负担就很重,就很容易产生卡顿的情况,
Qt为了保证程序的流畅性,默认情况下不会对鼠标移动进行追踪。也就是说鼠标移动的时候,不会调用mouseMoveEvent,除非显式告诉qt追踪鼠标位置。做法如下:
this->setMouseTracking(true);
鼠标滚轮动作
通过dalta()方法可以或者滚轮滚动的距离。
void Widget::wheelEvent(QWheelEvent *ev)
{qDebug()<<ev->delta();
}
键盘事件
要想获取到用户的键盘按键,QShortCut这是信号槽机制封装过,获取键盘按键的方式。站在底层的角度,也可以通过事件获取到当前用户键盘按下的情况。
void Widget::keyPressEvent(QKeyEvent *event)
{if(event->key() == Qt::Key_A){qDebug()<<"按下了 A 键";}
}
上述只是获取单个按键,有时候我们需要获取组合键之类的操作,如下 ctrl + a
void Widget::keyPressEvent(QKeyEvent *event)
{if(event->key() == Qt::Key_A && event->modifiers() == Qt::ControlModifier){qDebug()<<"按下了 ctrl + A 键";}
}
定时器事件
QTimer实现了定时器的功能,在QTimer背后是QTimerEvent定时器事件进行支撑的,QObject提供了一个timerEvent这个函数,startTimer 启动定时器,killTimer关闭定时器。
开启定时器事件
int timerId = this->startTimer(1000);
此处的timerId类似于Linux课堂上谈到的“文件描述符”,起到了身份标识的效果。
使用示例:
#include "widget.h"
#include "ui_widget.h"
#include <QWheelEvent>
#include <QDebug>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);timerId = this->startTimer(1000);
}Widget::~Widget()
{delete ui;
}void Widget::timerEvent(QTimerEvent *event)
{//如果一个程序中存在多个定时器(startTimer 创建的定时器,此时每个定时器都会触发timerEvent函数)//先判断以下这次触发是否是想要的定时器触发的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);
}
使用timerEvent比QTimer 还是要更复杂一点,手动管理timerId,还需要区分这次调用是哪个timer引起的。
窗口相关事件
窗口移动触发事件
moveEvent();
const QPoint & oldpos() const;
const QPoint & pos() const;
示例:
void Widget::moveEvent(QMoveEvent *event)
{qDebug()<<event->oldPos();qDebug()<<event->pos();
}
窗口大小改变时触发的事件
resizeEvent();
void Widget::resizeEvent(QResizeEvent *event)
{qDebug()<<event->size();
}
扩展
事件分发/事件过滤 属于Qt事件机制背后的一些逻辑,事件分发就是重写event函数,直接获取所有事件,这个后续进行介绍。