1、功能
制作一个汽车速度表
2、实现
从外到内进行绘制,初始化画布,画渐变色外圈,画刻度,写刻度文字,画指针,画扇形,画内圈渐变色,画黑色内圈,写当前值
3、效果
4、源码
a、头文件
#ifndef CARDASHBOARD_H
#define CARDASHBOARD_H
#include <QTimer>
#include <QWidget>
class CarDashboard : public QWidget {
Q_OBJECT
public:
CarDashboard(QWidget *parent = nullptr);
~CarDashboard();
protected:
void paintEvent(QPaintEvent *event);
private:
void startSpeed();
void initCanvas(QPainter &painter);
void drawOutterShine(QPainter &painter);
void drawScale(QPainter &painter);
void drawScaleText(QPainter &painter);
void drawPointer(QPainter &painter);
void drawSector(QPainter &painter);
void drawInnerShine(QPainter &painter);
void drawInnerBlack(QPainter &painter);
void drawCurrentValue(QPainter &painter);
private:
const int kScaleAngle = 240; // 刻度扇形角度
const int kScaleNum = 60; // 刻度数量
const int kLengthScale = 5; // 长刻度与刻度比例
const int kOneScaleValue = 4; // 一个刻度对应值
double av_angle_ = 0; // 平均角度
int start_rotate_angle_clock_ = 0; // 起始旋转角度(顺时针)(推算出来的)
int start_rotate_angle_ = 0; // 起始旋转角度(逆时针)(推算出来的)
int pointer_dir_ = 0; // 指针旋转方向
int scale_value_ = 0; // 刻度值
int height_half_ = 0; // 高度一半
int min_unit_ = 0; // 最小单位值
int font_size_ = 0; // 字体大小
int scale_text_radius_ = 0; // 刻度文字半径
int indent_value_ = 0; // 刻度缩进值
QTimer *timer_ = nullptr; // 定时器
};
#endif // CARDASHBOARD_H
b、源文件
#include "cardashboard.h"
#include <QPainter>
#include <QtMath>
CarDashboard::CarDashboard(QWidget *parent) : QWidget(parent) {
av_angle_ = kScaleAngle * 1.0 / kScaleNum;
start_rotate_angle_clock_ = -kScaleAngle / 2 + 270;
start_rotate_angle_ = kScaleAngle / 2 + 90;
startSpeed();
}
CarDashboard::~CarDashboard() {}
void CarDashboard::startSpeed() {
timer_ = new QTimer(this);
connect(timer_, &QTimer::timeout, [=]() {
update();
if (pointer_dir_ == 0) {
scale_value_++;
if (scale_value_ >= kScaleNum) {
pointer_dir_ = 1;
}
} else if (pointer_dir_ == 1) {
scale_value_--;
if (scale_value_ == 0) {
pointer_dir_ = 0;
}
}
});
timer_->setInterval(50);
timer_->start();
}
void CarDashboard::initCanvas(QPainter &painter) {
painter.setRenderHint(QPainter::Antialiasing, true);
// 黑色背景
painter.setBrush(Qt::black);
painter.drawRect(rect());
painter.setBrush(Qt::NoBrush);
painter.translate(QPoint(width() / 2, height() * 0.6));
height_half_ = height() / 2; // 高度一半
min_unit_ = height() / 16; // 最小单位值
font_size_ = min_unit_ * 2 / 5; // 字体大小
scale_text_radius_ = height_half_ - min_unit_ * 7 / 8; // 刻度文字半径
indent_value_ = min_unit_ / 8; // 刻度缩进大
}
void CarDashboard::drawOutterShine(QPainter &painter) {
const int radius = height_half_ + min_unit_ / 2;
painter.save();
QRect rect(-radius, -radius, radius * 2, radius * 2);
QRadialGradient gradient(0, 0, radius);
gradient.setColorAt(1.0, QColor(255, 0, 0, 200));
gradient.setColorAt(0.97, QColor(255, 0, 0, 120));
gradient.setColorAt(0.92, QColor(0, 0, 0, 0));
gradient.setColorAt(0.0, QColor(0, 0, 0, 0));
painter.setPen(Qt::NoPen);
painter.setBrush(gradient);
painter.drawPie(rect, start_rotate_angle_ * 16, -av_angle_ * kScaleNum * 16);
painter.restore();
}
void CarDashboard::drawScale(QPainter &painter) {
painter.save();
painter.setPen(QPen(Qt::white, 3));
painter.rotate(start_rotate_angle_clock_);
for (int i = 0; i <= kScaleNum; i++) {
if (i >= 40) {
painter.setPen(QPen(Qt::red, 3));
}
if (i % kLengthScale == 0) { // 长刻度
painter.drawLine(height_half_ - min_unit_ / 2, 0,
height_half_ - indent_value_, 0);
} else { // 短刻度
painter.drawLine(height_half_ - min_unit_ / 4, 0,
height_half_ - indent_value_, 0);
}
painter.rotate(av_angle_);
}
painter.restore();
}
// 这个函数是难点
void CarDashboard::drawScaleText(QPainter &painter) {
painter.save();
painter.setPen(QPen(Qt::white, 3));
QFont font("Arial", font_size_);
font.setBold(true);
painter.setFont(font);
for (int i = 0; i <= kScaleNum; i++) {
if (i % kLengthScale == 0) {
// 保存坐标
painter.save();
// 正弦 余弦
int del_x = qCos(qDegreesToRadians(start_rotate_angle_ - av_angle_ * i)) *
scale_text_radius_;
int del_y = qSin(qDegreesToRadians(start_rotate_angle_ - av_angle_ * i)) *
scale_text_radius_;
// 平移坐标系
painter.translate(QPoint(del_x, -del_y));
// 选择坐标系
painter.rotate(-kScaleAngle / 2 + av_angle_ * i);
// 绘制文字
painter.drawText(
QRect(-min_unit_ / 2, -min_unit_ / 2, min_unit_, min_unit_),
Qt::AlignCenter, QString::number(i * kOneScaleValue));
// 恢复坐标
painter.restore();
}
}
painter.restore();
}
void CarDashboard::drawPointer(QPainter &painter) {
painter.save();
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::white);
const QPointF point[4]{
QPointF(0, 0.0),
QPointF(height_half_ - min_unit_ * 5 / 2, -min_unit_ / 32.0),
QPointF(height_half_ - min_unit_ * 5 / 2, min_unit_ / 32.0),
QPointF(0, min_unit_ / 3.0),
};
painter.rotate(start_rotate_angle_clock_ + av_angle_ * scale_value_);
painter.drawPolygon(point, 4);
painter.restore();
}
void CarDashboard::drawSector(QPainter &painter) {
const int radius = height_half_ + min_unit_ / 2;
painter.save();
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(255, 0, 0, 50));
QRect rect(-radius, -radius, radius * 2, radius * 2);
painter.drawPie(rect, start_rotate_angle_ * 16,
-av_angle_ * scale_value_ * 16);
painter.restore();
}
void CarDashboard::drawInnerShine(QPainter &painter) {
painter.setBrush(QColor(255, 0, 0, 150));
painter.drawEllipse(QPoint(0, 0), min_unit_ * 2, min_unit_ * 2);
}
void CarDashboard::drawInnerBlack(QPainter &painter) {
painter.setBrush(Qt::black);
painter.drawEllipse(QPoint(0, 0), min_unit_ * 3 / 2, min_unit_ * 3 / 2);
}
void CarDashboard::drawCurrentValue(QPainter &painter) {
painter.setPen(QPen(Qt::white, 3));
QFont font("Arial", font_size_ * 3 / 2);
font.setBold(true);
painter.setFont(font);
painter.drawText(QRect(-min_unit_, -min_unit_, min_unit_ * 2, min_unit_),
Qt::AlignCenter,
QString::number(scale_value_ * kOneScaleValue));
font.setPointSize(font_size_ * 3 / 4);
painter.setFont(font);
painter.drawText(
QRect(-min_unit_, min_unit_ / 2, min_unit_ * 2, min_unit_ / 2),
Qt::AlignCenter, "Km/h");
}
void CarDashboard::paintEvent(QPaintEvent *event) {
Q_UNUSED(event);
QPainter painter(this);
// 初始化画布
initCanvas(painter);
// 画外圈渐变色
drawOutterShine(painter);
// 画刻度
drawScale(painter);
// 写刻度文字
drawScaleText(painter);
// 画指针
drawPointer(painter);
// 画扇形
drawSector(painter);
// 画内圈渐变色
drawInnerShine(painter);
// 画黑色内圈
drawInnerBlack(painter);
// 当前值
drawCurrentValue(painter);
}
5、难点
这里难点使用正弦和余弦计算绘制刻度字。
对你有用就点个赞👍,以后需要用到就收藏⭐