目录
前置知识
事件概念
常见的事件描述
进入和离开事件
代码示例:
鼠标事件
鼠标点击事件
鼠标释放事件
鼠标双击事件
鼠标滚轮动作
键盘事件
定时器事件
开启定时器事件
窗口相关事件
窗口移动触发事件
窗口大小改变时触发的事件
扩展
前置知识
在前面的介绍中,我们知道信号槽就是:用户进行的各种操作,就可能会产生出信号,可以给某个信号指定槽函数,当信号触发时,就能够自动的执行对应的槽函数。
这里的事件也是类似的,用户进行的各种操作,也会产生事件,程序员同样可以给事件关联上处理函数(处理的逻辑),当事件触发的时候,就能够执行到对应的代码。
事件本身是操作系统提供的机制,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函数,直接获取所有事件,这个后续进行介绍。