Qt进阶开发:鼠标及键盘事件

一、Qt中事件的概念

  在 Qt 中,事件(Event) 是程序和用户、系统之间交互的一种机制。它是 Qt 应用响应用户操作(如鼠标点击、键盘输入、窗口变化、定时器等)的核心组成部分。Qt 通过其事件系统实现了灵活、统一的事件处理模型。

事件是继承自 QEvent 的对象,用来描述“发生了什么事情”。比如:
在这里插入图片描述

二、Qt中事件处理方式

  一个事件由一个特定的QEvent子类来表示,但是有时一个事件又包含多个事件类型,比如鼠标事件可以分为鼠标按下、双击和移动等多种操作。这些事件类型都由 QEvent类的枚举型QEvent::Type来表示,其中包含了一百多种事件类型,虽然QEvent的子类可以表示一个事件,但是却不能用来处理事件,那么应该怎样来处理一个事件呢?QCoreApplication类的notify()函数的帮助文档给出了5种处理事件的方法:

  1. 重新实现部件的paintEvent()、mousePressEvent()等事件处理函数。
  2. 重新实现notify()函数。这个函数功能强大,提供了完全的控制,可以在事件过滤器得到事件之前就获得它们。但是,它一次只能处理一个事件。
  3. 向QApplication对象上安装事件过滤器。因为一个程序只有一个QApplication对象,所以这样实现的功能与使用notify()函数是相同的,优点是可以同时处理多个事件。
  4. 重新实现event()函数。QObiect类的event()函数可以在事件到达默认的事件处理函数之前获得该事件。
  5. 在对象上安装事件过滤器。使用事件过滤器可以在一个界面类中同时处理不同子部件的不同事件。

在实际编程中,最常用的是方法一,其次是方法五。因为方法二需要继承自QApplication类;而方法三要使用一个全局的事件过滤器,这将减缓事件的传递,所以,虽然这两种方法功能很强大,但是却很少被用到。

三、重新实现部件的事件处理函数

3.1 常用事件处理函数

在这里插入图片描述

3.2 自定义控件处理鼠标和绘图事件

示例目标:

  • 鼠标点击时在点击位置画一个红色的圆
  • 每次点击都重新绘制控件

3.2.1 自定义类头文件(MyWidget.h)

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include <QPoint>
#include <QVector>

class MyWidget : public QWidget {
   
   
    Q_OBJECT
public:
    explicit MyWidget(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;

private:
    QVector<QPoint> points;  // 存储所有点击点
};
#endif // MYWIDGET_H

3.2.2 实现文件(MyWidget.cpp)

#include "MyWidget.h"
#include <QPainter>
#include <QMouseEvent>

MyWidget::MyWidget(QWidget *parent)
    : QWidget(parent) {
   
   
    setMinimumSize(400, 300);
}

void MyWidget::mousePressEvent(QMouseEvent *event) {
   
   
    if (event->button() == Qt::LeftButton) {
   
   
        points.append(event->pos());  // 保存点击位置
        update();  // 触发重绘
    }
}

void MyWidget::paintEvent(QPaintEvent *event) {
   
   
    Q_UNUSED(event);

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);

    painter.setPen(Qt::red);
    painter.setBrush(Qt::red);

    for (const QPoint &pt : points) {
   
   
        painter.drawEllipse(pt, 5, 5);  // 在每个点击点画个小圆
    }
}

3.2.3 使用自定义控件(main.cpp 示例)

#include "MyWidget.h"
#include <QApplication>

int main(int argc, char *argv[]) {
   
   
    QApplication a(argc, argv);
    MyWidget w;
    w.show();
    return a.exec();
}

3.3 常用事件处理函数说明

paintEvent():

  • 这是唯一推荐的绘图入口
  • 使用 QPainter 来进行绘图操作
  • 通过 update() 来主动触发重绘(会间接调用 paintEvent())

mousePressEvent():

  • 接收 QMouseEvent*,可以通过 event->pos() 获取鼠标坐标
  • event->button() 检测是哪个键(左、中、右)

其他可重写事件:

  • enterEvent() / leaveEvent():鼠标进入/离开控件
  • focusInEvent() / focusOutEvent():控件获取/失去焦点
  • wheelEvent():鼠标滚轮
  • keyReleaseEvent():键盘释放
  • contextMenuEvent():右键菜单

注意:update() ≠ repaint():update() 是异步(推荐);repaint() 是立即刷新。

四、重写notify()函数

  QCoreApplication::notify() 或 QApplication::notify() 是 Qt 事件系统中最底层的事件分发函数之一。重写它可以让你在任何事件被分发到对象前先拦截并处理它,因此功能极其强大,常用于:

  • 全局事件监控(比如捕获所有按键、鼠标事件)
  • 调试/日志记录所有事件
  • 自定义事件处理行为

注意:它一次只处理一个事件,而且要注意别滥用,因为它拦截的是所有事件,非常频繁。

notify() 函数的原型:

virtual bool QApplication::notify(QObject *receiver, QEvent *event) override;
  • receiver: 将要接收事件的对象
  • event: 即将被分发的事件
  • 返回值:如果你处理了事件,返回 true;否则通常应该调用父类的 notify() 再返回结果。

示例:重写 notify() 监听所有按键和鼠标事件
自定义 QApplication 类:

// MyApplication.h
#include <QApplication>
#include <QEvent>
#include <QDebug>

class MyApplication : public QApplication {
   
   
public:
    using QApplication::QApplication;

    bool notify(QObject *receiver, QEvent *event) override {
   
   
        // 监听所有键盘事件
        if (event->type() == QEvent::KeyPress) {
   
   
            qDebug() << "全局键盘事件:receiver =" << receiver << " key =" << static_cast<QKeyEvent *>(event)->key();
        }

        // 监听所有鼠标点击事件
        if (event->type() == QEvent::MouseButtonPress) {
   
   
            qDebug() << "全局鼠标点击事件:receiver =" << receiver;
        }

        // 一定要调用父类 notify,否则事件无法继续传递!
        return QApplication::notify(receiver, event);
    }
};

使用这个自定义 QApplication:

#include "MyApplication.h"
#include <QPushButton>
#include <QWidget>

int main(int argc, char *argv[]) {
   
   
    MyApplication app(argc, argv);

    QWidget window;
    QPushButton btn("Click me", &window);
    window.show();

    return app.exec();
}

notify() 与 eventFilter() 的区别:
在这里插入图片描述
注意事项:

  • 不要忘记调用 QApplication::notify() 否则事件会被截断!
  • notify() 中不能做太重的操作,否则会拖慢整个程序
  • 因为它是全局入口,处理频率非常高,要保证稳定性

五、QApplication对象上安装事件过滤器

  在 Qt 中,如果向 QApplication 对象安装事件过滤器,就可以实现类似 notify() 的全局事件监控功能。不过,相比于重写 notify(),使用事件过滤器(eventFilter()) 有几个明显优点:

  • 也能捕获整个应用中所有控件的事件
  • 不需要继承 QApplication
  • 可以封装多个过滤器类,分别处理不同的事件
  • 更安全:不会意外截断事件流(只拦截你关心的事件)

示例:向 QApplication 安装事件过滤器,监控鼠标和键盘事件
创建事件过滤器类(继承 QObject):


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值