攀枝花市网站建设_网站建设公司_HTML_seo优化
2025/12/19 18:49:08 网站建设 项目流程

Qt事件核心概述

事件的本质

Qt是事件驱动的编程框架,程序的所有交互行为(如点击鼠标、按下键盘、窗口重绘)均由事件触发。QEvent是所有事件类的基类,封装了事件的类型、状态等核心信息,贯穿程序整个生命周期。

常见事件类型

事件类型 触发场景
键盘事件 键盘按键按下/松开
鼠标事件 鼠标移动、按键按下/松开、双击、滚轮滚动
绘图事件 窗口显示/隐藏、被遮挡后恢复、调用update()/repaint()
拖放事件 鼠标拖拽控件/数据并释放
定时器事件 调用startTimer()后指定时间到达时
进入/离开事件 鼠标指针移入/移出Widget区域
移动事件 Widget的位置(x/y坐标)发生改变时
显示/隐藏事件 Widget调用show()/hide()或被父窗口控制显示/隐藏时
焦点事件 Widget获取/失去焦点(如点击输入框、切换窗口)
自定义事件 程序内部自定义的业务事件(如数据加载完成、状态变更)

事件调度流程(核心)

  • 程序启动后创建QApplication对象(Qt应用唯一入口),该对象初始化事件循环(Event Loop) 并进入等待状态;
  • 用户操作(如点击鼠标)或系统触发(如定时器到期)生成事件,事件被封装为QEvent子类对象并加入事件队列
  • 事件循环从队列中取出事件,根据事件类型和目标对象(QObject子类)分发事件;
  • 目标对象通过重写事件处理函数响应事件,处理完成后事件自动销毁,事件循环继续处理下一个事件;
  • 事件处理过程中可能生成新事件(如点击按钮弹出对话框),新事件会加入队列等待处理;
  • 程序退出时(如调用qApp->quit()),事件循环终止,程序结束。

事件处理核心接口

class Q_CORE_EXPORT QObject
{
public:/*** @brief 事件分发核心函数,所有事件都会先经过此函数* @param event 待处理的事件对象(QEvent子类,如QMouseEvent、QKeyEvent)* @return bool 返回true表示事件已处理,不再向下传递;返回false表示未处理,交由父类处理* @note 子类重写时,未处理的事件必须调用父类的event(),否则会破坏事件传递逻辑*/virtual bool event(QEvent *event);/*** @brief 事件过滤器函数,用于拦截目标对象的事件* @param watched 被监视的目标对象(如某个输入框、按钮)* @param event 被拦截的事件对象* @return bool 返回true表示拦截事件(不再传递给watched);返回false表示放行* @note 使用前需通过`watched->installEventFilter(this)`安装过滤器,适用于全局拦截多个对象的事件*/virtual bool eventFilter(QObject *watched, QEvent *event);
};class Q_WIDGETS_EXPORT QWidget : public QObject, public QPaintDevice
{
public:/*** @brief 重写QObject::event,扩展Widget专属事件的分发逻辑* @param event 待处理的事件对象* @return bool 事件处理状态(同QObject::event)* @note QWidget会优先处理鼠标、键盘、绘图等可视化相关事件,再传递给父类*/bool event(QEvent *event) override;
};

鼠标事件(QMouseEvent/QWheelEvent)

核心概念

  • QMouseEvent:处理鼠标左键/右键/中键的按下、松开、双击、移动;
  • QWheelEvent:单独处理鼠标滚轮滚动(不属于QMouseEvent);
  • 鼠标移动事件默认仅在按下鼠标按键时触发,调用setMouseTracking(true)可开启“无按键移动跟踪”;
  • 鼠标按下时,Qt会自动“捕捉”鼠标轨迹,直到最后一个按键松开,期间父窗口持续接收鼠标事件。

核心处理函数(QWidget虚函数)

/*** @brief 鼠标按下事件处理函数,鼠标按键按下时触发* @param event 鼠标事件对象,包含按键类型、坐标、修饰键(如Ctrl/Shift)等信息* @return void 无返回值* @note event->button()返回按下的具体按键(Qt::LeftButton/RightButton/MidButton),仅单次按下有效;*       event->buttons()返回当前所有按下的按键(适用于多键同时按下,如左键+右键)*/
virtual void mousePressEvent(QMouseEvent *event);/*** @brief 鼠标松开事件处理函数,鼠标按键松开时触发* @param event 鼠标事件对象* @return void 无返回值* @note 与mousePressEvent配对,通常用于完成“点击”逻辑(按下+松开)*/
virtual void mouseReleaseEvent(QMouseEvent *event);/*** @brief 鼠标双击事件处理函数,连续快速按下/松开同一按键时触发* @param event 鼠标事件对象* @return void 无返回值* @note 双击的速度由系统设置(如Windows的“鼠标双击速度”)决定,Qt会自动识别*/
virtual void mouseDoubleClickEvent(QMouseEvent *event);/*** @brief 鼠标移动事件处理函数,鼠标位置变化时触发* @param event 鼠标事件对象,event->pos()返回相对于当前Widget的坐标,event->globalPos()返回屏幕坐标* @return void 无返回值* @note 默认需按下按键才触发,调用setMouseTracking(true)后无按键也触发*/
virtual void mouseMoveEvent(QMouseEvent *event);/*** @brief 鼠标滚轮事件处理函数,滚轮滚动时触发* @param event 滚轮事件对象,Qt5.15+推荐使用angleDelta()(返回QPoint,x=水平滚轮,y=垂直滚轮),传统delta()返回整数值(正向上/负向下,每格120)* @return void 无返回值*/
virtual void wheelEvent(QWheelEvent *event);/*** @brief 鼠标进入Widget区域事件处理函数* @param event 通用事件对象(无额外鼠标参数)* @return void 无返回值*/
virtual void enterEvent(QEvent *event);/*** @brief 鼠标离开Widget区域事件处理函数* @param event 通用事件对象* @return void 无返回值*/
virtual void leaveEvent(QEvent *event);

示例代码

#include <QMainWindow>
#include <QMouseEvent>
#include <QDebug>class MainWindow : public QMainWindow
{Q_OBJECT
public:/*** @brief 构造函数,初始化鼠标跟踪* @param parent 父窗口指针,用于内存管理*/MainWindow(QWidget *parent = nullptr) : QMainWindow(parent){setMouseTracking(true); // 开启无按键鼠标移动跟踪}protected:void mousePressEvent(QMouseEvent *event) override{// 判断按下的按键类型if (event->button() == Qt::LeftButton){qDebug() << "鼠标左键按下,位置:" << event->pos();}else if (event->button() == Qt::RightButton){qDebug() << "鼠标右键按下,位置:" << event->pos();}else if (event->button() == Qt::MidButton){qDebug() << "鼠标中键按下,位置:" << event->pos();}}void mouseReleaseEvent(QMouseEvent *event) override{qDebug() << "鼠标按键松开,按键:" << event->button() << ",位置:" << event->pos();}void mouseDoubleClickEvent(QMouseEvent *event) override{qDebug() << "鼠标双击,按键:" << event->button();}void mouseMoveEvent(QMouseEvent *event) override{static int count = 0;qDebug() << "鼠标移动,次数:" << ++count << ",位置:" << event->pos();}void wheelEvent(QWheelEvent *event) override{// Qt5.15+推荐使用angleDelta()QPoint delta = event->angleDelta();if (delta.y() > 0){qDebug() << "滚轮向上滚动,偏移量:" << delta.y();}else{qDebug() << "滚轮向下滚动,偏移量:" << delta.y();}}void enterEvent(QEvent *event) override{qDebug() << "鼠标进入窗口";}void leaveEvent(QEvent *event) override{qDebug() << "鼠标离开窗口";}
};

image

键盘事件(QKeyEvent)

核心概念

键盘事件触发于按键按下/松开,需重写keyPressEvent/keyReleaseEvent处理;

  • 只有获得焦点的Widget才能接收键盘事件(可通过setFocusPolicy(Qt::StrongFocus)设置焦点策略);
  • QKeyEvent包含按键值、修饰键(Ctrl/Shift/Alt)、是否自动重复(长按按键触发)等信息。

核心处理函数

/*** @brief 按键按下事件处理函数,按键按下时触发* @param event 键盘事件对象,包含按键值、修饰键等信息* @return void 无返回值* @note event->key()返回按键枚举值(如Qt::Key_Left/Key_Right/Key_A);*       event->modifiers()返回修饰键(如Qt::ControlModifier/Ctrl、Qt::ShiftModifier/Shift);*       event->isAutoRepeat()判断是否为长按自动重复触发(如长按A键连续输入)*/
virtual void keyPressEvent(QKeyEvent *event);/*** @brief 按键松开事件处理函数,按键松开时触发* @param event 键盘事件对象* @return void 无返回值* @note 与keyPressEvent配对,适用于“按下触发动作,松开恢复”的场景(如游戏按键)*/
virtual void keyReleaseEvent(QKeyEvent *event);

示例代码(窗口移动+快捷键)

#include <QWidget>
#include <QKeyEvent>
#include <QPoint>
#include <QDebug>class MyWidget : public QWidget
{Q_OBJECT
public:/*** @brief 构造函数,设置焦点策略* @param parent 父部件指针*/MyWidget(QWidget *parent = nullptr) : QWidget(parent){setFocusPolicy(Qt::StrongFocus); // 允许获取焦点,才能接收键盘事件resize(400, 300);}protected:void keyPressEvent(QKeyEvent *event) override{// 跳过自动重复的事件(长按按键)if (event->isAutoRepeat()){return;}// 组合键:Ctrl+Q退出程序if (event->key() == Qt::Key_Q && (event->modifiers() & Qt::ControlModifier)){qDebug() << "Ctrl+Q触发,程序退出";close();return;}// 方向键移动窗口QPoint pos = this->pos(); // 获取当前窗口位置if (event->key() == Qt::Key_Left){pos.setX(pos.x() - 10); // 左移10像素move(pos);}else if (event->key() == Qt::Key_Right){pos.setX(pos.x() + 10); // 右移10像素move(pos);}else if (event->key() == Qt::Key_Up){pos.setY(pos.y() - 10); // 上移10像素move(pos);}else if (event->key() == Qt::Key_Down){pos.setY(pos.y() + 10); // 下移10像素move(pos);}else{// 未处理的事件交给父类QWidget::keyPressEvent(event);}}void keyReleaseEvent(QKeyEvent *event) override{if (event->isAutoRepeat()){return;}qDebug() << "按键松开:" << event->key() << "(枚举值)";}
};

image

定时器事件(QTimerEvent)

核心概念

  • QTimerEvent是定时器触发的事件,底层基于startTimer()实现,轻量但功能简单;
  • 复杂定时场景推荐使用QTimer(支持信号槽、单次触发、暂停等);
  • 每个定时器有唯一ID,通过startTimer()创建,killTimer()销毁,析构时需手动停止避免内存泄漏。

核心接口与函数

/*** @brief 启动定时器,创建一个定时触发的事件源* @param interval 定时间隔(毫秒),如1000表示每1秒触发一次* @return int 成功返回定时器ID(非负整数),失败返回-1* @note 定时器ID是唯一标识,需保存用于后续停止定时器*/
int QObject::startTimer(int interval);/*** @brief 停止指定ID的定时器,终止定时事件触发* @param id 要停止的定时器ID(startTimer的返回值)* @return void 无返回值* @note 停止不存在的ID不会报错,建议停止前检查ID有效性*/
void QObject::killTimer(int id);/*** @brief 定时器事件处理函数,定时间隔到达时触发* @param event 定时器事件对象,event->timerId()返回触发的定时器ID* @return void 无返回值* @note 需重写此函数区分不同ID的定时器逻辑*/
virtual void QObject::timerEvent(QTimerEvent *event);

示例代码(多定时器+动态启停)

#include <QObject>
#include <QTimerEvent>
#include <QDebug>class TimerObject : public QObject
{Q_OBJECT
public:/*** @brief 构造函数,启动两个定时器* @param parent 父对象指针*/TimerObject(QObject *parent = nullptr) : QObject(parent){// 启动两个定时器:1秒/次、5秒/次m_timer1Id = startTimer(1000); // 1000ms = 1sm_timer2Id = startTimer(5000); // 5000ms = 5sqDebug() << "定时器启动,ID1:" << m_timer1Id << ",ID2:" << m_timer2Id;}/*** @brief 析构函数,停止所有定时器*/~TimerObject() override{// 析构时停止所有定时器(避免内存泄漏)if (m_timer1Id != -1){killTimer(m_timer1Id);}if (m_timer2Id != -1){killTimer(m_timer2Id);}}protected:void timerEvent(QTimerEvent *event) override{// 区分不同定时器IDif (event->timerId() == m_timer1Id){static int count = 0;qDebug() << "1秒定时器触发,次数:" << ++count;// 触发10次后停止该定时器if (count >= 10){killTimer(m_timer1Id);m_timer1Id = -1; // 标记为已停止qDebug() << "1秒定时器已停止";}}else if (event->timerId() == m_timer2Id){static int count = 0;qDebug() << "5秒定时器触发,次数:" << ++count;}else{// 未知定时器ID,交给父类处理QObject::timerEvent(event);}}private:int m_timer1Id = -1; // 1秒定时器ID,初始值-1表示未启动int m_timer2Id = -1; // 5秒定时器ID
};// 主函数调用示例
#include <QApplication>
int main(int argc, char *argv[])
{QApplication app(argc, argv);TimerObject timerObj;return app.exec();
}

image

自定义事件

核心概念

  • 自定义事件是QEvent的子类,用于实现程序内部的“自定义信号”(比信号槽更灵活,支持事件队列、优先级);
  • 事件类型需使用QEvent::User及以上(QEvent::UserQEvent::MaxUser为用户自定义范围);
  • 发送方式:postEvent()(异步,加入事件队列)、sendEvent()(同步,立即处理)。

核心接口与完整示例

#include <QApplication>
#include <QWidget>
#include <QEvent>
#include <QDebug>/*** @brief 自定义事件类,继承QEvent并指定唯一事件类型* @note 事件类型需大于等于QEvent::User(推荐QEvent::User + 自定义偏移,避免冲突)*/
class CustomEvent : public QEvent
{
public:/*** @brief 自定义事件构造函数* @param data 自定义数据(示例:整数型业务数据)* @note 事件类型固定为QEvent::User + 1,需保证全局唯一*/CustomEvent(int data) : QEvent(Type(QEvent::User + 1)), m_data(data) {}/*** @brief 获取自定义事件的业务数据* @return int 存储的整数数据*/int getData() const { return m_data; }private:int m_data; // 自定义业务数据
};/*** @brief 事件接收者类,重写event()或customEvent()处理自定义事件*/
class EventReceiver : public QWidget
{Q_OBJECT
public:EventReceiver(QWidget *parent = nullptr) : QWidget(parent) {}protected:/*** @brief 方式1:重写event()函数处理自定义事件(通用方式)* @param event 待处理的事件对象* @return bool 事件是否被处理*/bool event(QEvent *event) override{// 判断事件类型是否为自定义事件if (event->type() == QEvent::User + 1){CustomEvent *customEvent = dynamic_cast<CustomEvent*>(event);if (customEvent){qDebug() << "通过event()接收自定义事件,数据:" << customEvent->getData();}return true; // 事件已处理,不再传递}// 其他事件交给父类处理return QWidget::event(event);}/*** @brief 方式2:重写customEvent()函数(Qt提供的自定义事件专用处理函数)* @param event 待处理的自定义事件对象* @return void 无返回值* @note 仅处理类型为QEvent::User及以上的事件,无需手动判断类型*/void customEvent(QEvent *event) override{CustomEvent *customEvent = dynamic_cast<CustomEvent*>(event);if (customEvent){qDebug() << "通过customEvent()接收自定义事件,数据:" << customEvent->getData();}// 调用父类函数,确保事件传递(可选)QWidget::customEvent(event);}
};int main(int argc, char *argv[])
{QApplication app(argc, argv);/*** @brief 创建事件接收者对象* @note EventReceiver是自定义的QWidget子类,重写了event()和customEvent()函数,用于处理自定义事件;*       该对象创建在栈上,生命周期由作用域管理(main函数结束时自动析构);*       调用show()后,对象成为可视化Widget,进入Qt的事件循环体系,具备接收和处理事件的能力;*       只有继承QObject的类(QWidget是QObject子类)才能接收Qt事件,非QObject类无法处理事件。*/EventReceiver receiver;receiver.show();// 方式1:异步发送事件(推荐)CustomEvent *asyncEvent = new CustomEvent(100); // 堆上创建,Qt自动销毁/*** @brief 异步发送事件(核心函数)* @param receiver 事件接收对象(必须是QObject子类,如EventReceiver、QWidget等),事件最终会分发到该对象的event()函数* @param event 待发送的事件对象(需堆上new创建,Qt会在事件处理完成后自动销毁,无需手动delete)* @param priority 事件优先级,可选值:*                 - Qt::HighEventPriority(高优先级,优先处理)*                 - Qt::NormalEventPriority(默认,常规优先级)*                 - Qt::LowEventPriority(低优先级,最后处理)* @return void 无返回值* @note 1. 异步特性:事件不会立即处理,而是加入Qt的全局事件队列,等待事件循环轮询处理;*       2. 内存安全:事件对象必须堆上创建(new),Qt内部会接管内存,处理完后自动delete,避免内存泄漏;*       3. 线程安全:可跨线程发送事件,Qt会自动将事件投递到接收者所在线程的事件队列;*       4. 适用场景:非紧急事件(如自定义业务事件、UI更新事件),避免阻塞主线程。*/QCoreApplication::postEvent(&receiver, asyncEvent, Qt::HighEventPriority);// 方式2:同步发送事件CustomEvent syncEvent(200); // 栈上创建,无需手动销毁/*** @brief 同步发送事件(核心函数)* @param receiver 事件接收对象(QObject子类),事件会立即分发到该对象的event()函数* @param event 待发送的事件对象(推荐栈上创建,无需手动销毁,函数返回后可复用或自动析构)* @return bool 事件是否被接收者处理(即receiver的event()函数返回true表示已处理,false表示未处理)* @note 1. 同步特性:函数调用后会立即触发receiver的event()函数,直到事件处理完成才返回;*       2. 内存安全:事件对象推荐栈上创建(如示例中的syncEvent),若堆上创建需手动delete,Qt不会接管;*       3. 无优先级:同步事件跳过事件队列,直接处理,无视优先级设置;*       4. 线程限制:若接收者在其他线程,不能跨线程调用sendEvent,会导致未定义行为(需用postEvent);*       5. 适用场景:紧急事件(如必须立即响应的系统事件),但需避免在主线程同步发送耗时事件(阻塞UI)。*/QCoreApplication::sendEvent(&receiver, &syncEvent);return app.exec();
}

image

关键说明

  • postEvent():事件对象需用new创建,Qt会在事件处理完成后自动销毁,避免内存泄漏;
  • sendEvent():事件对象建议用栈创建,同步调用后不会自动销毁,手动delete会崩溃;
  • 优先级:高优先级事件会先于低优先级事件被处理,适用于紧急业务场景(如系统告警)。

绘图事件(QPaintEvent)与QPainter

核心概念

  • QPaintEvent:触发于Widget需要重绘时(如显示、被遮挡、调用update()),必须重写paintEvent()处理;
  • QPainter:Qt绘图核心类,支持绘制图形、文本、图像,提供画笔(QPen)、画刷(QBrush)、坐标变换等功能;
  • 绘图流程:创建QPainter → 设置绘图属性(画笔、画刷) → 调用绘制函数 → 结束绘图(自动或手动end())。

QPainter常用接口

#include <QPainter>
#include <QPen>
#include <QBrush>
#include <QFont>
#include <QImage>/*** @brief QPainter核心接口示例(结合paintEvent使用)*/
void paintEvent(QPaintEvent *event) override
{Q_UNUSED(event);QPainter painter(this); // 创建绘图对象,绑定当前Widget// 1. 基础设置:抗锯齿(提升绘制质量)painter.setRenderHint(QPainter::Antialiasing, true);// 2. 画笔设置(绘制轮廓、线条)QPen pen;pen.setColor(Qt::red); // 颜色pen.setWidth(2);       // 线宽pen.setStyle(Qt::DashLine); // 线型(实线、虚线等)painter.setPen(pen);// 3. 画刷设置(填充图形)QBrush brush;brush.setColor(Qt::yellow); // 填充颜色brush.setStyle(Qt::SolidPattern); // 填充样式(实心、渐变等)painter.setBrush(brush);// 4. 绘制基础图形painter.drawRect(10, 10, 200, 100); // 矩形(x,y,宽,高)painter.drawEllipse(250, 10, 100, 100); // 椭圆(x,y,宽,高——在矩形内)painter.drawLine(10, 150, 350, 150); // 直线(起点x,y → 终点x,y)// 5. 绘制文本QFont font("Arial", 16, QFont::Bold);painter.setFont(font);painter.setPen(Qt::blue);painter.drawText(QRect(10, 180, 350, 50), Qt::AlignCenter, "Qt绘图示例");// 6. 绘制图像QImage image("test.png"); // 加载图像(需确保路径正确)if (!image.isNull()){painter.drawImage(QPoint(10, 250), image.scaled(100, 100)); // 缩放绘制}// 7. 坐标变换(平移、旋转、缩放)painter.save(); // 保存当前绘图状态painter.translate(200, 300); // 平移坐标系(原点移至(200,300))painter.rotate(45); // 旋转45度painter.scale(0.8, 0.8); // 缩放0.8倍painter.drawRect(-50, -50, 100, 100); // 基于新坐标系绘制painter.restore(); // 恢复之前的绘图状态// 8. 绘制路径(复杂图形)QPainterPath path;path.moveTo(300, 250);path.lineTo(350, 300);path.lineTo(300, 350);path.closeSubpath(); // 闭合路径painter.setBrush(Qt::green);painter.drawPath(path);
}

完整绘图示例(自定义绘图Widget)

#include <QWidget>
#include <QPainter>
#include <QPen>
#include <QBrush>
#include <QFont>class PaintWidget : public QWidget
{Q_OBJECT
public:PaintWidget(QWidget *parent = nullptr) : QWidget(parent){resize(400, 400);setWindowTitle("Qt绘图示例");}protected:void paintEvent(QPaintEvent *event) override{Q_UNUSED(event);QPainter painter(this);// 1. 绘制带边框的矩形painter.setPen(QPen(Qt::red, 2));painter.setBrush(Qt::yellow);painter.drawRect(20, 20, 150, 100);// 2. 绘制圆形(椭圆的特殊情况)painter.setPen(QPen(Qt::blue, 1));painter.setBrush(Qt::cyan);painter.drawEllipse(200, 20, 100, 100);// 3. 绘制文本painter.setPen(Qt::black);painter.setFont(QFont("宋体", 14));painter.drawText(20, 150, "矩形区域");painter.drawText(220, 150, "圆形区域");// 4. 绘制三角形(通过路径)QPainterPath path;path.moveTo(50, 200);path.lineTo(150, 200);path.lineTo(100, 280);path.closeSubpath();painter.setPen(Qt::green);painter.setBrush(Qt::lightGray);painter.drawPath(path);// 5. 绘制渐变背景(线性渐变)QLinearGradient gradient(0, 300, width(), 300);gradient.setColorAt(0, Qt::red);gradient.setColorAt(0.5, Qt::yellow);gradient.setColorAt(1, Qt::blue);painter.setBrush(gradient);painter.setPen(Qt::NoPen);painter.drawRect(0, 300, width(), 100);}
};// 主函数调用
#include <QApplication>
int main(int argc, char *argv[])
{QApplication app(argc, argv);PaintWidget widget;widget.show();return app.exec();
}

image

自定义部件(Custom Widget)

核心思路

通过继承QWidget或其子类(如QPushButton),重写paintEvent()(绘图)、mousePressEvent()(交互)等函数,实现自定义功能的部件。

示例1:基础圆形部件

// MyCircleWidget.h
#include <QWidget>
#include <QPainter>class MyCircleWidget : public QWidget
{Q_OBJECT
public:explicit MyCircleWidget(QWidget *parent = nullptr) : QWidget(parent){setMinimumSize(100, 100); // 设置最小尺寸}protected:void paintEvent(QPaintEvent *event) override{Q_UNUSED(event);QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing); // 抗锯齿// 绘制黄色圆形,蓝色边框painter.setPen(QPen(Qt::blue, 3));painter.setBrush(Qt::yellow);// 调整圆形位置,与Widget边界有10像素间距QRect circleRect = rect().adjusted(10, 10, -10, -10);painter.drawEllipse(circleRect);}
};// 使用方式
#include <QApplication>
int main(int argc, char *argv[])
{QApplication app(argc, argv);MyCircleWidget circleWidget;circleWidget.resize(200, 200);circleWidget.show();return app.exec();
}

image

示例2:圆形进度条(带百分比)

#include <QWidget>
#include <QPainter>
#include <QTimer>
#include <QFont>
#include <QtMath>class CircleProgressBar : public QWidget
{Q_OBJECT
public:explicit CircleProgressBar(QWidget *parent = nullptr) : QWidget(parent), m_progress(0){setMinimumSize(150, 150);// 模拟进度增长(实际使用时可通过setProgress()设置)QTimer *timer = new QTimer(this);// 修复:捕获列表添加 this 和 timerconnect(timer, &QTimer::timeout, this, [this, timer]() {setProgress(m_progress + 1);if (m_progress >= 100) timer->stop();});timer->start(50);}/*** @brief 设置进度值* @param progress 进度(0-100)*/void setProgress(int progress){m_progress = qBound(0, progress, 100); // 限制在0-100update(); // 触发重绘}protected:void paintEvent(QPaintEvent *event) override{Q_UNUSED(event);QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);int width = this->width();int height = this->height();int radius = qMin(width, height) / 2 - 10; // 圆半径(减去边框间距)QPoint center(width / 2, height / 2); // 圆心// 1. 绘制背景圆(灰色边框)painter.setPen(QPen(Qt::lightGray, 8));painter.setBrush(Qt::NoBrush);painter.drawEllipse(center, radius, radius);// 2. 绘制进度圆弧(蓝色)painter.setPen(QPen(Qt::blue, 8));// 圆弧范围:从-90度(顶部)开始,顺时针绘制m_progress%的圆弧int startAngle = -90 * 16; // Qt中角度以1/16度为单位int spanAngle = m_progress * 3.6 * 16; // 360度 * m_progress% = 进度角度painter.drawArc(center.x() - radius, center.y() - radius,radius * 2, radius * 2,startAngle, spanAngle);// 3. 绘制百分比文本painter.setPen(Qt::black);painter.setFont(QFont("Arial", 18, QFont::Bold));QString text = QString("%1%").arg(m_progress);painter.drawText(rect(), Qt::AlignCenter, text);}private:int m_progress; // 进度值(0-100)
};// 使用方式
#include <QApplication>
int main(int argc, char *argv[])
{QApplication app(argc, argv);CircleProgressBar progressBar;progressBar.resize(200, 200);progressBar.show();return app.exec();
}

image

水波进度条(高级绘图示例)

实现原理

基于正弦函数生成波浪线,通过定时器更新波浪相位实现“波动”效果,结合圆形裁剪实现圆形水波进度条。

完整实现代码

#include <QWidget>
#include <QPainter>
#include <QPainterPath>
#include <QTimer>
#include <cmath>class WaveProgressBar : public QWidget
{Q_OBJECT
public:explicit WaveProgressBar(QWidget *parent = nullptr) : QWidget(parent),m_progress(0), m_waveOffset(0), m_waveAmplitude(8), m_waveFrequency(0.03){setMinimumSize(150, 150);// 定时器:每20ms更新一次波浪相位m_timer = new QTimer(this);connect(m_timer, &QTimer::timeout, this, [this]() {m_waveOffset += 0.1;update();});m_timer->start(20);}/*** @brief 设置进度值* @param progress 进度(0-100)*/void setProgress(int progress){m_progress = qBound(0, progress, 100);update();}protected:void paintEvent(QPaintEvent *event) override{Q_UNUSED(event);QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);int width = this->width();int height = this->height();QRect widgetRect = rect();// 1. 绘制圆形背景(半透明浅蓝色)painter.setPen(Qt::NoPen);painter.setBrush(QColor(0, 180, 255, 50));painter.drawEllipse(widgetRect.adjusted(5, 5, -5, -5));// 2. 计算波浪参数float waveHeight = height * m_progress / 100.0; // 波浪高度(进度对应)float waveY = height - waveHeight; // 波浪起始Y坐标// 3. 绘制波浪路径QPainterPath wavePath;wavePath.moveTo(0, height); // 左下角起点wavePath.lineTo(0, waveY);  // 左侧波浪起点// 生成正弦波浪线for (int x = 0; x <= width; x++){// 正弦函数:y = A*sin(ωx + φ) + 基准Y坐标float y = waveY + m_waveAmplitude * sin(m_waveFrequency * x + m_waveOffset);wavePath.lineTo(x, y);}wavePath.lineTo(width, waveY); // 右侧波浪终点wavePath.lineTo(width, height); // 右下角终点wavePath.closeSubpath();// 4. 裁剪波浪路径为圆形(与背景圆重合)QPainterPath circlePath;circlePath.addEllipse(widgetRect.adjusted(5, 5, -5, -5));QPainterPath clippedPath = wavePath.intersected(circlePath); // 取交集// 5. 绘制波浪填充色(半透明蓝色)painter.setBrush(QColor(0, 180, 255, 150));painter.drawPath(clippedPath);// 6. 绘制进度文本painter.setPen(Qt::white);painter.setFont(QFont("Arial", 20, QFont::Bold));QString text = QString("%1%").arg(m_progress);painter.drawText(widgetRect, Qt::AlignCenter, text);}private:int m_progress;          // 进度值(0-100)float m_waveOffset;      // 波浪相位偏移(控制波动)float m_waveAmplitude;   // 波浪振幅(高度)float m_waveFrequency;   // 波浪频率(密度)QTimer *m_timer;         // 波动定时器
};// 使用方式
#include <QApplication>
int main(int argc, char *argv[])
{QApplication app(argc, argv);WaveProgressBar progressBar;progressBar.resize(200, 200);progressBar.setProgress(60); // 设置初始进度60%progressBar.show();return app.exec();
}

image

Qt软键盘(自定义事件模拟输入)

核心需求

实现一个软键盘,点击按键时向当前焦点输入框发送键盘事件,模拟真实键盘输入。

完整实现代码

自定义按键类(KeyButton.h)

#ifndef KEYBUTTON_H
#define KEYBUTTON_H#include <QPushButton>
#include <QKeyEvent>
#include <QApplication>/*** @brief 软键盘自定义按键类,点击时发送键盘事件*/
class KeyButton : public QPushButton
{Q_OBJECT
public:explicit KeyButton(QWidget *parent = nullptr) : QPushButton(parent), m_key(-1){initStyle();}explicit KeyButton(const QString &text, QWidget *parent = nullptr) : QPushButton(text, parent), m_key(-1){initStyle();}/*** @brief 设置按键对应的键盘键值(如Qt::Key_A、'a')* @param key 键值(Qt::Key枚举或ASCII码)*/void setKey(int key) { m_key = key; }signals:/*** @brief 按键点击信号,传递键值* @param key 按键对应的键值*/void keyClicked(int key);private slots:/*** @brief 按键点击槽函数,发送键盘事件到焦点控件*/void onKeyClicked(){// 如果未设置键值,从按钮文本获取(如文本为"a",则键值为'a')if (m_key == -1 && !text().isEmpty()){m_key = text().at(0).toLatin1();}if (m_key != -1){emit keyClicked(m_key);// 创建键盘按下事件,发送给当前焦点控件QKeyEvent *pressEvent = new QKeyEvent(QEvent::KeyPress, m_key, Qt::NoModifier, text());QApplication::postEvent(QApplication::focusWidget(), pressEvent);// 创建键盘松开事件(模拟完整按键流程)QKeyEvent *releaseEvent = new QKeyEvent(QEvent::KeyRelease, m_key, Qt::NoModifier, text());QApplication::postEvent(QApplication::focusWidget(), releaseEvent);}}private:/*** @brief 初始化按键样式(去除焦点边框,设置大小)*/void initStyle(){setFocusPolicy(Qt::NoFocus); // 去除按键焦点(避免点击后焦点转移)setFixedSize(60, 60);        // 设置按键固定大小setStyleSheet("QPushButton { font-size: 20px; }");// 绑定点击信号到槽函数connect(this, &QPushButton::clicked, this, &KeyButton::onKeyClicked);}private:int m_key; // 按键对应的键盘键值
};#endif // KEYBUTTON_H

软键盘主窗口(SoftKeyboard.h)

#ifndef SOFTKEYBOARD_H
#define SOFTKEYBOARD_H#include <QWidget>
#include <QGridLayout>
#include "KeyButton.h"/*** @brief 软键盘主窗口,包含多个KeyButton*/
class SoftKeyboard : public QWidget
{Q_OBJECT
public:explicit SoftKeyboard(QWidget *parent = nullptr) : QWidget(parent){setWindowTitle("软键盘");initLayout();}private:/*** @brief 初始化键盘布局(3x3按键示例)*/void initLayout(){QGridLayout *layout = new QGridLayout(this);layout->setSpacing(10);// 创建按键(示例:a、b、c、d、e、f、删除、清空、确认)KeyButton *btnA = new KeyButton("a");KeyButton *btnB = new KeyButton("b");KeyButton *btnC = new KeyButton("c");KeyButton *btnD = new KeyButton("d");KeyButton *btnE = new KeyButton("e");KeyButton *btnF = new KeyButton("f");KeyButton *btnBackspace = new KeyButton("←");btnBackspace->setKey(Qt::Key_Backspace); // 设置删除键对应的键值KeyButton *btnClear = new KeyButton("清空");connect(btnClear, &KeyButton::keyClicked, this, [this]() {// 向焦点输入框发送清空信号(示例:QLineEdit)QWidget *focusWidget = QApplication::focusWidget();if (focusWidget){QMetaObject::invokeMethod(focusWidget, "clear", Qt::QueuedConnection);}});KeyButton *btnConfirm = new KeyButton("确认");connect(btnConfirm, &KeyButton::keyClicked, this, [this]() {qDebug() << "确认按钮点击";// 可添加确认逻辑(如提交输入内容)});// 添加按键到布局layout->addWidget(btnA, 0, 0);layout->addWidget(btnB, 0, 1);layout->addWidget(btnC, 0, 2);layout->addWidget(btnD, 1, 0);layout->addWidget(btnE, 1, 1);layout->addWidget(btnF, 1, 2);layout->addWidget(btnBackspace, 2, 0);layout->addWidget(btnClear, 2, 1);layout->addWidget(btnConfirm, 2, 2);setLayout(layout);resize(300, 300);}
};#endif // SOFTKEYBOARD_H

测试程序(main.cpp)

#include <QApplication>
#include <QWidget>
#include <QLineEdit>
#include <QVBoxLayout>
#include "SoftKeyboard.h"int main(int argc, char *argv[])
{QApplication app(argc, argv);// 创建主窗口,包含输入框和软键盘QWidget mainWidget;mainWidget.setWindowTitle("软键盘测试");QVBoxLayout *layout = new QVBoxLayout(&mainWidget);// 添加三个输入框(测试焦点切换)QLineEdit *edit1 = new QLineEdit;QLineEdit *edit2 = new QLineEdit;QLineEdit *edit3 = new QLineEdit;edit1->setPlaceholderText("输入框1");edit2->setPlaceholderText("输入框2");edit3->setPlaceholderText("输入框3");// 添加软键盘SoftKeyboard *keyboard = new SoftKeyboard;layout->addWidget(edit1);layout->addWidget(edit2);layout->addWidget(edit3);layout->addWidget(keyboard);mainWidget.resize(400, 500);mainWidget.show();return app.exec();
}

image

关键说明

  • 自定义按键KeyButton通过setFocusPolicy(Qt::NoFocus)去除焦点,避免点击后输入框失去焦点;
  • 点击按键时,通过QApplication::postEvent()向当前焦点控件发送QKeyEvent,模拟真实键盘输入;
  • 支持焦点切换:点击不同输入框后,软键盘输入会自动指向当前焦点的输入框。

事件过滤器(Event Filter)

核心作用

拦截一个或多个QObject的事件,在事件到达目标对象前进行预处理(如修改、拦截、日志记录)。

完整示例(拦截输入框键盘事件)

#include <QApplication>
#include <QWidget>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QEvent>
#include <QKeyEvent>
#include <QDebug>/*** @brief 事件过滤器类,拦截QLineEdit的键盘事件*/
class InputFilter : public QObject
{Q_OBJECT
public:explicit InputFilter(QObject *parent = nullptr) : QObject(parent) {}protected:/*** @brief 重写eventFilter()函数,拦截事件* @param watched 被监视的对象(如QLineEdit)* @param event 被拦截的事件* @return bool true=拦截事件,false=放行事件*/bool eventFilter(QObject *watched, QEvent *event) override{// 仅拦截QLineEdit的键盘按下事件if (watched->isWidgetType() && dynamic_cast<QLineEdit*>(watched) &&event->type() == QEvent::KeyPress){QKeyEvent *keyEvent = dynamic_cast<QKeyEvent*>(event);if (keyEvent){// 拦截数字键(禁止输入数字)if (keyEvent->key() >= Qt::Key_0 && keyEvent->key() <= Qt::Key_9){qDebug() << "拦截数字键:" << keyEvent->key();return true; // 拦截事件,不传递给输入框}// 允许其他键(如字母、符号)qDebug() << "放行键:" << keyEvent->key();}}// 其他事件放行,交给原对象处理return QObject::eventFilter(watched, event);}
};int main(int argc, char *argv[])
{QApplication app(argc, argv);QWidget mainWidget;QVBoxLayout *layout = new QVBoxLayout(&mainWidget);QLineEdit *edit1 = new QLineEdit;QLineEdit *edit2 = new QLineEdit;edit1->setPlaceholderText("禁止输入数字(输入框1)");edit2->setPlaceholderText("禁止输入数字(输入框2)");// 创建事件过滤器InputFilter *filter = new InputFilter;// 为两个输入框安装事件过滤器edit1->installEventFilter(filter);edit2->installEventFilter(filter);layout->addWidget(edit1);layout->addWidget(edit2);mainWidget.resize(300, 150);mainWidget.show();return app.exec();
}

关键步骤

  • 创建事件过滤器类,重写eventFilter()函数;
  • 为目标对象(如QLineEdit)调用installEventFilter()安装过滤器;
  • eventFilter()中判断事件类型和目标对象,决定是否拦截;
  • 不需要时调用removeEventFilter()移除过滤器。

总结

Qt事件处理是Qt应用的核心,涵盖鼠标、键盘、定时器、绘图等所有交互场景,关键要点:

  • 事件通过事件循环分发,QEvent是所有事件的基类;
  • 处理事件的三种方式:重写特定事件函数(如mousePressEvent)、重写event()、使用事件过滤器;
  • 自定义事件适用于复杂业务场景,支持异步发送和优先级;
  • QPainter是绘图核心,结合paintEvent()可实现自定义部件和复杂绘图效果;
  • 软键盘、水波进度条等高级组件,本质是事件处理与绘图的结合。
  • 掌握Qt事件处理机制,可灵活实现各类交互功能,适配桌面、嵌入式等不同平台的Qt应用开发。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询