Qt:事件处理与绘图详解

在 Qt 开发中,事件处理是交互逻辑的核心,绘图则是界面可视化的基础。本文将详细讲解 Qt 中的鼠标事件、定时器事件、绘图事件的处理方式,结合实战代码帮你掌握核心用法。

一、鼠标事件

当鼠标触发点击、移动等操作时,窗口会收到对应的鼠标事件,Qt 通过以Event结尾的事件处理函数响应这些操作,也可通过事件过滤器实现事件拦截。

1. 鼠标点击事件(mousePressEvent)

重写mousePressEvent函数,可获取鼠标点击的坐标和按键类型,并在 Label 上显示相关信息:

cpp 复制代码
void MyLabel::mousePressEvent(QMouseEvent *ev)
{
    // 获取鼠标点击坐标
    int x = ev->x();
    int y = ev->y();
    // 获取点击的按键
    Qt::MouseButton btn = ev->button();
    QString strButton = "";
    if (btn == Qt::LeftButton) strButton = "leftbutton";
    if (btn == Qt::RightButton) strButton = "Rightbutton";
    if (btn == Qt::MidButton) strButton = "Midbutton";

    // Label支持HTML格式显示
    QString str = QString("<h1><center>[%1,%2,%3]</center></h1>").arg(x).arg(y).arg(strButton);
    this->setText(str);
}

2. 鼠标移动事件(mouseMoveEvent)

鼠标移动事件需注意:ev->buttons()可获取按住的多个按键(位运算判断),而ev->button()仅能获取单次点击的按键。

cpp 复制代码
void MyLabel::mouseMoveEvent(QMouseEvent *ev)
{
    // 获取移动坐标
    int x = ev->x();
    int y = ev->y();
    // 获取按住的按键(支持多按键)
    Qt::MouseButtons btns = ev->buttons();
    QString strButton = "";
    if (btns & Qt::LeftButton) strButton += "leftbutton ";
    if (btns & Qt::RightButton) strButton += "Rightbutton ";
    if (btns & Qt::MidButton) strButton += "Midbutton ";

    // 显示移动信息
    QString str = QString("<h1><center>move[%1,%2,%3]</center></h1>").arg(x).arg(y).arg(strButton);
    this->setText(str);
}

3. 事件分发(event 函数)

所有事件先进入event函数,可在此拦截 / 重定向事件,返回true表示事件已处理(不再传递),返回false则事件继续传递到父窗口。

cpp 复制代码
bool MyLabel::event(QEvent *e)
{
    // 判断事件类型为鼠标移动
    if (e->type() == QEvent::MouseMove) {
        this->mouseMoveEvent(static_cast<QMouseEvent*>(e));
        return true; // 标记事件已处理
    }
    // 其他事件交给父类处理
    return QLabel::event(e);
}

4. 事件过滤器(eventFilter)

事件过滤器是更灵活的事件拦截方式,需先调用installEventFilter安装过滤器,再重写eventFilter函数:

cpp 复制代码
// 1. 安装事件过滤器(通常在构造函数中)
this->installEventFilter(this);

// 2. 重写事件过滤函数
bool MyLabel::eventFilter(QObject *watched, QEvent *event)
{
    // 拦截鼠标移动事件
    if (event->type() == QEvent::MouseMove) {
        return true; // 返回true表示拦截事件
    }
    // 其他事件不拦截
    return false;
}

二、定时器事件

定时器事件用于实现定时逻辑,Qt 提供两种定时器实现方式:原生timerEventQTimer类(推荐)。

1. 原生定时器(timerEvent)

  • startTimer(int ms):启动定时器,返回定时器 ID,参数为触发间隔(毫秒);

  • killTimer(int id):停止指定 ID 的定时器;

  • timerEvent(QTimerEvent *ev):定时器事件处理函数,通过ev->timerId()获取定时器 ID。

2. QTimer 类(推荐)

QTimer基于信号槽实现,使用更灵活,核心接口:

cpp 复制代码
// 创建定时器对象
QTimer *timer = new QTimer(this);
// 设置间隔(毫秒),绑定超时信号
connect(timer, &QTimer::timeout, this, [=]() {
    qDebug() << "定时器触发";
});
// 启动定时器
timer->start(1000); // 1秒触发一次
// 停止定时器
// timer->stop();

三、绘图事件(paintEvent)

绘图事件是 Qt 界面绘制的核心,窗口需要重绘时(如首次显示、大小改变)会触发paintEvent函数。

1. 核心绘图组件

|--------------|-----------------|
| 组件 | 作用 |
| QPainter | 画家(执行绘图操作) |
| QPen | 画笔(设置线条样式 / 颜色) |
| QBrush | 画刷(填充封闭图形) |
| QPaintDevice | 绘图设备(窗口 / 图片等) |

2. 基础绘图示例

重写paintEvent函数,实现画线、矩形、椭圆、文字等操作:

cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    // 创建画家,指定绘图设备为当前窗口
    QPainter painter(this);
    
    // 画家偏移(从(100,100)开始绘图)
    painter.translate(100, 100);

    // 配置画笔(红色、虚线、宽度3)
    QPen pen;
    pen.setColor(QColor(255, 0, 0));
    pen.setStyle(Qt::DashLine);
    pen.setWidth(3);
    painter.setPen(pen);

    // 配置画刷(青色、密集填充)
    QBrush brush;
    brush.setColor(Qt::cyan);
    brush.setStyle(Qt::Dense3Pattern);
    painter.setBrush(brush);

    // 绘制线条(起点(0,0),终点(100,100))
    painter.drawLine(0, 0, 100, 100);
    // 绘制矩形(左上角(0,0),宽100,高100)
    painter.drawRect(0, 0, 100, 100);
    // 绘制圆形(圆心(100,100),半径100)
    painter.drawEllipse(QPoint(100, 100), 100, 100);
    // 绘制文字
    painter.drawText(200, 100, "好好学习,天天向上");
}

3. 手动触发绘图事件

  • repaint():立即触发绘图事件(无优化,多次调用会多次触发);

  • update():优化版触发,多次调用仅触发一次(推荐);

  • ❗注意:禁止在paintEvent中调用repaint()/update(),会导致无限循环。

4. 绘图设备

Qt 支持多种绘图设备,常用的有:

(1)QPixmap(显示优化)

适合绘制界面图片,支持加载 / 保存:

cpp 复制代码
QPixmap pix(300, 300);
pix.fill(Qt::white); // 白色填充背景
QPainter painter(&pix);
painter.setPen(QColor(255, 0, 0));
painter.drawEllipse(QPoint(150, 150), 100, 100); // 画圆形
pix.save("D:\\qt\\pix.png"); // 保存图片
(2)QImage(像素级操作)

支持直接修改像素,适合图像处理:

cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    QImage img;
    img.load(":/pix.png"); // 加载图片
    // 修改指定区域像素颜色
    for (int x = 50; x < 100; x++) {
        for (int y = 50; y < 100; y++) {
            img.setPixelColor(x, y, QColor(255, 123, 0));
        }
    }
    // 绘制修改后的图片
    QPainter painter(this);
    painter.drawImage(0, 0, img);
}
(3)QPicture(记录绘图步骤)

可记录QPainter的绘图操作,后续可重现绘制过程,适合动态绘图回放。

总结

本文覆盖了 Qt 中三大核心事件:

  1. 鼠标事件:通过重写事件函数 / 事件过滤器实现交互;

  2. 定时器事件:推荐使用QTimer实现定时逻辑;

  3. 绘图事件:基于QPainter/QPen/QBrush实现界面绘制,支持多种绘图设备。

掌握这些知识点,可实现 Qt 界面的交互逻辑和自定义绘制,为复杂桌面应用开发打下基础。

相关推荐
驭渊的小故事1 小时前
Java数据结构集合框架(顺序表(ArrayList)的详细解析)(两千字详细解析)
java·开发语言
weixin_444012931 小时前
Go语言怎么防SQL注入_Go语言SQL注入防护教程【深入】
jvm·数据库·python
爱编程的小新☆1 小时前
Langchain4j对话记忆
数据库·缓存·持久化存储·langchain4j
m0_470857641 小时前
C#怎么实现蓝牙设备搜索_C#如何开发Bluetooth应用【指南】
jvm·数据库·python
cen__y1 小时前
Linux知识点复习总结(2)
linux·运维·服务器·c语言·开发语言
方便面不加香菜1 小时前
C++ 日期类的实现
开发语言·c++
曦夜日长1 小时前
Linux系统篇,开发工具(三):文件翻译的思路重构、库的深入理解、文件链接时区别与细节
linux·数据库·重构
雁迟1 小时前
第五章:条件判断与分支语句
开发语言·r语言
2303_821287382 小时前
在 Go 中声明包级全局 Map 的正确方法
jvm·数据库·python