Qt总结之七:QPaintEvent绘制雷达图(二)_qt三维雷达图-程序员宅基地

技术标签: 雷达扫描图  【-------Qt------】  QPaintEvent  Qt  

前言

这里使用另一套框架写的雷达扫描图

这里PaintEvent事件比上一个版本写的好,但是不喜欢Widget嵌套的方式,后续会把两个版本整合到一起。

 一、实现原理

雷达效果包括三个部分:背景、转动的扇形和闪烁的点。 
(1)背景的实现很简单,首先填充背景色,然后绘制横纵坐标轴以及三个同心圆。代码如下:

//背景
    painter.fillRect(rect(),QColor(15,45,188));

    //边长
    int len = m_drawArea.width();

    //底盘(x轴、y轴和3个圆)
    painter.setPen(QPen(Qt::white));
    painter.drawLine(m_drawArea.topLeft() + QPoint(0,len/2),m_drawArea.topRight() + QPoint(0,len/2));
    painter.drawLine(m_drawArea.topLeft() + QPoint(len/2,0),m_drawArea.bottomLeft() + QPoint(len/2,0));
    painter.drawEllipse(m_drawArea.center(),len/2,len/2);
    painter.drawEllipse(m_drawArea.center(),len/3,len/3);
    painter.drawEllipse(m_drawArea.center(),len/6,len/6);

在这里有个m_drawArea是用来描述绘制雷达的区域的。先确定这个绘制区域,更容易计算绘制的内容。 m_drawArea的大小在resizeEvent事件中确定。

void CRadar::resizeEvent(QResizeEvent *event)
{
    //以较短的边长作为绘制区域边长
    if(width() > height())
    {
        m_drawArea = QRect((width() - height())/2,0,height(),height());
    }
    else
    {
        m_drawArea = QRect(0,(height() - width())/2,width(),width());
    }

    m_drawArea.adjust(10,10,-10,-10);
}

为了确保雷达为圆形,我们以较短的边作为矩形的边长。然后使用adjust缩小一点。adjust前两个参数为左上角x和y的增量,后两个参数为右下角x和y的增量。

(2)转动部分由一个扇形和一条直线组成。扇形使用了锥形渐变色。代码如下:

    //转动部分
        //---//线
    qreal x = m_drawArea.center().x() + (qreal)len/2 * cos(-m_pieRotate*3.14159/180);
    qreal y = m_drawArea.center().y() + (qreal)len/2 * sin(-m_pieRotate*3.14159/180);
    painter.setPen(QPen(Qt::white));
    painter.drawLine(m_drawArea.center(),QPointF(x,y));

       //----//扇形
    QConicalGradient gradient;
    gradient.setCenter(m_drawArea.center());
    gradient.setAngle(m_pieRotate + 180); //渐变与旋转方向恰好相反,以扇形相反的边作为渐变角度。
    gradient.setColorAt(0.4,QColor(255,255,255,100)); //从渐变角度开始0.5 - 0.75为扇形区域,由于Int类型计算不精确,将范围扩大到0.4-0.8
    gradient.setColorAt(0.8,QColor(255,255,255,0));
    painter.setBrush(QBrush(gradient));
    painter.setPen(Qt::NoPen);
    painter.drawPie(m_drawArea,m_pieRotate*16,90*16);

直线的绘制很简单,只需要计算转动角度对应圆上的点的坐标即可。扇形的绘制也很简单。难点在于渐变色的应用,由于锥形渐变是逆时针的,而我们的雷达是顺时针的转动。所以要计算好扇形区域在渐变的哪个范围。这里设置了渐变角度为扇形角度旋转180度,那么扇形区域应该在渐变角度的0.5 - 0.75范围内。由于这里使用的是int类型,怕计算的不精确,将渐变颜色的范围设置大一些。

(3)第三部分是随机闪烁的点,用来点缀。在这里使用了一个list存放点的坐标,另一个list存放这些点的颜色alapha值。使用一个定时器随机改变这些点的坐标值和alapha值。


void CRadar::timerEvent(QTimerEvent *event)
{
    if(m_timerId == event->timerId())
    {
        m_pieRotate -= 10;
        update();
    }
    else if(m_pointTimerId == event->timerId())
    {
        //随机更换装饰的点
        for(int i = 0; i < m_points.count(); ++i)
        {
            int offsetX = rand()%m_drawArea.width();
            int offsetY = rand()%m_drawArea.width();
            int alapha = rand()%255;
            m_points.replace(i,QPoint(offsetX,offsetY) + m_drawArea.topLeft());
            m_pointsAlapha.replace(i,alapha);
        }

        update();
    }
}

第一个定时器是用来改变转动部分的角度的。第二个定时器才是用来改变闪烁点的。


二、代码区

radar.h

#ifndef RADAR_H
#define RADAR_H

#include <QWidget>

class Radar : public QWidget
{
	Q_OBJECT

public:
	Radar(QWidget *parent);
	~Radar();
protected:
	void paintEvent(QPaintEvent *event);    //绘制事件
	void resizeEvent(QResizeEvent *event);  //大小重置事件
	void timerEvent(QTimerEvent *event);    //定时器事件
private:
	QRect         m_drawArea;      //绘制区域
	int           m_pieRotate;     //扇形旋转区域
	int           m_timerId;       //定时器ID
	int           m_pointTimerId;  //变更点定时器
	int           m_nSpeed;        //速度
	QList<QPoint> m_points;        //绘制点
	QList<int>    m_pointsAlapha;  //绘制点颜色alapha值

	
};

#endif // RADAR_H

radarscannice.h

#ifndef RADARSCANNICE_H
#define RADARSCANNICE_H

#include <QtWidgets/QWidget>
#include "ui_radarscannice.h"

class RadarScanNice : public QWidget
{
	Q_OBJECT

public:
	RadarScanNice(QWidget *parent = 0);
	~RadarScanNice();

private:
	Ui::RadarScanNiceClass ui;
};

#endif // RADARSCANNICE_H

radar.cpp

#include "radar.h"
#include <QPainter>
#include <QTimerEvent>
#include <QConicalGradient>
#include <qDebug>

Radar::Radar(QWidget *parent)
	: QWidget(parent)
{
	//初始化
	m_pieRotate = 0;
	m_timerId = -1;
	m_nSpeed = 50;
	m_points << QPoint() << QPoint() << QPoint() << QPoint() << QPoint();
	m_pointsAlapha << 100 << 100 << 100 << 100 << 100;

	//启动定时器
	m_timerId = startTimer(m_nSpeed);
	m_pointTimerId = startTimer(1200);
}

Radar::~Radar()
{

}
void Radar::paintEvent(QPaintEvent *event)
{
	QPainter painter(this);

	//抗锯齿
	painter.setRenderHint(QPainter::Antialiasing);

	//背景
	painter.fillRect(rect(), QColor(15, 45, 188));

	//边长
	int len = m_drawArea.width();

	//底盘(x轴、y轴和3个圆)
	painter.setPen(QPen(Qt::white));
	painter.drawLine(m_drawArea.topLeft() + QPoint(0, len / 2), m_drawArea.topRight() + QPoint(0, len / 2));
	painter.drawLine(m_drawArea.topLeft() + QPoint(len / 2, 0), m_drawArea.bottomLeft() + QPoint(len / 2, 0));
	painter.drawEllipse(m_drawArea.center(), len / 2, len / 2);
	painter.drawEllipse(m_drawArea.center(), len / 3, len / 3);
	painter.drawEllipse(m_drawArea.center(), len / 6, len / 6);

	//转动部分
	//---//线
	qreal x = m_drawArea.center().x() + (qreal)len / 2 * cos(-m_pieRotate*3.14159 / 180);
	qreal y = m_drawArea.center().y() + (qreal)len / 2 * sin(-m_pieRotate*3.14159 / 180);
	painter.setPen(QPen(Qt::white));
	painter.drawLine(m_drawArea.center(), QPointF(x, y));

	//----//扇形
	QConicalGradient gradient;
	gradient.setCenter(m_drawArea.center());
	gradient.setAngle(m_pieRotate + 180); //渐变与旋转方向恰好相反,以扇形相反的边作为渐变角度。
	gradient.setColorAt(0.4, QColor(255, 255, 255, 100)); //从渐变角度开始0.5 - 0.75为扇形区域,由于Int类型计算不精确,将范围扩大到0.4-0.8
	gradient.setColorAt(0.8, QColor(255, 255, 255, 0));
	painter.setBrush(QBrush(gradient));
	painter.setPen(Qt::NoPen);
	painter.drawPie(m_drawArea, m_pieRotate * 16, 90 * 16);

	//装饰-随机点
	for (int i = 0; i < m_points.count(); ++i)
	{
		int colorAlaph = m_pointsAlapha.at(i);
		painter.setPen(QPen(QColor(255, 255, 255, colorAlaph), 3));
		painter.drawPoint(m_points.at(i));
	}
}

void Radar::resizeEvent(QResizeEvent *event)
{
	//以较短的边长作为绘制区域边长
	if (width() > height())
	{
		m_drawArea = QRect((width() - height()) / 2, 0, height(), height());
	}
	else
	{
		m_drawArea = QRect(0, (height() - width()) / 2, width(), width());
	}

	m_drawArea.adjust(10, 10, -10, -10);
}

void Radar::timerEvent(QTimerEvent *event)
{
	if (m_timerId == event->timerId())
	{
		m_pieRotate -= 10;
		update();
	}
	else if (m_pointTimerId == event->timerId())
	{
		//随机更换装饰的点
		for (int i = 0; i < m_points.count(); ++i)
		{
			int offsetX = rand() % m_drawArea.width();
			int offsetY = rand() % m_drawArea.width();
			int alapha = rand() % 255;
			m_points.replace(i, QPoint(offsetX, offsetY) + m_drawArea.topLeft());
			m_pointsAlapha.replace(i, alapha);
		}

		update();
	}
}

 

radarscannice.cpp

#include "radarscannice.h"
#include "radar.h"

RadarScanNice::RadarScanNice(QWidget *parent)
	: QWidget(parent)
{
	ui.setupUi(this);

	Radar* radar = new Radar(this);
	radar->setFixedSize(300, 300);
	radar->move(150, 50);
}

RadarScanNice::~RadarScanNice()
{

}

main.cpp

#include "radarscannice.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	RadarScanNice w;
	w.show();
	return a.exec();
}

 


效果图:

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Aidam_Bo/article/details/85266316

智能推荐

【Learing OpenCV】win7+vs2017+OpenCV4.0.1环境配置_opencv4 win7配置-程序员宅基地

文章浏览阅读3.4k次,点赞3次,收藏17次。这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入欢迎使用Ma..._opencv4 win7配置

【C语言】将一个3X2的矩阵(3行2列)的矩阵存入一个3X2的二维数组中,并输出矩阵。同时,找出矩阵中的最大值以及最大值所在的行下标和列下标,输出最大值所在的行下标和列下标及最大值。_将一个3x2的矩阵(3行2列)的矩阵存入一个3x2的二维数组中,并输出矩阵。同时,找出矩-程序员宅基地

文章浏览阅读4.6k次,点赞3次,收藏9次。【问题描述】 将一个3X2的矩阵(3行2列)的矩阵存入一个3X2的二维数组中,并输出矩阵。同时,找出矩阵中的最大值以及最大值所在的行下标和列下标,输出最大值所在的行下标和列下标及最大值。【输入输出样例】【样例说明】输入提示符中冒号为英文符号,后面无空格,需换行。输出矩阵时整数按照%4d格式输出。最后输出结束不换行。..._将一个3x2的矩阵(3行2列)的矩阵存入一个3x2的二维数组中,并输出矩阵。同时,找出矩

使用 React Testing Library 和 Jest 完成单元测试-程序员宅基地

文章浏览阅读1.2k次。引言在2020的今天,构建一个 web 应用对于我们来说,并非什么难事。因为有很多足够多优秀的的前端框架(比如 React,Vue 和 Angular);以及一些易用且强大的UI库(比如 Ant Design)为我们保驾护航,极大地缩短了应用构建的周期。但是,互联网时代也急剧地改变了许多软件设计,开发和发布的方式。开发者面临的问题是,需求越来越多,应用越来越复杂,时不时会有一种失控的的感觉,并在心...

利用css 动画实现节流_css动画forwards-程序员宅基地

文章浏览阅读2.7w次。css节流_css动画forwards

java封装、继承和多态练习题_java封装继承多态的题目-程序员宅基地

文章浏览阅读1.1k次。_java封装继承多态的题目

iphone:自定义UIAlertView-程序员宅基地

文章浏览阅读208次。由于项目中有这样一个需求:需要在保存是弹出框选择保存的地点。选择UIAlertView来实现,但是要在UIAlertView中增加UISwitch的控件,这就需要自定义一个继承UIAlertView的类来自定义UIAlertView了。实现效果如下:(还没加图的)我需要在点击确定的时候,知道两个Switch的状态,才能进一步做相应的功能。自定义了SaveAlertView类。在..._自定义uialert

随便推点

【JY】ABAQUS混凝土CDP插件分享-程序员宅基地

文章浏览阅读2.1k次,点赞3次,收藏23次。导读:为简便钢筋混凝土构件或者结构的本构模型设置,本期给大家推荐一款Abaqus混凝土CDP模型插件,供大家应用参考。这个插件无需繁琐的Excel操作,仅需选择混凝土等级即可在Abaqus..._abaqus混凝土cdp模型参数

python二进制转字符串_Python 二进制串转字符串例子-程序员宅基地

文章浏览阅读421次。0x01 问题我现在有一串 0和1组成的字符串,就像这样的110011011011001100001110011111110111010111011000010101110101010110011011101011101110110111011110011111101我把它叫做二进制串,我怎么能把它转成我能看懂的字符串呢?0x02 思路一个ascii码是8位,但是一般都用7位来表示,所以我可以把字..._python 0x01转为字符串

手把手教你写!Android平台HTTPS抓包解决方案及问题分析,年薪50W_android 怎么写一个抓包-程序员宅基地

文章浏览阅读165次。前言最近经常被朋友问到的两个问题。问题一: “从事IT工作3年了,做技术好累啊,是不是做到30岁就不能继续往下做啊?”问题二: “我已经30岁了,还能不能学编程?”我给出的答案是:只要你兴趣还在,可以一直做,什么时候都不会晚;种一棵树最好的时间是十年前,其次是现在。本人目前在 IT 行业工作了 6 年,做过大大小小的项目,虽然跟网上那些大牛比还差很远。但也确实经历过同样困惑,也迷茫过,踩过很多坑,今后也会继续踩。所以关于这一类问题,也有心得体验,在此算是做个人分享吧。学会深入思考,总结沉淀我想_android 怎么写一个抓包

滴滴夜莺:从监控告警系统向运维平台演化-程序员宅基地

文章浏览阅读5.1k次,点赞7次,收藏68次。简述滴滴夜莺(Nightingale)是一款经过大规模生产环境验证的、分布式高性能的运维监控系统。基于Open-Falcon,结合滴滴内部的最佳实践,在性能、可维护性、易用性方面做了大量的改进,支撑了滴滴内部数十亿监控指标,覆盖了从系统、容器、到应用等各层面的监控需求。夜莺于2020年3月底开源至今,GitHub Star已突破2000,并且于9月底发布了最新的3.0版本。本次更新夜莺被拆成了四个子系统,分别是:用户资源中心(RDB)平台底座,所有的运维系统,都需要依赖这个,内置用户、权限、角色、_滴滴夜莺

linux socket bind 内核详解,Linux内核Socket实现之------Socket绑定bind(3)-程序员宅基地

文章浏览阅读793次。socket系列文章都是承接第一篇socket创建,因此这里的编号和内核版本都继承了第一篇文章。2. SYSCALL_DEFINE3函数Bind系统调用通过SYSCALL_DEFINE3调用各个协议不同的bind函数,SYSCALL_DEFINE3(bind,int, fd, struct sockaddr __user *, umyaddr, int, addrlen){structsocke..._sock->ops->bind

linux磁盘挂载之parted_parted /dev/sdb-程序员宅基地

文章浏览阅读3k次,点赞2次,收藏22次。因为fdisk命令支持的最大分区容量为2T如果需要挂载大容量的分区磁盘需要使用parted命令。_parted /dev/sdb

推荐文章

热门文章

相关标签