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();
}

运行截图:

相关推荐
用户8055336980319 小时前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner19 小时前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner9 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00612 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术12 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript