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();
}
运行截图:
