制作一个圆形仪表盘,此表盘通用各种参数指示,如:温度,速度,压力,等进度相关的类别。
qt5.7 MinGW
windows8
首先 演示一下效果图
表盘采用的是上半圆形表盘,有彩色的渐变条框,这个用QConicalGradient实现,其余的还稍微容易一些,
偶尔水群,听过刘大师一句话非常有道理,只要心中有坐标,万物皆可paint,绘制自定义控件,搞明白坐标是基础的部分,其次 才是想怎么去画想要的图形。
下面话不多说直接把此通用圆形表盘代码带注释简单讲解:
首先是一些必备的参数先声明好,还有一些提供外部修改参数的接口,这个根据自己需求预留,此 控件我暂时只 预留了修改范围和修改标题。
public Q_SLOTS:
void setRange(int minValued, int maxValued);
//修改范围
void setCurentSpeed(int speed);
//设置标题
void setTitle(const QString &titled);
private:
void drawFrame(QPainter& painter);//绘制边框与背景
void drawNumberIndicator(QPainter& painter);//绘制指示数字
void drawDividing(QPainter& painter);//绘制刻度
void drawNumberSpeed(QPainter &painter);//显示数字速度
void drawIndicator(QPainter& painter);//绘制速度指针
private:
int m_startAngle;
int m_endAngle;
int m_refSize; //绘制的参考大小
int m_radius;
int m_minSpeed;//最小值
int m_maxSpeed;//最大值
int m_curSpeed;//当前值
int m_true_curSpeed;//当前值
double m_anglePerVel;//温度 对应的 角度
QString title; //标题
m_startAngle = 180; //顺时针角度
m_endAngle = 0;
m_refSize = 200;
m_radius = m_refSize /2;
m_minSpeed = 0; //由于绘制刻度,需要为5倍数
m_maxSpeed = 100; //由于绘制刻度,需要为5倍数
m_curSpeed = m_minSpeed;
//温度对应的角度
m_anglePerVel = (360.0 - (m_startAngle - m_endAngle)) / (m_maxSpeed - m_minSpeed);
setWindowFlags(Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
接下来就是paintevent部分了,在这里,每个控件通用的语句就是先定好中心和一个范围,如下代码上半部分所示
void SpeedPanel::paintEvent(QPaintEvent */*event*/)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);// 抗锯齿
float scale = qMin(width(),height());
//设置缩放比例和原点的先后顺序很重要
painter.scale(scale/m_refSize,scale/m_refSize);
painter.translate(m_refSize/2,m_refSize/2); //设置坐标原点
drawFrame(painter);
drawDividing(painter);//绘制刻度
drawNumberIndicator(painter);//绘制指示数字
drawIndicator(painter);//绘制速度指针
drawNumberSpeed(painter);//显示数字速度
}
在之后还有5个函数,分别是绘制表盘从底层到 顶层,一层一层覆盖
此部分核心 代码直接贴上,注释都有可以看懂
void SpeedPanel::drawFrame(QPainter &painter)
{
painter.save();
painter.setPen(Qt::NoPen);//确保没有边框线----填满,不留边界线
//绘制灰色圆形外边框
QLinearGradient lg1(-m_radius,-m_radius,m_radius,m_radius);//渐变区域
lg1.setColorAt(0,Qt::gray);
lg1.setColorAt(0.5,Qt::white);
lg1.setColorAt(1,Qt::gray);
painter.setBrush(lg1);//设置画笔
painter.drawEllipse(-m_radius,-m_radius,m_refSize,m_refSize);//画圆
//绘制圆形渐变色
QConicalGradient conical(0, 0, 0);
conical.setColorAt(0,Qt::red);
conical.setColorAt(0.3,Qt::yellow);
conical.setColorAt(0.5,Qt::green);
conical.setColorAt(1,Qt::white);
painter.setPen(Qt::transparent);
painter.setBrush(conical);
painter.drawEllipse(QPoint(0,0),95,95);
//白色覆盖下半圆
QPen pen;
pen.setCapStyle(Qt::FlatCap);
pen.setWidthF(10);
pen.setColor(Qt::white);
painter.setPen(pen);
QRectF rect = QRectF(-90, -90, 90 * 2, 90 * 2);
painter.drawArc(rect, 0 * 16, -180 * 16);
//中间白色圆圈覆盖
painter.setBrush(Qt::white);
painter.drawEllipse(QPoint(0,0),81,81);
painter.restore();
}
//绘制刻度
void SpeedPanel::drawDividing(QPainter &painter)
{
painter.save();
painter.rotate(m_startAngle);//将坐标系顺时针旋转150°,到达起始位置
QPen pen(Qt::black);
painter.setPen(pen);
int step = (m_maxSpeed - m_minSpeed) / 5;
double angleStep = (360.0 - (m_startAngle - m_endAngle)) / step;
for (int i = m_minSpeed; i <= m_maxSpeed; i += 5)
{
if (i >= 100){ //绘制红色
pen.setColor(Qt::red);
painter.setPen(pen);
}
if (i % 25 == 0){//粗线
pen.setWidth(2);
painter.setPen(pen);
painter.drawLine(88,0,75,0);
}else if (i % 10 == 0){//中等
pen.setWidth(1);
painter.setPen(pen);
painter.drawLine(88,0,80,0);
}else if (i % 5 == 0){ //短线
pen.setWidth(0);
painter.setPen(pen);
painter.drawLine(83,0,80,0);
}
painter.rotate(angleStep);
}
painter.restore();
}
//绘制数字指示
void SpeedPanel::drawNumberIndicator(QPainter &painter)
{
painter.save();
painter.setPen(Qt::black);
double x,y;
double angle, angleArc;
double w,h;
QFontMetricsF fm(this->font());
for (int i = m_minSpeed; i <= m_maxSpeed; i+= 25)//每隔20Km设置一个数字
{
angle = 360 - (m_startAngle + (i - m_minSpeed) * m_anglePerVel); //角度
angleArc = angle * 3.14 / 180; //转换为弧度
x = 65 * cos(angleArc);
y = -65 * sin(angleArc); // 负号的意义在于 Y轴正方向向下
QString speed = QString::number(i);
if (i % 25 == 0)
{
w = fm.width(speed);
h = fm.height();
painter.drawText(QPointF(x - w / 2,y + h/4),speed);
}
}
painter.restore();
}
void SpeedPanel::drawIndicator(QPainter &painter)
{
//绘制指针
painter.save();
double curAngle = m_startAngle + m_curSpeed * m_anglePerVel;
painter.rotate(curAngle); //旋转坐标系
QRadialGradient haloGradient(0, 0, 60, 0, 0); //辐射渐变
haloGradient.setColorAt(0, QColor(200,60,60));
haloGradient.setColorAt(1, QColor(0,0,0)); //灰
painter.setPen(Qt::red); //定义线条文本颜色 设置线条的颜色//Qt::transparent
painter.setBrush(haloGradient);//刷子定义形状如何填满 填充后的颜色
static const QPointF points[3] = {
QPointF(0.0, 3),
QPointF(0.0, -3),
QPointF(70.0, 0),
};
painter.drawPolygon(points,3); //绘制三角形指针
painter.restore();
//绘制旋转中心
painter.save();
QRadialGradient rg(0,0,10,0,0);
rg.setColorAt(0.0,Qt::darkGray);
rg.setColorAt(0.5,Qt::white);
rg.setColorAt(1.0,Qt::darkGray);
painter.setPen(Qt::NoPen);
painter.setBrush(rg);
painter.drawEllipse(QPoint(0,0),10,10);
painter.restore();
}
void SpeedPanel::drawNumberSpeed(QPainter &painter)
{
painter.save();
painter.setPen(Qt::black);
QString speed = QString("%1").arg(m_true_curSpeed);
QFontMetricsF fm(this->font());
qreal w = fm.size(Qt::TextSingleLine,speed).width();
painter.drawText(-w/2,40,speed);
QFontMetricsF fm1(this->font());
qreal w2 = fm1.size(Qt::TextSingleLine,title).width();
painter.drawText(-w2/2,70,title);
painter.restore();
}
之后就是预留的接口部分,我留了一个设置范围的接口,这块一开始也不小心反了错误,在外部程序调用此空间后,范围计算的
角度使用初始化时的范围,并不是外部修改的范围, 然后导致程序显示不正常,在修改范围后重新计算一下没个单位对应的角度即可。
void SpeedPanel::setRange(int minValued, int maxValued)
{
m_minSpeed=minValued; //最小值
m_maxSpeed=maxValued; //最大值
m_anglePerVel = (360.0 - (m_startAngle - m_endAngle)) / (m_maxSpeed - m_minSpeed);
update();
}
我心坐标,坐标我心。