目录
bug1: restore()和 save() 没有配对使用
前言:
这个表盘在上一个项目"简易表盘"的基础上搭建,请先看上篇:
整体代码
widget.h
#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
#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);
}
效果演示
实现刻度文字正确排版
移动坐标系,达到正确排版
优化代码
//刻度文字达到正确排版效果
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
#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
#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
// 画指针
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
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);
速度显示优化
给内圈画上黑色
void Widget::drawEllispseInnerBlack(QPainter &painter, int radius)
{
painter.setBrush(Qt::black);
painter.drawEllipse(QPoint(0,0),radius,radius);
}
优化速度显示文本格式
//画当前速度
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");
}
调整绘画顺序
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
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
// 画外围发光圈
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
绘制汽车logo的API
//需要先添加资源文件
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:中间显示的速度不匹配
处理:
这样修改后得到的速度就是实际值