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

使用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.mfbz.cn/a/886629.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CMU 10423 Generative AI:lec15(Scaling Laws 大规模语言模型的扩展法则)

文章目录 一 概述1. **扩展规律的背景**2. **两种主要的扩展规律**3. **模型容量扩展规律**4. **信息论下界**5. **计算扩展规律**6. **训练高效性**7. **结论与启示** 二 2bit/parameter 概念&#xff08;模型的存储能力分析&#xff09;**1. 概念解释****2. 图表解读****3. 量…

匿名方法与Lambda表达式+泛型委托

匿名方法 和委托搭配使用&#xff0c;方便我们快速对委托进行传参&#xff0c;不需要我们定义一个新的函数&#xff0c;直接用delegate关键字代替方法名&#xff0c;后面跟上参数列表与方法体。 格式&#xff1a;delegate(参数列表){方法体} lambda表达式 是匿名方法的升级…

通信工程学习:什么是IP网际协议

IP&#xff1a;网际协议 IP网际协议&#xff08;Internet Protocol&#xff0c;简称IP&#xff09;是整个TCP/IP协议栈中的核心协议之一&#xff0c;它负责在网络中传送数据包&#xff0c;并提供寻址和路由功能。以下是对IP网际协议的详细解释&#xff1a; 一、对IP网际协议的…

Flask-3

文章目录 ORMFlask-SQLAlchemySQLAlchemy中的session对象数据库连接设置常用的SQLAlchemy字段类型常用的SQLAlchemy列约束选项 数据库基本操作模型类定义 数据表操作创建和删除表 数据操作基本查询SQLAlchemy常用的查询过滤器SQLAlchemy常用的查询结果方法多条件查询分页器聚合…

全局安装cnpm并设置其使用淘宝镜像的仓库地址(地址最新版)

npm、cnpm和pnpm基本概念 首先介绍一下npm和cnpm是什么&#xff0c;顺便说一下pnpm。 npm npm&#xff08;Node Package Manager&#xff09;是Node.js的默认包管理器&#xff0c;用于安装、管理和分享JavaScript代码包。它是全球最大的开源库生态系统之一&#xff0c;提供了数…

共享单车轨迹数据分析:以厦门市共享单车数据为例(八)

副标题&#xff1a;基于POI数据的站点综合评价——以厦门市为例&#xff08;三&#xff09; 什么是优劣解距离法&#xff08;TOPSIS&#xff09;&#xff1f; 优劣解距离法&#xff08;Technique for Order Preference by Similarity to Ideal Solution&#xff0c;简称TOPSI…

排序算法之——归并排序,计数排序

文章目录 前言一、归并排序1. 归并排序的思想2. 归并排序时间复杂度及空间复杂度3. 归并排序代码实现1&#xff09;递归版本2&#xff09;非递归版本 二、计数排序1. 计数排序的思想2. 计数排序的时间复杂度及空间复杂度3. 计数排序代码实现 总结&#xff08;排序算法稳定性&am…

ATLAS/ICESat-2 L3B 每 3 个月网格动态海洋地形图 V001

目录 简介 摘要 代码 引用 网址推荐 0代码在线构建地图应用 机器学习 ATLAS/ICESat-2 L3B Monthly 3-Month Gridded Dynamic Ocean Topography V001 ATLAS/ICESat-2 L3B 每月 3 个月网格动态海洋地形图 V001 简介 该数据集包含中纬度、北极和南极网格上动态海洋地形&…

基于大数据的Python+Django电影票房数据可视化分析系统设计与实现

目录 1 引言 2 系统需求分析 3 技术选型 4 系统架构设计 5 关键技术实现 6 系统实现 7 总结与展望 1 引言 随着数字媒体技术的发展&#xff0c;电影产业已经成为全球经济文化不可或缺的一部分。电影不仅是艺术表达的形式&#xff0c;更是大众娱乐的重要来源。在这个背景…

C++之多线程

前言 多线程和多进程是并发编程的两个核心概念,它们在现代计算中都非常重要,尤其是在需要处理大量数据、提高程序性能和响应能力的场景中。 多线程的重要性: 资源利用率:多线程可以在单个进程中同时执行多个任务,这可以更有效地利用CPU资源,特别是在多核处理器上。 性…

【Spring基础3】- Spring的入门程序

目录 3-1 Spring的下载3-2 Spring的 jar 包3-3 第一个 Spring程序第一步&#xff1a;添加spring context的依赖&#xff0c;pom.xml配置如下第二步&#xff1a;添加junit依赖第三步&#xff1a;定义bean&#xff1a;User第四步&#xff1a;编写spring的配置文件&#xff1a;bea…

macOS终端配置自动补全功能

如何在macOS终端中配置自动补全功能 终端是一个非常强大的工具&#xff0c;它可以用来完成很多任务&#xff0c;比如创建、复制、移动、删除文件&#xff0c;执行脚本和运行程序。不过它的默认设置对用户不太友好&#xff0c;作为开发者&#xff0c;我们通常习惯代码编辑器的辅…

docker pull 超时Timeout失败的解决办法

当国内开发者docker pull遇到如下提示时&#xff0c;不要惊讶 [rootvm /]# docker pull postgres Using default tag: latest Error response from daemon: Get "https://registry-1.docker.io/v2/": dial tcp 128.121.146.235:443: i/o timeout [rootvm /]# 自2024…

创建Vue项目的时出现:无法加载文件 E:\software\node\node_global\vue.ps1,因为在此系统上禁止运行脚本

创建Vue项目的时出现的问题:出现&#xff1a;无法加载文件 E:\software\node\node_global\vue.ps1&#xff0c;因为在此系统上禁止运行脚本 解决方法&#xff1a; .PowerShelll的执行政策阻止了该操作,用 get-ExecutionPolicy 查看执行策略的状态为受限 输入Set-ExecutionPo…

T10:数据增强

T10周&#xff1a;数据增强 **一、前期工作**1.设置GPU,导入库2.加载数据 **二、数据增强****三、增强方式**方法一&#xff1a;将其嵌入model中方法二&#xff1a;在Dataset数据集中进行数据增强 **四、训练模型****五、自定义增强函数****六、总结** &#x1f368; 本文为&am…

[ RK3566-Android11 ] 关于移植 RK628F 驱动以及后HDMI-IN图像延迟/无声等问题

问题描述 由前一篇文章https://blog.csdn.net/jay547063443/article/details/142059700?fromshareblogdetail&sharetypeblogdetail&sharerId142059700&sharereferPC&sharesourcejay547063443&sharefromfrom_link&#xff0c;移植HDMI-IN部分驱动后出现&a…

硬件-开关电源-结构组成及元件作用

文章目录 一&#xff1a;开关电源组成1.1 开关电源是什么&#xff1f;1.2 开关电源六个组成部分 二&#xff1a;六个组成部分的作用2.1 EMC区域2.2 输入整流滤波区域2.3 控制区域2.4 变压器2.5 输出整流滤波区域2.6 反馈电路区域道友:勿以小恶弃人大美&#xff0c;勿以小怨忘人…

【C++】——list的介绍和模拟实现

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 博主主页&#xff1a;Yan. yan.                        …

ARM 架构、cpu

一、ARM的架构 ARM是一种基于精简指令集&#xff08;RISC&#xff09;的处理器架构. 1、ARM芯片特点 ARM芯片的主要特点有以下几点&#xff1a; 精简指令集&#xff1a;ARM芯片使用精简指令集&#xff0c;即每条指令只完成一项简单的操作&#xff0c;从而提高指令的执行效率…

EasyCVR视频汇聚平台:解锁视频监控核心功能,打造高效安全监管体系

随着科技的飞速发展&#xff0c;视频监控技术已成为现代社会安全、企业管理、智慧城市构建等领域不可或缺的一部分。EasyCVR视频汇聚平台作为一款高性能的视频综合管理平台&#xff0c;凭借其强大的视频处理、汇聚与融合能力&#xff0c;在构建智慧安防/视频监控系统中展现出了…