概述

基于QT的仪表盘有很多种办法,比如使用QWT,ChartDirector 或H5混合的echart组件,或者基于QT的绘图功能绘制,或者基于美工提供的图片的基础上增加动态效果。然而搞明白QT自定义控件的绘图后,这种实现是最简单的。且定制度高,想要什么效果就可以自己绘制个。这里介绍下Qt自定义控件之仪表盘的完整实现。

效果预览

以下是三种不同的手绘实现,网上的其他文章有的只提供效果或代码片段,这里附上完整能用的源码。最后再对其中的一个的实现做详细的原理实现和细节介绍。

源码实现

这两种实现的完整源码如下:

#ifndef MYDIAL_H
#define MYDIAL_H#include <QWidget>class MyDial : public QWidget
{Q_OBJECTpublic:MyDial(QWidget *parent = 0);~MyDial();void setValue(double val);protected:void paintEvent(QPaintEvent *);void drawCrown(QPainter *painter);void drawBackground(QPainter *painter);void drawScale(QPainter *painter);void drawScaleNum(QPainter *painter);void drawTitle(QPainter *painter);void drawIndicator(QPainter *painter);void drawNumericValue(QPainter *painter);private:QColor m_background;QColor m_foreground;int m_maxValue;int m_minValue;int m_startAngle;int m_endAngle;int m_scaleMajor;int m_scaleMinor;double m_value;int m_precision;QTimer *m_updateTimer;QString m_units;QString m_title;public Q_SLOTS:void UpdateAngle();private:};#endif // MYDIAL_H
#include "mydial.h"#include <QPainter>// 构造函数
MyDial::MyDial(QWidget *parent){m_background = Qt::black;m_foreground = Qt::white;m_startAngle = 60;m_endAngle = 60;m_scaleMajor = 10;m_minValue = 0;m_maxValue = 100;m_scaleMajor = 10;//分度m_scaleMinor = 10;m_units = "L/min";m_title = "Water flow";m_precision = 0;m_value = 0;setWindowFlags(Qt::FramelessWindowHint);//无窗体setAttribute(Qt::WA_TranslucentBackground);//背景透明//resize(400, 400);}MyDial::~MyDial()
{}void MyDial::setValue(double val)
{m_value = val;
}
//绘制表冠
void MyDial::drawCrown(QPainter *painter)
{ painter->save();int radius = 100;QLinearGradient lg1(0, -radius, 0, radius);lg1.setColorAt(0, Qt::white); //设置渐变的颜色和路径比例lg1.setColorAt(1, Qt::gray); //只是粗略的颜色,具体的可以参考RGB颜色查询对照表painter->setBrush(lg1); // 创建QBrush对象,把这个渐变对象传递进去:painter->setPen(Qt::NoPen); //边框线无色painter->drawEllipse(-radius, -radius, radius << 1, radius << 1);painter->setBrush(m_background = Qt::black);painter->drawEllipse(-92, -92, 184, 184);painter->restore();
}//绘制刻度数字
void MyDial::drawScaleNum(QPainter *painter)
{ painter->save();painter->setPen(m_foreground);//m_startAngle是起始角度,m_endAngle是结束角度,m_scaleMajor在一个量程中分成的刻度数double startRad = ( 270-m_startAngle) * (3.14 / 180);double deltaRad = (360 - m_startAngle - m_endAngle) * (3.14 / 180) / m_scaleMajor;double sina,cosa;int x, y;QFontMetricsF fm(this->font());double w, h, tmpVal;QString str;for (int i = 0; i <= m_scaleMajor; i++){sina = sin(startRad - i * deltaRad);cosa = cos(startRad - i * deltaRad);tmpVal = 1.0 * i *((m_maxValue - m_minValue) / m_scaleMajor) + m_minValue;// tmpVal = 50;str = QString( "%1" ).arg(tmpVal);  //%1作为占位符   arg()函数比起 sprintf()来是类型安全的w = fm.size(Qt::TextSingleLine,str).width();h = fm.size(Qt::TextSingleLine,str).height();x = 82 * cosa - w / 2;y = -82 * sina + h / 4;painter->drawText(x, y, str); //函数的前两个参数是显示的坐标位置,后一个是显示的内容,是字符类型""}painter->restore();
}// 绘制刻度线
void MyDial::drawScale(QPainter *painter) //绘制刻度线
{ painter->save();painter->rotate(m_startAngle);int steps = (m_scaleMajor * m_scaleMinor); //相乘后的值是分的份数double angleStep = (360.0 - m_startAngle - m_endAngle) / steps; //每一个份数的角度// painter->setPen(m_foreground); //m_foreground是颜色的设置// QPen pen = painter->pen(); //第一种方法QPen pen ;pen.setColor(Qt::green); //推荐使用第二种方式for (int i = 0; i <= steps; i++){if (i % m_scaleMinor == 0)//整数刻度显示加粗{pen.setWidth(1); //设置线宽painter->setPen(pen); //使用面向对象的思想,把画笔关联上画家。通过画家画出来painter->drawLine(0, 62, 0, 72); //两个参数应该是两个坐标值}else{pen.setWidth(0);painter->setPen(pen);painter->drawLine(0, 67, 0, 72);}painter->rotate(angleStep);}painter->restore();
}void MyDial::drawTitle(QPainter *painter)
{ painter->save();painter->setPen(m_foreground);//painter->setBrush(m_foreground);QString str(m_title); //显示仪表的功能QFontMetricsF fm(this->font());double w = fm.size(Qt::TextSingleLine,str).width();painter->drawText(-w / 2, -30, str);painter->restore();
}// 显示的单位,与数值
void MyDial::drawNumericValue(QPainter *painter)
{ QString str = QString("%1 %2").arg(m_value, 0, 'f', m_precision).arg(m_units);QFontMetricsF fm(font());double w = fm.size(Qt::TextSingleLine,str).width();painter->setPen(m_foreground);painter->drawText(-w / 2, 42, str);
}void MyDial::UpdateAngle()
{update();
}// 绘制表针,和中心点
void MyDial::drawIndicator(QPainter *painter)
{painter->save();QPolygon pts;pts.setPoints(3, -2, 0, 2, 0, 0, 60);  /* (-2,0)/(2,0)/(0,60) *///第一个参数是 ,坐标的个数。后边的是坐标painter->rotate(m_startAngle);double degRotate = (360.0 - m_startAngle - m_endAngle) / (m_maxValue - m_minValue)*(m_value - m_minValue);//画指针painter->rotate(degRotate);  //顺时针旋转坐标系统QRadialGradient haloGradient(0, 0, 60, 0, 0);  //辐射渐变haloGradient.setColorAt(0, QColor(60, 60, 60));haloGradient.setColorAt(1, QColor(160, 160, 160)); //灰painter->setPen(Qt::white); //定义线条文本颜色  设置线条的颜色painter->setBrush(haloGradient);//刷子定义形状如何填满 填充后的颜色painter->drawConvexPolygon(pts); //这是个重载函数,绘制多边形。painter->restore();//画中心点QColor niceBlue(150, 150, 200);QConicalGradient coneGradient(0, 0, -90.0);  //角度渐变coneGradient.setColorAt(0.0, Qt::darkGray);coneGradient.setColorAt(0.2, niceBlue);coneGradient.setColorAt(0.5, Qt::white);coneGradient.setColorAt(1.0, Qt::darkGray);painter->setPen(Qt::NoPen);  //没有线,填满没有边界painter->setBrush(coneGradient);painter->drawEllipse(-5, -5, 10, 10);
}// 重绘函数
void MyDial ::paintEvent(QPaintEvent *)
{ int width=this->width();int height=this->height();QPainter painter(this);//一个类中的this表示一个指向该类自己的指针painter.setRenderHint(QPainter::Antialiasing);  /* 使用反锯齿(如果可用) */painter.translate(width/2, height/2);   /* 坐标变换为窗体中心 */int side = qMin(width, height);painter.scale(side / 200.0, side / 200.0);      /* 比例缩放 */drawCrown(&painter);                                 /* 画表盘边框 */drawScaleNum(&painter);                          /* 画刻度数值值 */drawScale(&painter);                                 /* 画刻度线 */drawTitle(&painter);                                 /* 画单位 */drawNumericValue(&painter);                      /* 画数字显示 */drawIndicator(&painter);                             /* 画表针 */}

另一个的实现,文末有引用,感谢

#ifndef GAUGEPANEL_H
#define GAUGEPANEL_H#include <QWidget>
#include <QPropertyAnimation>
#include <QtMath>
#include <QPainter>#ifdef quc
#if (QT_VERSION < QT_VERSION_CHECK(5,7,0))
#include <QtDesigner/QDesignerExportWidget>
#else
#include <QtUiPlugin/QDesignerExportWidget>
#endifclass QDESIGNER_WIDGET_EXPORT GaugePanel : public QWidget
#else
class GaugePanel : public QWidget
#endif{
Q_OBJECT
Q_PROPERTY(double value READ getValue WRITE setValue)
Q_PROPERTY(int hShearValue READ getHShearValue WRITE setHShearValue)
Q_PROPERTY(int vShearValue READ getVShearValue WRITE setVShearValue)
Q_PROPERTY(double radiusInner READ getRadiusInner WRITE setRadiusInner)
Q_PROPERTY(double radiusOuter READ getRadiusOuter WRITE setRadiusOuter)
Q_PROPERTY(double radiusHalo READ getRadiusHalo WRITE setRadiusHalo)
Q_PROPERTY(QColor colorOuterFrame READ getColorOuterFrame WRITE setColorOuterFrame)
Q_PROPERTY(QColor colorInnerStart READ getColorInnerStart WRITE setColorInnerStart)
Q_PROPERTY(QColor colorInnerEnd READ getColorInnerEnd WRITE setColorInnerEnd)
Q_PROPERTY(QColor colorOuterStart READ getColorOuterStart WRITE setColorOuterStart)
Q_PROPERTY(QColor colorOuterEnd READ getColorOuterEnd WRITE setColorOuterEnd)
Q_PROPERTY(QColor colorHaloStart READ getColorHaloStart WRITE setColorHaloStart)
Q_PROPERTY(QColor colorHaloEnd READ getColorHaloEnd WRITE setColorHaloEnd)public:
explicit GaugePanel(QWidget *parent = nullptr);
~GaugePanel();protected:
void paintEvent(QPaintEvent *);private:
void drawOuterGradient(QPainter *painter);
void drawInnerGradient(QPainter *painter);
void drawOuterHalo(QPainter *painter);
void drawScale(QPainter *painter);
void drawScaleNum(QPainter *painter);
void drawPointer(QPainter *painter);
void drawPointerSector(QPainter *painter);
void drawValue(QPainter *painter);
void drawUnit(QPainter *painter);private:
double value;                   //目标值
int hShearValue, vShearValue;//H、V扭曲值
double radiusInner;             //渐变内圈内半径
double radiusOuter;             //渐变外圈内半径
double radiusHalo;              //光晕内半径
QColor colorOuterFrame;         //表盘外边框颜色
QColor colorInnerStart;         //渐变内圈起始颜色
QColor colorInnerEnd;           //渐变内圈结束颜色
QColor colorOuterStart;         //渐变外圈起始颜色
QColor colorOuterEnd;           //渐变外圈结束颜色
QColor colorHaloStart;          //光晕起始颜色
QColor colorHaloEnd;            //光晕结束颜色QPropertyAnimation *hShearAnimation, *vShearAnimation;public:
double getValue()               const;
int    getHShearValue()         const;
int    getVShearValue()         const;
double getRadiusInner()         const;
double getRadiusOuter()         const;
double getRadiusHalo()          const;
QColor getColorOuterFrame()     const;
QColor getColorInnerStart()     const;
QColor getColorInnerEnd()       const;
QColor getColorOuterStart()     const;
QColor getColorOuterEnd()       const;
QColor getColorHaloStart()      const;
QColor getColorHaloEnd()        const;void setValue(int value);
void setValue(double value);
void setHShearValue(int value);
void setVShearValue(int value);//表盘外边框颜色
void setColorOuterFrame(QColor color);//内层渐变区半径
void setRadiusInner(int radius);
void setRadiusInner(double radius);//外层渐变区半径
void setRadiusOuter(int radius);
void setRadiusOuter(double radius);//外层光晕区半径
void setRadiusHalo(int radius);
void setRadiusHalo(double radius);//内层渐变颜色
void setColorInnerStart(QColor color);
void setColorInnerEnd(QColor color);//外层渐变颜色
void setColorOuterStart(QColor color);
void setColorOuterEnd(QColor color);//光晕颜色
void setColorHaloStart(QColor color);
void setColorHaloEnd(QColor color);void startShearAnimal(int duration, int hShearValue, int vShearValue);public slots:
void updateValue(double value);Q_SIGNALS:void valueChanged(qreal value);
};#endif // GaugePanel_H
#include "gaugepanel.h"GaugePanel::GaugePanel(QWidget *parent) : QWidget(parent)
{value = hShearValue = vShearValue = 0.0;radiusInner = 65.0;radiusOuter = 76.25;radiusHalo = 87.5;colorOuterFrame = QColor(50, 154, 255, 250);colorInnerStart = QColor(50, 154, 255, 180);colorInnerEnd = QColor(50, 154, 255, 70);colorOuterStart = QColor(50, 154, 255, 150);colorOuterEnd = QColor(50, 154, 255, 200);colorHaloStart = QColor(100, 180, 255, 80);colorHaloEnd = QColor(30, 80, 120, 20);hShearAnimation = new QPropertyAnimation(this, "hShearValue");vShearAnimation = new QPropertyAnimation(this, "vShearValue");//setWindowFlags(Qt::FramelessWindowHint);//无窗体//setAttribute(Qt::WA_TranslucentBackground);//背景透明
}GaugePanel::~GaugePanel()
{hShearAnimation->stop();vShearAnimation->stop();delete hShearAnimation;delete vShearAnimation;
}void GaugePanel::paintEvent(QPaintEvent *)
{int width = this->width();int height = this->height();int side = qMin(width, height);//绘制准备工作,启用反锯齿,平移坐标轴中心,等比例缩放QPainter painter(this);painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);painter.translate(width / 2, height / 2);painter.scale(side / 215.0, side / 215.0);painter.shear(double(hShearValue/100.0f), double(vShearValue/100.0f));//内层渐变drawInnerGradient(&painter);//外层渐变drawOuterGradient(&painter);//外层光晕drawOuterHalo(&painter);//刻度线drawScale(&painter);//刻度值drawScaleNum(&painter);//绘制指针drawPointer(&painter);//绘制指针扇形drawPointerSector(&painter);//绘制值drawValue(&painter);//绘制单位drawUnit(&painter);
}void GaugePanel::drawOuterGradient(QPainter *painter)
{if(radiusHalo <= radiusOuter)return;painter->save();QRectF rectangle(0-radiusHalo, 0-radiusHalo, radiusHalo*2, radiusHalo*2);QPen framePen(colorOuterFrame);framePen.setWidthF(1.5f);painter->setPen(framePen);painter->drawEllipse(rectangle);painter->setPen(Qt::NoPen);QPainterPath smallCircle;QPainterPath bigCircle;float radius = radiusOuter;smallCircle.addEllipse(-radius, -radius, radius * 2, radius * 2);radius += (radiusHalo - radiusOuter);bigCircle.addEllipse(-radius, -radius, radius * 2, radius * 2);//大圆抛去小圆部分QPainterPath gradientPath = bigCircle - smallCircle;QRadialGradient gradient(0, 0, radius, 0, 0);//gradient.setSpread(QGradient::ReflectSpread);gradient.setColorAt(0.85, colorOuterStart);gradient.setColorAt(0.98, colorOuterEnd);painter->setBrush(gradient);painter->drawPath(gradientPath);painter->restore();
}void GaugePanel::drawInnerGradient(QPainter *painter)
{if(radiusOuter <= radiusInner)return;painter->save();painter->setPen(Qt::NoPen);QPainterPath smallCircle;QPainterPath bigCircle;float radius = radiusInner;smallCircle.addEllipse(-radius, -radius, radius * 2, radius * 2);radius += (radiusOuter - radiusInner);bigCircle.addEllipse(-radius, -radius, radius * 2, radius * 2);//大圆抛去小圆部分QPainterPath gradientPath = bigCircle - smallCircle;QRadialGradient gradient(0, 0, radius, 0, 0);//gradient.setSpread(QGradient::ReflectSpread);gradient.setColorAt(0.7, colorInnerStart);gradient.setColorAt(1, colorInnerEnd);painter->setBrush(gradient);painter->drawPath(gradientPath);painter->restore();
}void GaugePanel::drawOuterHalo(QPainter *painter)
{painter->save();painter->setPen(Qt::NoPen);QPainterPath smallCircle;QPainterPath bigCircle;float radius = radiusHalo;smallCircle.addEllipse(-radius, -radius, radius * 2, radius * 2);radius += (100.0 - radiusHalo);bigCircle.addEllipse(-radius, -radius, radius * 2, radius * 2);//大圆抛去小圆部分QPainterPath gradientPath = bigCircle - smallCircle;QRadialGradient gradient(0, 0, 100, 0, 0);gradient.setSpread(QGradient::ReflectSpread);gradient.setColorAt(radiusHalo/100, colorHaloStart);gradient.setColorAt(1, colorHaloEnd);painter->setBrush(gradient);painter->drawPath(gradientPath);painter->restore();
}void GaugePanel::drawScale(QPainter *painter)
{float radius = 85;painter->save();painter->setPen(QColor(255, 255, 255));painter->rotate(30);int steps = (30);double angleStep = (360.0 - 60) / steps;QPen pen = painter->pen();pen.setCapStyle(Qt::RoundCap);for (int i = 0; i <= steps; i++) {if (i % 3 == 0) {pen.setWidthF(1.5);painter->setPen(pen);QLineF line(0.0f, radius - 8.0f, 0.0f, radius);painter->drawLine(line);} else {pen.setWidthF(0.5);painter->setPen(pen);QLineF line(0.0f, radius - 3.0f, 0.0f, radius);painter->drawLine(line);}painter->rotate(angleStep);}painter->restore();
}void GaugePanel::drawScaleNum(QPainter *painter)
{float radius = 95.0f;painter->save();painter->setPen(QColor(255, 255, 255));double startRad = (330 - 90) * (M_PI / 180);double deltaRad = (300) * (M_PI / 180) / 10;for (int i = 0; i <= 10; i++) {double sina = sin(startRad - i * deltaRad);double cosa = cos(startRad - i * deltaRad);double value = 1.0 * i * ((30) / 10);//刻度值范围QString strValue = QString("%1").arg((double)value, 0, 'f', 0);double textWidth = fontMetrics().width(strValue);double textHeight = fontMetrics().height();int x = radius * cosa - textWidth / 2;int y = -radius * sina + textHeight / 4;painter->drawText(x, y, strValue);}painter->restore();
}void GaugePanel::drawPointer(QPainter *painter)
{painter->save();float radius = 83.0;painter->rotate(30+int(value*10));QPen pen = painter->pen();pen.setWidthF(1.0);pen.setColor(QColor(50, 154, 255, 200));painter->setPen(pen);QLineF line(0.0f, 0.0f, 0.0f, radius);painter->drawLine(line);painter->restore();
}void GaugePanel::drawPointerSector(QPainter *painter)
{float radius = 87.5f;painter->save();painter->setPen(Qt::NoPen);QRectF rect(-radius, -radius, radius * 2, radius * 2);painter->setBrush(QColor(50, 154, 255, 50));painter->drawPie(rect, -120*16, -value*16*10);painter->restore();
}void GaugePanel::drawValue(QPainter *painter)
{int radius = 100;painter->save();painter->setPen(QColor(255, 255, 255));painter->setFont(QFont("Arial", 22, 22, true));QRectF textRect(-radius, -radius, radius * 2, radius * 2);QString strValue = QString("%1").arg((double)value, 0, 'f', 0);painter->drawText(textRect, Qt::AlignCenter, strValue);painter->restore();
}void GaugePanel::drawUnit(QPainter *painter)
{int radius = 100;painter->save();painter->setPen(QColor(255, 255, 255));painter->setFont(QFont("Arial", 9, -1, true));QRectF textRect(-radius, -radius+20, radius * 2, radius * 2);painter->drawText(textRect, Qt::AlignCenter, "km/h");painter->restore();
}double GaugePanel::getValue() const
{return this->value;
}int GaugePanel::getHShearValue() const
{return this->hShearValue;
}int GaugePanel::getVShearValue() const
{return this->vShearValue;
}double GaugePanel::getRadiusInner() const
{return radiusInner;
}double GaugePanel::getRadiusOuter() const
{return radiusOuter;
}double GaugePanel::getRadiusHalo() const
{return radiusHalo;
}QColor GaugePanel::getColorOuterFrame() const
{return colorOuterFrame;
}QColor GaugePanel::getColorInnerStart() const
{return colorInnerStart;
}QColor GaugePanel::getColorInnerEnd() const
{return colorInnerEnd;
}QColor GaugePanel::getColorOuterStart() const
{return colorOuterStart;
}QColor GaugePanel::getColorOuterEnd() const
{return colorOuterEnd;
}QColor GaugePanel::getColorHaloStart() const
{return colorHaloStart;
}QColor GaugePanel::getColorHaloEnd() const
{return colorHaloEnd;
}void GaugePanel::setValue(int value)
{setValue(double(value));
}void GaugePanel::setValue(double value) {updateValue(value);
}void GaugePanel::setHShearValue(int value)
{if(value > 100 || value < -100)return;this->hShearValue = value;update();
}void GaugePanel::setVShearValue(int value)
{if(value > 100 || value < -100)return;this->vShearValue = value;update();
}void GaugePanel::setColorOuterFrame(QColor color)
{colorOuterFrame = color;
}void GaugePanel::setRadiusInner(int radius)
{setRadiusInner(double(radius));
}void GaugePanel::setRadiusInner(double radius)
{if(radius >= 0.0f && radius < 100.0f){radiusInner = radius;update();}
}void GaugePanel::setRadiusOuter(int radius)
{setRadiusOuter(double(radius));
}void GaugePanel::setRadiusOuter(double radius)
{if(radius > 0.0f && radius < 100.0f){radiusOuter = radius;update();}
}void GaugePanel::setRadiusHalo(int radius)
{setRadiusHalo(double(radius));
}void GaugePanel::setRadiusHalo(double radius)
{if(radius > 0.0f && radius < 100.0f){radiusHalo = radius;update();}
}void GaugePanel::setColorInnerStart(QColor color)
{colorInnerStart = color;
}void GaugePanel::setColorInnerEnd(QColor color)
{colorInnerEnd = color;
}void GaugePanel::setColorOuterStart(QColor color)
{colorOuterStart = color;
}void GaugePanel::setColorOuterEnd(QColor color)
{colorOuterEnd = color;
}void GaugePanel::setColorHaloStart(QColor color)
{colorHaloStart = color;
}void GaugePanel::setColorHaloEnd(QColor color)
{colorHaloEnd = color;
}void GaugePanel::startShearAnimal(int duration, int hShearValue, int vShearValue)
{if(hShearValue == this->hShearValue && vShearValue == this->vShearValue){return;}if(hShearAnimation->state() != QPropertyAnimation::Stopped){hShearAnimation->stop();}if(vShearAnimation->state() != QPropertyAnimation::Stopped){vShearAnimation->stop();}hShearAnimation->setDuration(duration);hShearAnimation->setStartValue(this->hShearValue);hShearAnimation->setEndValue(hShearValue);hShearAnimation->start();vShearAnimation->setDuration(duration);vShearAnimation->setStartValue(this->vShearValue);vShearAnimation->setEndValue(vShearValue);vShearAnimation->start();
}void GaugePanel::updateValue(double value)
{if(value > 30.0 || value < 0.0){return;}this->value = value;//update();this->update();// emit valueChanged(value);
}

原理分析

实现原理

以实现下图的模拟表盘为例,分析下实现的原理和细节。

表盘的实现是基于QT的QPainter类中的绘图方法,自定义实现一个QWidget控件。

将表盘分解析为3个组成部分。表盘的外形轮廓、指针和显示的当前速度的数值。

外形轮廓由一个圆弧和一些指示刻度组成,它的绘制肯定要使用QT中的画圆弧的函数、画线函数还有显示文本函数。

指针是一个不规则的多边形,它的绘制会用到QT中的绘制多边形的函数。

显示当前速度值比较简单些,直接使用显示文本函数绘制。

先有了静态部分的基础,再开始考虑指针的动态旋转过程和旋转过程中的渐变效果是如何实现的。

指针旋转的角度应该和当前的转速相互对应。当前转速改变时,会根据新的转速计算出当前指针位于什么角度的位置,然后可以调用QT的旋转角度函数让多边形指针旋转到这个位置。

旋转的渐变效果其实是通过绘制扇形实现的,要绘制扇形的角度和指针旋转的角度是一样的。

由于绘制的扇形的内部的着色采用了颜色的线性内插,所以不同的角度显示的颜色程度不同。

因此给人以渐变的效果。让转速自增1还是自减1,转速改变时调用函数让界面进行重新绘制。

我们知道了每个刻度线对应的角度和半径,我们就可以求出每条刻度线的起点和终点,公式如下:

int x = 圆心的横坐标 + 半径 * cos(角度 * π / 180);
int y = 圆心的纵坐标 + 半径 * sin(角度 * π / 180);

实现源码

#include "myspeed.h"#include <QPainter>
#include <QBrush>
#include <QLabel>
#include <QTimerEvent>
#include <QLinearGradient>
#include <QFont>#include <QtMath>MySpeed::MySpeed(QWidget *parent): QWidget(parent)
{//resize(800, 480);//setWindowTitle("test");m_foreground = Qt::black;speed = 0;status = 0;m_startAngle = 45;m_endAngle = 45;m_minValue = 0;m_maxValue = 100;m_scaleMajor = 10;//分度m_scaleMinor = 10;m_units = "L/min";m_title = "My Speed";m_precision = 0;m_value = 0;m_angle = (qreal)270/(m_maxValue-1);time_id = this->startTimer(50);
}MySpeed::~MySpeed()
{
}void MySpeed::paintEvent(QPaintEvent *event)
{int width=this->width();int height=this->height();QPainter painter(this);painter.translate(width/2, height/2);int side = qMin(width, height);painter.scale(side / 200.0, side / 200.0);      /* 比例缩放 */drawFrame(&painter);drawScale(&painter);                                 /* 画刻度线 */drawScaleNum(&painter);                          /* 画刻度数值值 */drawUnit(&painter);drawPointer(&painter);drawSpeed(&painter);
}void MySpeed::drawFrame(QPainter *painter)
{painter->save();// 半径100int radius = 100;painter->setBrush(QBrush(QColor(0, 255, 0, 255), Qt::SolidPattern));painter->drawArc(-radius, -radius, radius<<1, radius<<1, -135*16, -270*16);painter->restore();}// 绘制刻度线
void MySpeed::drawScale(QPainter *painter)
{painter->save();painter->rotate(m_startAngle);int steps = (m_scaleMajor * m_scaleMinor); //相乘后的值是分的份数double angleStep = (360.0 - m_startAngle - m_endAngle) / steps; //每一个份数的角度// painter->setPen(m_foreground); //m_foreground是颜色的设置// QPen pen = painter->pen(); //第一种方法QPen pen ;pen.setColor(m_foreground); //推荐使用第二种方式for (int i = 0; i <= steps; i++){if (i % m_scaleMinor == 0)//整数刻度显示加粗{pen.setWidth(1); //设置线宽painter->setPen(pen); //使用面向对象的思想,把画笔关联上画家。通过画家画出来painter->drawLine(0, 90, 0, 100); //两个参数应该是两个坐标值}else{pen.setWidth(0);painter->setPen(pen);painter->drawLine(0, 95, 0, 100);}painter->rotate(angleStep);}painter->restore();
}
// 绘制刻度
void MySpeed::drawScaleNum(QPainter *painter)
{painter->save();painter->setPen(m_foreground);//m_startAngle是起始角度,m_endAngle是结束角度,m_scaleMajor在一个量程中分成的刻度数double startRad = ( 270-m_startAngle) * (3.14 / 180);double deltaRad = (360 - m_startAngle - m_endAngle) * (3.14 / 180) / m_scaleMajor;double sina,cosa;int x, y;QFontMetricsF fm(this->font());double w, h, tmpVal;QString str;for (int i = 0; i <= m_scaleMajor; i++){sina = sin(startRad - i * deltaRad);cosa = cos(startRad - i * deltaRad);tmpVal = 1.0 * i *((m_maxValue - m_minValue) / m_scaleMajor) + m_minValue;// tmpVal = 50;str = QString( "%1" ).arg(tmpVal);  //%1作为占位符   arg()函数比起 sprintf()来是类型安全的w = fm.size(Qt::TextSingleLine,str).width();h = fm.size(Qt::TextSingleLine,str).height();x = 82 * cosa - w / 2;y = -82 * sina + h / 4;painter->drawText(x, y, str); //函数的前两个参数是显示的坐标位置,后一个是显示的内容,是字符类型""}painter->restore();
}void MySpeed::drawPointer(QPainter *painter)
{int radius = 100;QPoint point[4] = {QPoint(0, 10),QPoint(-10, 0),QPoint(0, -80),QPoint(10, 0),};painter->save();QLinearGradient linear;linear.setStart(-radius, -radius);linear.setFinalStop(radius<<1, radius<<1);linear.setColorAt(0, QColor(0, 255, 255, 0));linear.setColorAt(1, QColor(0, 255, 255, 255));painter->setPen(Qt::NoPen);painter->setBrush(linear);painter->drawPie(-radius, -radius, radius<<1, radius<<1, 225 * 16, -(m_angle * this->speed) * 16);painter->restore();painter->save();painter->setBrush(QBrush(QColor(0, 0, 0, 255), Qt::SolidPattern));painter->rotate(-135 + this->speed * m_angle);painter->drawPolygon(point, 4);painter->restore();
}void MySpeed::drawSpeed(QPainter *painter)
{painter->save();painter->setPen(QColor("#0"));//  绘制速度QFont font("Times", 10, QFont::Bold);font.setBold(true);font.setPixelSize(46);painter->setFont(font);painter->drawText(-60, 0, 120, 92, Qt::AlignCenter, QString::number(speed));painter->restore();
}void MySpeed::drawUnit(QPainter *painter)
{QString str = QString("%1").arg(m_units);QFontMetricsF fm(font());double w = fm.size(Qt::TextSingleLine,str).width();painter->setPen(m_foreground);painter->drawText(-w / 2, 82, str);
}void MySpeed::timerEvent(QTimerEvent *e)
{int timerId = e->timerId();if(this->time_id == timerId) {if(this->status == 0) {this->speed += 1;if(this->speed >= m_maxValue)this->status = 1;}else {this->speed -= 1;if(this->speed <= 0)this->status = 0;}this->update();}
}
#ifndef MYSPEED_H
#define MYSPEED_H#include <QWidget>class MySpeed : public QWidget
{Q_OBJECTpublic:MySpeed(QWidget *parent = nullptr);~MySpeed();void paintEvent(QPaintEvent *event);void timerEvent(QTimerEvent *e);private:void drawFrame(QPainter *painter);void drawScale(QPainter *painter);void drawScaleNum(QPainter *painter);void drawPointer(QPainter *painter);void drawSpeed(QPainter *painter);void drawUnit(QPainter *painter);int speed;int time_id;int status;qreal m_angle;QColor m_foreground;int m_maxValue;int m_minValue;int m_startAngle;int m_endAngle;int m_scaleMajor;int m_scaleMinor;double m_value;int m_precision;//QTimer *m_updateTimer;QString m_units;QString m_title;};
#endif // MYSPEED_H

引用

Qt控件:仪表盘_EEer!的博客-CSDN博客_qt 仪表盘

Qt:绘制仪表盘_Francis_Ye的博客-CSDN博客_qt 仪表盘

Qt总结之八:绘制仪表盘_ooMelloo的博客-CSDN博客_qt 仪表盘

qt实现一个简单的仪表盘_黑色肥猫的博客-CSDN博客_qt 仪表盘

QT绘制简易表盘_我不是萧海哇~~~~的博客-CSDN博客_qt绘制仪表盘

学习QT之自定义控件入门学习#-仪表盘_贝勒里恩的博客-CSDN博客_qt仪表盘

Qt:汽车仪表盘控件_苏三爱吃炸酱面的博客-CSDN博客_qt汽车仪表盘

QT样式表之径向渐变(qradialgradient)_Allen Roson的博客-CSDN博客

学习QT之QT绘图原理详解_贝勒里恩的博客-CSDN博客_qt 绘图原理

Qt自定义控件 -- 仪表盘01_flist的博客-CSDN博客_qt 仪表盘

Qt自定义控件之仪表盘的完整实现相关推荐

  1. Qt自定义控件创建和使用

    Qt自定义控件创建和使用 Qt中很方便的是使用各种自定义控件来分模块实现各种子功能,用于实现代码的解耦: 之前在使用Qt 5.12创建自定义控件时,出现了各种问题,多次重装QtCreator和VS,屡 ...

  2. Qt自定义控件(IP输入框,windows下)

    1.建立自定义控件项目 组代表后来能从哪里找到 剩下的点"下一步"就行了 2.编辑代码 在生成项目的cpp中添加自己的内容(没有.ui界面了,设计tab是灰色的) 放一下我的代码 ...

  3. Qt自定义控件------SwitchButton

    简介   一般用来两种互斥状态的切换. 效果 下面放效果图 控件拆分 一.控件大体有两部分组成,一个是背景,一个是白色的滑块.原本背景是想用QWidget然后直接设置圆角的,然后发现效果很差.所以背景 ...

  4. qt实现汽车仪表盘设计linux,基于Qt汽车车速仪表盘设计.doc

    基于Qt汽车车速仪表盘设计 研 究 生 课 程 论 文 学 院: 信息工程学院 专 业: 课 程: 姓 名: 学 号: 授课教师: 一 研究背景 仪表作为汽车整个系统中十分重要的部分,是提高汽车综合性 ...

  5. linux qt 自定义控件,编写Qt Designer自定义控件(一)——如何创建并使用Qt自定义控件...

    要想在Qt Designer中使用自定义控件,必须要使Qt Designer能够知道我们的自定义控件的存在.有两种方法可以把新自定义控件的信息通知给Qt Designer:"升级(promo ...

  6. 【Qt开发笔记】Qt自定义控件开发与使用,自定义控件实现容器与控件内布局

    1.开发环境 Qt版本:Qt 4.8.7 编译器:MinGw 系统:Windows 2.创建Qt4自定义控件 创建一个Qt自定义控件工程. 工程名为Custom. 控件类取名Custom. 然后完成创 ...

  7. Qt自定义控件之圆形按钮、圆形头像

    Qt自定义控件之圆形按钮.圆形头像 前言 代码实现 实验效果 前言 现在很多软件的头像或者按钮都是圆形了,看起来比较舒服.比如QQ登录头像,酷狗客户端的一些按钮都是圆形.Qt实现圆形头像,大致有几种思 ...

  8. Qt 自定义控件提升,头文件找不到的问题

    Qt 自定义控件提升,头文件找不到的问题 在附加包含目录添加: ./

  9. Qt自定义控件 -- 仪表盘01

    转自:https://blog.csdn.net/baidu_33850454/article/details/81773323 简介 实现原理 边框绘制 drawFrame(QPainter &am ...

最新文章

  1. Java基础:网络编程
  2. python分类预测_python做logistic分类预测尝试
  3. Docker最全教程——MongoDB容器化(十三)
  4. Azure进阶攻略 | 你的程序也能察言观色?这个真的可以有!
  5. IOS之Autorotation and Autosizing
  6. 根据自身工作经验总结的一个工作问题解决思路
  7. springmvc以html作为视图_SpringMVC如何自定义视图
  8. AvalonDock 2.0+Caliburn.Micro+MahApps.Metro实现Metro风格插件式系统(菜单篇)
  9. division by zero_MySQL5.7 group_by报错问题解决办法,大部分程序员都收藏了
  10. 电子密码锁程序C语言,基于51单片机的电子密码锁程序
  11. LINQ分页和排序,skip和Take 用法
  12. css3箭头水平淡入淡出
  13. 新版游戏陪玩约玩APP源码 多人连麦聊天/语音直播社交双端APP源代码 附搭建教程文档
  14. 6个有效激励合作伙伴和促进推荐的小技巧
  15. 绝地求生+守望先锋?2019首款黑马游戏Apex英雄凭什么如此火爆?
  16. uniapp - APP云打包、蒲公英平台发布APP的步骤
  17. static变量有什么作用
  18. 《linux设备驱动程序》——Linux设备模型
  19. 路由选择顺序详解-最长掩码匹配/路由协议优先级/开销
  20. android如何避免钓鱼页面,Android应用钓鱼劫持风险的检测与防范

热门文章

  1. 游戏美术设计外包哪家比较好
  2. Html5的废弃与新增全局属性及元素
  3. Java9中2个被废弃的使用方法
  4. Spark(火花)快速、通用的大数据处理引擎框架
  5. pfSense book之DNS解析
  6. 【Linux基础】文件权限(待补充)
  7. 学习后可自考的网络安全证书
  8. ZARA公司用品牌指导极速供应链
  9. 【蓝桥杯摆烂篇】蓝桥模拟赛【摆烂day3】
  10. c sharp连接mysql