
03_Qt教案 事件篇
本文最后更新于 2025-02-20,学习久了要注意休息哟
第一章 事件
1.1 事件的概念
众所周知Qt是一个基于C++的框架,主要用来开发带窗口的应用程序(不带窗口的也行,但不是主流)。我们使用的基于窗口的应用程序都是基于事件,其目的主要是用来实现回调(因为只有这样程序的效率才是最高的)。所以在Qt框架内部为我们提供了一些列的事件处理机制,当窗口事件产生之后,事件会经过:事件派发 -> 事件过滤->事件分发->事件处理几个阶段。Qt窗口中对于产生的一系列事件都有默认的处理动作,如果我们有特殊需求就需要在合适的阶段重写事件的处理动作。
事件(event)是由系统或者 Qt 本身在不同的场景下发出的。当用户按下/移动鼠标、敲下键盘,或者是窗口关闭/大小发生变化/隐藏或显示都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如鼠标/键盘事件等;另一些事件则是由系统自动发出,如计时器事件。
每一个Qt应用程序都对应一个唯一的 QApplication应用程序对象,然后调用这个对象的exec()函数,这样Qt框架内部的事件检测就开始了(程序将进入事件循环来监听应用程序的事件)。
事件派发 -> 内核 向应用程序 发送一个事件信号
事件过滤 -> 对事件 进行过滤 让一些无用事件不进入到应用程序中
事件分发 -> 分发给每个窗口
事件处理 -> 需要对事件处理函数 做 重写
1.2 事件过滤器
1.2.1 什么是事件过滤器
事件过滤器(Event Filter)是 Qt 事件处理机制中的高级功能,允许一个对象拦截并处理其他对象的事件。通过事件过滤器可以实现:
- 对特定控件的事件进行预处理
- 集中管理多个对象的同类事件
- 在不继承类的情况下扩展事件处理能力
1.2.2 事件处理流程
标准事件流程:
事件发生 → 事件接收对象 → event() → 特定事件处理函数(如 mousePressEvent)
带过滤器的流程:
事件发生 → 事件接收对象 → 事件过滤器 → event() → 特定事件处理函数
1.2.2 操作步骤
1、注册过滤器
// 在需要监视的对象上安装过滤器
targetObject->installEventFilter(filterObject);
// targetObject 安装者 一般是控件
// filterObject 事件接收对象 一般是当前窗口
2、重写 eventFilter 函数
bool FilterObject::eventFilter(QObject* watched, QEvent* event)
{
// 判定对象
if(watched == targetObject)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Escape) {
qDebug() << "ESC 键被拦截";
return true; // 事件已处理
}
}
}
return QObject::eventFilter(watched, event); // 继续传递事件
}
| 参数 | 说明 |
| :—— | :—————————- |
| watched | 被监视的对象指针 |
| event | 发生的事件对象 |
| 返回值 | true 表示已处理,阻止继续传递 |
1.3 自定义控件
由于 Qt 的 UI 设计器中提供的都是标准控件,自定义控件无法直接拖拽到窗口中,需要以下步骤:
- 在项目中新建 一个 .cpp 和 .h 文件 需要继承
QWidget
- 在设计器中拖入一个
QWidget
标准控件到窗口中。 - 鼠标右键点击该
QWidget
,选择 “提升为…”。 - 在弹出的对话框中选择提升的类为
MyButton
。
提升完成后,该控件就变成了自定义的 MyButton
类型。
第二章 鼠标事件
2.1 处理函数
// 当鼠标进入窗口时,触发该事件。该事件仅在进入的瞬间触发一次。
[virtual protected] void QWidget::enterEvent(QEvent *event);
// 当鼠标离开窗口时,触发该事件。该事件仅在离开的瞬间触发一次。
[virtual protected] void QWidget::leaveEvent(QEvent *event);
// 当鼠标左键、右键或中键被按下时,触发该事件。通过参数可判断按下的是哪个键。
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
// 当鼠标左键、右键或中键被释放时,触发该事件。通过参数可判断释放的是哪个键。
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);
// 当鼠标移动(包括按住一个或多个鼠标键时移动)时,触发该事件。通过参数可判断在移动过程中哪些键被按下。
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);
// 当鼠标双击时,触发该事件。通过参数可判断双击使用的是哪个键。
[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event);
2.2 QMouseEvent 常用API
1、位置相关
// 获取鼠标事件发生时的位置,相对于事件目标(控件)的位置。
QPoint pos = event->pos(); // 获取鼠标相对于控件的坐标
// 获取鼠标事件发生时的位置,相对于屏幕的全局坐标。
QPoint globalPos = event->globalPos(); // 获取鼠标相对于屏幕的全局坐标
2、按键相关
// 获取鼠标事件发生时,哪个鼠标按钮被按下或释放。返回值是 Qt::MouseButton 枚举值。
Qt::MouseButton button = event->button(); // 获取按下或释放的鼠标按钮
// 获取当前按下的所有鼠标按钮,返回一个 Qt::MouseButtons 枚举值(可能是多个按钮) 移动的时候用 。
Qt::MouseButtons buttons = event->buttons(); // 获取当前按下的鼠标按钮(可能多个)
```c++
Qt::NoButton // 没有按键按下
Qt::AllButtons // 所有按键按下
Qt::LeftButton // 左键
Qt::RightButton // 右键
Qt::MidButton // 中键
3、修饰键相关
// 获取鼠标事件时修饰键的状态(如 Shift、Ctrl、Alt)。
Qt::KeyboardModifiers modifiers = event->modifiers(); // 获取修饰键的状态
4、鼠标滚轮
// 获取鼠标滚轮的滚动量,返回一个 QPoint,dx 表示水平方向的滚动量,dy 表示垂直方向的滚动量。
QPoint delta = event->angleDelta(); // 获取鼠标滚轮的旋转量
5、鼠标位置变化
// 获取鼠标在上一个事件中的位置。通常用于追踪鼠标移动轨迹。
QPoint lastPos = event->lastPos(); // 获取鼠标上一次事件发生的位置
6、事件类型
// 获取事件的类型,返回值是 QEvent::Type 枚举值(例如:QEvent::MouseButtonPress、QEvent::MouseMove 等)。
QEvent::Type eventType = event->type(); // 获取事件类型
7、事件处理控制
// 标记事件已被处理,停止事件继续传播。
event->accept(); // 事件已处理,停止传播
// 标记事件没有被处理,允许事件继续传播。
event->ignore(); // 允许事件继续传播
1.3 示例程序
- 通过鼠标 移动一个方块
- 记录鼠标进入和退出方块的次输
1.1.1 自定义控件
自定义一个标签控件类 LabelX
,让它继承自 QLabel
,然后重写父类的 enterEvent()
和 leaveEvent()
。
需要注意的是,我们需要对LabelX
类做一些修改,来到 labelx.h
将父类由 QWidget
修改为 QLabel
,如下:
#include <QLabel>
class LabelX : public QLabel
{
//
};
来到 labelx.cpp
将父类由 QWidget
修改为 QLabel
,如下:
#include "labelx.h"
LabelX::LabelX(QWidget* parent) : QLabel{parent}
{
}
LabelX.h 文件
class LabelX : public QLabel
{
protected:
// 鼠标进入/离开事件
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
};
LabelX.cpp 文件
// 进入窗口
void LabelX::enterEvent(QEvent* event)
{
Q_UNUSED(event) // 消除 为使用变量的警告
this->setText(QString("enterEvent: %1").arg(cnt++));
}
// 离开窗口
void LabelX::leaveEvent(QEvent* event)
{
Q_UNUSED(event)
this->setText("离开窗口");
}
1.1.2 标准控件
如果我们使用标准控件的话,那么就需要使用到事件过滤器
示例程序
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 安装事件过滤器
ui->label_2->installEventFilter(this);
}
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
// 判定当前对象
if(watched == ui->label_2)
{
// 判定事件
if(event->type() == QEvent::Enter)
{
ui->label_2->setText("进入");
}
if(event->type() == QEvent::Leave)
{
ui->label_2->setText("离开");
}
}
return QWidget::eventFilter(watched, event);
}
第三章 键盘事件
3.1 处理函数
键盘事件是用户通过键盘与程序交互的重要方式,主要包括按下和释放事件。
// 当键盘上的按键被按下时,触发该事件。通过参数可判断按下的是哪个键。
[virtual protected] void QWidget::keyPressEvent(QKeyEvent *event);
// 当键盘上的按键被释放时,触发该事件。通过参数可判断释放的是哪个键。
[virtual protected] void QWidget::keyReleaseEvent(QKeyEvent *event);
1、自定义控件
2、标准控件的事件过滤
3、直接用主窗口接受信号 控制 方块
3.2 QKeyEvent 常用API
1、键位相关
// 获取按下或释放的键值。返回一个 Qt::Key 枚举值,表示按下的键。
Qt::Key key = event->key(); // 获取按下或释放的键值
// 获取事件发生时按下的修饰键状态(如 Shift、Ctrl、Alt)。
Qt::KeyboardModifiers modifiers = event->modifiers(); // 获取修饰键的状态
2、键盘输入文本
// 获取键盘事件发生时,按键所对应的文本字符。如果是可打印字符,则返回字符本身。
QString text = event->text(); // 获取按键对应的文本字符(如 'A'、'1')
3、键盘输入码
// 获取按下键的 Unicode 编码(返回按下的键的 Unicode 值)。
ushort unicode = event->unicode(); // 获取键的 Unicode 编码
4、键盘状态
// 判断某个键是否被按下。返回 true 表示该键被按下,false 表示没有按下。
bool isKeyDown = event->isAutoRepeat(); // 判断是否是自动重复的按键事件
5、事件类型
// 获取事件的类型,返回值是 QEvent::Type 枚举值(如:QEvent::KeyPress、QEvent::KeyRelease)。
QEvent::Type eventType = event->type(); // 获取事件类型
6、事件处理控制
// 标记事件已被处理,停止事件继续传播。
event->accept(); // 事件已处理,停止传播
// 标记事件没有被处理,允许事件继续传播。
event->ignore(); // 允许事件继续传播
1.3 示例程序
第四章 定时器事件
定时器事件用于处理定时任务,定时器可以在指定时间间隔后触发某些操作,常用于定时刷新、定时检查等功能。
// 启动一个定时器,定时器在每隔指定毫秒数后触发。
// timerId:定时器的标识符,interval:时间间隔,单位是毫秒。
// void QWidget::startTimer(int interval); // 启动定时器
4.1 处理函数
// 当定时器触发时,调用该函数。通过 timerId 参数判断哪个定时器触发。
[virtual protected] void QWidget::timerEvent(QTimerEvent *event);
// timerId:触发的定时器标识符。
// 如果有多个定时器,可以通过 timerId 来区分。
4.2 API
// 启动一个定时器,定时器会每隔指定的时间间隔触发一次 timerEvent。
int timerId = startTimer(1000); // 启动定时器,时间间隔为 1000 毫秒(1秒)
// 停止指定的定时器,释放定时器资源。
killTimer(timerId); // 停止定时器
// 处理定时器触发的事件。
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == timerId) {
// 执行定时任务
}
}
4.3 示例程序
第五章 拖放事件
5.1 处理函数
事件介绍
// QEvent::DragEnter
// 当拖动文件进入到窗口/控件中时,触发该事件。对应的事件类是 QDragEnterEvent。
QEvent::DragEnter;
// QEvent::DragLeave
// 当拖动文件离开窗口/控件时,触发该事件。对应的事件类是 QDragLeaveEvent。
QEvent::DragLeave;
// QEvent::DragMove
// 当拖动文件在窗口/控件中移动时,触发该事件。对应的事件类是 QDragMoveEvent。
QEvent::DragMove;
// QEvent::Drop
// 当拖动文件在窗口/控件中释放时,触发该事件。对应的事件类是 QDropEvent。
QEvent::Drop;
事件处理函数
// dragEnterEvent(QDragEnterEvent *event)
// 当拖动对象进入控件区域时触发,通常用于判断拖动内容是否有效。
void QWidget::dragEnterEvent(QDragEnterEvent *event);
// dragLeaveEvent(QDragLeaveEvent *event)
// 当拖动对象离开控件区域时触发,通常用于处理离开时的操作。
void QWidget::dragLeaveEvent(QDragLeaveEvent *event);
// dragMoveEvent(QDragMoveEvent *event)
// 当拖动对象在控件区域内移动时触发,用于实时处理拖动中的逻辑。
void QWidget::dragMoveEvent(QDragMoveEvent *event);
// dropEvent(QDropEvent *event)
// 当拖放操作完成,目标控件接受数据时触发。
void QWidget::dropEvent(QDropEvent *event);
5.2 API
5.2.1 QTextEdit
QTextEdit
是一个富文本编辑器控件,支持编辑和显示多种格式的文本(如纯文本和富文本)。
// 设置文本内容
void setPlainText(const QString &text);
// 设置文本框中的纯文本内容(不支持富文本)。
void setHtml(const QString &html);
// 设置文本框中的HTML格式内容。
QString toPlainText() const;
// 获取当前文本框中的纯文本内容。
QString toHtml() const;
// 获取当前文本框中的HTML格式内容。
void append(const QString &text);
// 在文本框内容末尾追加文本。
void setAlignment(Qt::Alignment alignment);
// 设置文本的对齐方式,如左对齐、右对齐或居中。
void setTextColor(const QColor &color);
// 设置文本的颜色。
void setFont(const QFont &font);
// 设置文本的字体。
void setReadOnly(bool readOnly);
// 设置文本框是否为只读模式。
5.2.2 QDragEnterEvent
QDragEnterEvent
是拖动进入控件时触发的事件。用于检测拖动的对象类型和内容。
// 获取拖动数据的 MIME 类型
QMimeData *mimeData() const;
// 获取包含拖动数据的 `QMimeData` 对象。
bool isAccepted() const;
// 返回当前拖动事件是否被接受。
void accept() const;
// 接受当前拖动事件。
void ignore() const;
// 忽略当前拖动事件。
// 检查拖动的数据类型
bool hasUrls() const;
// 判断拖动的数据是否是 URL(通常用于文件拖放)。
bool hasText() const;
// 判断拖动的数据是否是文本。
bool hasImage() const;
// 判断拖动的数据是否是图片。
5.2.3 QDragLeaveEvent
QDragLeaveEvent
是当拖动对象离开控件区域时触发的事件。通常用于取消拖放时的界面反馈(例如,撤销高亮显示等)。
// 获取拖动事件中的 MIME 数据
QMimeData *mimeData() const;
// 获取包含拖动数据的 `QMimeData` 对象。
QPoint pos() const;
// 获取拖动对象离开时的鼠标位置。
void accept() const;
// 接受当前的拖放离开事件,通常表示我们处理了这个事件。
void ignore() const;
// 忽略当前的拖放离开事件,表示不处理这个事件。
5.2.4 QDropEvent
QDropEvent
是拖动操作完成时触发的事件。它处理拖放到控件中的数据。
// 获取拖动事件中的 MIME 数据
QMimeData *mimeData() const;
// 获取包含拖动数据的 `QMimeData` 对象。
// 获取当前拖动事件的目标位置
QPoint pos() const;
// 获取事件发生时相对于控件的坐标。
void accept() const;
// 接受当前的拖放操作。
void ignore() const;
// 忽略当前的拖放操作。
// 获取拖动放置的 URL
QList<QUrl> urls() const;
// 获取拖动事件中所有文件的 URL 列表。
QString text() const;
// 获取拖放操作中的文本数据(如果有)。
5.2.5 QMimeData
QMimeData
是一个用于封装拖放操作中的数据的类。它可以存储多种类型的数据,如文本、URL、图片等。
// 获取所有的URL列表(通常用于文件拖放)
QList<QUrl> urls() const;
// 获取所有拖动数据的 URL 列表。
// 判断是否包含文本数据
bool hasText() const;
// 判断 `QMimeData` 是否包含文本数据。
// 获取文本数据
QString text() const;
// 获取存储的文本数据。
// 判断是否包含图片数据
bool hasImage() const;
// 判断 `QMimeData` 是否包含图片数据。
// 设置文本数据
void setText(const QString &text);
// 设置 `QMimeData` 对象的文本数据。
// 设置URL数据
void setUrls(const QList<QUrl> &urls);
// 设置 `QMimeData` 对象的 URL 数据(通常用于文件拖放)。
5.3 示例程序
#include <QTextEdit>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QMimeData>
class MyTextEdit : public QTextEdit {
Q_OBJECT
public:
// 构造函数,初始化控件并启用拖放功能
MyTextEdit(QWidget *parent = nullptr) : QTextEdit(parent) {
setAcceptDrops(true); // 启用拖放功能,允许控件接受拖动的内容
}
protected:
// 处理拖动进入事件:当拖动的对象进入控件区域时触发
void dragEnterEvent(QDragEnterEvent *event) override {
// 判断拖动的数据是否包含文件URL
if (event->mimeData()->hasUrls()) {
// 如果是文件URL,接受该拖放操作
event->acceptProposedAction();
} else {
// 否则,忽略此拖放操作
event->ignore();
}
}
// 处理拖动离开事件:当拖动的对象离开控件区域时触发
void dragLeaveEvent(QDragLeaveEvent *event) override {
// 允许拖动离开控件时的事件,通常用于视觉反馈
event->accept();
}
// 处理拖动移动事件:当拖动的对象在控件区域内移动时触发
void dragMoveEvent(QDragMoveEvent *event) override {
// 在拖动过程中持续接受操作,允许用户继续拖动
event->acceptProposedAction();
}
// 处理拖放事件:当拖动的对象被放置在控件中时触发
void dropEvent(QDropEvent *event) override {
// 判断拖动的数据是否包含文件URL
if (event->mimeData()->hasUrls()) {
// 获取拖放的数据中所有的URL列表
QList<QUrl> urlList = event->mimeData()->urls();
// 如果URL列表不为空,则获取第一个文件的路径
if (!urlList.isEmpty()) {
QString filePath = urlList.first().toLocalFile(); // 转换为本地文件路径
QFile file(filePath); // 创建 QFile 对象以读取文件
if (file.open(QIODevice::ReadOnly)) { // 以只读模式打开文件
QTextStream in(&file); // 创建文本流以读取文件内容
setPlainText(in.readAll()); // 将文件内容显示到 QTextEdit 中
file.close(); // 关闭文件
}
}
event->acceptProposedAction(); // 接受并处理拖放操作
}
}
};
第六章 绘图事件
第七章 右键菜单事件
QEvent::ContextMenu
事件是在用户右键点击控件时触发的,它对应的事件类是 QContextMenuEvent
。通常在该事件中你会创建和显示一个上下文菜单(右键菜单)。
7.1 处理函数
void QWidget::contextMenuEvent(QContextMenuEvent *event)
7.2 基本用法
#include <QWidget>
#include <QMenu>
#include <QAction>
#include <QContextMenuEvent>
#include <QMessageBox>
class MyWidget : public QWidget {
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr) : QWidget(parent) {}
protected:
// 处理右键菜单事件
void contextMenuEvent(QContextMenuEvent *event) override {
// 创建右键菜单
QMenu menu(this);
// 添加菜单项
QAction *action1 = menu.addAction("菜单项 1");
QAction *action2 = menu.addAction("菜单项 2");
QAction *action3 = menu.addAction("菜单项 3");
// 连接菜单项的点击事件
connect(action1, &QAction::triggered, this, &MyWidget::onMenuItem1Clicked);
connect(action2, &QAction::triggered, this, &MyWidget::onMenuItem2Clicked);
connect(action3, &QAction::triggered, this, &MyWidget::onMenuItem3Clicked);
// 弹出菜单,位置为全局位置
menu.exec(event->globalPos());
}
private slots:
// 菜单项 1 的槽函数
void onMenuItem1Clicked() {
QMessageBox::information(this, "菜单项 1", "你点击了菜单项 1");
}
// 菜单项 2 的槽函数
void onMenuItem2Clicked() {
QMessageBox::information(this, "菜单项 2", "你点击了菜单项 2");
}
// 菜单项 3 的槽函数
void onMenuItem3Clicked() {
QMessageBox::information(this, "菜单项 3", "你点击了菜单项 3");
}
};
第八章 小项目 - 无边框窗口
8.1 项目概述
本节将通过一个小项目,展示如何创建一个无边框的窗口。在 Qt 中,窗口通常有边框和标题栏,但有时我们希望移除这些边框,创建一个自定义的、更加自由的窗口。我们将学习如何去掉窗口的边框、如何处理窗口的拖动、如何添加自定义的关闭按钮等。
8.2 无边框窗口的创建
8.2.1 基本概念
- 介绍什么是无边框窗口,如何去掉窗口的默认边框和标题栏。
- Qt 如何支持无边框窗口的创建。
8.2.2 实现代码
#include <QWidget>
#include <QMouseEvent>
#include <QApplication>
class MyWindow : public QWidget {
Q_OBJECT
public:
MyWindow(QWidget *parent = nullptr) : QWidget(parent) {
// 去除标题栏
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
// 设置无边框
setWindowFlags(Qt::FramelessWindowHint);
// 背景透明
setAttribute(Qt::WA_TranslucentBackground);
setFixedSize(400, 300); // 固定窗口大小
}
protected:
// 处理鼠标事件,实现窗口拖动
void mousePressEvent(QMouseEvent *event) override {
m_dragging = true;
m_dragStartPosition = event->globalPos() - frameGeometry().topLeft();
}
void mouseMoveEvent(QMouseEvent *event) override {
if (m_dragging) {
move(event->globalPos() - m_dragStartPosition);
}
}
void mouseReleaseEvent(QMouseEvent *event) override {
m_dragging = false;
}
private:
bool m_dragging = false;
QPoint m_dragStartPosition;
};
8.3 实现窗口拖动
8.3.1 拖动窗口的原理
- 介绍如何利用鼠标事件来实现无边框窗口的拖动功能。
- 鼠标按下时记录位置,鼠标移动时计算窗口的新位置,鼠标释放时结束拖动。
8.3.2 拖动代码
void MyWindow::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
m_dragging = true;
m_dragStartPosition = event->globalPos() - frameGeometry().topLeft();
}
}
void MyWindow::mouseMoveEvent(QMouseEvent *event) {
if (m_dragging) {
move(event->globalPos() - m_dragStartPosition);
}
}
void MyWindow::mouseReleaseEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
m_dragging = false;
}
}
8.4 添加自定义按钮
8.4.1 自定义关闭按钮
- 为无边框窗口添加一个自定义的关闭按钮。
- 如何通过自定义按钮触发关闭窗口的功能。
8.4.2 代码实现
#include <QPushButton>
#include <QHBoxLayout>
MyWindow::MyWindow(QWidget *parent) : QWidget(parent) {
setWindowFlags(Qt::FramelessWindowHint);
setFixedSize(400, 300);
// 创建关闭按钮
QPushButton *closeButton = new QPushButton("关闭", this);
connect(closeButton, &QPushButton::clicked, this, &QWidget::close);
QHBoxLayout *layout = new QHBoxLayout(this);
layout->addWidget(closeButton);
setLayout(layout);
}
8.5 无边框窗口的常见应用
无边框窗口的用途
- 无边框窗口通常用于浮动窗体、弹出窗口、游戏窗口等应用场景。
- 在一些现代应用中,去掉边框可以增强界面的美观和用户体验。
无边框窗口与透明背景的结合
- 结合透明背景,可以创建类似工具条、浮动提示框等界面。
- 使用无边框窗口时,可以选择透明或半透明背景,增加界面的灵活性和美观性。
- 感谢你赐予我前进的力量