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

news2024/11/24 18:30:14
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、难点

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2230493.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

​Java面试经典 150 题.P13. 罗马数字转整数(012)​

本题来自&#xff1a;力扣-面试经典 150 题 面试经典 150 题 - 学习计划 - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台https://leetcode.cn/studyplan/top-interview-150/ 题解&#xff1a; class Solution {public int romanToInt(String s) {int sum…

stl_stack/queue

一.适配器 stack和queue实际上并不能算是一种容器&#xff0c;而是一种容器适配器。而适配器作为stl的6大组件之一&#xff0c;其实是一种设计模式。适配器模式其实就是将一个类的接口&#xff08;该接口无法直接满足客户的需求&#xff09;转换成客户希望的另一个接口&#x…

计算机网络:网络层 —— 虚拟专用网 VPN

文章目录 虚拟专用网 VPN 概述内联网 VPN外联网 VPN 虚拟专用网 VPN 概述 虚拟专用网&#xff08;Virtual Private Network&#xff0c;VPN&#xff09;&#xff1a;利用公用的因特网作为本机构各专用网之间的通信载体&#xff0c;这样形成的网络又称为虚拟专用网。 出于安全…

SSM酒店预订住宿管理系统-计算机毕业设计源码01085

摘要 随着互联网和移动技术的快速发展&#xff0c;酒店行业也面临着巨大的变革和机遇。传统的酒店管理方式存在着信息不透明、预订流程繁琐等问题&#xff0c;无法满足现代消费者对便捷、高效、个性化服务的需求。因此&#xff0c;开发酒店预订住宿管理系统具有重要的意义。本文…

STM32CubeMX学习(三) SPI+DMA通信

STM32CubeMX学习&#xff08;三&#xff09; SPIDMA通信 一、简介二、新建STM32CubeMX项目并使用外部时钟三、SPI3配置四、相关代码五、测试 一、简介 本文将基于STM32F103RCT芯片介绍如何在STM32CubeMXKEIL5开发环境下进行SPIDMA通信。 操作系统&#xff1a;WIN10 x64硬件电…

LLaMA系列一直在假装开源...

伙伴们&#xff0c;很奇怪~ 关于LLM的开源与闭源模型的竞争又开始愈发激烈。 众所周知&#xff0c;开源模型以其开放性和社区驱动的特点受到一部分用户的青睐&#xff0c;而闭源模型则因其专业性和性能优化被广泛应用于商业领域。由于大模型最近2年的突然兴起&#xff0c;开源…

C语言 核心语法2

时间&#xff1a;2024.11.1 一、学习内容 1、计算机的存储规则 1.1存储规则 视频是图片和声音的结合体。 在计算机中&#xff0c;任意数据都是以二进制的形式进行存储的。 在计算机中&#xff0c;二进制可以表示万事万物。 1.2十进制 1.3二进制的运算过程 1.4文本存储 …

客户端与微服务之间的桥梁---网关

当我们创建好了N多个微服务或者微服务的实例之后&#xff0c;每个服务暴露出不同的端口地址&#xff0c;一般对于客户端请求&#xff0c;只需要请求一个端口&#xff0c;要隔离客户端和微服务的直接关系&#xff0c;保证微服务的安全性和灵活性&#xff0c;避免敏感信息的泄露。…

萤石设备视频接入平台EasyCVR私有化部署视频平台高速公路视频上云的高效解决方案

经济的迅猛发展带来了高速公路使用频率的激增&#xff0c;其封闭、立交和高速的特性变得更加显著。然而&#xff0c;传统的人工巡查方式已不足以应对当前高速公路的监控挑战&#xff0c;监控盲点和响应速度慢成为突出问题。比如&#xff0c;非法占用紧急车道的情况屡见不鲜&…

【论文速读】| APILOT:通过避开过时API陷阱,引导大语言模型生成安全代码

基本信息 原文标题&#xff1a;APILOT: Navigating Large Language Models to Generate Secure Code by Sidestepping Outdated API Pitfalls 原文作者&#xff1a;Weiheng Bai, Keyang Xuan, Pengxiang Huang, Qiushi Wu, Jianing Wen, Jingjing Wu, Kangjie Lu 作者单位&a…

泡泡玛特行至巅峰,又顷刻“瓦解”?

今年&#xff0c;在海外“一呼百应”的LABUBU与“老妈”泡泡玛特一同步入了潮玩时代的全新阶段。 先是LABUBU获授“神奇泰国体验官”&#xff0c;首个LABUBU主题店也落地曼谷。随后泡泡玛特也在发布三季度财报后迎来股价新高&#xff08;10月24日收盘价75.85元/股&#xff0c;…

如何看待长周期项目?

有一个客户&#xff0c;想找你做一个软件项目。你大体评估了一下&#xff0c;项目成本300万&#xff0c;项目收入400万&#xff0c;有大概100万左右的毛利。但项目的周期&#xff0c;会比较长&#xff0c;大概是3年。 你会做吗&#xff1f; 我从自己的经验和直觉来看&#x…

Flutter仿微信,高度还原,开源

Flutter仿微信开源项目&#xff0c;持续更新中 Flutter仿微信项目&#xff0c;已开源&#x1f680;&#x1f680;&#x1f680;说明效果预览开发进度说明未来计划项目结构说明组件封装示例最后持续更新中... Flutter仿微信项目&#xff0c;已开源&#x1f680;&#x1f680;&am…

HBA:基于分层激光雷达集束调整的一致性建图

文章目录 前言一、介绍二、相关工作三、方法A. 概述B. 自底向上的分层BA&#xff08;Bundle Adjustment&#xff09;C. 自顶向下位姿图优化 四. 实验A. 精度分析 前言 代码&#xff1a;github 原文&#xff1a;原文 摘要——重建准确且一致的大规模LiDAR点云地图对机器人应用至…

Docker — 跨平台和环境部署

Docker 是一个开源的容器化平台&#xff0c;通过将应用程序和其依赖打包在一个轻量级、独立的容器中&#xff0c;能够跨平台和环境部署。 1. Docker 基本概念 镜像 (Image)&#xff1a;Docker 镜像是一个只读模板&#xff0c;包含运行应用程序所需的代码、库、依赖和环境配置。…

消息队列-Rabbitmq(消息发送,消息接收)

将来我们开发业务功能的时候&#xff0c;肯定不会在控制台收发消息&#xff0c;而是应该基于编程的方式。由于RabbitMQ采用了AMQP协议&#xff0c;因此它具备跨语言的特性。任何语言只要遵循AMQP协议收发消息&#xff0c;都可以与RabbitMQ交互。并且RabbitMQ官方也提供了各种不…

一机多控无人机集群飞行控制技术详解

一机多控无人机集群飞行控制技术是指通过单一控制端或多个协同工作的控制端&#xff0c;对多架无人机进行集群管理和控制的技术。这种技术结合了通信技术、路径规划、碰撞避免、分布式与集中式控制等多个方面&#xff0c;以实现无人机集群的协同作战或完成其他特定任务。以下是…

DEVOPS: 认证与调度

概述 不知道大家有没有意识到一个现实&#xff0c;就是大部分时候&#xff0c;我们已经不像以前一样通过命令行&#xff0c;或者可视窗口来使用一个系统了现在我们上微博、或者网购&#xff0c;操作的其实不是眼前这台设备&#xff0c;而是一个又一个集群 通常&#xff0c;这样…

【鸢尾花书籍】编程不难

&#x1f4dd;本文介绍 本文为作者拜读鸢尾花书籍《编程不难》后所做的笔记&#xff0c;整理成文章&#xff0c;以供回顾 &#x1f44b;作者简介&#xff1a;一个正在积极探索的本科生 &#x1f4f1;联系方式&#xff1a;943641266(QQ) &#x1f6aa;Github地址&#xff1a;htt…

HTML 文档规范与解析模式:DOCTYPE、<html> 标签以及结构化页面

文章目录 `<!DOCTYPE html>` 文档类型声明标准模式与怪异模式HTML5 的简化声明`<html>` 标签`<head>` 标签`<body>` 标签小结<!DOCTYPE html> 文档类型声明 在 HTML 文档中,<!DOCTYPE html> 是一个重要的文档类型声明,主要用于告知浏览…