开发过程中有些细节容易被忽略,今天挑几个重点聊一聊。
🔥个人主页:Cx330🌸
❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》
《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔
《Git深度解析》:版本管理实战全解 《Qt 极境架构》MySQL 核心技术与实战
🌟心向往之行必能
🎥Cx330🌸的简介:
目录
前言:
在 Qt 开发中,挺多初学者常常混淆信号槽(Signals & Slots)和事件(Events)。实际上,事件是 Qt 框架更底层的通信与响应机制。无论是鼠标点击、键盘输入、窗口重绘还是定时器触发,其本质都是 Qt 事件循环分发的 QEvent 对象。
这篇文章将梳理 Qt 事件处理的核心概念、常用事件类型及其实战应用,并深入剖析事件传递与过滤器的全流程。
一. 信号槽 vs 事件机制:它们有什么区别?
维度
信号与槽 (Signals & Slots)
事件机制 (Event System)
层级
高层通信机制(基于元对象系统)
底层交互与系统消息传递机制
触发源
通常由对象主动发出(如按钮被点击发出 clicked())
由操作系统或 Qt 框架分发(如操作系统捕获到硬件输入)
处理方式
通过 connect() 将信号绑定到槽函数
通过重写虚函数(如 mousePressEvent)或事件过滤器处理
关系
许多信号本质上是在事件处理函数中被触发的(例如:在 mouseReleaseEvent 中发出 clicked 信号)
事件是信号的底层支撑
二. Qt 事件体系结构 (QEvent 继承树)
Qt 中所有的事件类都继承自 QEvent 抽象基类。当事件发生时,Qt 会创建一个具体的 QEvent 派生类对象并分发给对应的 QObject 实例。
┌─────────────┐
│ QEvent │
└──────┬──────┘
│
┌──────────────┬───────┴───────┬──────────────┬──────────────┐
│ │ │ │ │
┌───┴──────────┐ ┌─┴────────────┐ ┌┴────────────┐ ┌┴────────────┐ ┌┴────────────┐
│ QInputEvent │ │ QTimerEvent │ │ QResizeEvent│ │ QPaintEvent │ │ QCloseEvent │
└───┬──────────┘ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
│
┌──┴───────────────┬────────────────┐
│ │ │
┌┴─────────────┐ ┌──┴───────────┐ ┌──┴────────────┐
│ QMouseEvent │ │ QKeyEvent │ │ QWheelEvent │
└──────────────┘ └──────────────┘ └──────────────┘
常用的 QEvent 派生类及其用途:
QMouseEvent:鼠标按键、移动、双击等事件。QKeyEvent:键盘按按下与释放事件。QWheelEvent:鼠标滚轮滚动事件。QTimerEvent:定时器超时事件。QResizeEvent:控件/窗口尺寸改变事件。QPaintEvent:控件需要重绘的事件。
三. 常用事件处理与实战案例
3.1 鼠标事件与鼠标追踪 (Mouse Events)
鼠标事件涵盖点击、释放、双击、移动、滚轮等操作,核心通过 QMouseEvent 类获取事件详情(如点击位置、按键类型)。
核心事件函数:
mousePressEvent(QMouseEvent *event):鼠标按下时触发;mouseReleaseEvent(QMouseEvent *event):鼠标释放时触发;mouseDoubleClickEvent(QMouseEvent *event):鼠标双击时触发;mouseMoveEvent(QMouseEvent *event):鼠标移动时触发(需开启**setMouseTracking(true)**);wheelEvent(QWheelEvent *event):鼠标滚轮滚动时触发。
// label.h
#ifndef LABEL_H
#define LABEL_H
#include
#include
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
Label::Label(QWidget* parent)
: QLabel(parent)
{
}
void Label::enterEvent(QEvent *event)
{
(void) event;
qDebug() modifiers() == Qt::ControlModifier)
{
qDebug() setupUi(this);
// 开启定时器事件
// 此处的 timeId 是一个定时器的身份标识
timeId = this->startTimer(1000);
}
Widget::~Widget()
{
delete ui;
}
void Widget::timerEvent(QTimerEvent *event)
{
// 如果一个程序中存在多个定时器 (startTimer 创建的定时器), 此时每个定时器都会触发 timeEvent 函数
// 先判断一下这次触发的是否是想要的定时器触发的
if(event->timerId() != this->timeId)
{
// 如果不是我们的定时器触发的,就直接忽略
return;
}
// 是我们自己搞的定时器
int value = ui->lcdNumber->intValue();
if(value killTimer(this->timeId);
return;
}
value -= 1;
ui->lcdNumber->display(value);
}
实战示例2:QTimer 实战(信号槽机制)
// widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QTimer *timer = new QTimer(this);
timer->setInterval(1000); // 1秒间隔
// 连接定时器触发信号
connect(timer, &QTimer::timeout, this, []() {
static int num = 0;
qDebug() clicked([=]() { timer->start(); });
// 停止定时器
ui->pushButton_stop->clicked([=]() { timer->stop(); });
}
3.4 窗口事件:移动与大小改变(补充重点)
窗口移动、大小改变是常用系统事件,需重写 moveEvent 和 resizeEvent 函数,适用于窗口状态监控、自适应布局等场景。
核心事件函数:
moveEvent(QMoveEvent *event):窗口移动时触发(获取新位置);resizeEvent(QResizeEvent *event):窗口大小改变时触发(获取新尺寸)。
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void moveEvent(QMoveEvent* event);
void resizeEvent(QResizeEvent* event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include
#include
#include
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::moveEvent(QMoveEvent *event)
{
qDebug() pos();
}
void Widget::resizeEvent(QResizeEvent *event)
{
qDebug() size();
}
四. 事件分发器:事件处理的中间层
事件分发器 event(QEvent *event) 是 Qt 事件处理的核心枢纽,所有事件都会先经过该函数,再分发到对应的事件处理函数(如 mousePressEvent)。通过重写 event 函数,可实现事件拦截、自定义分发逻辑。
核心原理:
- 事件触发后,Qt 会创建
QEvent对象,调用控件的event函数; event函数根据事件类型(event->type()),调用对应的事件处理函数;- 若
event函数返回true,表示事件被拦截,不再向下分发;返回false,则继续分发。
实战示例:拦截鼠标按下事件
// widget.h
bool event(QEvent *event) override;
// widget.cpp
bool Widget::event(QEvent *event)
{
// 拦截鼠标按下事件
if (event->type() == QEvent::MouseButtonPress) {
qDebug() installEventFilter(filterObj)`**;
- 在过滤器对象中重写 **`eventFilter(QObject *obj, QEvent *event)`** 函数;
- 过滤器函数返回 **`true` **表示拦截事件,**`false` **表示继续分发。

**实战示例:拦截标签的鼠标事件**
// widget.h
bool eventFilter(QObject obj, QEvent event) override;
// widget.cpp
Widget::Widget(QWidget parent) : QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
// 给 label 安装事件过滤器(this 为过滤器对象)
ui->label->installEventFilter(this);
}
bool Widget::eventFilter(QObject obj, QEvent *event)
{
// 仅拦截 label 的事件
if (obj == ui->label) {
if (event->type() == QEvent::MouseButtonPress) {
qDebug() type() == QEvent::MouseMove) {
qDebug()
今天的内容大概就这些,实际开发中大家还会遇到更多细节,欢迎留言分享自己的经验。
评论 (0)
暂无评论