QT、VS中用QPainter编写车速仪表盘

目录

一、准备工作

二、画局部控件

1、外环

2、内环

3、扇形

4、刻度和文字

5、指针

三、完整程序代码


参考链接:

        QT 自定义控件之速度表盘_黑塞的博客-CSDN博客

        QT | QPainter,PaintEvent,setRenderHint(防止图形走样)_parker_1的博客-CSDN博客

        Qt 之图形(QPainterPath)_青春不老,奋斗不止!-CSDN博客_qpainterpath

        Qt开发技术:Qt绘图系统(二)QPainter详解_长沙红胖子-CSDN博客_qpainter

        自定义控件学习笔记(二)drawPath()_yy471101598的博客-CSDN博客_drawpath

        Qpainter画时钟_qqwangfan的专栏-CSDN博客

        

这里是一个职场小白的学习历程,所有参考过的网址链接都会贴在上方文章开头处。

--为了方便查看,部分参考链接都挪到了我疑惑时查找的地方

如有侵权请及时告知。

        绘画仪表盘需要分别绘制

        外圆环、内圆环、扇形、文本和刻度、指针五个东西

这几个元素合并之后就形成了

接下来让我们根据上述拆分的五小部分,分别阐述如何实现

一、准备工作

         要想让控件显示在界面上,单纯的在mainwindow.cpp的构造函数中编辑是没有用的。

        我们需要重写paintEvent函数

        从qt帮助文档可知virtual void paintEvent(QPaintEvent *) override
        

        所有在头文件中需要填写

        (我这里先不给完整代码,在最后会放完整的程序代码。也可直接观看参考链接的原码)

#include <QPainter>

----略-----

protected:
    void paintEvent(QPaintEvent *event) override;

----略-----

然后重写这个函数

下面是初始化画笔和画图并显示的操作

添加初始化代码

QPainter paint(this);

paint.setBrush(QBrush(Qt::red, Qt::SolidPattern));

其中反走样功能可以不要,因为我们的控件没复杂到需要这个功能。

void MainWindow::paintEvent(QPaintEvent *event)
{
    QPainter paint(this);
    //反走样功能,也就是防止锯齿现象出现
    paint.setRenderHint(QPainter::Antialiasing, true);
    //设置了填充的颜色和图案
    paint.setBrush(QBrush(Qt::red, Qt::SolidPattern));

    QPainterPath path;
    //void QPainterPath::addEllipse(qreal x, qreal y, qreal width, qreal height)
    path.addEllipse(100, 100, 100, 100);
    //通过路径绘制图形
    paint.drawPath(path);
}

然后就可以得到一个红彤彤的圆

接下来就开始将上述的五个图形都画出来

二、画局部控件

1、外环

    

 设计思想:

        先设置一个大圆A然后设置一个小圆B,大圆面积减去小圆所占面积,就可以得到一个圈。

void MainWindow::draw_Ellipse(QVector<double> &num1, 
                        QVector<double> &num2, QPainter &paint)
{
    //画笔
    //void QPainterPath::addEllipse(qreal x, qreal y, qreal width, qreal height)

    QPainterPath big;
    big.addEllipse(num1.at(0), num1.at(1), num1.at(2), num1.at(3));

    QPainterPath small;
    big.addEllipse(num2.at(0), num2.at(1), num2.at(2), num2.at(3));

    QPainterPath path;
    //大圆减去小圆得到的一个环
    //就像是删除大圆与小圆的交集
    path = big-small;
    //显示QPainterPath
    paint.drawPath(path);
}

2、内环

    

设计思想:

        与外环的设计思想基本一样,且调用了外环的函数,唯一不同的是多了删除边线。

        删除前

       

        删除后就不必说了吧

        具体代码

void MainWindow::draw_Ellipse2(QVector<double> &num1, QVector<double> &num2, QPainter &paint)
{
    //设置画笔颜色和填充形状
    //QBrush-用于填充几何图形的调色板,由颜色和填充风格组成
    paint.setBrush(QBrush(Qt::gray, Qt::SolidPattern));
    //设置画笔删除边线
    paint.setPen(Qt::NoPen);
    draw_Ellipse1(num1, num2, paint);
}

       原文作者在这里还使用了一个类型QRadialGradient

       并且还有一段看起来毫无无意义的代码

void MainWindow::draw_Ellipse(QVector<double> &num, QVector<double> &num1, QPainter &paint, QRadialGradient &radgrient)
{
    radgrient.setColorAt(0.0,Qt::darkGray);
    radgrient.setColorAt(0.2,Qt::white);
    radgrient.setColorAt(0.5,Qt::darkGray);

    //忽略下方代码
    //paint.setBrush(QBrush(Qt::gray,Qt::SolidPattern));
    //paint.setPen(Qt::NoPen);
    //draw_Ellipse(num,num1,paint);
}

         经过百度发现这是一段以圆心为中心渐变填充的函数类型QRadialGradient,但是这里原作者并没有使用到它。

        参考链接:Qt之图形(渐变填充) - 挨踢人啊 - 博客园

        若是要调用的话还应该使用

        paint.setBrush(radrient);

当然这些在本程序中并不影响,我怀疑原作者这样写只是为了强行重载。

3、扇形

     

原作者称这个为扇形,但是我看着不止扇形。

应当还可以拆出三个部分:绿环、红环、扇形

其中的绿环和红环的做法应当是与外环一样,调用了最外圈的蓝环函数。

然后画了一个扇形图,覆盖了绿环。

然而实际研究中我发现原作者并不是这么想的

它将这一块分层了四个部分

头两个图形就像一个龙虾钳子,姑且称它为钳子吧

绿钳子和灰钳子是要合并到一起变成一个钳子的

结果为:

要想得到这样一个结果首先得设置角度,因为这个不再仅仅是靠x,y轴定点填充了。

这里不仅要确定xy轴和宽高的值,还要确定角度。

而角度的知识可以参考以下链接:

  DrawPie函数的使用说明_千本樱景严的博客-CSDN博客

链接中有个图很好,我截出来放一放

提取以下就是整圆的大小在360*16,正值顺时针旋转,负值逆时针旋转,而0在三点钟方向

即上方整圆面积为360*12------spanAngle

旋转角度为顺时针-360*2-------startAngle

所以上方是旋转之后的结果

旋转之前的结果为

 一个360面积占这么多

所以做一个扇形图

首先我们要确定两个角度,即圆占面积角度和圆旋转角度------两个int

也要确定xywidth、height-----------QRectF

然后使用QPainter的drawPie函数进行绘制

然后还有一小部分,也是所占角度和旋转角度加上轴和大小。

根据计算获得

QRectF rect_top2(260, 260, 180.0, 180.0);//x,y,width,height

paint.drawPie(rect_top2, -360*6-60, 360*5-160);//startAngle, spanAngle

红圈圈和最开始的蓝圈圈的做法是一样的

所以最终代码为:

void MainWindow::draw_pie(QPainter &paint)
{
    //正值逆时针,负值顺时针,0度位于三点钟的位置
    //外圆起始角度
    int startAngle = -360*2;

    //整圆360*16
    //外圆覆盖范围
    int spanAngle = 360*12;

    QRectF rect_top(260, 260, 180.0, 180.0); //绿环
    QRectF rect_top1(280, 280, 140.0, 140.0);//灰环
    QRectF rect_top2(260, 260, 180.0, 180.0);//灰扇形

    paint.setBrush(QBrush(Qt::darkGreen, Qt::SolidPattern));
    paint.drawPie(rect_top, startAngle, spanAngle);//绿环

    paint.setBrush(QBrush(Qt::gray, Qt::SolidPattern));
    paint.drawPie(rect_top1, startAngle, spanAngle);//灰环
    paint.drawPie(rect_top2, -360*6-60, 360*5-160);//灰扇形

    //红环
    paint.setBrush(QBrush(Qt::darkRed, Qt::SolidPattern));
    QVector<double> num5 = {320, 320, 60, 60};
    QVector<double> num6 = {327.5, 327.5, 45, 45};
    draw_Ellipse1(num5, num6, paint);
}

 4、刻度和文字

 设计思想:

利用QPainer的translate函数确定刻度和文字的位置

然后用QPainer的setPen的画笔颜色

最后用for和if、switch语句逻辑判断,逻辑很容易看懂,多看一下代码就好了。

这里原作者将刻度分为了两个部分:

50-100一部分、0-40一部分

代码如下:

void MainWindow::draw_text_line(QPainter &paint)
{
   //设置刻度环的位置
   paint.translate(350, 350);
   //设置刻度环的颜色
   paint.setPen(Qt::black);

   for (int i=0; i<=25 ;i++)
   {
       if(i%5 == 0)
       {
           //x1,y1,x2,y2
           //从(x1, y1)到(x2, y2)画一条线。
           paint.drawLine(0, -90, 0, -100);
           switch (i/5)
           {
               case 0: paint.drawText(-5, -103, tr("%1").arg(50));break;
               case 1: paint.drawText(-5, -103, tr("%1").arg(60));break;
               case 2: paint.drawText(-5, -103, tr("%1").arg(70));break;
               case 3: paint.drawText(-5, -103, tr("%1").arg(80));break;
               case 4: paint.drawText(-5, -103, tr("%1").arg(90));break;
               case 5: paint.drawText(-5, -103, tr("%1").arg(100));break;
           }
       }
       else
       {
           paint.drawLine(0, -95, 0, -100);
       }
       //使用旋转分散项目
       paint.rotate(5.2);
   }
   paint.rotate(95);
   for(int i=0; i<=24; i++)
   {
       if(i%5==0)
       {
           paint.drawLine(0, -90, 0, -100);
           switch (i/5)
           {
                case 0: paint.drawText(-5, -103, tr("%1").arg(0));break;
                case 1: paint.drawText(-5, -103, tr("%1").arg(10));break;
                case 2: paint.drawText(-5, -103, tr("%1").arg(20));break;
                case 3: paint.drawText(-5, -103, tr("%1").arg(30));break;
                case 4: paint.drawText(-5, -103, tr("%1").arg(40));break;
           }
       }
       else
       {
           paint.drawLine(0, -95, 0, -100);
       }
       paint.rotate(5.2);
   }
}

然后我将这两个for循环揉成了一个

void MainWindow::draw_text_line(QPainter &paint)
{
   //设置刻度环的位置
   paint.translate(350, 350);
   //设置刻度环的颜色
   paint.setPen(Qt::black);

   for (int i=0;i<=50;i++)
   {
       if(i%5==0)
       {
           paint.drawLine(0, -90, 0, -100);
           switch (i/5)
           {
           //drawText(int,int,QString)
           //使用绘制器当前定义的文本方向,在位置(x, y)绘制给定文本。
           case 0: paint.drawText(-5, -103, tr("%1").arg(0));break;
           case 1: paint.drawText(-5, -103, tr("%1").arg(10));break;
           case 2: paint.drawText(-5, -103, tr("%1").arg(20));break;
           case 3: paint.drawText(-5, -103, tr("%1").arg(30));break;
           case 4: paint.drawText(-5, -103, tr("%1").arg(40));break;
           case 5: paint.drawText(-5, -103, tr("%1").arg(50));break;
           case 6: paint.drawText(-5, -103, tr("%1").arg(60));break;
           case 7: paint.drawText(-5, -103, tr("%1").arg(70));break;
           case 8: paint.drawText(-5, -103, tr("%1").arg(80));break;
           case 9: paint.drawText(-5, -103, tr("%1").arg(90));break;
           case 10: paint.drawText(-5, -103, tr("%1").arg(100));break;
           }
       }
       else
       {
           paint.drawLine(0, -95, 0, -100);
       }
       paint.rotate(5);
   }
}

发现了问题

我的代码的运行结果是这样子的

 然后我发现无论我怎么设置起始位置,都是从圆心上方的正中心开始画圈。

所以这里我明白了作者为什么分成了两份,作者将50-100放在了开头,然后让0-40强行旋转。

让40与50连接从而形成了最终结果。

作者0-40旋转前的结果

 旋转后就变成了最终结果

然而我现在的情况是这样的

我思考了一下,既然半个环能旋转,那我让整个刻度环旋转呢

于是我试验了一下

我想让0逆时针旋转120度的样子大概就是

 然后我paint.rotate(120);,发现转过了。。。。

 开始我以为是这个函数不是以正常角度旋转的,后来我发现,这不就是顺时针的120吗

我突然想起顺时针是正值,逆时针是负值

当我把paint.rotate(-120);这样改成负数后

 

 就与我期望效果差不多了,角度有些不合适但是问题不大,思路是对的。

于是刻度与文本的最终代码为:

void MainWindow::draw_text_line(QPainter &paint)
{
   //设置刻度环的位置
   paint.translate(350, 350);
   //设置刻度环的颜色
   paint.setPen(Qt::black);

   //旋转整个刻度环
   paint.rotate(-125);
   for (int i=0;i<=50;i++)
   {
       if(i%5==0)
       {
           //x1,y1,x2,y2
           //从(x1, y1)到(x2, y2)画一条线。
           paint.drawLine(0, -90, 0, -100);
           switch (i/5)
           {
           //drawText(int,int,QString)
           //使用绘制器当前定义的文本方向,在位置(x, y)绘制给定文本。
           case 0: paint.drawText(-5, -103, tr("%1").arg(0));break;
           case 1: paint.drawText(-5, -103, tr("%1").arg(10));break;
           case 2: paint.drawText(-5, -103, tr("%1").arg(20));break;
           case 3: paint.drawText(-5, -103, tr("%1").arg(30));break;
           case 4: paint.drawText(-5, -103, tr("%1").arg(40));break;
           case 5: paint.drawText(-5, -103, tr("%1").arg(50));break;
           case 6: paint.drawText(-5, -103, tr("%1").arg(60));break;
           case 7: paint.drawText(-5, -103, tr("%1").arg(70));break;
           case 8: paint.drawText(-5, -103, tr("%1").arg(80));break;
           case 9: paint.drawText(-5, -103, tr("%1").arg(90));break;
           case 10: paint.drawText(-5, -103, tr("%1").arg(100));break;
           }
       }
       else
       {
           paint.drawLine(0, -95, 0, -100);
       }
       //利用旋转分散项目
       paint.rotate(5);
   }
}

5、指针

      最后一个部件指针分为两个小部分

      指针和指针指向的数字即文本

      所以就是指针和文本两部分

      指针这边设计思路如下:

      指针可以理解为一个等腰三角形,首先要确定三角形的三个

       然后使用setBrush进行颜色填充

       用drawConvexPolygon绘制多边形形状,确定好点和点的数量。

       最后将指针旋转角度到自己满意的位置即可。

        文字就不必说了,跟上面刻度的文字赋值是一样的。

        上代码:

void MainWindow::draw_point(QPainter &paint)
{
    paint.save();//保存当前小部件的形状和状态

    //1、指针
    //指针形状
    //指针可理解为等腰三角形
    QPoint hand[3]={
        QPoint(0, -90),//顶点
        QPoint(4, -25),//右底点
        QPoint(-4, -25),//左底点
    };

    //指针颜色
    QColor handcolor(200, 10, 10, 200);
    paint.setBrush(handcolor);//填充指针颜色

    paint.rotate(105);//旋转指针角度
    paint.rotate(value*2.5);//使指针运动了起来

    //绘制指针
    //---官方文档解释(绘制由第一个点定义的凸多边形使用当前笔计数数组中的点。)
    //drawConvexPolygon(图形点的位置, 点的个数)
    paint.drawConvexPolygon(hand, 3);

    paint.restore();//恢复小部件的形状和状态


    //2、文字
    paint.rotate(-130);//旋转文字角度
    paint.drawText(-6, 3, tr("%1").arg(value));//圆心中间数字的位置和内容
}

        其中的save函数和restore函数不是很理解,为什么非要有它们。因为删除这两个函数结果也没有什么影响。查看百度和帮助文档只知道是保存和恢复的作用。但是不知道为什么要这样做。希望有大佬看到了可以指点一下我的迷茫,感谢!

然后这五个小部件就此结束了。

现在我们只需要把它合并关联起来,粘贴到画布上就可以了。

直接在paintEvent重写该函数然后调用。

三、完整程序代码

所以最终完整程序的代码为:

目录结构

MainWindows.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include <QPainter>
#include <QVector>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    //声明为explicit的构造函数不能在隐式转换中使用
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

    int value;

private:
    //绘画初始化
    void painter_init(QPainter &paint);

    //绘制圆环1
    void draw_Ellipse1(QVector<double> &num1, QVector<double> &num2, QPainter &paint);

    //重载draw_Ellipse函数,绘制圆环2
    void draw_Ellipse2(QVector<double> &num1, QVector<double> &num2, QPainter &paint);

    //绘画扇形
    void draw_pie(QPainter &paint);

    //绘制文本和刻度
    void draw_text_line(QPainter &paint);

    //绘制指针
    void draw_point(QPainter &paint);

protected:
    void paintEvent(QPaintEvent *event) override;
protected slots:
    void move(int num);//拖动水平条的槽函数
};
#endif // MAINWINDOW_H

MainWindow.cpp

#include "MainWindow.h"
#include "ui_MainWindow.h"

#include <QDebug>

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

    value = 0;
    ui->horizontalSlider->setRange(0, 100);
    connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), this, SLOT(move(int)));
}

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

void MainWindow::move(int num)
{
    value = num;
    qDebug() << value;
    update();
}

//外环------理解
void MainWindow::draw_Ellipse1(QVector<double> &num1, QVector<double> &num2, QPainter &paint)
{
    //画笔
    //void QPainterPath::addEllipse(qreal x, qreal y, qreal width, qreal height)

    QPainterPath big;
    big.addEllipse(num1.at(0), num1.at(1), num1.at(2), num1.at(3));

    QPainterPath small;
    big.addEllipse(num2.at(0), num2.at(1), num2.at(2), num2.at(3));

    QPainterPath path;
    //大圆减去小圆得到的一个环
    //就像是删除大圆与小圆的交集
    path = big-small;
    //显示QPainterPath
    paint.drawPath(path);
}

//内环-----理解
void MainWindow::draw_Ellipse2(QVector<double> &num1, QVector<double> &num2, QPainter &paint)
{
    //设置画笔颜色和填充形状
    //QBrush-用于填充几何图形的调色板,由颜色和填充风格组成
    paint.setBrush(QBrush(Qt::gray, Qt::SolidPattern));
    //设置画笔删除边线
    paint.setPen(Qt::NoPen);
    draw_Ellipse1(num1, num2, paint);
}

//扇形-----理解
void MainWindow::draw_pie(QPainter &paint)
{
    //正值逆时针,负值顺时针,0度位于三点钟的位置
    //外圆起始角度
    int startAngle = -360*2;

    //整圆360*16
    //外圆覆盖范围
    int spanAngle = 360*12;

    QRectF rect_top(260, 260, 180.0, 180.0); //绿环
    QRectF rect_top1(280, 280, 140.0, 140.0);//灰环
    QRectF rect_top2(260, 260, 180.0, 180.0);//灰扇形

    paint.setBrush(QBrush(Qt::darkGreen, Qt::SolidPattern));
    paint.drawPie(rect_top, startAngle, spanAngle);//绿环

    paint.setBrush(QBrush(Qt::gray, Qt::SolidPattern));
    paint.drawPie(rect_top1, startAngle, spanAngle);//灰环
    paint.drawPie(rect_top2, -360*6-60, 360*4+120);//灰扇形

    //红环
    paint.setBrush(QBrush(Qt::darkRed, Qt::SolidPattern));
    QVector<double> num5 = {320, 320, 60, 60};
    QVector<double> num6 = {327.5, 327.5, 45, 45};
    draw_Ellipse1(num5, num6, paint);
}

//文字和刻度----理解
void MainWindow::draw_text_line(QPainter &paint)
{
   //设置刻度环的位置
   paint.translate(350, 350);
   //设置刻度环的颜色
   paint.setPen(Qt::black);

   //旋转整个刻度环
   paint.rotate(-125);
   for (int i=0;i<=50;i++)
   {
       if(i%5==0)
       {
           //x1,y1,x2,y2
           //从(x1, y1)到(x2, y2)画一条线。
           paint.drawLine(0, -90, 0, -100);
           switch (i/5)
           {
           //drawText(int,int,QString)
           //使用绘制器当前定义的文本方向,在位置(x, y)绘制给定文本。
           case 0: paint.drawText(-5, -103, tr("%1").arg(0));break;
           case 1: paint.drawText(-5, -103, tr("%1").arg(10));break;
           case 2: paint.drawText(-5, -103, tr("%1").arg(20));break;
           case 3: paint.drawText(-5, -103, tr("%1").arg(30));break;
           case 4: paint.drawText(-5, -103, tr("%1").arg(40));break;
           case 5: paint.drawText(-5, -103, tr("%1").arg(50));break;
           case 6: paint.drawText(-5, -103, tr("%1").arg(60));break;
           case 7: paint.drawText(-5, -103, tr("%1").arg(70));break;
           case 8: paint.drawText(-5, -103, tr("%1").arg(80));break;
           case 9: paint.drawText(-5, -103, tr("%1").arg(90));break;
           case 10: paint.drawText(-5, -103, tr("%1").arg(100));break;
           }
       }
       else
       {
           paint.drawLine(0, -95, 0, -100);
       }
       //利用旋转分散项目
       paint.rotate(5);
   }
}

//指针-----理解
void MainWindow::draw_point(QPainter &paint)
{
    paint.save();//保存当前小部件的形状和状态

    //1、指针
    //指针形状
    //指针可理解为等腰三角形
    QPoint hand[3]={
        QPoint(0, -90),//顶点
        QPoint(4, -25),//右底点
        QPoint(-4, -25),//左底点
    };

    //指针颜色
    QColor handcolor(200, 10, 10, 200);
    paint.setBrush(handcolor);//填充指针颜色

    paint.rotate(105);//旋转指针角度
    paint.rotate(value*2.5);//使指针运动了起来

    //绘制指针
    //---官方文档解释(绘制由第一个点定义的凸多边形使用当前笔计数数组中的点。)
    //drawConvexPolygon(图形点的位置, 点的个数)
    paint.drawConvexPolygon(hand, 3);

    paint.restore();//恢复小部件的形状和状态


    //2、文字
    paint.rotate(-130);//旋转文字角度
    paint.drawText(-6, 3, tr("%1").arg(value));//圆心中间数字的位置和内容
}

//初始化
void MainWindow::painter_init(QPainter &paint)
{
    paint.setRenderHint(QPainter::Antialiasing, true);
    paint.setBrush(QBrush(Qt::darkBlue, Qt::SolidPattern));
}

//合并五个部件(圆环、指针、文本刻度、扇形)
void MainWindow::paintEvent(QPaintEvent *event)
{
    QPainter paint(this);
    painter_init(paint);//初始化绘画

    //获取外圆环
    //参数属性: X轴,Y轴,width,height
    QVector<double> num1 = {200, 200, 300, 300};//设置圆环位置和大小
    QVector<double> num2 = {220, 220, 260, 260};//设置圆环位置和大小
    draw_Ellipse1(num1,num2,paint);//绘制外圆

//    获取内圆环
    QVector<double> num3 = {220, 220, 260, 260};
    QVector<double> num4 = {260, 260, 180, 180};
    draw_Ellipse2(num3, num4, paint);

//    获取扇形并重叠
    draw_pie(paint);

//    显示刻度和文本
    draw_text_line(paint);

//    显示指针
    draw_point(paint);
}

完美结束!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值