18

Qt自定义控件之仪表盘2--QPaint绘制仪表盘

 3 years ago
source link: http://www.cnblogs.com/pingwen/p/13416933.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

0、前言

前面一篇文章写道了仪表盘的特点,实现了一个贴图的仪表盘,属于低配版本的仪表盘。

主要是有任何改动时候就需要重新设计图片,不能适配不同控件大小,即使让它自由拉伸,但仪表盘放大缩小时候显示效果会变差。这篇文章设计了一个自己绘制的仪表盘,有背景表盘,刻度线、刻度值,指针,以及动态运行效果。

1、demo顶层设计

设计2个控件,1个widget提升为Mydial仪表盘控件,一个滑动条控件,来测试仪表盘指针旋转效果用。

FRBF3e.png!web

代码调用,绑定滑动条信号到仪表盘的槽函数即可。
#include "widget.h"
#include "ui_frmwidget.h"
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    connect(ui->horizontalSlider, &QSlider::valueChanged, ui->dial, &MyDial::valueChanged);
}

Widget::~Widget()
{
    delete ui;
}

2、绘制仪表盘

绘制背景图,绘制表盘圆圈。

void MyDial::drawBg(QPainter *painter)
{
    int r = radius;
    painter->save();
    painter->setPen(Qt::NoPen);
    painter->setBrush(QColor(172, 172, 172));
    painter->drawEllipse(-r, -r, r * 2, r * 2);

    r =  radius * 0.9;
    painter->setBrush(QColor(40, 40, 40));
    painter->setPen(Qt::NoPen);
    painter->drawEllipse(-r, -r, r * 2, r * 2);
    painter->restore();
}

ymuABfb.png!web

绘制刻度线,模仿手表表盘逢5个数值后刻度使用加粗长线表示。

刻度画图有2中典型做法。

方法1:每次旋转后,计算坐标角度,然后使用sin和cos得到2个坐标点,绘制这2个点之间连接线。

方法2:每次坐标轴旋转固定角度,那么x轴就和刻度线垂直了,x为0;y轴和刻度线重合,只计算y轴上的距离就是坐标点,省去了计算三角函数的工作量。

void MyDial::drawDial(QPainter *painter)
{
    int r = radius*0.85;
    double lineWidth = 1;
    painter->save();

    painter->rotate(Angle);
    /*
    为什么旋转?
    如果不旋转画笔的坐标轴,那么每次画的时候需要按照角度来重新计算,x=r*cos,y=r*sin. 计算复杂
    但是如果旋转坐标轴,那么首次旋转angle角度,则y轴和第一条斜线重合,x=0,只需要计算y。
    画100条线,就是分100份来表示进度。每次旋转的角度=360-(起始角度*2--分左右)/100
    */
    double rotate = (double)(360 - (Angle * 2)) / 100;

    for (int i = 0; i <= 100; i++) {
        QColor color = QColor(84, 84, 84);
        if(i>80) color = QColor(250, 0, 0);
        if((i % 10) == 0)
        {
            painter->setPen(QPen(color, 1.3*lineWidth));
            painter->drawLine(0, r, 0, r / 1.2);
        }
        else if((i % 2) == 0)
        {
            painter->setPen(QPen(color, 1*lineWidth));
            painter->drawLine(0, r, 0, r / 1.1);
        }
        painter->rotate(rotate);
    }

    painter->restore();
}

QB7ZBni.png!web

绘制刻度值。逢5个点绘制一个刻度值,每次跳跃一个大刻度,计算出对应的坐标位置。但是数字长度和宽度会影响显示效果,所以要按照字体计算数字的长度和宽度,然后修正显示的起点位置。

void MyDial::drawScaleNum(QPainter *painter)
{
    painter->save();

    qDebug()<< "drawText";
    int r = (int)(radius*0.6);

    painter->setFont(QFont("Arial", 13));
    painter->setPen(QPen(QColor(255,255,255)));
    QFontMetricsF fm = QFontMetricsF(painter->font());

    int gap = (360-Angle*2) / 10;
    for(int i=0; i<=10; i+=1)
    {
        int angle = 90+Angle+gap*i;  //角度,10格子画一个刻度值

        float angleArc =( angle % 360) * 3.14 / 180; //转换为弧度
        int x = (r)*cos(angleArc);
        int y = (r)*sin(angleArc);

        QString speed = QString::number(i);
        int w = (int)fm.width(speed);
        int h = (int)fm.height();
        x = x - w/2;
        y = y + h/4;

        qDebug()<< "x:"<<x<<" y:"<<y;
        //painter->drawPoint(QPointF(x, y));
        painter->drawText(QPointF(x, y),speed);
    }
    painter->restore();
}

bMBZfeF.png!web

绘制指针。

绘制指针有2种典型方法:

方法1:按照旋转角度计算指针坐标点,主要是终点的坐标和角度。先把指针按照一条线来绘制,然后再圆心位置画垂直相交的线,得到2个点,用这2个点和指针终点构成一个三角形,绘制出指针形状。

方法2:先在圆心绘制一个三角形,然后使用旋转角度方式让其转到对应位置即可,免去了计算坐标和三角函数。

void MyDial::drawIndicator(QPainter *painter)
{
    painter->save();
    QPolygon pts;
    int r = radius*0.6;
    pts.setPoints(3, -2, 0, 2, 0, 0, r);
    double degRotate =Angle +  (360.0 - Angle - Angle) / 100 * percent;

    //画指针
    painter->rotate(degRotate);
    QRadialGradient haloGradient(0, 0, 60, 0, 0);  //辐射渐变,内部填充颜色
    haloGradient.setColorAt(0, QColor(100, 100, 100));
    haloGradient.setColorAt(1, QColor(250, 50, 50)); //red
    painter->setPen(QColor(250, 150, 150)); // 边框颜色
    painter->setBrush(haloGradient);
    painter->drawConvexPolygon(pts);
    painter->restore();

    // 画中心圆圈
    QRadialGradient radial(0, 0, 14);  //渐变
    radial.setColorAt(0.0, QColor(100, 100, 100));
    radial.setColorAt(1.0, QColor(250, 50, 50));
    painter->setPen(Qt::NoPen);  //填满没有边界
    painter->setBrush(radial);
    painter->drawEllipse(-7, -7, 14, 14);

    painter->restore();
}

IVziuuB.png!web

绘制动态速度值,动态数值。这一步比较简单,就是在垂直方向绘制一行文本即可。

void MyDial::drawText(QPainter *painter)
{
   painter->save();

   painter->setFont(QFont("Arial", 10));
   painter->setPen(QPen(QColor(255,255,255)));
   QFontMetricsF fm = QFontMetricsF(painter->font());
   QString speed = QString::number(percent) + " km/h";
   int w = (int)fm.width(speed);
   int h = (int)fm.height();
   painter->drawText(QPointF(-w/2, (int)(0.5*radius)),speed);
   painter->restore();
}

vUfU3y.png!web

通过滑动条触发valueChanged。

void MyDial::valueChanged(int value)
{
    this->percent = value;
    update();
}

进一步触发重绘动作。

void MyDial::paintEvent(QPaintEvent *)
{
    int width = this->width();
    int height = this->height();

    //绘制准备工作,启用反锯齿,平移坐标轴中心,等比例缩放
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.translate(width / 2, height / 2);

    int side = qMin(width, height);
    painter.scale(side / 200.0, side / 200.0);

    //绘制最外框圆形背景
    drawBg(&painter);
     //绘制刻度
     drawDial(&painter);
     //绘制刻度数值
     drawScaleNum(&painter);
     //绘制指针
     drawIndicator(&painter);
     //绘制表盘上文本当前值
     drawText(&painter);
}

3、作品效果展示

vQba6ru.gif

4、参考文档

1、Qt编写自定义控件1-汽车仪表盘

https://www.cnblogs.com/feiyangqingyun/p/10739099.html

2、Qt总结之八:绘制仪表盘

https://blog.csdn.net/Aidam_Bo/article/details/85266798

3、Qt自定义控件 -- 仪表盘01

https://blog.csdn.net/pingis58/article/details/82150237

尊重技术文章,转载请注明!

Qt自定义控件之仪表盘2--QPaint绘制仪表盘

https://www.cnblogs.com/pingwen/p/13416933.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK