Qt自定义控件:汽车速度表

1、功能

制作一个汽车速度表

2、实现

从外到内进行绘制,初始化画布,画渐变色外圈,画刻度,写刻度文字,画指针,画扇形,画内圈渐变色,画黑色内圈,写当前值

3、效果
4、源码
a、头文件
cpp 复制代码
#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、源文件
cpp 复制代码
#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、难点

这里难点使用正弦和余弦计算绘制刻度字。

对你有用就点个赞👍,以后需要用到就收藏⭐

相关推荐
用户805533698033 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner3 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz8 天前
QML Hello World 入门示例
qt
xcyxiner11 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner12 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner12 天前
DicomViewer (添加模型类)3
qt
xcyxiner13 天前
DicomViewer (目录调整) 2
qt
xcyxiner13 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
桥田智能15 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
森G15 天前
75、服务器源码解析---------云视频服务项目
linux·服务器·网络·c++·qt