Qt 实现炫酷仪表盘源码分享
一、效果展示 二、源码分享 1、工程结构 2、speedmeterwidget.h 3、speedmeterwidget.cpp 4、mainWindow.cpp 5、完整工程下载 三、实现原理 1、 QPainter 的基本概念 2、绘图设备与坐标系 3、 基本绘图操作 3.1、 绘制几何图形 3.2 、绘制文本 3.3 、绘制图像 4、 画笔(QPen)与画刷(QBrush) 4.1、 QPen - 控制轮廓绘制 4.2 、QBrush - 控制填充 5、 高级绘图特性 5.1、 抗锯齿(Antialiasing) 5.2 、合成模式(Composition Mode) 5.3 、裁剪区域(Clipping) 6、 在速度表项目中的应用 6.1、 坐标系变换 6.2 、渐变效果 6.3 、文本绘制与对齐 7、性能优化建议 8、Qt6 中的新特性 9、常见问题与调试技巧 9.1、 绘图不显示 9.2、 性能问题 9.3、 坐标计算错误 总结
一、效果展示


二、源码分享
1、工程结构

2、speedmeterwidget.h
#ifndef SPEEDMETERWIDGET_H#define SPEEDMETERWIDGET_H#include<QWidget>#include<QPainter>#include<QRadialGradient>#include<QFont>#include<QFontDatabase>#include<QPointF>#include<QRectF>class SpeedMeterWidget : public QWidget{Q_OBJECTpublic:explicitSpeedMeterWidget(QWidget *parent = nullptr);voidsetCurrentValue(int val);protected:voidpaintEvent(QPaintEvent *event)override;private:int m_maxValue = 260;int m_minValue = 0;int m_currentValue = 130;int m_redValue = 200;QColor m_bgColor;QFont m_digitFont;};#endif// SPEEDMETERWIDGET_H
3、speedmeterwidget.cpp
#include "SpeedMeterWidget.h"#include <cmath>#include <QFontMetricsF>SpeedMeterWidget::SpeedMeterWidget(QWidget *parent): QWidget(parent),m_bgColor(QColor("#00020414")){// 加载外部数字字体 Digitall.ttf,资源文件里注册int fontId = QFontDatabase::addApplicationFont(":/font/image/Digitall.ttf");if(fontId != -1){QString family = QFontDatabase::applicationFontFamilies(fontId).first();m_digitFont = QFont(family);}else{// 兜底字体m_digitFont = QFont("Arial", 40);}}void SpeedMeterWidget::setCurrentValue(int val){m_currentValue = val;update(); // 触发重绘}void SpeedMeterWidget::paintEvent(QPaintEvent *event){QWidget::paintEvent(event);QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);int w = width();int h = height();qreal centerX = w / 2.0;qreal centerY = h / 2.0;qreal radius = qMin(w, h) / 2.0 * 0.9;// 1. 背景填充painter.fillRect(0, 0, w, h, m_bgColor);// 2. 内层红色径向渐变圆环QRadialGradient radialGrad(centerX, centerY, radius * 0.6);QColor colCenter("#E43223");QColor colEdge("#00E43223");radialGrad.setColorAt(0, colCenter);radialGrad.setColorAt(1, colEdge);painter.setBrush(radialGrad);painter.setPen(Qt::NoPen);painter.drawEllipse(QPointF(centerX, centerY), radius * 0.6, radius * 0.6);// 3. 中间黑色内圆painter.setBrush(Qt::black);painter.drawEllipse(QPointF(centerX, centerY), radius * 0.5, radius * 0.5);// ====================== 绘制刻度线 ======================painter.save();const int startAngle = 45;qreal r = radius;const int totalSteps = 130;qreal rotateStep = (360.0 - startAngle * 2) / totalSteps;painter.translate(centerX, centerY);painter.rotate(startAngle);for(int i = 0; i <= totalSteps; ++i){int val = i * 2;QColor bigColor, smallColor;if(val >= m_redValue){bigColor = Qt::red;smallColor = Qt::red;}else{bigColor = QColor("#B6C6D6");smallColor = QColor("#525B62");}QPen pen;pen.setCapStyle(Qt::RoundCap);if(i % 10 == 0){pen.setColor(bigColor);pen.setWidth(9);painter.setPen(pen);painter.drawLine(QPointF(0, r), QPointF(0, r / 1.1));}else if (i % 5 == 0){pen.setColor(smallColor);pen.setWidth(8);painter.setPen(pen);painter.drawLine(QPointF(0, r), QPointF(0, r / 1.08));}else{pen.setColor(smallColor);pen.setWidth(6);painter.setPen(pen);painter.drawLine(QPointF(0, r), QPointF(0, r / 1.05));}painter.rotate(rotateStep);}painter.restore();// ====================== 外圈刻度数字 ======================painter.save();qreal numR = radius * 0.75;qreal numGapAngle = (360.0 - startAngle * 2) / 13.0;qreal valStep = (m_maxValue - m_minValue) / 13.0;qreal currentDrawVal = m_minValue;int startDeg = startAngle;QFont numFont = m_digitFont;numFont.setPointSizeF(w * 0.04);painter.setFont(numFont);QFontMetricsF fm(numFont);for(int i = 0; i < 14; ++i){if(currentDrawVal >= m_redValue)painter.setPen(Qt::red);elsepainter.setPen(Qt::white);qreal angleDeg = -90.0 + startDeg + numGapAngle * i;qreal rad = qDegreesToRadians(angleDeg);qreal x = -numR * cos(rad);qreal y = -numR * sin(rad);painter.save();painter.translate(centerX + x, centerY + y);QString text = QString::number(qRound(currentDrawVal));qreal textW = fm.horizontalAdvance(text);qreal textH = w * 0.06;qreal offsetY = textH / 4.0;painter.drawText(QPointF(-textW / 2.0, offsetY), text);painter.restore();currentDrawVal += valStep;}painter.restore();// ====================== 指针绘制 ======================painter.save();qreal degRotate = (270.0 / (m_maxValue - m_minValue)) * (m_currentValue - m_minValue) + 135.0;painter.translate(centerX, centerY);painter.rotate(degRotate);QPen pen;pen.setColor(Qt::white);pen.setWidth(8);pen.setCapStyle(Qt::RoundCap);painter.setPen(pen);painter.drawLine(QPointF(radius * 0.5, 0), QPointF(radius * 0.9, 0));painter.restore();// ====================== 中心三位数字 + KM/h ======================painter.save();painter.setPen(Qt::white);// 大数字字体qreal numFontSize = w * 0.12;QFont bigFont = m_digitFont;bigFont.setPointSizeF(numFontSize);painter.setFont(bigFont);QFontMetricsF fmBig(bigFont);int val = m_currentValue;int hundred = val / 100;int ten = (val / 10) % 10;int unit = val % 10;qreal singleW = fmBig.horizontalAdvance("0");qreal gap = singleW * 0.001;qreal totalWidth = singleW * 3 + gap * 2;qreal startX = centerX - totalWidth / 2.0;// 百位if(hundred == 0)painter.setPen(QColor("#666666"));elsepainter.setPen(Qt::white);painter.drawText(QPointF(startX, centerY), QString::number(hundred));// 十位qreal tenX = startX + singleW + gap;if(ten == 0 && hundred == 0)painter.setPen(QColor("#666666"));elsepainter.setPen(Qt::white);painter.drawText(QPointF(tenX, centerY), QString::number(ten));// 个位qreal unitX = tenX + singleW + gap;if(unit == 0 && hundred == 0 && ten == 0)painter.setPen(QColor("#666666"));elsepainter.setPen(Qt::white);painter.drawText(QPointF(unitX, centerY), QString::number(unit));// KM/h 下方文字QFont unitFont = m_digitFont;unitFont.setPointSizeF(w * 0.05);painter.setFont(unitFont);QRectF rectUnit(0, centerY + 50, w, h);painter.drawText(rectUnit, Qt::AlignHCenter, "KM/h");painter.restore();}
4、mainWindow.cpp
#include"mainwindow.h"#include<QDebug>#include<QWidget>#include<QVBoxLayout>#include<QSlider>#include"speedmeterwidget.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent){this->resize(800, 800);QWidget *central = new QWidget(this);central->setStyleSheet("background-color: #020414;");this->setCentralWidget(central);QVBoxLayout *lay = new QVBoxLayout(central);lay->setContentsMargins(0, 0, 0, 20);SpeedMeterWidget *meter = new SpeedMeterWidget();lay->addWidget(meter);QSlider *slider = new QSlider(Qt::Horizontal);slider->setRange(0, 260);slider->setValue(130);lay->addWidget(slider);// 滑块联动更新数值QObject::connect(slider, &QSlider::valueChanged, meter, &SpeedMeterWidget::setCurrentValue);}MainWindow::~MainWindow(){}
5、完整工程下载
https://download.csdn.net/download/qq_15181569/93203495三、实现原理
QPainter 是 Qt 框架中用于执行绘图操作的核心类,它提供了丰富的 API 来绘制各种图形、文本和图像。在 Qt6 中,QPainter 的功能得到了进一步增强和优化。
1、 QPainter 的基本概念
QPainter 采用画家算法(Painter’s Algorithm)进行绘图,即后绘制的图形会覆盖先绘制的图形。它支持在多种绘图设备上工作:
- QWidget
:在窗口部件上绘图 - QPixmap
:在位图上绘图 - QImage
:在图像上绘图 - QPrinter
:在打印机上绘图
2、绘图设备与坐标系
// 创建 QPainter 对象QPainter painter(this); // 在 QWidget 上绘图painter.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿
QPainter 使用逻辑坐标系,默认原点在左上角,X轴向右,Y轴向下。可以通过以下方法变换坐标系:
// 坐标变换painter.translate(100, 100); // 平移坐标系painter.rotate(45); // 旋转坐标系painter.scale(2.0, 2.0); // 缩放坐标系painter.shear(0.5, 0); // 错切变换
3、 基本绘图操作
3.1、 绘制几何图形
// 绘制线条painter.drawLine(QPointF(0, 0), QPointF(100, 100));// 绘制矩形painter.drawRect(QRectF(10, 10, 100, 50));// 绘制椭圆/圆形painter.drawEllipse(QPointF(50, 50), 30, 30);// 绘制多边形QPolygonF polygon;polygon << QPointF(0, 0) << QPointF(50, 0) << QPointF(25, 50);painter.drawPolygon(polygon);
3.2 、绘制文本
// 设置字体QFont font("Arial", 12, QFont::Bold);painter.setFont(font);// 绘制文本painter.drawText(QPointF(10, 30), "Hello Qt6!");painter.drawText(QRectF(10, 50, 200, 50), Qt::AlignCenter, "居中文本");
3.3 、绘制图像
QPixmap pixmap(":/images/logo.png");painter.drawPixmap(10, 10, pixmap);
4、 画笔(QPen)与画刷(QBrush)
4.1、 QPen - 控制轮廓绘制
QPen pen;pen.setColor(Qt::red); // 设置颜色pen.setWidth(3); // 设置线宽pen.setStyle(Qt::DashLine); // 设置线型pen.setCapStyle(Qt::RoundCap); // 设置端点样式pen.setJoinStyle(Qt::RoundJoin); // 设置连接样式painter.setPen(pen);
4.2 、QBrush - 控制填充
QBrush brush;brush.setColor(Qt::blue); // 纯色填充brush.setStyle(Qt::SolidPattern); // 填充样式// 渐变填充QLinearGradient gradient(0, 0, 100, 100);gradient.setColorAt(0, Qt::white);gradient.setColorAt(1, Qt::black);brush = QBrush(gradient);painter.setBrush(brush);
5、 高级绘图特性
5.1、 抗锯齿(Antialiasing)
painter.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿painter.setRenderHint(QPainter::TextAntialiasing); // 文本抗锯齿painter.setRenderHint(QPainter::SmoothPixmapTransform); // 平滑图像变换
5.2 、合成模式(Composition Mode)
// 设置合成模式painter.setCompositionMode(QPainter::CompositionMode_SourceOver); // 默认:源覆盖目标painter.setCompositionMode(QPainter::CompositionMode_SourceIn); // 源在目标内部painter.setCompositionMode(QPainter::CompositionMode_Xor); // 异或模式
5.3 、裁剪区域(Clipping)
// 设置裁剪区域QPainterPath clipPath;clipPath.addEllipse(50, 50, 100, 100);painter.setClipPath(clipPath);// 恢复裁剪painter.setClipping(false);
6、 在速度表项目中的应用
在本项目的 SpeedMeterWidget::paintEvent() 中,我们充分利用了 QPainter 的各种功能:
6.1、 坐标系变换
// 保存当前绘图状态painter.save();// 平移坐标系到圆心painter.translate(centerX, centerY);// 旋转坐标系绘制刻度线painter.rotate(startAngle);for (int i = 0; i <= totalSteps; ++i) {painter.drawLine(...);painter.rotate(rotateStep);}// 恢复绘图状态painter.restore();
6.2 、渐变效果
// 创建径向渐变QRadialGradient radialGrad(centerX, centerY, radius * 0.6);radialGrad.setColorAt(0, QColor("#E43223")); // 中心颜色radialGrad.setColorAt(1, QColor("#00E43223")); // 边缘颜色(透明)painter.setBrush(radialGrad);painter.drawEllipse(QPointF(centerX, centerY), radius * 0.6, radius * 0.6);
6.3 、文本绘制与对齐
// 计算文本宽度进行居中QFontMetricsF fm(numFont);QString text = QString::number(qRound(currentDrawVal));qreal textW = fm.horizontalAdvance(text);painter.drawText(QPointF(-textW / 2.0, offsetY), text);
7、性能优化建议
- 避免频繁创建 QPainter 对象
:在 paintEvent 中创建一次即可 - 合理使用 save()/restore()
:及时恢复绘图状态 - 预计算不变的值
:如字体度量、几何计算等 - 使用 QPainterPath
:复杂图形使用路径绘制 - 开启硬件加速
:在支持 OpenGL 的设备上使用 QOpenGLWidget
8、Qt6 中的新特性
Qt6 对 QPainter 进行了以下改进:
- 更好的 HiDPI 支持
:自动处理高分辨率显示 - 改进的文本渲染
:支持更多字体特性 - 增强的渐变效果
:支持更多渐变类型 - 性能优化
:绘图操作更加高效
9、常见问题与调试技巧
9.1、 绘图不显示
检查是否在 paintEvent 中绘图 确认调用了 update() 触发重绘 验证绘图区域是否在可见范围内
9.2、 性能问题
使用 QElapsedTimer 测量绘图时间 减少不必要的绘图操作 考虑使用缓存(QPixmapCache)
9.3、 坐标计算错误
使用 qDebug() 输出坐标值 绘制参考线辅助调试 注意坐标系变换的顺序
总结
QPainter 是 Qt 图形系统的核心,掌握它的使用对于开发高质量的 GUI 应用至关重要。通过本项目的实践,您应该能够理解如何:
使用坐标系变换简化复杂绘图 应用渐变和抗锯齿提升视觉效果 优化绘图性能 处理文本和图形的精确对齐

夜雨聆风