Qt 事件:覆盖介绍、处理、各种类型及运用全详解

文章目录

  • [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

    1. 选中项目名称,右键选择"add new...",选择"C++ Class"。
    2. 类名设为 mybutton,基类初始选 QWidget,后续修改为 QLabel。
    3. 勾选"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
  1. 在 UI 中选中 mybutton,右键选择"提升为..."。
  2. 提升的类名称设为 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)。
  1. 单个按键示例
  • 调用 key() 获取普通按键
  • 示例:
cpp 复制代码
void Widget::keyPressEvent(QKeyEvent *event)
{
    if(event->key()==Qt::Key_C)
    {
        qDebug()<<"C"<<endl;
    }
}
  1. 组合按键示例

修改键枚举

枚举值 描述
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 中定时器用于周期性执行操作或制作动画,分为 QTimerEventQTimer 两类。

1.5.1 QTimerEvent

  • 通过 startTimer(int interval) 开启定时器,返回定时器 ID。
  • 定时时间到达时触发 timerEvent(QTimerEvent *e),通过 timerId() 获取定时器 ID 区分多个定时器。
  • 间隔单位:毫秒。
  1. 示例:定时器计数
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()(定时时间到达)。
  1. 示例:启停计数
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() 返回窗口变化后的位置
  1. 示例:移动放大窗口
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 事件分发器(拓展)

  1. 概述

    事件分发器(Event Dispatcher)用于将事件传递给对应对象,可拦截事件。所有继承 QObject 的类均可重写 bool event(QEvent *e) 实现事件捕获和拦截。

  2. 工作原理

    1. 所有事件都会进入 event(QEvent *e) 函数。
    2. 函数根据事件类型(e->type())调用对应事件处理函数(如 mousePressEvent)。
    3. 返回值:true 表示拦截事件(不向下分发),false 表示继续分发。
  3. 核心事件类型

    通过 QEvent::Type 枚举定义,常见类型包括:QEvent::MouseButtonPress(鼠标按下)、QEvent::KeyPress(按键按下)、QEvent::Paint(绘屏)等。

  4. 示例:拦截鼠标按下事件

    • 步骤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 事件过滤器(拓展)

  1. 概述

    事件过滤器在事件到达分发器前拦截,无需继承目标控件,可给任意对象安装过滤器,更灵活。

  2. 使用步骤

    1. 给目标对象安装过滤器:target->installEventFilter(filterObj)
    2. 重写过滤器对象的 eventFilter(QObject *obj, QEvent *e) 函数。
  3. 示例:拦截 Label 的鼠标按下事件

    • 步骤1:新建项目与 UI 设计
      • 新建 QWidget 项目,勾选 UI,拖入一个 Label。
    • 步骤2:添加自定义类 myLabel
      • 类名 myLabel,基类 QLabel,生成 mylabel.h 和 mylabel.cpp。
    • 步骤3:提升 UI 中的 Label
      • 选中 Label 右键"提升为...",类名设为 myLabel,头文件设为 mylabel.h。
    • 步骤4:在 mylabel.h 中声明事件和分发器(可选)
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);
}
相关推荐
追烽少年x2 小时前
第三章 异常(一)
c++
苦藤新鸡2 小时前
14.合并区间(1,3)(2,5)=(1,5)
c++·算法·leetcode·动态规划
人道领域2 小时前
JavaWeb从入门到进阶(前端工程化)
前端
shughui2 小时前
APP、Web、H5、iOS与Android的区别及关系
android·前端·ios
Amumu121382 小时前
React Router 6介绍
前端·react.js·前端框架
山峰哥2 小时前
SQL调优实战:让查询效率飙升10倍的降本密码
服务器·前端·数据库·sql·编辑器·深度优先
_OP_CHEN3 小时前
【算法基础篇】(四十八)突破 IO 与数值极限:快速读写 +__int128 实战指南
c++·算法·蓝桥杯·算法竞赛·快速读写·高精度算法·acm/icpc
玖釉-3 小时前
[Vulkan 实战] 深入解析 Vulkan Compute Shader:实现高效 N-Body 粒子模拟
c++·windows·图形渲染
云泽8083 小时前
深入浅出 C++ 继承:从基础概念到模板、转换与作用域的实战指南
开发语言·c++