QT绘图项目 - 汽车表盘

目录

前言:

整体代码

widget.h

widget.cpp

效果演示

实现刻度文字正确排版

优化代码

达到效果

封装整理代码结构:

widget.h

widget.cpp

指针样式美化

优化后的指针API

效果演示

设置高速刻度为红色

优化刻度API

效果演示

速度显示优化

给内圈画上黑色

优化速度显示文本格式

调整绘画顺序

效果演示

实现内环发光圈:

内环发光效果API

插入位置

效果演示

实现外环发个圈

外环发光效果API

插入位置

实现效果

绘制汽车logo

绘制汽车logo的API

插入位置

实现效果

deBug及微调

[bug1: restore()和 save() 没有配对使用](#bug1: restore()和 save() 没有配对使用)

bug2:中间显示的速度不匹配


前言:

这个表盘在上一个项目"简易表盘"的基础上搭建,请先看上篇:

Qt绘图项目 - 简易表盘-CSDN博客

整体代码

widget.h

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
   void paintEvent(QPaintEvent *event);

private:
    Ui::Widget *ui;
    QTimer *timer;
    int currentValue; //实现指针移动
    int mark = 0; // 控制指针的移动方向
    int startAbgle; // 设置起始角度
    double angle;
    void initCanvas(QPainter& painter);
    void drawMiddleCircle(QPainter& painter,int radius);
    void drawCurrentSpeed(QPainter& painter);
    void drawScale(QPainter& painter,int radius);
    void drawScaleText(QPainter& painter,int radius);
    void drawPointLine(QPainter &painter,int length);
    void drawSpeedPie(QPainter& painter,int radius);
    void startSpeed();
    void drawEllispseInnerBlack(QPainter& painter,int radius);
    void drawEllispseInnerBShine(QPainter& painter,int radius);
    void drawEllispseOutterBShine(QPainter& painter,int radius);
    void drawLogo(QPainter& painter,int radius);
};
#endif // WIDGET_H

widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QPainter>
#include <QTimer>
#include <QtMath>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    setFixedSize(800,600);
    startAbgle = 150; // 设置起始角度为150
    startSpeed();


}

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

void Widget::initCanvas(QPainter& painter)
{
    //设置抗锯齿
    painter.setRenderHints(QPainter::Antialiasing,true);
    //设置黑色的背景色
    painter.setBrush(Qt::black); // 设置 黑色的画刷
    painter.drawRect(rect());

    //坐标系,平移到中心
    QPoint cent(rect().width()/2,rect().height()*0.6); //自定义中心位置
    painter.translate(cent);

}

void Widget::drawMiddleCircle(QPainter &painter,int radius)
{
    //画小圆
    painter.setPen(QPen(Qt::white,4)); //设置白色的画笔
    painter.drawEllipse(QPoint(0,0),60,60);
}

//画当前速度
void Widget::drawCurrentSpeed(QPainter &painter)
{
    painter.setPen(Qt::white);
    //显示当前速度值:
    QFont font("Arial",30);
    font.setBold(true);
    painter.setFont(font);
    painter.drawText(QRect(-60,-60,120,70),Qt::AlignCenter,QString::number(currentValue*4));

    QFont font2("Arial",13);
    font2.setBold(true);
    painter.setFont(font2);
    painter.drawText(QRect(-60,-60,120,160),Qt::AlignCenter,"Km/h");

}

// 画出当前刻度
void Widget::drawScale(QPainter &painter,int radius)
{
    //保存原点:
    painter.save(); // 三点钟方向
    //画刻度:

    //1.算出一个刻度需要的角度 270 度分成 50份
    angle = 240*1.0/60; //需要先转为double类型,否则销售几乎部分会被吞掉造成误差
    //2.设置起始角度
    painter.rotate(startAbgle); // 从135度开始
    //设置字体样式:
    // painter.setFont(QFont("华文宋体",8));
    //3.分类讨论 画出所有刻度
    for(int i=0;i<=60;++i){//包括0-  一共有61根刻度线
        if(i>=40){  // 时速160 = 40*4 认定为高速
            painter.setPen(QPen(Qt::red,5)); //将高速速设定为红色
        }

        if(i%5 == 0){ // 被5 整除进一步细分表盘
            //画出一个长刻度
            painter.drawLine(radius-20,0,radius-3,0);
        }
        else{
            //画出一个长刻度
            painter.drawLine(radius-10,0,radius-3,0);
        }
        painter.rotate(angle);//选择angle的角度准备去画下一个刻度
    }
    painter.restore();
}

// 写刻度文字
void Widget::drawScaleText(QPainter &painter, int radius)
{
    //设置字体:

    QFont font("Arial",13);
    font.setBold(true); // 设置粗体
    painter.setFont(font);
    int r = radius-43;

    for(int i=0;i<=60;++i){
        if(i%5 == 0){ // 每5格标记一个刻度
            //保存坐标系
            painter.save();
            //算出平移点  // 弧度 = 角度*3.1415/180
            int delX =  qCos(qDegreesToRadians(210-angle*i))*r;//QT中sin、cos认的是弧度
            int delY =  qSin(qDegreesToRadians(210-angle*i))*r;//qDegreesToRadians - 角度转弧度

            //平移坐标系
            painter.translate(QPoint(delX,-delY));
            //旋转坐标系:
            painter.rotate(-120+angle*i);//angle=4,30*4=120,实参是0,120
            //写上文字
            painter.drawText(-25,-25,50,30,Qt::AlignCenter,QString::number(i*4));
            //恢复坐标系
            painter.restore();
        }
    }

}

// 画指针
void Widget::drawPointLine(QPainter &painter,int length)
{
    //画指针 --> 线
    //先恢复到之前保存的原点:

    painter.save(); // 接着保存原点
    // 通过定时器去改变currentValue的值,去控制指针的移动
    // painter.drawLine(60,0,height()/2-20-38,0); // 58

    //指针样式美化:

    painter.setRenderHint(QPainter::Antialiasing,true);
    painter.setBrush(Qt::white);
    painter.setPen(Qt::NoPen);
    //painter.translate(rect().center());
    static const QPointF points[4] = {
        QPointF(0,0),
        QPointF(0,15.0),
        QPointF(220.0,1.1),
        QPointF(220.0,-1.1)
    };

    //给指针弧度偏移 --> 让指针转动起来
    painter.rotate(startAbgle+angle*currentValue); // 进行坐标系偏移
    painter.drawPolygon(points, 4);
    painter.restore();
}

//画扇形
void Widget::drawSpeedPie(QPainter &painter, int radius)
{

    QRect rentangle(-radius,-radius,radius*2,radius*2);
    painter.setPen(Qt::NoPen);
    painter.setBrush(QColor(255,0,0,80));

    painter.drawPie(rentangle,(-startAbgle)*16,-angle*currentValue*16);
}

void Widget::startSpeed()
{
    timer = new QTimer(this);
    currentValue = 0;
    connect(timer,&QTimer::timeout,[=](){
        if(mark == 0){
            currentValue++;
            if(currentValue >= 60)
                mark = 1;
        }
        else if(mark ==1){
            currentValue--;
            if(currentValue <= 0)
                mark = 0;
        }
        update(); // 触发绘图事件
    });
    timer->start(30);
}

// ,给内圈画上黑色
void Widget::drawEllispseInnerBlack(QPainter &painter, int radius)
{
    painter.setBrush(Qt::black);
    painter.drawEllipse(QPoint(0,0),radius,radius);
}

// 内环发光效果
void Widget::drawEllispseInnerBShine(QPainter &painter, int radius)
{
    // 径向 渐变实现
    QRadialGradient radialGradient(0,0,radius);
    radialGradient.setColorAt(0.0,QColor(255,0,0,200));
    radialGradient.setColorAt(1.0,QColor(0,0,0,100));
    painter.setBrush(radialGradient);
    painter.drawEllipse(QPoint(0,0),radius,radius);
}

// 画外围发光圈
void Widget::drawEllispseOutterBShine(QPainter &painter, int radius)
{

    QRect rentangle(-radius,-radius,radius*2,radius*2);
    painter.setPen(Qt::NoPen);
    //渐变色处理外环光亮
    QRadialGradient radialGradient(0,0,radius);
    radialGradient.setColorAt(1,QColor(255,0,0,200));
    radialGradient.setColorAt(0.97,QColor(255,0,0,120));
    radialGradient.setColorAt(0.9,QColor(0,0,0,0));
    radialGradient.setColorAt(0,QColor(0,0,0,0));
    painter.setBrush(radialGradient);

    painter.drawPie(rentangle,(-150)*16,-angle*60*16);
}

// 绘制汽车logo
void Widget::drawLogo(QPainter &painter, int radius)
{
 QRect rectangle(-65,radius*0.38,130,50);
 painter.drawPixmap(rectangle,QPixmap(":/C:/Users/31472/Desktop/Images/qi_che_biao_pan/lu_hua.png"));

}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);

    int rad = height()/2;
    // 初始化画布
    initCanvas(painter);

    //不用画大圆了,用刻度尺去表出框架即可
    //painter.drawEllipse(QPoint(0,0),height()/2,height()/2);

    //画出中间的小圆
    drawMiddleCircle(painter,60);

    //画刻度
    drawScale(painter,rad);

    //写刻度文字:
    drawScaleText(painter,rad);
    // 画指针
    drawPointLine(painter,rad-58);
    //画扇形
    drawSpeedPie(painter,rad);
    //画渐变色发光内圈圆
    drawEllispseInnerBShine(painter,110);
    // 画黑色内圈
    drawEllispseInnerBlack(painter,80);
    //画出当前速度 -- 放正在画黑的下面,要设置在黑圈的图层下
    drawCurrentSpeed(painter);
    //画外环发光圈
    drawEllispseOutterBShine(painter,rad+15);
    //画汽车logo
    drawLogo(painter,rad);

}

效果演示

实现刻度文字正确排版

移动坐标系,达到正确排版

优化代码

cpp 复制代码
//刻度文字达到正确排版效果

    int r = height()/2-43;
    painter.restore();
    for(int i=0;i<=60;++i){
        if(i%5 == 0){ // 每5格标记一个刻度
        //保存坐标系
        painter.save();
        //算出平移点  // 弧度 = 角度*3.1415/180
        int delX =  qCos(qDegreesToRadians(210-angle*i))*r;//QT中sin、cos认的是弧度
        int delY =  qSin(qDegreesToRadians(210-angle*i))*r;//qDegreesToRadians - 角度转弧度

        //平移坐标系
        painter.translate(QPoint(delX,-delY));
        //旋转坐标系:
        painter.rotate(-120+angle*i);//angle=4,30*4=120,实参是0,120
        //写上文字
        painter.drawText(-25,-25,50,30,Qt::AlignCenter,QString::number(i*4));
        //恢复坐标系
        painter.restore();
        }
    }

达到效果

封装整理代码结构:

widget.h

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
   void paintEvent(QPaintEvent *event);

private:
    Ui::Widget *ui;
    QTimer *timer;
    int currentValue; //实现指针移动
    int mark = 0; // 控制指针的移动方向
    int startAbgle; // 设置起始角度
    double angle;
    void initCanvas(QPainter& painter);
    void drawMiddleCircle(QPainter& painter,int radius);
    void drawCurrentSpeed(QPainter& painter);
    void drawScale(QPainter& painter,int radius);
    void drawScaleText(QPainter& painter,int radius);
    void drawPointLine(QPainter &painter,int length);
    void drawSpeedPie(QPainter& painter,int radius);
    void startSpeed();
};
#endif // WIDGET_H

widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QPainter>
#include <QTimer>
#include <QtMath>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    startAbgle = 150; // 设置起始角度为150
    startSpeed();


}

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

void Widget::initCanvas(QPainter& painter)
{
    //设置抗锯齿
    painter.setRenderHints(QPainter::Antialiasing,true);
    //设置黑色的背景色
    painter.setBrush(Qt::black); // 设置 黑色的画刷
    painter.drawRect(rect());

    //坐标系,平移到中心
    painter.translate(rect().center());

}

void Widget::drawMiddleCircle(QPainter &painter,int radius)
{
    //画小圆
    painter.setPen(QPen(Qt::white,4)); //设置白色的画笔
    painter.drawEllipse(QPoint(0,0),60,60);
}

//画当前速度
void Widget::drawCurrentSpeed(QPainter &painter)
{
    //显示当前速度值:
    painter.setFont(QFont("华文宋体",15));
    painter.drawText(QRect(-60,-60,120,120),Qt::AlignCenter,QString::number(currentValue));

}

// 画出当前刻度
void Widget::drawScale(QPainter &painter,int radius)
{
    //保存原点:
    painter.save(); // 三点钟方向
    //画刻度:

    //1.算出一个刻度需要的角度 270 度分成 50份
    angle = 240*1.0/60; //需要先转为double类型,否则销售几乎部分会被吞掉造成误差
    //2.设置起始角度
    painter.rotate(startAbgle); // 从135度开始
    //设置字体样式:
   // painter.setFont(QFont("华文宋体",8));
    //3.分类讨论 画出所有刻度
    for(int i=0;i<=60;++i){//包括0-  一共有61根刻度线
        if(i%5 == 0){ // 被5 整除进一步细分表盘
            //画出一个长刻度
            painter.drawLine(radius-20,0,radius-3,0);
        }
        else{
            //画出一个长刻度
            painter.drawLine(radius-10,0,radius-3,0);
        }
        painter.rotate(angle);//选择angle的角度准备去画下一个刻度
    }

}

// 写刻度文字
void Widget::drawScaleText(QPainter &painter, int radius)
{
    int r = radius-43;
    painter.restore();
    for(int i=0;i<=60;++i){
        if(i%5 == 0){ // 每5格标记一个刻度
        //保存坐标系
        painter.save();
        //算出平移点  // 弧度 = 角度*3.1415/180
        int delX =  qCos(qDegreesToRadians(210-angle*i))*r;//QT中sin、cos认的是弧度
        int delY =  qSin(qDegreesToRadians(210-angle*i))*r;//qDegreesToRadians - 角度转弧度

        //平移坐标系
        painter.translate(QPoint(delX,-delY));
        //旋转坐标系:
        painter.rotate(-120+angle*i);//angle=4,30*4=120,实参是0,120
        //写上文字
        painter.drawText(-25,-25,50,30,Qt::AlignCenter,QString::number(i*4));
        //恢复坐标系
        painter.restore();
        }
    }

}

// 画指针
void Widget::drawPointLine(QPainter &painter,int length)
{
    //画指针 --> 线
    //先恢复到之前保存的原点:
    painter.restore();
    painter.save(); // 接着保存原点
    // 通过定时器去改变currentValue的值,去控制指针的移动
    painter.rotate(startAbgle+angle*currentValue); // 进行坐标系偏移
    painter.drawLine(60,0,height()/2-20-38,0); // 58
}

//画扇形
void Widget::drawSpeedPie(QPainter &painter, int radius)
{
    painter.restore();
    QRect rentangle(-radius,-radius,radius*2,radius*2);
    painter.setPen(Qt::NoPen);
    painter.setBrush(QColor(255,128,64,150));

    painter.drawPie(rentangle,(-startAbgle)*16,-angle*currentValue*16);
}

void Widget::startSpeed()
{
    timer = new QTimer(this);
    currentValue = 0;
    connect(timer,&QTimer::timeout,[=](){
        if(mark == 0){
            currentValue++;
            if(currentValue >= 60)
                mark = 1;
        }
        else if(mark ==1){
            currentValue--;
            if(currentValue <= 0)
                mark = 0;
        }


        update(); // 触发绘图事件
    });
    timer->start(30);
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);

    // 初始化画布
    initCanvas(painter);

    //不用画大圆了,用刻度尺去表出框架即可
    //painter.drawEllipse(QPoint(0,0),height()/2,height()/2);

    //画出中间的小圆
    drawMiddleCircle(painter,60);
    //画出当前速度
    drawCurrentSpeed(painter);
    //画刻度
    drawScale(painter,height()/2);

    //写刻度文字:
    drawScaleText(painter,height()/2);
    // 画指针
    drawPointLine(painter,height()/2-58);


    //画扇形
    drawSpeedPie(painter,height()/2);

}

指针样式美化

优化后的指针API

cpp 复制代码
// 画指针
void Widget::drawPointLine(QPainter &painter,int length)
{
    //画指针 --> 线
    //先恢复到之前保存的原点:
    painter.restore();
    painter.save(); // 接着保存原点
    // 通过定时器去改变currentValue的值,去控制指针的移动
     // painter.drawLine(60,0,height()/2-20-38,0); // 58


    //指针样式美化:

    painter.setRenderHint(QPainter::Antialiasing,true);
    painter.setBrush(Qt::white);
    painter.setPen(Qt::NoPen);
    //painter.translate(rect().center());
    static const QPointF points[4] = {
        QPointF(0,0),
        QPointF(0,15.0),
        QPointF(220.0,1.1),
        QPointF(220.0,-1.1)

    };

    //给指针弧度偏移 --> 让指针转动起来
    painter.rotate(startAbgle+angle*currentValue); // 进行坐标系偏移
    painter.drawPolygon(points, 4);

}

效果演示

可以看到我们投票通过画多边形的方式对指针进行了美化

设置高速刻度为红色

优化刻度API

cpp 复制代码
void Widget::drawScale(QPainter &painter,int radius)
{
    //保存原点:
    painter.save(); // 三点钟方向
    //画刻度:

    //1.算出一个刻度需要的角度 270 度分成 50份
    angle = 240*1.0/60; //需要先转为double类型,否则销售几乎部分会被吞掉造成误差
    //2.设置起始角度
    painter.rotate(startAbgle); // 从135度开始
    //设置字体样式:
   // painter.setFont(QFont("华文宋体",8));
    //3.分类讨论 画出所有刻度
    for(int i=0;i<=60;++i){//包括0-  一共有61根刻度线
        if(i>=40){  // 时速160 = 40*4 认定为高速
         painter.setPen(QPen(Qt::red,5)); //将高速速设定为红色
        }

        if(i%5 == 0){ // 被5 整除进一步细分表盘
            //画出一个长刻度
            painter.drawLine(radius-20,0,radius-3,0);
        }
        else{
            //画出一个长刻度
            painter.drawLine(radius-10,0,radius-3,0);
        }
        painter.rotate(angle);//选择angle的角度准备去画下一个刻度
    }

}

效果演示

//注意我这里设置了字体格式:

复制代码
    QFont font("Arial",13);
复制代码
    font.setBold(true); // 设置粗体
复制代码
    painter.setFont(font);

速度显示优化

给内圈画上黑色

cpp 复制代码
void Widget::drawEllispseInnerBlack(QPainter &painter, int radius)
{
  painter.setBrush(Qt::black);
  painter.drawEllipse(QPoint(0,0),radius,radius);
}

优化速度显示文本格式

cpp 复制代码
//画当前速度
void Widget::drawCurrentSpeed(QPainter &painter)
{
    painter.setPen(Qt::white);
    //显示当前速度值:
    QFont font("Arial",30);
    font.setBold(true);
    painter.setFont(font);
    painter.drawText(QRect(-60,-60,120,70),Qt::AlignCenter,QString::number(currentValue));

    QFont font2("Arial",13);
    font2.setBold(true);
    painter.setFont(font2);
    painter.drawText(QRect(-60,-60,120,160),Qt::AlignCenter,"Km/h");

}

调整绘画顺序

cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);

    // 初始化画布
    initCanvas(painter);

    //不用画大圆了,用刻度尺去表出框架即可
    //painter.drawEllipse(QPoint(0,0),height()/2,height()/2);

    //画出中间的小圆
    drawMiddleCircle(painter,60);

    //画刻度
    drawScale(painter,height()/2);

    //写刻度文字:
    drawScaleText(painter,height()/2);
    // 画指针
    drawPointLine(painter,height()/2-58);

    //画扇形
    drawSpeedPie(painter,height()/2);
    // 画黑色内圈
    drawEllispseInnerBlack(painter,80);
    //画出当前速度 -- 放正在画黑的下面,要设置在黑圈的图层下
    drawCurrentSpeed(painter);

}

效果演示

实现内环发光圈:

内环发光效果API

cpp 复制代码
void Widget::drawEllispseInnerBShine(QPainter &painter, int radius)
{
    // 径向 渐变实现
    QRadialGradient radialGradient(0,0,radius);
    radialGradient.setColorAt(0.0,QColor(255,0,0,200));
    radialGradient.setColorAt(1.0,QColor(0,0,0,100));
    painter.setBrush(radialGradient);
    painter.drawEllipse(QPoint(0,0),radius,radius);
}

插入位置

效果演示

实现外环发个圈

外环发光效果API

cpp 复制代码
// 画外围发光圈
void Widget::drawEllispseOutterBShine(QPainter &painter, int radius)
{
    painter.restore();
    QRect rentangle(-radius,-radius,radius*2,radius*2);
    painter.setPen(Qt::NoPen);
    //渐变色处理外环光亮
    QRadialGradient radialGradient(0,0,radius);
    radialGradient.setColorAt(1,QColor(255,0,0,200));
    radialGradient.setColorAt(0.97,QColor(255,0,0,120));
    radialGradient.setColorAt(0.9,QColor(0,0,0,0));
    radialGradient.setColorAt(0,QColor(0,0,0,0));
    painter.setBrush(radialGradient);

    painter.drawPie(rentangle,(-150)*16,-angle*60*16);
}

插入位置

实现效果

绘制汽车logo的API

//需要先添加资源文件

cpp 复制代码
void Widget::drawLogo(QPainter &painter, int radius)
{
 QRect rectangle(-65,radius*0.38,130,50);
 painter.drawPixmap(rectangle,QPixmap(":/C:/Users/31472/Desktop/Images/qi_che_biao_pan/lu_hua.png"));

}

插入位置

实现效果

deBug及微调

bug1: restore()和 save() 没有配对使用

处理手段: 把多余的restore()去掉

bug2:中间显示的速度不匹配

处理:

这样修改后得到的速度就是实际值

相关推荐
古月居GYH8 分钟前
在C++上实现反射用法
java·开发语言·c++
Betty’s Sweet11 分钟前
[C++]:IO流
c++·文件·fstream·sstream·iostream
敲上瘾24 分钟前
操作系统的理解
linux·运维·服务器·c++·大模型·操作系统·aigc
不会写代码的ys31 分钟前
【类与对象】--对象之舞,类之华章,共绘C++之美
c++
兵哥工控33 分钟前
MFC工控项目实例三十二模拟量校正值添加修改删除
c++·mfc
长弓聊编程43 分钟前
Linux系统使用valgrind分析C++程序内存资源使用情况
linux·c++
cherub.1 小时前
深入解析信号量:定义与环形队列生产消费模型剖析
linux·c++
暮色_年华1 小时前
Modern Effective C++item 9:优先考虑别名声明而非typedef
c++
重生之我是数学王子1 小时前
QT基础 编码问题 定时器 事件 绘图事件 keyPressEvent QT5.12.3环境 C++实现
开发语言·c++·qt
我们的五年2 小时前
【Linux课程学习】:进程程序替换,execl,execv,execlp,execvp,execve,execle,execvpe函数
linux·c++·学习