Qt/C++开源控件 自定义雷达控件

news2024/11/16 9:24:39

使用Qt框架创建一个简单的雷达图,包含动态扫描、目标点生成、刻度和方向标识。代码实现使用C++编写,适合用作学习和扩展的基础。
在这里插入图片描述

1. 头文件与基本设置
#include "RadarWidget.h"
#include <QPainter>
#include <QPen>
#include <QBrush>
#include <cmath>
#include <cstdlib>
#include <ctime>
  • 头文件包含:包含必要的Qt模块和C++标准库,如 QPainter 用于绘制图形,cmath 用于数学计算,cstdlibctime 用于随机数生成。
  • 自定义类RadarWidget 继承自 QWidget,表示一个自定义的绘制控件。
2. 构造函数
RadarWidget::RadarWidget(QWidget *parent)
    : QWidget(parent), angle(0), maxTargets(5) {
    // 初始化随机数种子
    std::srand(std::time(nullptr));

    // 创建定时器来更新角度
    timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &RadarWidget::updateAngle);
    timer->start(50);  // 设置定时器间隔为50ms

    // 每隔一段时间生成随机目标点
    QTimer *targetTimer = new QTimer(this);
    connect(targetTimer, &QTimer::timeout, this, &RadarWidget::generateRandomTargets);
    targetTimer->start(2000);  // 每2秒生成一次新的随机目标点
}
  • 初始化:构造函数设置初始的角度为0和最大目标数为5。
  • 随机数种子:使用当前时间初始化随机数生成器,以便每次运行时产生不同的目标点。
  • 定时器
    • 扫描角度更新:每50毫秒调用 updateAngle 方法,更新雷达的扫描角度。
    • 目标点生成:每2秒调用 generateRandomTargets 方法,生成新的目标点。
3. 画图事件
void RadarWidget::paintEvent(QPaintEvent *) {
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.fillRect(rect(), Qt::black);
  • 画图事件:重载 paintEvent 方法是自定义绘制的核心。
  • 画笔设置:使用 QPainter 来进行绘制,开启抗锯齿以改善绘图质量。
  • 背景填充:将控件背景填充为黑色。
4. 获取控件的中心和半径
    int w = width();
    int h = height();
    int radius = qMin(w, h) / 2 * 0.9;  // 设置半径,防止绘制到最边缘
    QPoint center(w / 2, h / 2);
  • 尺寸计算:获取控件的宽度和高度。
  • 半径设置:计算半径为控件最小边长的一半,乘以0.9以避免绘制到边缘。
  • 中心点:确定雷达图的中心位置。
5. 绘制背景网格和圆形网格
    // 绘制背景网格
    painter.setPen(QPen(QColor(0, 255, 0, 50), 1));  // 绿色网格线
    int gridSize = 20;
    for (int i = 0; i <= w; i += gridSize) {
        painter.drawLine(i, 0, i, h);  // 垂直网格线
        painter.drawLine(0, i, w, i);  // 水平网格线
    }

    // 绘制雷达的圆形网格
    painter.setPen(QPen(QColor(0, 255, 0), 1));
    for (int i = 1; i <= 5; ++i) {
        painter.drawEllipse(center, radius * i / 5, radius * i / 5);
    }
  • 背景网格:绘制绿色背景网格线,以每20像素为间隔,增强视觉效果。
  • 圆形网格:绘制5个同心圆,帮助标识距离。
6. 绘制中心十字线和方向线
    // 绘制十字网格线
    painter.drawLine(center.x() - radius, center.y(), center.x() + radius, center.y());
    painter.drawLine(center.x(), center.y() - radius, center.x(), center.y() + radius);

    // 绘制八条方向线
    static const int directions[8] = {0, 45, 90, 135, 180, 225, 270, 315};
    painter.setPen(QPen(QColor(0, 255, 0), 1));  // 方向线颜色
    for (int i = 0; i < 8; ++i) {
        double angle = directions[i] * PI / 180.0;  // 将角度转换为弧度
        int x = center.x() + radius * std::cos(angle);
        int y = center.y() + radius * std::sin(angle);
        painter.drawLine(center, QPoint(x, y));  // 绘制从中心到边缘的线
    }
  • 中心十字线:绘制水平和垂直的十字线,进一步增强雷达图的视觉引导。
  • 方向线:绘制八条方向线(0°, 45°, 90°, 135°, 180°, 225°, 270°, 315°),指示各个方位。
7. 绘制刻度线和标注
    // 绘制刻度线和方向标注
    painter.setPen(QPen(Qt::white, 1));
    for (int i = 0; i < 360; i += 10) {
        // 计算刻度线的起点和终点
        int tickLength = (i % 30 == 0) ? 10 : 5;  // 每30度一个长刻度,其他是短刻度
        double radian = i * PI / 180.0;
        int x1 = center.x() + (radius - tickLength) * std::cos(radian);
        int y1 = center.y() + (radius - tickLength) * std::sin(radian);
        int x2 = center.x() + radius * std::cos(radian);
        int y2 = center.y() + radius * std::sin(radian);

        painter.drawLine(x1, y1, x2, y2);

        // 每30度绘制数字标注
        if (i % 30 == 0) {
            QString angleText = QString::number(i) + "°";
            int textX = center.x() + (radius + 15) * std::cos(radian) - 10;
            int textY = center.y() + (radius + 15) * std::sin(radian) + 5;
            painter.drawText(textX, textY, angleText);
        }
    }
  • 刻度线:每10度绘制一条刻度线,每30度绘制较长的刻度线。通过简单的三角函数计算线条的起止点。
  • 数字标注:在每30度的刻度线上添加数字标注,表示当前角度。
8. 绘制方向标注
    // 绘制八个方向标注
    QFont font = painter.font();
    font.setBold(true);
    painter.setFont(font);
    painter.drawText(center.x() + radius + 5, center.y(), "E");     // East
    painter.drawText(center.x() - radius - 25, center.y(), "W");     // West
    painter.drawText(center.x(), center.y() - radius - 20, "N");     // North
    painter.drawText(center.x(), center.y() + radius + 10, "S");     // South

    painter.drawText(center.x() + radius * 0.707 + 10, center.y() - radius * 0.707 - 10, "NE");  // Northeast
    painter.drawText(center.x() + radius * 0.707 + 10, center.y() + radius * 0.707 + 10, "SE");  // Southeast
    painter.drawText(center.x() - radius * 0.707 - 25, center.y() + radius * 0.707 + 10, "SW");  // Southwest
    painter.drawText(center.x() - radius * 0.707 - 25, center.y() - radius * 0.707 - 10, "NW"); 

 // Northwest
  • 方向文字标注:绘制东、南、西、北及四个对角的方向文字标注。通过调整文本位置以确保其位于相应的方位。
9. 绘制雷达扫描区域
    // 绘制雷达扫描的渐变区域(顺时针扫描,范围为圆的1/6)
    QConicalGradient gradient(center, -angle * 180.0 / PI);  // 使用负角度实现顺时针渐变效果
    gradient.setColorAt(0.0, QColor(0, 255, 0, 180));  // 扫描的前端亮绿色
    gradient.setColorAt(0.1, QColor(0, 255, 0, 2));  // 中间部分逐渐变淡
    gradient.setColorAt(1.0, Qt::transparent);         // 扫描尾部完全透明

    painter.setBrush(gradient);
    painter.setPen(Qt::NoPen);

    // 调整扫描区域的角度范围
    painter.drawPie(center.x() - radius, center.y() - radius, radius * 2, radius * 2,
                    int(-angle * 180.0 / PI * 16), 30 * 16);  // 控制为30度的扫描区域
  • 渐变区域:使用 QConicalGradient 创建一个顺时针的渐变区域,表现雷达扫描效果。
  • 绘制扫描区域:通过 drawPie 方法绘制出当前扫描的区域,以视觉上表现雷达的动态扫描。
10. 绘制动态目标点
    // 动态绘制目标点
    painter.setBrush(QColor(255, 0, 0, 180));  // 目标点使用红色半透明
    painter.setPen(Qt::NoPen);
    for (const QPoint &target : targets) {
        painter.drawEllipse(target, 6, 6);  // 绘制随机生成的红色目标点
    }
  • 目标点设置:以红色半透明的方式绘制动态生成的目标点,使其在雷达图上更加突出。
11. 绘制当前扫描线
    // 绘制当前扫描线
    painter.setPen(QPen(QColor(0, 255, 0), 2));
    painter.drawLine(center, QPoint(center.x() + radius * std::cos(angle), center.y() + radius * std::sin(angle)));
}
  • 扫描线绘制:绘制一条从中心到当前扫描位置的绿色线条,直观地展示当前雷达的扫描方向。
12. 更新扫描角度
void RadarWidget::updateAngle() {
    // 以一定的步伐增加扫描角度,使扫描线顺时针旋转
    angle += 0.05;
    if (angle >= 2 * PI)
        angle = 0;
    update();  // 调用重绘
}
  • 更新扫描角度:在每次定时器触发时,增加扫描角度,使其顺时针旋转。
  • 重绘:调用 update() 方法触发控件重绘,确保绘制的内容保持最新。
13. 生成随机目标点
void RadarWidget::generateRandomTargets() {
    targets.clear();  // 清空现有目标点

    int w = width();
    int h = height();
    int radius = qMin(w, h) / 2 * 0.9;  // 半径也需要保持一致
    QPoint center(w / 2, h / 2);

    // 随机生成目标点(最多maxTargets个)
    for (int i = 0; i < maxTargets; ++i) {
        // 随机生成目标点位置,限制在雷达半径范围内
        int randRadius = std::rand() % (radius - 10);  // 防止目标点超出边界
        double randAngle = (std::rand() % 360) * PI / 180.0;
        int x = center.x() + randRadius * std::cos(randAngle);
        int y = center.y() + randRadius * std::sin(randAngle);  // 顺时针方向
        targets.push_back(QPoint(x, y));
    }

    update();  // 更新界面显示
}
  • 目标点生成:清空现有目标点,并随机生成指定数量(最多 maxTargets)的新目标点,确保其位置在雷达半径范围内。
  • 随机化:使用随机半径和角度来生成目标点,确保每次生成的目标点位置不相同。

总结

这个开源Demo提供了一个简单易用的雷达图控件,涵盖了Qt绘图的基本用法和动态内容更新的实现。用户可以在此基础上进行扩展,例如:

  • 添加用户交互功能,例如点击目标点获取信息。
  • 实现更复杂的目标点生成逻辑,或者根据外部数据动态更新目标位置。
  • 改进UI界面,使其更符合现代应用的设计规范。

通过深入了解和分析这个Demo,你将能够掌握Qt绘图机制和动态控件的设计,适合用于学习和实际项目中。希望这对你有帮助!

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

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

相关文章

解决银河麒麟操作系统V10软件包架构不符问题

TOC &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在银河麒麟桌面操作系统V10中安装软件包时&#xff0c;如果遇到“软件架构与本机架构不符”的提示&#xff0c;可以尝试以下步骤来解决问题&#xff1a; 1. 确认架构一致性 查看本机架构…

基于STM32的智能门禁系统

目录 引言项目背景环境准备 硬件准备软件安装与配置系统设计 系统架构关键技术代码示例 密码验证模块电机控制实现门禁状态监控应用场景结论 1. 引言 智能门禁系统能够通过密码或其他验证方式&#xff08;如指纹、刷卡等&#xff09;控制门的开关&#xff0c;结合电机控制与…

2024年7月大众点评武汉餐饮美食店铺基础信息

在做一些城市分析、学术研究分析、商业选址、商业布局分析等数据分析挖掘时&#xff0c;大众点评的数据参考价值非常大&#xff0c;截至2024年7月&#xff0c;大众点评美食店铺剔除了暂停营业、停止营业后的最新数据情况分析如下。 武汉餐饮美食店铺约9.6万家&#xff0c;有均…

MySQL高阶2051-商店中每个成员的级别

目录 题目 准备数据 分析数据 实现 总结 题目 一个商店想对其成员进行分类。有三个层次: "钻石": 如果转换率 大于或等于 80."黄金": 如果转换率 大于或等于 50 且小于 80."白银": 如果转化率 小于 50."青铜": 如果该成员从未访…

澳洲本科毕业论文的初稿撰写要点分析

临近毕业季的时候&#xff0c;如何更好地完成澳洲本科毕业论文成为了困扰大家的一大难题。澳洲毕业论文的质量高低关系到留学生能否顺利毕业。因此大家都会关心如何更好地完成毕业论文。我们在之前一些文章中介绍了如何确立论点&#xff0c;如何查找资料以及如何完成高质量的di…

HarmonyOS/OpenHarmony 如何将rawfile中文件复制到沙箱中

关键词&#xff1a;h5离线加载、HarmonyOS、OpenHarmony、文件操作、复制、解压 当下有一个场景&#xff0c;需要离线加载 h5离线资源zip包&#xff0c;并实现资源包的动态更新&#xff0c;那么仅靠 $rawfile并不能实现该功能&#xff0c;那么我们该如何实现&#xff1f; 我们…

面试题05.08绘制直线问题详解(考察点为位运算符)

目录 一题目&#xff1a; 二详细思路汇总&#xff1a; 三代码解答&#xff08;带注释版&#xff09;&#xff1a; 一题目&#xff1a; leetcode原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 二详细思路汇总&#xff1a; 这里先剧透一下简单版思路哦&…

Azure DevOps Server:不能指派新增的用户

Contents 1. 概述2. 解决方案 1. 概述 近期和微软Azure DevOps项目组解决了一个“无法指派开发人员”的问题&#xff0c;在此分享给大家。问题描述&#xff1a; 在一个数据量比较大的Azure DevOps Server的部署环境中&#xff0c;用户发现将新用户的AD域账户添加到Azure DevOps…

睢宁自闭症寄宿学校:培养特殊孩子的未来

在自闭症儿童的教育与康复领域&#xff0c;每一所学校的努力都是对孩子们未来无限可能的一次深刻诠释。从江苏睢宁到广东广州&#xff0c;自闭症寄宿学校正以不同的方式&#xff0c;为这些特殊的孩子铺设一条通往未来的希望之路。其中&#xff0c;广州的星贝育园自闭症儿童寄宿…

数据分析-29-基于pandas的窗口操作和对JSON格式数据的处理

文章目录 1 窗口操作1.1 滑动窗口思想1.2 函数df.rolling2 JSON格式数据2.1 处理简单JSON对象和JSON列表2.1.1 处理简单的JSON结构2.1.2 处理空字段2.1.3 获取部分字段2.2 处理多级json2.2.1 展开所有级别(默认)2.2.2 自定义展开层级2.3 处理嵌套列表JSON3 参考附录1 窗口操作 …

三维激光扫描技术在文保修缮项目中的应用

三维激光扫描技术作为一种新兴的高精度空间数据获取手段&#xff0c;其在文物保护和修缮项目中的应用日益广泛。这项技术通过快速获取物体表面的三维密集点云数据&#xff0c;为文物的数字化存档、保护、修复及再利用提供了强有力的技术支持。 数据采集&#xff1a;高精度与非接…

正则表达式(补充)

一、常见匹配模式 模式描述\w匹配字母数字及下划线\W匹配非字母数字下划线\s匹配任意空白字符&#xff0c;等价于 [\t\n\r\f].\S匹配任意非空字符\d匹配任意数字&#xff0c;等价于 [0-9]\D匹配任意非数字\A匹配字符串开始\Z匹配字符串结束&#xff0c;如果是存在换行&#xf…

[含文档+PPT+源码等]精品大数据项目-Django基于随机森林实现的空气质量指数预测研究系统

大数据项目-Django基于随机森林实现的空气质量指数预测研究系统的背景可以从以下几个方面进行阐述&#xff1a; 一、环境背景 空气污染问题日益严重&#xff1a; 随着工业化和城市化的快速发展&#xff0c;空气污染问题已成为全球性的挑战。空气中的主要污染物如PM2.5、PM10、…

DC00025【含论文】基于协同过滤推荐算法springboot视频推荐管理系统

1、项目功能演示 DC00025【含文档】基于springboot短视频推荐管理系统协同过滤算法视频推荐系统javaweb开发程序设计vue 2、项目功能描述 短视频推荐系统分为用户和系统管理员两个角色 2.1 用户角色 1、用户登录、用户注册 2、视频中心&#xff1a;信息查看、视频收藏、点赞、…

邂逅温暖,网上寻找通勤搭子曲折之旅,天下没有不散的筵席

在城市的钢铁丛林中&#xff0c;每日的通勤就像是一场孤独的战役。拥挤的地铁、堵塞的道路&#xff0c;让人心生疲惫。于是&#xff0c;我决定在网上寻找一位通勤搭子&#xff0c;希望能为这段枯燥的旅程增添一抹温暖的色彩。 我在各个社交平台上发布了寻找通勤搭子的帖子&…

用ChatGPT一天搞定一周工作,27个技巧你必须知道

在当今数字化时代&#xff0c;自动化工具已成为提升工作效率的关键利器。人工智能技术&#xff0c;尤其是语言模型如ChatGPT&#xff0c;正在彻底改变企业处理日常任务的方式。本文将深入探讨如何利用这些先进的计算机科学成果来优化工作流程&#xff0c;提高生产力&#xff0c…

Ambiguous mapping. Cannot map *** method 报错解决

报错 Ambiguous mapping. Cannot map basicController method There is already helloController bean method 原因 There is already helloController bean method BasicController 里面有一个"hello"重名了 解决 改一下HelloController.java里面的"hello…

【MySQL 06】表的增删查改

目录 1.insert 增添数据 1.1单行数据 全列插入 1.2多行数据 指定列插入 1.3插入否则更新 1.4.插入否则替换 2.select查找 2.1 全列查找 2.2指定列查找 2.3查询字段为表达式 2.4为查询结果指定别名 2.5 结果去重 2.6 where条件查询 2.7结果排序 2.8.筛选分页结果…

jmeter操作数据库

jmeter操作数据库 一、打开数据库 二、jmeter下载驱动&#xff0c;安装jdbc驱动 1、下载好的驱动包 2、将驱动包复制粘贴 存放在包的路径下 &#xff08;1&#xff09;jdk下面 a、路径&#xff1a;jdk1\jre\lib b、jdk1\jre\lib\ext &#xff08;2&#xff09;jmeter下 a、…

8641 冒泡排序

### 思路 冒泡排序是一种简单的排序算法&#xff0c;通过重复地遍历待排序的列表&#xff0c;比较相邻的元素并交换它们的位置来排序列表。每次遍历会将最大的元素“冒泡”到列表的末尾。当在一趟遍历中没有发生任何交换时&#xff0c;排序结束。 ### 伪代码 1. 读取输入的待排…