文章目录
- [1. Qt 事件](#1. Qt 事件)
-
- [1.1 事件介绍](#1.1 事件介绍)
- [1.2 事件的处理](#1.2 事件的处理)
- [1.3 按键事件](#1.3 按键事件)
- [1.4 鼠标事件](#1.4 鼠标事件)
-
- [1.4.1 鼠标单击事件](#1.4.1 鼠标单击事件)
- [1.4.2 鼠标释放事件](#1.4.2 鼠标释放事件)
- [1.4.3 鼠标双击事件](#1.4.3 鼠标双击事件)
- [1.4.4 鼠标移动事件](#1.4.4 鼠标移动事件)
- [1.4.5 滚轮事件](#1.4.5 滚轮事件)
- [1.5 定时器](#1.5 定时器)
-
- [1.5.1 QTimerEvent](#1.5.1 QTimerEvent)
- [1.5.2 QTimer](#1.5.2 QTimer)
- [1.5.3 QResizeEvent QMoveEvent](#1.5.3 QResizeEvent QMoveEvent)
- [1.6 事件分发器(拓展)](#1.6 事件分发器(拓展))
- [1.7 事件过滤器(拓展)](#1.7 事件过滤器(拓展))
1. Qt 事件
1.1 事件介绍
事件是应用程序内部或者外部产生的事情或者动作的统称。在 Qt 中使用一个对象来表示一个事件,所有的 Qt 事件均继承于抽象类 QEvent。事件是由系统或者 Qt 平台本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件是在用户操作时发出,如键盘事件、鼠标事件等,另一些事件则是由系统本身自动发出,如定时器事件。
常见 Qt 事件
| 事件名称 | 描述 |
|---|---|
| 鼠标事件 | 鼠标左键、鼠标右键、鼠标滚轮,鼠标的移动,鼠标按键的按下和松开 |
| 键盘事件 | 按键类型、按键按下、按键松开 |
| 定时器事件 | 定时时间到达 |
| 进入离开事件 | 鼠标的进入和离开 |
| 滚轮事件 | 鼠标滚轮滚动 |
| 绘屏事件 | 重绘屏幕的某些部分 |
| 显示隐藏事件 | 窗口的显示和隐藏 |
| 移动事件 | 窗口位置的变化 |
| 窗口事件 | 是否为当前窗口 |
| 大小改变事件 | 窗口大小改变 |
| 焦点事件 | 键盘焦点移动 |
| 拖拽事件 | 用鼠标进行拖拽 |
1.2 事件的处理
事件处理一般常用的方法为:重写相关的 Event 函数。在 Qt 中,几乎所有的 Event 函数都是虚函数,所以可以重新实现。如:在实现鼠标的进入和离开事件时,直接重新实现 enterEvent() 和 leaveEvent() 即可。
核心函数原型
[virtual protected] void QWidget::enterEvent (QEvent *event):鼠标进入控件时触发,event 参数传递事件相关信息。[virtual protected] void QWidget::leaveEvent (QEvent *event):鼠标离开控件时触发。
示例1:鼠标进入事件
-
步骤1:新建 Qt 项目
-
步骤2:设计 UI 文件
- 在 UI 中拖入一个 Label,设置边界框(frameshape)方便观察鼠标进入/移出效果。
-
步骤3:添加自定义类 mybutton
- 选中项目名称,右键选择"add new...",选择"C++ Class"。
- 类名设为 mybutton,基类初始选 QWidget,后续修改为 QLabel。
- 勾选"Add Q_OBJECT",生成 mybutton.h 和 mybutton.cpp 文件。
-
步骤4:在 mybutton.h 中声明事件函数
cpp
#ifndef MYBUTTON_H
#define MYBUTTON_H
#include <QWidget>
#include <QPushButton>
#include <QDebug>
#include <iostream>
class mybutton : public QPushButton
{
Q_OBJECT
public:
mybutton(QWidget *parent = nullptr);
~mybutton();
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
};
#endif // MYBUTTON_H
- 步骤6:在 mybutton.cpp 中重写事件函数
cpp
mybutton::mybutton(QWidget *parent)
:QPushButton(parent)
{
}
mybutton::~mybutton()
{
}
void mybutton::enterEvent(QEvent *event)
{
qDebug()<<"鼠标进入"<<endl;
}
void mybutton::leaveEvent(QEvent *event)
{
qDebug()<<"鼠标离开"<<endl;
}
- 步骤7:提升 UI 中的 pushbutton
- 在 UI 中选中 mybutton,右键选择"提升为..."。
- 提升的类名称设为 mybutton,头文件设为 mybutton.h,点击"提升"。
- 步骤8:执行效果
- 鼠标进入 pushbutton 区域后,应用程序输出栏打印"鼠标进入"。

示例2:鼠标点击获取坐标
- 步骤1:在 mybutton.h 中声明 mousePressEvent
cpp
class mybutton : public QPushButton
{
Q_OBJECT
public:
mybutton(QWidget *parent = nullptr);
~mybutton();
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
void mousePressEvent(QMouseEvent* event);
};
- 步骤2:在 mybutton.cpp 中重写 mousePressEvent
cpp
#include "mylabel.h"
#include <QDebug>
#include <QMouseEvent>
MyLabel::MyLabel(QWidget *parent) : QLabel(parent) {}
void MyLabel::enterEvent(QEvent *ev) {
qDebug() << "鼠标进入";
}
void mybutton::mousePressEvent(QMouseEvent *event)
{
qDebug()<<event->x()<<event->y()<<endl;
}
- 扩展:区分鼠标左右键与屏幕坐标
cpp
void mybutton::mousePressEvent(QMouseEvent *ev) {
// 鼠标左键(窗口内坐标)
if (ev->button() == Qt::LeftButton) {
QString str = QString("鼠标左键: x=%1, y=%2").arg(ev->x()).arg(ev->y());
qDebug() << str.toUtf8().data();
}
// 鼠标右键(屏幕坐标)
if (ev->button() == Qt::RightButton) {
QString str = QString("鼠标右键: 屏幕 x=%1, 屏幕 y=%2").arg(ev->globalX()).arg(ev->globalY());
qDebug() << str.toUtf8().data();
}
}
1.3 按键事件
Qt 中的按键事件通过 QKeyEvent 类实现,当键盘按键按下或释放时触发。
- 按键类型通过
Qt::Key枚举定义(如 Qt::Key_A、Qt::Key_Escape)。 - 修改键通过
Qt::KeyboardModifier枚举定义(如 Ctrl、Shift、Alt)。
- 单个按键示例
- 调用 key() 获取普通按键
- 示例:
cpp
void Widget::keyPressEvent(QKeyEvent *event)
{
if(event->key()==Qt::Key_C)
{
qDebug()<<"C"<<endl;
}
}
- 组合按键示例
修改键枚举
| 枚举值 | 描述 |
|---|---|
| Qt::NoModifier | 无修改键 |
| Qt::ShiftModifier | Shift 键 |
| Qt::ControlModifier | Ctrl 键 |
| Qt::AltModifier | Alt 键 |
| Qt::MetaModifier | Windows 键(Windows 系统)/ Command 键(macOS) |
| Qt::KeypadModifier | 数字键盘 Num Lock 开启 |
| Qt::GroupSwitchModifier | 输入法组切换(X11 系统) |
-
对于Shift、Ctrl、Alt键,调用 modifiers 获取按键
-
示例:Ctrl+A 组合键
cpp
void Widget::keyPressEvent(QKeyEvent *event)
{
if(event->key()==Qt::Key_C&&event->modifiers()==Qt::ControlModifier)
{
qDebug()<<"Ctrl + C"<<endl;
}
}
1.4 鼠标事件
Qt 中鼠标事件通过 QMouseEvent 类实现,可获取鼠标按键状态、坐标等信息。
- 头文件:
#include <QMouseEvent> - 核心函数:mousePressEvent(按下)、mouseReleaseEvent(释放)、mouseDoubleClickEvent(双击)、mouseMoveEvent(移动)、wheelEvent(滚轮)。
- 鼠标按键枚举:Qt::LeftButton(左键)、Qt::RightButton(右键)、Qt::MidButton(滚轮)。
1.4.1 鼠标单击事件
- 示例1:鼠标左键按下
cpp
void mybutton::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
qDebug() << "鼠标左键被按下";
}
}
- 示例2:鼠标右键按下
cpp
void mybutton::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::RightButton) {
qDebug() << "鼠标右键被按下";
}
}
- 示例3:鼠标滚轮按下
cpp
void mybutton::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::MidButton) {
qDebug() << "鼠标滚轮被按下";
}
}
1.4.2 鼠标释放事件
函数原型
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event)
- 示例
cpp
void mybutton::mouseReleaseEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
qDebug() << "鼠标左键被释放";
}
}
1.4.3 鼠标双击事件
函数原型
[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event)
- 示例
cpp
void mybutton::mouseDoubleClickEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
qDebug() << "鼠标左键被双击";
}
else if(event->button()==Qt::RightButton)
{
qDebug() << "鼠标右键被双击";
}
}
1.4.4 鼠标移动事件
函数原型
-
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event):鼠标移动时触发。 -
void setMouseTracking(bool enable):设置鼠标追踪,默认 false(仅按下时追踪),设为 true 可实时追踪。(Qt默认不会追踪,因为会产生大量时间,耗费性能) -
示例
cpp
mybutton::mybutton(QWidget *parent)
:QPushButton(parent)
{
this->setMouseTracking(true);
}
mybutton::~mybutton()
{
}
void mybutton::mouseMoveEvent(QMouseEvent *event)
{
// 输出鼠标当前窗口内坐标
qDebug() << "[" << event->x() << "," << event->y() << "]";
}
1.4.5 滚轮事件
-
滚轮事件通过 QWheelEvent 类实现,
delta()函数返回滚轮滑动距离(正数向前,负数向后)。 -
头文件:
#include <QWheelEvent> -
示例
cpp
void mybutton::wheelEvent(QWheelEvent *event)
{
static int x = 0;
x += event->delta(); // delta() 每次滚动默认返回 ±120
if (event->delta() > 0) {
qDebug() << "滚轮往前" << x;
} else {
qDebug() << "滚轮往后" << x;
}
}
1.5 定时器
Qt 中定时器用于周期性执行操作或制作动画,分为 QTimerEvent 和 QTimer 两类。
1.5.1 QTimerEvent
- 通过
startTimer(int interval)开启定时器,返回定时器 ID。 - 定时时间到达时触发
timerEvent(QTimerEvent *e),通过timerId()获取定时器 ID 区分多个定时器。 - 间隔单位:毫秒。
- 示例:定时器计数
cpp
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void keyPressEvent(QKeyEvent *event);
void timerEvent(QTimerEvent* event);
private:
Ui::Widget *ui;
int timerId;
};
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)
{
if(timerId==event->timerId())//判断是当前定时器触发
{
int value=ui->lcdNumber->value();
if(value<=0) this->killTimer(timerId);
else ui->lcdNumber->display(value-1);
}
}
1.5.2 QTimer
- 更高层次接口,支持信号槽,可设置单次触发。
- 核心函数:
start(int interval)(启动)、stop()(停止)。 - 核心信号:
timeout()(定时时间到达)。
- 示例:启停计数
cpp
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void keyPressEvent(QKeyEvent *event);
void timerEvent(QTimerEvent* event);
void handler();
private:
Ui::Widget *ui;
QTimer* timer;
};
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
timer=new QTimer(this);
connect(timer,&QTimer::timeout,this,&Widget::handler);
timer->start(1000);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handler()
{
int value=ui->lcdNumber->value();
if(value<=0) timer->stop();
else ui->lcdNumber->display(value-1);
}
1.5.3 QResizeEvent QMoveEvent
核心属性/方法:
| 方法 | 作用 |
|---|---|
| oldSize() | 返回控件变化前 的尺寸(QSize 类型) |
| size() | 返回控件变化后 的新尺寸(QSize 类型) |
| oldPos() | 返回窗口变化前的位置 |
| pos() | 返回窗口变化后的位置 |
- 示例:移动放大窗口
cpp
void Widget::resizeEvent(QResizeEvent *event)
{
qDebug()<<event->oldSize()<<endl;
qDebug()<<event->size()<<endl;
}
void Widget::moveEvent(QMoveEvent *event)
{
qDebug()<<event->oldPos()<<endl;
qDebug()<<event->pos()<<endl;
}
1.6 事件分发器(拓展)
-
概述
事件分发器(Event Dispatcher)用于将事件传递给对应对象,可拦截事件。所有继承 QObject 的类均可重写
bool event(QEvent *e)实现事件捕获和拦截。 -
工作原理
- 所有事件都会进入
event(QEvent *e)函数。 - 函数根据事件类型(
e->type())调用对应事件处理函数(如 mousePressEvent)。 - 返回值:true 表示拦截事件(不向下分发),false 表示继续分发。
- 所有事件都会进入
-
核心事件类型
通过 QEvent::Type 枚举定义,常见类型包括:QEvent::MouseButtonPress(鼠标按下)、QEvent::KeyPress(按键按下)、QEvent::Paint(绘屏)等。
-
示例:拦截鼠标按下事件
- 步骤1:在 widget.h 中声明事件和分发器
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QMouseEvent>
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
// 鼠标点击事件
void mousePressEvent(QMouseEvent *ev);
// 事件分发器
bool event(QEvent *event);
};
#endif // WIDGET_H
- 步骤2:在 widget.cpp 中实现功能
cpp
#include "widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent) : QWidget(parent) {}
Widget::~Widget() {}
void Widget::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
qDebug() << "鼠标左键被按下!";
}
}
bool Widget::event(QEvent *ev) {
// 判断事件类型为鼠标按下
if (ev->type() == QEvent::MouseButtonPress) {
qDebug() << "Event中鼠标被按下!";
return true; // 拦截事件,不向下分发(mousePressEvent 不会执行)
}
// 其他事件交给父类处理
return QWidget::event(ev);
}
1.7 事件过滤器(拓展)
-
概述
事件过滤器在事件到达分发器前拦截,无需继承目标控件,可给任意对象安装过滤器,更灵活。
-
使用步骤
- 给目标对象安装过滤器:
target->installEventFilter(filterObj)。 - 重写过滤器对象的
eventFilter(QObject *obj, QEvent *e)函数。
- 给目标对象安装过滤器:
-
示例:拦截 Label 的鼠标按下事件
- 步骤1:新建项目与 UI 设计
- 新建 QWidget 项目,勾选 UI,拖入一个 Label。
- 步骤2:添加自定义类 myLabel
- 类名 myLabel,基类 QLabel,生成 mylabel.h 和 mylabel.cpp。
- 步骤3:提升 UI 中的 Label
- 选中 Label 右键"提升为...",类名设为 myLabel,头文件设为 mylabel.h。
- 步骤4:在 mylabel.h 中声明事件和分发器(可选)
- 步骤1:新建项目与 UI 设计
cpp
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QLabel>
#include <QMouseEvent>
class myLabel : public QLabel {
Q_OBJECT
public:
explicit myLabel(QWidget *parent = nullptr);
// 鼠标点击事件
void mousePressEvent(QMouseEvent *event);
// 事件分发器(可选)
bool event(QEvent *e);
};
#endif // MYLABEL_H
- 步骤5:在 mylabel.cpp 中实现事件(可选)
cpp
#include "mylabel.h"
#include <QDebug>
myLabel::myLabel(QWidget *parent) : QLabel(parent) {}
void myLabel::mousePressEvent(QMouseEvent *event) {
QString str = QString("鼠标按下: x=%1, y=%2").arg(event->x()).arg(event->y());
qDebug() << str.toUtf8().data();
}
bool myLabel::event(QEvent *e) {
if (e->type() == QEvent::MouseButtonPress) {
QMouseEvent *event = static_cast<QMouseEvent*>(e);
QString str = QString("Event函数中鼠标按下: x=%1, y=%2").arg(event->x()).arg(event->y());
qDebug() << str.toUtf8().data();
return false; // 不拦截,继续分发到 mousePressEvent
}
return QLabel::event(e);
}
- 步骤6:在 widget.h 中声明事件过滤器
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
// 事件过滤器
bool eventFilter(QObject *obj, QEvent *e);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
- 步骤7:在 widget.cpp 中安装过滤器并实现功能
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QMouseEvent>
#include <QDebug>
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
// 步骤1:给 Label 安装事件过滤器(过滤器对象为当前窗口 this)
ui->label->installEventFilter(this);
}
Widget::~Widget() {
delete ui;
}
// 步骤2:重写事件过滤器函数
bool Widget::eventFilter(QObject *obj, QEvent *e) {
// 判断目标对象是 Label,且事件类型是鼠标按下
if (obj == ui->label && e->type() == QEvent::MouseButtonPress) {
QMouseEvent *event = static_cast<QMouseEvent*>(e);
QString str = QString("事件过滤器中鼠标按下: x=%1, y=%2").arg(event->x()).arg(event->y());
qDebug() << str.toUtf8().data();
return true; // 拦截事件,myLabel 的 event 和 mousePressEvent 不执行
}
// 其他事件交给父类处理
return QWidget::eventFilter(obj, e);
}