QT仪表盘

gaugewidget.h

复制代码
/**
 * @file gaugewidget.h
 * @brief 科技感仪表盘控件头文件
 * @author 
 * @date 2026-04-01
 */

#ifndef GAUGEWIDGET_H
#define GAUGEWIDGET_H

#include <QWidget>
#include <QPainter>
#include <QTimer>
#include <QTimerEvent>
#include <QLinearGradient>
#include <QRadialGradient>
#include <QPolygonF>
#include <QtMath>
#include <QRandomGenerator>

/**
 * @class GaugeWidget
 * @brief 科技感仪表盘控件类
 * 
 * 该类实现了一个具有科技感的圆形仪表盘,支持分段颜色、指针动画、
 * 发光效果和粒子效果等视觉元素。
 */
class GaugeWidget : public QWidget
{
    Q_OBJECT

public:
    /**
     * @brief 构造函数
     * @param parent 父控件
     */
    explicit GaugeWidget(QWidget *parent = nullptr);
    
    /**
     * @brief 析构函数
     */
    ~GaugeWidget();

    /**
     * @brief 设置仪表盘数值
     * @param value 目标数值
     */
    void setValue(double value);
    
    /**
     * @brief 设置仪表盘标题
     * @param title 标题文本
     */
    void setTitle(const QString &title);
    
    /**
     * @brief 设置仪表盘数值范围
     * @param min 最小值
     * @param max 最大值
     */
    void setRange(double min, double max);
    
    /**
     * @brief 设置是否启用动画效果
     * @param animated 是否启用动画
     */
    void setAnimated(bool animated);

protected:
    /**
     * @brief 绘制事件处理函数
     * @param event 绘图事件
     */
    void paintEvent(QPaintEvent *event) override;
    
    /**
     * @brief 定时器事件处理函数,用于实现动画效果
     * @param event 定时器事件
     */
    void timerEvent(QTimerEvent *event) override;

private:
    double m_value;          ///< 当前数值
    double m_targetValue;    ///< 目标数值(用于动画)
    double m_min;            ///< 最小值
    double m_max;            ///< 最大值
    QString m_title;         ///< 仪表盘标题
    bool m_animated;         ///< 是否启用动画
    int m_animationTimer;    ///< 动画定时器ID
    double m_animationStep;  ///< 动画步长
};

#endif // GAUGEWIDGET_H

gaugewidget.cpp

复制代码
/**
 * @file gaugewidget.cpp
 * @brief 科技感仪表盘控件实现文件
 * @author 
 * @date 2026-04-01
 */

#include "gaugewidget.h"

/**
 * @brief GaugeWidget构造函数
 * @param parent 父控件
 * 
 * 初始化仪表盘的默认值和布局
 */
GaugeWidget::GaugeWidget(QWidget *parent) : QWidget(parent)
    , m_value(68.0)          // 默认值68%
    , m_targetValue(68.0)    // 目标值,用于动画
    , m_min(0.0)             // 最小值0%
    , m_max(100.0)           // 最大值100%
    , m_title("完成率")        // 默认标题
    , m_animated(false)       // 默认不启用动画
    , m_animationTimer(-1)    // 动画定时器ID
    , m_animationStep(0.0)    // 动画步长
{
    setFixedSize(300, 220); // 设置固定大小
}

/**
 * @brief GaugeWidget析构函数
 * 
 * 清理动画定时器
 */
GaugeWidget::~GaugeWidget()
{
    if (m_animationTimer != -1) {
        killTimer(m_animationTimer);
    }
}

/**
 * @brief 设置仪表盘数值
 * @param value 目标数值
 * 
 * 根据是否启用动画,直接设置值或启动动画效果
 */
void GaugeWidget::setValue(double value)
{
    m_targetValue = value;
    if (!m_animated) {
        // 不使用动画,直接设置值
        m_value = value;
        update();
    } else {
        // 启动动画
        if (m_animationTimer != -1) {
            killTimer(m_animationTimer);
        }
        // 50步动画,每步20ms,共1秒
        m_animationStep = (m_targetValue - m_value) / 50.0;
        m_animationTimer = startTimer(20); // 50 FPS
    }
}

/**
 * @brief 设置仪表盘标题
 * @param title 标题文本
 * 
 * 更新标题并触发重绘
 */
void GaugeWidget::setTitle(const QString &title)
{
    if (m_title != title) {
        m_title = title;
        update();
    }
}

/**
 * @brief 设置仪表盘数值范围
 * @param min 最小值
 * @param max 最大值
 * 
 * 更新数值范围并触发重绘
 */
void GaugeWidget::setRange(double min, double max)
{
    m_min = min;
    m_max = max;
    update();
}

/**
 * @brief 设置是否启用动画效果
 * @param animated 是否启用动画
 * 
 * 控制指针移动时是否使用动画效果
 */
void GaugeWidget::setAnimated(bool animated)
{
    m_animated = animated;
}

/**
 * @brief 定时器事件处理函数,用于实现动画效果
 * @param event 定时器事件
 * 
 * 处理指针动画,逐步更新数值直到达到目标值
 */
void GaugeWidget::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == m_animationTimer) {
        // 检查是否接近目标值
        if (qAbs(m_value - m_targetValue) < qAbs(m_animationStep)) {
            m_value = m_targetValue;
            killTimer(m_animationTimer);
            m_animationTimer = -1;
        } else {
            m_value += m_animationStep;
        }
        update(); // 触发重绘
    }
}

/**
 * @brief 绘制事件处理函数
 * @param event 绘图事件
 * 
 * 绘制仪表盘的所有视觉元素,包括背景、颜色分段、刻度、指针、数值等
 */
void GaugeWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing); // 启用抗锯齿

    // 设置科技感背景 - 多层渐变
    QLinearGradient backgroundGradient(0, 0, width(), height());
    backgroundGradient.setColorAt(0, QColor(20, 25, 40));
    backgroundGradient.setColorAt(0.5, QColor(15, 20, 35));
    backgroundGradient.setColorAt(1, QColor(10, 15, 30));
    painter.fillRect(rect(), backgroundGradient);

    // 绘制网格背景
    painter.setPen(QPen(QColor(50, 60, 80, 100), 1));
    for (int i = 0; i < width(); i += 20) {
        painter.drawLine(i, 0, i, height());
    }
    for (int i = 0; i < height(); i += 20) {
        painter.drawLine(0, i, width(), i);
    }

    // 计算中心点和半径
    int centerX = width() / 2;
    int centerY = height() / 2;
    int radius = 100;

    // 绘制标题
    QFont titleFont("Consolas", 12, QFont::Bold);
    painter.setFont(titleFont);
    painter.setPen(QColor(150, 200, 255));
    painter.drawText(0, 20, width(), 20, Qt::AlignCenter, m_title);

    // 计算归一化值 (0-1)
    double normalizedValue = (m_value - m_min) / (m_max - m_min);
    if (normalizedValue < 0) normalizedValue = 0;
    if (normalizedValue > 1) normalizedValue = 1;

    // 绘制颜色分段 - 全圆,使用协调的霓虹色
    QList<QPair<double, QColor>> techRanges;
    techRanges << qMakePair(0.3, QColor(0, 200, 255))  // 青色
               << qMakePair(0.7, QColor(150, 100, 255))  // 紫色
               << qMakePair(1.0, QColor(255, 100, 180)); // 粉色

    double startAngle = 0.0;
    for (int i = 0; i < techRanges.size(); i++) {
        double endAngle;
        if (i == techRanges.size() - 1) {
            endAngle = 360.0; // 最后一个分段的结束角度固定为360度
        } else {
            endAngle = (techRanges[i].first * 360.0);
        }

        // 绘制增强的发光效果
        QRadialGradient glowGradient(centerX, centerY, radius);
        glowGradient.setColorAt(0, techRanges[i].second);
        glowGradient.setColorAt(0.8, techRanges[i].second);
        glowGradient.setColorAt(1, QColor(0, 0, 0, 0));
        
        QPen glowPen(glowGradient, 28);
        glowPen.setStyle(Qt::SolidLine);
        painter.setPen(glowPen);
        painter.drawArc(centerX - radius, centerY - radius, radius * 2, radius * 2,
                       static_cast<int>(startAngle * 16), static_cast<int>((endAngle - startAngle) * 16));

        // 绘制主颜色
        QPen rangePen(techRanges[i].second, 20);
        painter.setPen(rangePen);
        painter.drawArc(centerX - radius, centerY - radius, radius * 2, radius * 2,
                       static_cast<int>(startAngle * 16), static_cast<int>((endAngle - startAngle) * 16));

        startAngle = endAngle;
    }

    // 绘制发光刻度线
    for (int i = 0; i <= 10; i++) {
        double angle = (i / 10.0) * 360.0;
        double radians = qDegreesToRadians(angle);
        int innerRadius = radius - 25;
        int outerRadius = radius;
        QPointF startPoint(centerX + qSin(radians) * innerRadius,
                          centerY - qCos(radians) * innerRadius);
        QPointF endPoint(centerX + qSin(radians) * outerRadius,
                        centerY - qCos(radians) * outerRadius);
        
        // 发光刻度线
        QLinearGradient tickGradient(startPoint, endPoint);
        tickGradient.setColorAt(0, QColor(100, 150, 200, 50));
        tickGradient.setColorAt(0.5, QColor(150, 200, 255, 255));
        tickGradient.setColorAt(1, QColor(100, 150, 200, 50));
        
        QPen tickPen(tickGradient, 2);
        painter.setPen(tickPen);
        painter.drawLine(startPoint, endPoint);

        // 绘制刻度标签(每2个刻度显示一个标签)
        if (i % 2 == 0) {
            int labelRadius = radius - 40;
            QPointF labelPoint(centerX + qSin(radians) * labelRadius,
                              centerY - qCos(radians) * labelRadius);
            QString label = QString("%1").arg(i * 10);
            QFont labelFont("Consolas", 8);
            painter.setFont(labelFont);
            painter.setPen(QColor(150, 200, 255));
            painter.drawText(labelPoint.x() - 10, labelPoint.y() + 4, 20, 15, Qt::AlignCenter, label);
        }
    }

    // 绘制指针(三角形形状,渐变效果)
    double pointerAngle = normalizedValue * 360.0;
    double radians = qDegreesToRadians(pointerAngle);
    int pointerLength = 80;

    // 渐变指针
    QPolygonF pointer;
    pointer << QPointF(centerX, centerY)
            << QPointF(centerX + qSin(radians - 0.1) * 10, centerY - qCos(radians - 0.1) * 10)
            << QPointF(centerX + qSin(radians) * pointerLength, centerY - qCos(radians) * pointerLength)
            << QPointF(centerX + qSin(radians + 0.1) * 10, centerY - qCos(radians + 0.1) * 10);
    
    // 指针渐变
    QLinearGradient pointerGradient(centerX, centerY, 
                                   centerX + qSin(radians) * pointerLength, 
                                   centerY - qCos(radians) * pointerLength);
    pointerGradient.setColorAt(0, QColor(255, 180, 80));
    pointerGradient.setColorAt(1, QColor(255, 120, 40));
    
    // 指针发光效果
    painter.setPen(QPen(QColor(255, 200, 100), 4));
    painter.setBrush(pointerGradient);
    painter.drawPolygon(pointer);

    // 动态光影效果 - 随指针移动的光斑
    QRadialGradient lightSpotGradient(
        centerX + qSin(radians) * (radius - 10),
        centerY - qCos(radians) * (radius - 10),
        30
    );
    lightSpotGradient.setColorAt(0, QColor(255, 255, 255, 150));
    lightSpotGradient.setColorAt(0.5, QColor(255, 255, 255, 50));
    lightSpotGradient.setColorAt(1, QColor(255, 255, 255, 0));
    
    painter.setPen(Qt::NoPen);
    painter.setBrush(lightSpotGradient);
    painter.drawEllipse(
        centerX + qSin(radians) * (radius - 10) - 30,
        centerY - qCos(radians) * (radius - 10) - 30,
        60, 60
    );

    // 绘制玻璃态中心圆
    QLinearGradient centerGradient(centerX - 15, centerY - 15, centerX + 15, centerY + 15);
    centerGradient.setColorAt(0, QColor(80, 100, 140, 200));
    centerGradient.setColorAt(1, QColor(50, 70, 100, 200));
    
    // 玻璃态效果
    painter.setBrush(centerGradient);
    painter.setPen(QPen(QColor(100, 200, 255, 150), 2));
    painter.drawEllipse(centerX - 15, centerY - 15, 30, 30);

    // 绘制数值文本 - 玻璃态背景
    QRect valueRect(centerX - 40, centerY + 30, 80, 40);
    QLinearGradient valueGradient(valueRect.topLeft(), valueRect.bottomRight());
    valueGradient.setColorAt(0, QColor(80, 100, 140, 150));
    valueGradient.setColorAt(1, QColor(50, 70, 100, 150));
    
    painter.setBrush(valueGradient);
    painter.setPen(QPen(QColor(100, 200, 255, 150), 1));
    painter.drawRoundedRect(valueRect, 5, 5);
    
    // 绘制数值文本
    QFont valueFont("Consolas", 24, QFont::Bold);
    painter.setFont(valueFont);
    painter.setPen(QColor(255, 255, 255));
    QString valueText = QString("%1%").arg(static_cast<int>(m_value));
    painter.drawText(valueRect, Qt::AlignCenter, valueText);

    // 绘制科技感箭头
    painter.setPen(QColor(100, 200, 255));
    painter.setBrush(QBrush(QColor(100, 200, 255)));
    QPolygonF arrow;
    arrow << QPointF(centerX, centerY + 80)
          << QPointF(centerX - 5, centerY + 70)
          << QPointF(centerX + 5, centerY + 70);
    painter.drawPolygon(arrow);

    // 绘制多样化粒子效果
    painter.setPen(Qt::NoPen);
    for (int i = 0; i < 8; i++) {
        double angle = qDegreesToRadians(static_cast<double>(QRandomGenerator::global()->bounded(360)));
        int particleRadius = QRandomGenerator::global()->bounded(1, 4);
        int particleDistance = radius - QRandomGenerator::global()->bounded(5, 40);
        int alpha = QRandomGenerator::global()->bounded(50, 200);
        
        QPointF particlePos(centerX + qSin(angle) * particleDistance,
                           centerY - qCos(angle) * particleDistance);
        
        // 粒子发光效果
        QRadialGradient particleGradient(particlePos, particleRadius * 2);
        particleGradient.setColorAt(0, QColor(100, 200, 255, alpha));
        particleGradient.setColorAt(1, QColor(100, 200, 255, 0));
        
        painter.setBrush(particleGradient);
        painter.drawEllipse(particlePos, particleRadius * 2, particleRadius * 2);
    }

    // 绘制发光边框
    QRadialGradient borderGradient(centerX, centerY, radius + 20);
    borderGradient.setColorAt(0.8, QColor(100, 200, 255, 100));
    borderGradient.setColorAt(1, QColor(100, 200, 255, 0));
    
    painter.setPen(QPen(borderGradient, 4));
    painter.setBrush(Qt::NoBrush);
    painter.drawEllipse(centerX - radius - 10, centerY - radius - 10, (radius + 10) * 2, (radius + 10) * 2);
}

mainwindow.h

复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTimer>
#include "gaugewidget.h"

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow() override;

private slots:
    void updateGauges();

private:
    Ui::MainWindow *ui;
    QList<GaugeWidget*> m_gauges;
    QTimer *m_updateTimer;
};
#endif // MAINWINDOW_H

mainwindow.cpp

复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QGridLayout>
#include <QRandomGenerator>

/**
 * @brief MainWindow constructor
 * @param parent 父控件
 */
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , m_updateTimer(nullptr)
{
    ui->setupUi(this);
    setWindowTitle("科技感仪表盘");
    
    // 创建中心控件和网格布局
    QWidget *centralWidget = new QWidget(this);
    QGridLayout *gridLayout = new QGridLayout(centralWidget);
    gridLayout->setContentsMargins(20, 20, 20, 20);
    gridLayout->setSpacing(20);
    setCentralWidget(centralWidget);
    
    // 创建2x2网格布局的仪表盘
    GaugeWidget *gauge1 = new GaugeWidget(this);
    GaugeWidget *gauge2 = new GaugeWidget(this);
    GaugeWidget *gauge3 = new GaugeWidget(this);
    GaugeWidget *gauge4 = new GaugeWidget(this);
    
    // 设置仪表盘属性
    gauge1->setTitle("完成率");
    gauge1->setValue(68);
    gauge1->setRange(0, 100);
    gauge1->setAnimated(true);
    
    gauge2->setTitle("完成率");
    gauge2->setValue(68);
    gauge2->setRange(0, 100);
    gauge2->setAnimated(true);
    
    gauge3->setTitle("完成率");
    gauge3->setValue(68);
    gauge3->setRange(0, 100);
    gauge3->setAnimated(true);
    
    gauge4->setTitle("完成率");
    gauge4->setValue(68);
    gauge4->setRange(0, 100);
    gauge4->setAnimated(true);
    
    // 添加到网格布局
    gridLayout->addWidget(gauge1, 0, 0);
    gridLayout->addWidget(gauge2, 0, 1);
    gridLayout->addWidget(gauge3, 1, 0);
    gridLayout->addWidget(gauge4, 1, 1);
    
    // 添加到仪表盘列表,用于更新
    m_gauges << gauge1 << gauge2 << gauge3 << gauge4;
    
    // 创建更新定时器,用于动态数据
    m_updateTimer = new QTimer(this);
    connect(m_updateTimer, &QTimer::timeout, this, &MainWindow::updateGauges);
    m_updateTimer->start(1000); // 每1秒更新一次
}

/**
 * @brief 更新所有仪表盘的数值
 */
void MainWindow::updateGauges()
{
    // 生成0-100之间的随机值
    for (GaugeWidget *gauge : m_gauges) {
        int randomValue = QRandomGenerator::global()->bounded(0, 101);
        gauge->setValue(randomValue);
    }
}

/**
 * @brief MainWindow destructor
 */
MainWindow::~MainWindow()
{
    if (m_updateTimer) {
        m_updateTimer->stop();
        delete m_updateTimer;
    }
    delete ui;
}

main.cpp

复制代码
#include "mainwindow.h"

#include <QApplication>

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

运行截图:

相关推荐
wjcroom2 小时前
融释涡旋理论-对狭义相对论和洛伦兹变换的兼容
开发语言·前端
大明者省2 小时前
Python 程序在 Ubuntu 系统的完整部署流程
开发语言·python·ubuntu
咸甜适中2 小时前
rust序列化和反序列化(json、yaml、toml)详解
开发语言·rust·json
智算菩萨2 小时前
【Tkinter】14 事件处理机制深度解析:从基础绑定到高级传播,构建交互式绘图笔记应用
开发语言·笔记·python·microsoft·ui·ai编程·tkinter
東雪木2 小时前
Java学习——接口 (interface) 与抽象类 (abstract) 的本质区别、选型标准
java·开发语言·jvm·学习·java面试
小和尚敲木头2 小时前
router.push(‘/‘)跳转不触发重定向
开发语言·前端·javascript
_MyFavorite_2 小时前
JAVA重点基础、进阶知识及易错点总结(16)多线程基础(Thread & Runnable)
java·开发语言
misty youth2 小时前
提示词合集【自用】
开发语言·前端·ai编程
zero15972 小时前
Python 8天极速入门笔记(大模型工程师专用):第六篇-函数进阶 + 模块导入,大模型实战调用前置
开发语言·python·大模型编程语言