个人博客地址
|----------------------------------------|
| 个人博客: 花开富贵 |
文章目录
- 个人博客地址
-
- [1 概述](#1 概述)
- [2 事件处理](#2 事件处理)
- [3 鼠标事件](#3 鼠标事件)
-
- [3.1 处理鼠标点(单)击事件](#3.1 处理鼠标点(单)击事件)
- [3.2 鼠标释放事件](#3.2 鼠标释放事件)
- [3.3 鼠标双击事件](#3.3 鼠标双击事件)
- [3.4 鼠标移动事件](#3.4 鼠标移动事件)
- [3.5 滚轮事件](#3.5 滚轮事件)
1 概述
事件是应用程序内部或者外部产生的事件或动作的简称;
这个概念与信号和槽类似, 关于信号和槽而言, 我们将信号和对应的槽通过connect进行连接, 连接后档某个控件对象发出信号后, 将会调用所绑定的槽函数, 从而进行对应的处理动作;
事件也是类似, 但事件与信号的概念区别于粒度, 事件的粒度要小于事件的粒度, 简单来说, 当一个按钮控件被按下时将会发出Clicked()信号, 而事件作为更细粒度的概念, 当我们在进行鼠标移动, 窗口挪动或是窗口大小的变动, 都会产生对应的事件, 而在Qt中, 可以对某个控件Widget进行事件的重写从而完善一个应用的多功能;
换句话来说, 实际上信号也是事件的一种体现, 信号通过封装事件, 如判断一个点击事件在一个QPushButton控件中产生, 因此发出对应的信号;
cpp
// 伪代码:QPushButton 的内部逻辑
// 1. 系统检测到鼠标按下,发送 QMouseEvent
void QPushButton::mousePressEvent(QMouseEvent *e) {
// 记录下:按钮被按下了
this->isPressed = true;
// 调用父类处理,保证默认外观变化(比如按钮凹下去)
QWidget::mousePressEvent(e);
}
// 2. 系统检测到鼠标抬起,发送 QMouseEvent
void QPushButton::mouseReleaseEvent(QMouseEvent *e) {
// 逻辑判断:
// 如果之前被按下了,且鼠标抬起的位置还在按钮范围内
if (this->isPressed && this->rect().contains(e->pos())) {
// 【关键点】:在这里,事件被转化为了信号!
emit clicked();
}
this->isPressed = false;
QWidget::mouseReleaseEvent(e);
}
换个角度可以这么理解, 信号是桌上的饺子, 而事件是饺子的原材料(面粉, 水, 馅...), 只是有时候我们常吃的是饺子, 但除了饺子以外, 我们还希望可能吃到包子馒头面条面包等食物, 而Qt不一定提供这些成品, 因此Qt提供了一系列的接口让我们可以通过组合原材料生成自定义的成品;
在Qt中, 所有的事件基于QEvent类进行派生, 常见的事件有:
-
QMouseEvent- 鼠标事件当鼠标在控件内进行按下, 释放, 移动或者双击时将会触发;
常见于获取鼠标点击的坐标
(x, y), 判断是左键还是右键; -
QKeyEvent- 键盘事件当键盘按下或释放时触发;
常见于捕获快捷键, 如
Ctrl+C, 或者控制角色移动的W/A/S/D; -
QTimerEvent- 定时器事件当一个由
startTimer()启动的定时器时间达到间隔时触发;常见于周期性任务, 如每秒刷新一次时钟显示;
-
QDropEvent- 拖放结束事件属于拖放机制的一部分, 当拖拽操作进入控件并在内松开鼠标左键时触发;
常见于处理被拖动的文件, 或处理被拖入的数据;
-
QInputEvent- 输入事件基类这是一个抽象类, 不是具体的某个动作, 是
QMouseEvent,QKeyEvent,QTouchEvent等的父类;通常不直接进行处理, 在某些通用处理时, 可以用来检查通用的修饰键状态, 如(
Shift,Ctrl)是否被按下; -
QPaintEvent- 绘图事件当控件需要重新绘制自身外观时触发, 如窗口刚显示, 窗口大小改变, 被遮挡后重新露出, 或是手动调用
update()/repaint();画图时必须在该事件处理函数中使用
QPainter;
除了这些事件以外, 还有:
| 事件 | 描述 |
|---|---|
| 鼠标事件 | 鼠标左键、鼠标右键、鼠标滚轮,鼠标的移动,鼠标按键的按下和松开 |
| 键盘事件 | 按键类型、按键按下、按键松开 |
| 定时事件 | 定时时间到达 |
| 进入离开事件 | 鼠标的进入和离开 |
| 滚轮事件 | 鼠标滚轮滚动 |
| 绘屏事件 | 重绘屏幕的某些部分 |
| 显示隐藏事件 | 窗口的显示和隐藏 |
| 移动事件 | 窗口位置的变化 |
| 窗口事件 | 是否为当前窗口 |
| 大小改变事件 | 窗口大小改变 |
| 焦点事件 | 键盘焦点移动 |
| 拖拽事件 | 用鼠标进行拖拽 |
2 事件处理
事件处理本质上就是将一个事件与处理函数进行关联, 与信号类似, 当一个事件触发后, 调用制定的函数调用;
但对于事件处理而言, 在Qt中针对事件的处理大多是重写Qt所给的事件的虚函数;
以鼠标的进入和离开事件为例, 鼠标的进入事件为enterEvent, 离开事件为leaveEvent;
而在进行重写事件时也表示, 我们需要继承哪一个基类, 此次我们基于QLabel写一个派生类Label;
cpp
/*label.h*/
class Label : public QLabel
{
Q_OBJECT
public:
Label(QWidget* parent = nullptr);
void enterEvent(QEnterEvent* event); // 需要注意类型
void leaveEvent(QEvent* event);
};
/*label.cpp*/
Label::Label(QWidget* parent):QLabel(parent) {}
void Label::enterEvent(QEnterEvent* event)
{
(void)event;
qDebug()<<"enterEvent";
}
void Label::leaveEvent(QEvent *event)
{
(void)event;
qDebug()<<"leaveEvent";
}
/*mainwidow.cpp*/
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
/* 创建核心控件并设置 */
QWidget* centerWidget = new QWidget(this);
this->setCentralWidget(centerWidget);
/* 创建布局管理器 */
QHBoxLayout* hlayout = new QHBoxLayout(centerWidget);
centerWidget->setLayout(hlayout); // 设置布局管理器进核心控件
/* 创建自定义Label */
Label* _label = new Label();
_label->setFrameShape(QLabel::Box); // 设置外框线
_label->setText("This is a My Label"); // 设置文本
hlayout->addWidget(_label); // 将Label设置进布局管理器
}
当鼠标进入时, 打印进入信息 "enterEvent", 离开时打印 "leaveEvent" ;
运行结果为:

除此之外, 也可以使用QtDesigner, 在设计模式中拖进基类, 并 "提升于" 对应的派生类;

此处忽略逻辑, 运行结果与上Gif相同;
3 鼠标事件
在上文的介绍中, 介绍了鼠标的进入与离开事件, 此处针对进入与离开的事件就不进行赘述, 除了进入与离开事件以外, 鼠标还有一系列的其他事件, 包括但不限于, 点击, 释放, 双击;
此处重点介绍点击, 释放, 双击, 滚轮以及移动事件;
3.1 处理鼠标点(单)击事件
这里主要是获取鼠标的点击事件, 我们在上文提到, 实际在鼠标点击时同样会触发对应的事件;
cpp
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
鼠标点击事件通常依靠mousePressEvent(QMouseEvent*), 其中参数QMouseEvent中将包含各类信息, 包括鼠标所点击的位置信息, 因此可以通过这些信息实现, 当鼠标点击时获取对应的坐标位置;
cpp
/* label.cpp */
void Label::mousePressEvent(QMouseEvent *event)
{
qDebug()<<"X: "<<event->x()<<" Y: "<<event->y();
qDebug()<<"globalX: "<<event->globalX()<<" globalY: "<<event->y();
qDebug()<<"|| ######################################### ||";
}
/* mainwindow.cpp */
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QWidget* centerWidget = new QWidget(this);
this->setCentralWidget(centerWidget);
QHBoxLayout* hLayout = new QHBoxLayout(centerWidget);
centerWidget->setLayout(hLayout);
hLayout->setContentsMargins(50,50,50,50);
Label* label = new Label();
label->setText("Click here to get X and Y");
label->setAlignment(Qt::AlignCenter); // 字体居中
hLayout->addWidget(label);
}
运行结果为:

可以看到, 获取到的不仅是基于当前控件的坐标, 还能获取基于全局控件(主控件)的位置;
除此之外, 该事件并不是只能触发左键, 能触发该事件的鼠标键包括, 左右键, 滚轮键, 或者一些前后键;
本质来说, 当鼠标上的有效键被点击时, 都将触发该事件;
当然, 在这个event对象中, 也包含着不同按键的属性, 可以通过判断对应的按键来判断是什么按键进行了点击;
cpp
void Label::mousePressEvent(QMouseEvent *event)
{
qDebug()<<"|| ######################################### ||";
switch (event->button()) {
case Qt::LeftButton:
qDebug()<<"左键";
break;
case Qt::RightButton:
qDebug()<<"右键";
break;
case Qt::MiddleButton:
qDebug()<<"滚轮键";
break;
case Qt::BackButton:
qDebug()<<"后退键";
break;
case Qt::ForwardButton:
qDebug()<<"前进键";
break;
default:
qDebug()<<"其他键";
break;
}
qDebug()<<"X: "<<event->x()<<" Y: "<<event->y();
qDebug()<<"globalX: "<<event->globalX()<<" globalY: "<<event->y();
qDebug()<<"|| ######################################### ||";
}
运行结果为:

3.2 鼠标释放事件
当鼠标按下后, 将会触发点击事件, 除此之外, 当鼠标从点击状态松开, 将同样会触发一个事件, 这个事件为释放事件, 即Release;
对应的虚函数为:
cpp
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event) override;
同样的, 鼠标释放时, 将会返回一个QMouseEvent对象指针, 这个对象中, 同样给出了获得了释放鼠标的键属性, 可以采用event->button()的方式进行获取;
cpp
Label::Label(QWidget* parent):QLabel(parent) {
this->setFrameShape(QLabel::Box);
}
void Label::mousePressEvent(QMouseEvent *event)
{
switch (event->button()) {
case Qt::LeftButton:
qDebug()<<"左键";
break;
case Qt::RightButton:
qDebug()<<"右键";
break;
case Qt::MiddleButton:
qDebug()<<"滚轮键";
break;
case Qt::BackButton:
qDebug()<<"后退键";
break;
case Qt::ForwardButton:
qDebug()<<"前进键";
break;
default:
qDebug()<<"其他键";
break;
}
}
void Label::mouseReleaseEvent(QMouseEvent *event)
{
switch (event->button()) {
case Qt::LeftButton:
qDebug()<<"左键释放";
break;
case Qt::RightButton:
qDebug()<<"右键释放";
break;
case Qt::MiddleButton:
qDebug()<<"滚轮键释放";
break;
case Qt::BackButton:
qDebug()<<"后退键释放";
break;
case Qt::ForwardButton:
qDebug()<<"前进键释放";
break;
default:
qDebug()<<"其他键";
break;
}
}
运行结果为:

实际上, 一个Clicked信号就是由一个mousePreeeEvent事件与一个mouseReleaseEvent事件共同组成的;
3.3 鼠标双击事件
鼠标双击事件是通过虚函数mouseDoubleClickEvent()实现的;
cpp
[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event);
通常情况下, 在进行双击操作时, 可能会误操作单击, 因此此处将单击与释放部分进行注释;
cpp
void Label::mouseDoubleClickEvent(QMouseEvent *event){
switch (event->button()) {
case Qt::LeftButton:
qDebug()<<"左键双击";
break;
case Qt::RightButton:
qDebug()<<"右键双击";
break;
case Qt::MiddleButton:
qDebug()<<"滚轮键双击";
break;
case Qt::BackButton:
qDebug()<<"后退键双击";
break;
case Qt::ForwardButton:
qDebug()<<"前进键双击";
break;
default:
qDebug()<<"其他键";
break;
}
}
运行结果为:

3.4 鼠标移动事件
鼠标移动事件对应的虚函数为:
cpp
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);
此次基于QWidget类派生一个Widget类, 并在Widget中重写该函数;
cpp
/*widgeht.cpp*/
Widget::Widget(QWidget *parent)
: QWidget{parent}
{}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
qDebug()<<"x: "<<event->x()<<" y: "<<event->y();
}
/*mainwindow.cpp*/
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
Widget* centerWidget = new Widget(this);
this->setCentralWidget(centerWidget);
}
运行结果为:

可以看到运行结果;
但这里有一个问题, 这里的鼠标是在按下后才进行移动跟踪, 本身并没有进行移动跟踪, 本质上是因为通常情况下, 鼠标每当移动都会进行大量的事件发生, 因此为了减缓CPU的负担, 因此通常情况下, 对应的默认鼠标跟踪并不会被打开, 当需要跟踪时进行点击(点击时跟踪);
但并不是完全不能进行停留跟踪, 当需要停留跟踪时, 需要手动将mouseTracking属性设置为True;
cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
Widget* centerWidget = new Widget(this);
this->setCentralWidget(centerWidget);
centerWidget->setMouseTracking(true); // 设置鼠标跟踪
}
运行结果为:

3.5 滚轮事件
滚轮事件通过wheelEvent产生;
cpp
[virtual protected] void QWidget::wheelEvent(QWheelEvent *event);
在3.4中对应的Widget中实现该功能:
cpp
/*widget.h*/
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
void mouseMoveEvent(QMouseEvent* event) override;
void wheelEvent(QWheelEvent *event) override;
signals:
private:
int cur=0;
};
/*widget.cpp*/
void Widget::wheelEvent(QWheelEvent *event)
{
cur = cur+event->angleDelta().y();
if (cur > 1200) {
cur = 1200; // 超过上限,强制拉回 1200
}
else if (cur < -1200) {
cur = -1200; // 低于下限,强制拉回 -1200
}
qDebug()<<cur;
}
当滚轮在进行上下滚动时, 将会触发对应的事件, 事件记录每次操作的值;
而在这段代码中, 涉及了一个滚轮的值, 默认为0, 并设置上限与下限位±1200, 当数值超过时将会拉回最值, 以完成界限的控制;
运行结果为:
