目标
- 基于QWidget绘制速度仪表盘, 仪表盘颜色为渐变色,
- 可指定当前显示的文字内容和文字颜色
- 可指定当前指针旋转的数值
效果图

控件完整代码
#pragma once
#include <QWidget>
#include <QMap>
class QPaintEvent;
class QPainter;
/// 
/// @brief: 速度仪表控件, 类似网速的控件 
/// 
class GaugeSpeedWidget  : public QWidget
{
	Q_OBJECT
public:
	explicit GaugeSpeedWidget(QWidget*parent = nullptr);
	virtual ~GaugeSpeedWidget();
	/// 
	/// @brief:	设置指示器显示的数值
	/// @param: const qreal value 
	///  @ret:	void
	///				
	void setIndicatorValue(const qreal value);
	/// 
	/// @brief:	设置指示器显示的单位
	/// @param: const QString & str 
	///  @ret:	void
	///				
	void setUnitText(const QString& str);
	/// 
	/// @brief:	设置显示的数值保留几位小数,若为负数,则整数显示, 最大显示2位小数
	/// @param: const int count 
	///  @ret:	void
	///				
	void setIndicatorValueDecimal(const int count);
private:
	void paintEvent(QPaintEvent* event);
	/// 
	/// @brief:	绘制圆弧
	/// @param: QPainter * painter 
	///  @ret:	void
	///				
	void drawArc(QPainter* painter);
	/// 
	/// @brief:	绘制圆弧上的刻度线
	/// @param: QPainter * painter 
	///  @ret:	void
	///				
	void drawArcDialLine(QPainter* painter);
	/// 
	/// @brief:	绘制刻度数值
	/// @param: QPainter * painter 
	///  @ret:	void
	///				
	void drawArcDialLineNumber(QPainter* painter);
	/// 
	/// @brief:	绘制指针
	/// @param: QPainter * painter 
	///  @ret:	void
	///				
	void drawIndicator(QPainter* painter);
	
	/// 
	/// @brief:	绘制指示器的数值
	/// @param: QPainter * painter 
	///  @ret:	void
	///				
	void drawIndicatorValueText(QPainter* painter);
private:
	struct PainterHelper;
	/// pie的半径
	qreal		m_pieRadius{ 10.0 };
	/// 圆弧角度
	const qreal m_arcAngle{ 240.0 };
	/// 红色圆弧所占角度比例
	const qreal m_redArcAngleRate{ 0.2 };
	/// 蓝色圆弧所占角度比例
	const qreal m_blueArcAngleRate{ 0.4 };
	/// 绿色圆弧所占角度比例
	const qreal m_greenArcAngleRate{ 0.4 };
	/// 圆弧刻度线的配置, <key-索引,value-刻度线的颜色>
	QMap<int, QString>	m_arcLineColorMap{};
	/// 当前指针的位置, 范围: 0~1024.0, 若超过极值,指针固定在极值的位置
	qreal m_longIndicatorValue{0};
	/// 指针的填充颜色 #D42231  B2DFF6
	QString m_longIndicatorFillColor{"#5D6B99"};
	/// 指针的边线的颜色
	QString m_longIndicatorBorderLineColor{ "#5D6B99" };
	/// 显示数值的精度,小数点后的个数
	int		m_indicatorValueDecimalPrecision{0};
	/// 显示的文本的颜色
	QString	m_indicatorValueTextColor{"#18BED6"};
	/// 单位文本
	QString m_unitText{"KB/S"};
	/// 单位文本显示的颜色
	QString m_unitTextColor{"#18BED6"};
};
#include "GaugeSpeedWidget.h"
#include <QPainter>
struct GaugeSpeedWidget::PainterHelper
{
	PainterHelper(QPainter& p) : m_painter(p)
	{
		m_painter.save();
	}
	virtual ~PainterHelper()
	{
		m_painter.restore();
	}
private:
	QPainter& m_painter;
};
GaugeSpeedWidget::GaugeSpeedWidget(QWidget*parent)
	: QWidget(parent)
{
	m_arcLineColorMap.insert(0, "#F22A78");
	m_arcLineColorMap.insert(1, "#F12A78");
	m_arcLineColorMap.insert(2, "#F12B78");
	m_arcLineColorMap.insert(3, "#0FB7ED");
	m_arcLineColorMap.insert(4, "#0FB7CC");
	m_arcLineColorMap.insert(5, "#10B7EC");
	m_arcLineColorMap.insert(6, "#2EC3F9");
	m_arcLineColorMap.insert(7, "#13B48D");
	m_arcLineColorMap.insert(8, "#14B38D");
	m_arcLineColorMap.insert(9, "#15B18D");
	m_arcLineColorMap.insert(10, "#17AF8E");
	setWindowFlags(Qt::FramelessWindowHint);
	setAttribute(Qt::WA_TranslucentBackground);
}
GaugeSpeedWidget::~GaugeSpeedWidget()
{}
/// 
/// @brief:	setIndicatorValue
/// @param: const qreal value - 
///  @ret:	void
///				
void GaugeSpeedWidget::setIndicatorValue(const qreal value)
{
	const qreal maxValue = 1024.0;
	const qreal minValue = 0.0;
	m_longIndicatorValue = value;
	if (minValue > value)
	{
		m_longIndicatorValue = minValue;
	}
	if (maxValue < value)
	{
		m_longIndicatorValue = maxValue;
	}
	update();
}
/// 
/// @brief:	setUnitText
/// @param: const QString & str - 
///  @ret:	void
///				
void GaugeSpeedWidget::setUnitText(const QString& str)
{
	m_unitText = str;
	update();
}
/// 
/// @brief:	setIndicatorValueDecimal
/// @param: const int count - 
///  @ret:	void
///				
void GaugeSpeedWidget::setIndicatorValueDecimal(const int count)
{
	m_indicatorValueDecimalPrecision = count;
	const int maxValue = 2;
	const int minValue = 0;
	if (maxValue < m_indicatorValueDecimalPrecision)
	{
		m_indicatorValueDecimalPrecision = maxValue;
	}
	if (minValue > m_indicatorValueDecimalPrecision)
	{
		m_indicatorValueDecimalPrecision = minValue;
	}
	update();
}
/// 
/// @brief:	paintEvent
/// @param: QPaintEvent * event - 
///  @ret:	void
///				
void GaugeSpeedWidget::paintEvent(QPaintEvent* event)
{
	if (false == isVisible())
	{
		return;
	}
	QPainter painter(this);
	painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing, true);
	int widgetWidth = this->width();
	int widgetHeight = this->height();
	int side = qMin(widgetWidth, widgetHeight);
	m_pieRadius = side * 0.9 / 2.0;
	//绘制准备工作,启用反锯齿,平移坐标轴中心,等比例缩放
	painter.translate(side / 2 + 4, side / 2 + 4);
	/// 绘制刻度线
	drawArcDialLine(&painter);
	// 绘制圆弧
	drawArc(&painter);
	/// 绘制刻度数值
	drawArcDialLineNumber(&painter);
	/// 绘制指针
	drawIndicator(&painter);
	/// 绘制指示器当前显示的数值
	drawIndicatorValueText(&painter);
}
/// 
/// @brief:	绘制圆弧
/// @param: QPainter * painter - 
///  @ret:	void
///				
void GaugeSpeedWidget::drawArc(QPainter* painter)
{
	PainterHelper ph(*painter);
	/// 保持边距
	const qreal alignOffset = 0;
	/// 圆弧所在矩形
	QRectF outerRect{ -m_pieRadius + alignOffset, -m_pieRadius + alignOffset, m_pieRadius * 2, m_pieRadius * 2 };
	// 定义圆弧的参数
	//QRectF outerRect(50, 50, 200, 200); // 外圆弧的外接矩形
	//QRectF innerRect(60, 60, 180, 180); // 内圆弧的外接矩形(通过缩小矩形实现宽度)
	QRectF innerRect(outerRect.x() + 10, outerRect.y() + 10, outerRect.width() - 20, outerRect.height() - 20);
	int startAngle = -40 * 16;           // 起始角度,单位为1/16度
	int totalSpan = 250 * 16;           // 总跨度角度,单位为1/16度
	// 创建渐变色
	QConicalGradient gradient(outerRect.center(), startAngle / 16); // 渐变色中心点与起始角度对齐
	gradient.setColorAt(0.0, QColor("#F22A78"));	 
	gradient.setColorAt(48 / 250.0, QColor("#0FB7EE"));  
	gradient.setColorAt(144 / 250.0, QColor("#2AEBA2"));  
	gradient.setColorAt(1, QColor("#00FF00"));
	// 创建外圆弧路径
	QPainterPath outerPath;
	outerPath.arcMoveTo(outerRect, startAngle / 16); // 移动到起始点
	outerPath.arcTo(outerRect, startAngle / 16, totalSpan / 16); // 绘制外圆弧
	// 创建内圆弧路径
	QPainterPath innerPath;
	innerPath.arcMoveTo(innerRect, (startAngle + totalSpan) / 16); // 移动到终点
	innerPath.arcTo(innerRect, (startAngle + totalSpan) / 16, -totalSpan / 16); // 绘制内圆弧(反向)
	// 将两条路径连接起来
	QPainterPath path;
	path.addPath(outerPath);
	path.connectPath(innerPath);
	// 使用渐变色填充路径
	painter->fillPath(path, gradient);
}
/// 
/// @brief:	drawArcDialLine
/// @param: QPainter * painter - 
///  @ret:	void
///				
void GaugeSpeedWidget::drawArcDialLine(QPainter* painter)
{
	PainterHelper ph(*painter);
	painter->setBrush(Qt::NoBrush);
	const qreal lineLen = 13;
	qreal pieRadius = m_pieRadius;
	/// colorStr-划线的颜色
	/// rotateAngle - 较零位的旋转角度
	auto drawArcLine = [&painter, &lineLen, &pieRadius](const QString& colorStr, const qreal rotateAngle)
	{
		painter->setPen(QPen(QColor(colorStr), 2));
		painter->rotate(rotateAngle);
		painter->drawLine(pieRadius - lineLen, 0, pieRadius - 1, 0);
	};
	/// 总共需要绘制11根刻度线,
	const qint32 lineCount = 11;
	/// 绘制每根刻度线需要旋转的角度
	qreal rotateAngle = m_arcAngle / (lineCount - 1);
	/// 多旋转, 是为了统一绘制刻度线. 
	painter->rotate(150 - rotateAngle);
	for (int index = lineCount - 1; index >= 0; --index)
	{
		/// 得到刻度线的颜色
		const QString colorStr = m_arcLineColorMap.value(index);
		rotateAngle = ((lineCount - 10) * 100) * (240.0 / 1000.0);
		/// 顺时针绘制刻度线,所以角度为正数
		drawArcLine(colorStr, rotateAngle);
	}
}
/// 
/// @brief:	drawArcDialLineNumber
/// @param: QPainter * painter - 
///  @ret:	void
///				
void GaugeSpeedWidget::drawArcDialLineNumber(QPainter* painter)
{
	PainterHelper ph(*painter);
	qreal textRadius = m_pieRadius * 0.75;
	QFontMetrics fm(painter->font());
	
	/// 总共需要绘制11根刻度线,
	const qint32 lineCount = 11;
	/// 绘制每根刻度线需要旋转的角度
	//const qreal rotateAngle = 250.0 / (lineCount - 1);
	/// 绘制数值的方向时顺时针, 从左向右绘制,
	qreal startAngle = 60 + 90;
	for (int index = 0; index < lineCount; ++ index)
	{
		QString valueStr = QString::number(index * 100);
		int tmpAngle = startAngle + ((index * 100) * (250.0 / 1024));
		qreal angleArc = (tmpAngle % 360) * 3.14159265 / 180.0;
		qreal textXX = (textRadius) * cos(angleArc);
		qreal textYY = (textRadius) * sin(angleArc);
		int textW = (int)fm.width(valueStr);
		int textH = (int)fm.height();
		textXX -= textW / 2;
		textYY -= textH / 2;
		const QString textColor = m_arcLineColorMap[lineCount - 1 - index];
		painter->setPen(QColor(textColor));
		painter->drawText(textXX, textYY, textW, textH, Qt::AlignCenter, valueStr);
	}
}
/// 
/// @brief:	绘制长指针
/// @param: QPainter * painter - 
///  @ret:	void
///				
void GaugeSpeedWidget::drawIndicator(QPainter* painter)
{
	PainterHelper ph(*painter);
	/// 长指针是一个四边形组成
	/// 指针偏转方向: 顺时针,
	const qreal startAngle = 60 + 90;
	
	/// 表盘的范围时0~100,角度范围 240度。 计算当前指针的偏转角度
	const qreal tmpIndicatorAngle = m_longIndicatorValue * (m_arcAngle / 1000.0);
	/// 指针的偏转角度=起始角度+当前角度
	const qreal indicatorAngle = startAngle + tmpIndicatorAngle;
	/// 旋转指定角度
	painter->rotate(indicatorAngle);
	/// 绘制固定的四边形, 长指针较长的一端指向X的正半轴
	QPointF longIndicatorPointArr[4] = 
	{
		QPointF(-12, 0),
		QPointF(0, -6),
		QPointF(m_pieRadius * 1.0, 0),
		//QPointF(m_pieRadius * 1.0, 1),
		QPointF(0, 6)
	};
	painter->setPen(QColor(m_longIndicatorBorderLineColor));
	painter->setBrush(QColor(m_longIndicatorFillColor));
	painter->drawPolygon(longIndicatorPointArr, sizeof(longIndicatorPointArr) / sizeof(longIndicatorPointArr[0]));
}
/// 
/// @brief:	drawIndicatorValueText
/// @param: QPainter * painter - 
///  @ret:	void
///				
void GaugeSpeedWidget::drawIndicatorValueText(QPainter* painter)
{
	PainterHelper ph(*painter);
	const QString valueStr = QString("%1").arg(m_longIndicatorValue, 0, 'f', m_indicatorValueDecimalPrecision);
	painter->setBrush(Qt::NoBrush);
	painter->setPen(QColor(m_indicatorValueTextColor));
	QRectF valueRect{};
	{
		QFont fontTmp = painter->font();
		fontTmp.setPointSize(22);
		painter->setFont(fontTmp);
		QFontMetrics fm(fontTmp);
		/// 计算文字显示的矩形方框
		qreal textW = (m_pieRadius * 0.7 * 0.8) * 2;
		qreal textH = fm.height();
		qreal textYY = m_pieRadius * 0.7 * 0.5;
		painter->drawText(-textW / 2, textYY, textW, textH, Qt::AlignCenter, valueStr);
		valueRect = QRectF{ -textW / 2, textYY, textW, textH };
	}
	/// 绘制显示的单位
	QFont fontTmp = painter->font();
	fontTmp.setPointSize(12);
	painter->setFont(fontTmp);
	QFontMetrics fm(fontTmp);
	/// 单位显示的宽和高为数值显示的宽高相同
	qreal textWW = valueRect.width();
	qreal textHH = fm.height();
	qreal textXX = valueRect.x();
	qreal textYY = valueRect.y() + valueRect.height() + 3;
	painter->setPen(QColor(m_unitTextColor));
	painter->drawText(textXX, textYY, textWW, textHH, Qt::AlignCenter, m_unitText);
}