Qt利用paintEvent绘制双Y轴坐标折线图

news2024/11/24 15:44:45

在这里插入图片描述

在项目中,根据需求需要开发双Y轴坐标折线图,但是由于它有多个图层,如果使用QChart绘制的话,达不到需求的要求,只能自己绘制,具体的利用translate()接口将坐标系统移动到界面的左下角(30, height() - 50)处,这样向上就是-y,向下+y,数据映射到坐标系上面更加的方便,具体实现如下:

#pragma once
#include <qwidget.h>
#include <QPainter>
#include <QPaintEvent>
#include <iostream>
class DrawBoneArg : public QWidget
{
	Q_OBJECT
public:
	//tuple元素前两个是左Y轴最大最小值,中间两个是右Y轴最大最小值,最后两个是x轴最大最小值,   其他三个参数分别是轴的偏移量(每个间隔多少值)
	explicit DrawBoneArg(std::tuple<double, double, double, double, double, double > AxisMaxMinValue = { 0.58,1.42,-5,2,20,100 }, double leftYAxisOffset = 0.12, double rightYAxisOffset = 1, int xAxisOffset = 5, QWidget* parent = nullptr);

	//从数据库中读出的标准数据
	void setStandardData(std::vector<std::map<unsigned int, double> > standardData);

	//绘制区域的最大最小值(对应的左Y轴值),颜色,字体
	void setRegionMaxMinColorTextValue(std::vector<std::tuple<double, double, QColor, std::string> > RegionData);

	void setInspectResult(unsigned int age, const double boneValue);

	void setInspectResult(std::vector<std::pair<unsigned int, double> > re);
private:
	void drawBackGround(QPainter& painter);

	void drawAxis(QPainter& painter);

	void drawRegion(QPainter& painter);

	void drawValueLine(QPainter& painter);

	void drawInspectResult(QPainter& painter);

	void initWin();
protected:
	void paintEvent(QPaintEvent* pEvent);

private:

	//坐标轴上刻度跟刻度之间的距离
	float m_yLeftAxisCoefficient;
	float m_yRightAxisCoefficient;
	float m_xAgeAxisCoefficient;

	//绘制折线标准数据第一个是年龄,第二个是标准版骨密度值
	std::vector<std::map<unsigned int, double> > m_boneStandardData;

	//前两个元素左Y轴最小最大值, 中间两个元素右Y轴最小最大值, 最后两个X轴最小最大值
	std::tuple<double, double, double, double, double, double> m_dAxisMaxMinValue;

	std::vector<std::tuple<double, double, QColor, std::string> >  m_regionData;
	
	//x轴偏移量
	int m_iAgeOffset;

	//左Y轴偏移量
	double m_dLeftYAxisOffset;

	//右Y轴偏移量
	double m_dRightYAxisOffset;

	//当前检查者检查结果
	std::vector<std::pair<unsigned int, const double> > m_InspectResult;
};
#include "DrawBoneArg.h"

DrawBoneArg::DrawBoneArg(std::tuple<double, double, double, double, double, double> AxisMaxMinValue, double leftYAxisOffset, double rightYAxisOffset, int xAxisOffset, QWidget* parent)
	: QWidget(parent)
	, m_dAxisMaxMinValue(AxisMaxMinValue)
	, m_iAgeOffset(xAxisOffset)
	, m_dLeftYAxisOffset(leftYAxisOffset)
	, m_dRightYAxisOffset(rightYAxisOffset)
{
	this->initWin();
	
}

void DrawBoneArg::initWin()
{

	std::map<unsigned int, double> map1;
	map1.insert(std::pair<unsigned int, double>(20, 1.06));
	map1.insert(std::pair<unsigned int, double>(25, 1.06));
	map1.insert(std::pair<unsigned int, double>(30, 1.06));
	map1.insert(std::pair<unsigned int, double>(35, 1.06));
	map1.insert(std::pair<unsigned int, double>(40, 1.06));
	map1.insert(std::pair<unsigned int, double>(45, 1.06));
	map1.insert(std::pair<unsigned int, double>(50, 1.03));
	map1.insert(std::pair<unsigned int, double>(55, 0.98));
	map1.insert(std::pair<unsigned int, double>(60, 0.94));
	map1.insert(std::pair<unsigned int, double>(65, 0.93));
	map1.insert(std::pair<unsigned int, double>(70, 0.90));
	map1.insert(std::pair<unsigned int, double>(75, 0.88));
	map1.insert(std::pair<unsigned int, double>(80, 0.86));
	map1.insert(std::pair<unsigned int, double>(85, 0.84));
	map1.insert(std::pair<unsigned int, double>(90, 0.83));
	map1.insert(std::pair<unsigned int, double>(95, 0.82));
	map1.insert(std::pair<unsigned int, double>(100, 0.81));
	this->m_boneStandardData.emplace_back(map1);

	map1.clear();
	map1.insert(std::pair<unsigned int, double>(20, 1.18));
	map1.insert(std::pair<unsigned int, double>(25, 1.18));
	map1.insert(std::pair<unsigned int, double>(30, 1.18));
	map1.insert(std::pair<unsigned int, double>(35, 1.18));
	map1.insert(std::pair<unsigned int, double>(40, 1.18));
	map1.insert(std::pair<unsigned int, double>(45, 1.18));
	map1.insert(std::pair<unsigned int, double>(50, 1.14));
	map1.insert(std::pair<unsigned int, double>(55, 1.11));
	map1.insert(std::pair<unsigned int, double>(60, 1.08));
	map1.insert(std::pair<unsigned int, double>(65, 1.02));
	map1.insert(std::pair<unsigned int, double>(70, 0.98));
	map1.insert(std::pair<unsigned int, double>(75, 0.96));
	map1.insert(std::pair<unsigned int, double>(80, 0.952));
	map1.insert(std::pair<unsigned int, double>(85, 0.947));
	map1.insert(std::pair<unsigned int, double>(90, 0.942));
	map1.insert(std::pair<unsigned int, double>(95, 0.938));
	map1.insert(std::pair<unsigned int, double>(100, 0.935));
	this->m_boneStandardData.emplace_back(map1);

	map1.clear();
	map1.insert(std::pair<unsigned int, double>(20, 1.30));
	map1.insert(std::pair<unsigned int, double>(25, 1.30));
	map1.insert(std::pair<unsigned int, double>(30, 1.30));
	map1.insert(std::pair<unsigned int, double>(35, 1.30));
	map1.insert(std::pair<unsigned int, double>(40, 1.30));
	map1.insert(std::pair<unsigned int, double>(45, 1.30));
	map1.insert(std::pair<unsigned int, double>(50, 1.26));
	map1.insert(std::pair<unsigned int, double>(55, 1.20));
	map1.insert(std::pair<unsigned int, double>(60, 1.17));
	map1.insert(std::pair<unsigned int, double>(65, 1.11));
	map1.insert(std::pair<unsigned int, double>(70, 1.09));
	map1.insert(std::pair<unsigned int, double>(75, 1.08));
	map1.insert(std::pair<unsigned int, double>(80, 1.07));
	map1.insert(std::pair<unsigned int, double>(85, 1.065));
	map1.insert(std::pair<unsigned int, double>(90, 1.06));
	map1.insert(std::pair<unsigned int, double>(95, 1.055));
	map1.insert(std::pair<unsigned int, double>(100, 1.045));
	this->m_boneStandardData.emplace_back(map1);

	std::tuple<double, double, QColor, std::string> tup1(0.58, 0.88, QColor(255,1,0), "红色");
	std::tuple<double, double, QColor, std::string> tup2(0.88, 1.06, QColor(254,255,0), "黄色");
	std::tuple<double, double, QColor, std::string> tup3(1.06, 1.42, QColor(0, 130, 0), "绿色");
	m_regionData.emplace_back(tup1);
	m_regionData.emplace_back(tup2);
	m_regionData.emplace_back(tup3);

	//this->setInspectResult(70, 1.06);

	std::pair<unsigned int, double> pa{ 75, 1.07 };
	std::pair<unsigned int, double> pa1{ 73, 1.12 };
	std::vector<std::pair<unsigned int, double> > vec;
	vec.push_back(pa);
	vec.push_back(pa1);
	setInspectResult(vec);
}

void DrawBoneArg::setStandardData(std::vector<std::map<unsigned int, double> > standardData)
{
	m_boneStandardData = standardData;
	this->update();
}

void DrawBoneArg::setRegionMaxMinColorTextValue(std::vector<std::tuple<double, double, QColor, std::string> > data)
{
	m_regionData = data;
}

void DrawBoneArg::setInspectResult(unsigned int age, const double boneValue)
{
	m_InspectResult.clear();
	std::pair<unsigned int, const double> re = std::make_pair(age, boneValue);
	m_InspectResult.emplace_back(re);
	this->update();
}

void DrawBoneArg::setInspectResult(std::vector<std::pair<unsigned int, double> > re)
{
	if (re.empty())return;
	m_InspectResult.clear();
	auto it = re.begin();
	while (it != re.end())
	{
		m_InspectResult.emplace_back(*it);
		++it;
	}
	this->update();
}

void DrawBoneArg::paintEvent(QPaintEvent* pEvent)
{
	QPainter painter(this);
	painter.setRenderHint(QPainter::Antialiasing, true);
	this->drawBackGround(painter);
	this->drawAxis(painter);
	this->drawRegion(painter);
	this->drawValueLine(painter);
	this->drawInspectResult(painter);
	QWidget::paintEvent(pEvent);
}

void DrawBoneArg::drawBackGround(QPainter& painter)
{
	painter.setPen(Qt::NoPen);
	painter.setBrush(QColor(214,210,207));
	painter.drawRect(rect());
}

void DrawBoneArg::drawAxis(QPainter& painter)
{
	//将坐标系移动到界面左下角(30, this->height()_50)处
	painter.translate(30, this->height() - 50);

	painter.setPen(QPen(Qt::black));
	painter.drawLine(QPointF(0, 0), QPointF(0, -this->height() + 100));//左竖线
	QPointF yRe = QPointF(0, -this->height() + 100) - QPointF(0, 0);

	int leftYNum = (std::get<1>(this->m_dAxisMaxMinValue) - std::get<0>(this->m_dAxisMaxMinValue)) / this->m_dLeftYAxisOffset;
	float coefficient = yRe.y() / leftYNum;
	m_yLeftAxisCoefficient = coefficient;
	float y = 0;
	float v = std::get<0>(m_dAxisMaxMinValue);
	for (int i = 0; i <= leftYNum; i++)
	{
		painter.drawLine(QPointF(0, y), QPointF(-5, y));
		painter.drawText(QPointF(-25, y-2), QString::number(v, 'f', 2));
		v += m_dLeftYAxisOffset;
		y += coefficient;
	}
	QFont font;
	font.setPointSizeF(12);
	painter.drawText(QPointF(-20, -this->height() + 85), "BMD(g/cm2)");//QString::fromUtf8("2\u00B2")
	
	painter.drawLine(QPointF(0, 0), QPointF(this->width() - 60, 0));//底横线
	QPointF re = QPointF(this->width() - 60, 0) - QPointF(0, 0);
	int xAixsNum = (std::get<5>(this->m_dAxisMaxMinValue) - std::get<4>(m_dAxisMaxMinValue)) / this->m_iAgeOffset;
	float coefficientX = re.x() / xAixsNum;
	m_xAgeAxisCoefficient = coefficientX;
	float x = 0;
	int age = std::get<4>(this->m_dAxisMaxMinValue);
	for (int i = 0; i <= xAixsNum; i++)
	{
		painter.drawLine(QPointF(x, 0), QPointF(x, 5));
		if (i % 2 == 0)
			painter.drawText(QPointF(x - 5, 15), QString::number(age));
		x += coefficientX;
		age += m_iAgeOffset;
	}

	font.setPointSizeF(12);
	painter.setFont(font);
	painter.drawText(re.x() / 2 - 25, 30, QStringLiteral("年龄(岁)"));
	font.setPointSize(10);
	painter.setFont(font);
	painter.drawText(re.x() / 2 - 10, 45, QStringLiteral("中国"));

	
	painter.drawLine(QPointF(0, -this->height() + 100), QPointF(this->width() - 60, -this->height() + 100));//上横线
	QPointF xTop = QPointF(this->width() - 60, -this->height() + 100) - QPointF(0, -this->height() + 100);
	float textX = xTop.x() / 2 - 50;
	font.setPointSizeF(15);
	painter.drawText(textX, -this->height() + 85, QStringLiteral("骨密度-脊柱[L1-L4]"));

	
	painter.drawLine(QPointF(this->width() - 60, -this->height() + 100), QPointF(this->width() - 60, 0));//右竖线
	QPointF yRightRe = QPointF(this->width() - 60, -this->height() + 100) - QPointF(this->width() - 60, 0);

	int rightYAxis = (std::get<3>(this->m_dAxisMaxMinValue) - std::get<2>(this->m_dAxisMaxMinValue)) / m_dRightYAxisOffset;
	float coefficientYRight = yRightRe.y() / rightYAxis;
	m_yRightAxisCoefficient = coefficientYRight;
	float yRight = 0;
	float vy = std::get<2>(this->m_dAxisMaxMinValue);
	for (int i = 0; i <= rightYAxis; i++)
	{
		painter.drawLine(QPointF(this->width() - 60, yRight), QPointF(this->width() - 55, yRight));
		painter.drawText(QPointF(this->width() - 52, yRight), QString::number(vy));
		vy += m_dRightYAxisOffset;
		yRight += coefficientYRight;
	}

	font.setPointSizeF(10);
	painter.drawText(QPointF(this->width() - 120, -this->height() + 85), QString("YA T-score"));
}


void DrawBoneArg::drawRegion(QPainter& painter)
{
	QPen pen(Qt::black);
	pen.setWidth(2);
	painter.setPen(pen);
	
	auto it = this->m_regionData.begin();
	while (it != this->m_regionData.end())
	{
		float yAxisValue = ((std::get<1>(*it) - std::get<0>(m_dAxisMaxMinValue)) / m_dLeftYAxisOffset) * m_yLeftAxisCoefficient;
		float height = ((std::get<1>(*it) - std::get<0>(*it)) / m_dLeftYAxisOffset) * m_yLeftAxisCoefficient;
		QBrush brush(std::get<2>(*it));
		painter.setBrush(brush);
		painter.drawRect(0, yAxisValue, this->width() - 60, -height);
		QFont font;
		font.setPointSizeF(12);
		painter.setFont(font);
		painter.drawText(2, yAxisValue - 2, this->width() - 60, -height, Qt::AlignLeft | Qt::AlignBottom, QString::fromLocal8Bit(std::get<3>(*it).c_str()));
		++it;
	}
}



void DrawBoneArg::drawValueLine(QPainter& painter)
{
	QPen pen(Qt::black);
	pen.setWidth(2);
	painter.setPen(pen);
	auto it = this->m_boneStandardData.begin();
	while (it != this->m_boneStandardData.end())
	{
		auto mapIt = it->begin();
		QPolygonF poly;
		while (mapIt != it->end())
		{
			double x = ((mapIt->first - std::get<4>(m_dAxisMaxMinValue)) / m_iAgeOffset) * m_xAgeAxisCoefficient;
			double y = ((mapIt->second - std::get<0>(m_dAxisMaxMinValue)) / m_dLeftYAxisOffset) * m_yLeftAxisCoefficient;
			poly << QPointF(x, y);
			++mapIt;
		}
		painter.drawPolyline(poly);
		++it;
	}
}

void DrawBoneArg::drawInspectResult(QPainter& painter)
{
	if (m_InspectResult.empty())return;
	auto it = m_InspectResult.begin();
	QPen pen(Qt::black);
	pen.setWidth(1);
	painter.setPen(pen);
	while (it != m_InspectResult.end())
	{
		double x = ((it->first - std::get<4>(m_dAxisMaxMinValue)) / m_iAgeOffset)* m_xAgeAxisCoefficient;
		double y = ((it->second - std::get<0>(m_dAxisMaxMinValue)) / m_dLeftYAxisOffset) * m_yLeftAxisCoefficient;
		QBrush br(Qt::white);
		painter.setBrush(br);
		painter.drawRect( QRectF(x-7.5, y-7.5, 15, 15));

		br.setColor(QColor(0, 0, 0));
		painter.setBrush(br);
		painter.drawRect(QRectF(x - 3.8, y - 3.8, 8, 8));
		++it;
	}
}

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

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

相关文章

IDEA启动终端报错: Cannot open Local Terminal Failed to start ***

1.问题 打开IDEA后&#xff0c;启动终端发现终端报错没有办法正常使用&#xff0c;错误提示 Cannot open Local Terminal Failed to start [powershell.exe] in D:\vue_admin Error running process: CreateProcess failed. Code 2 See IDE log (Help | Show Log in Explorer…

开源创新,燃起未来的火花

尊敬的各位嘉宾、领导、专家、企业代表、优秀开发者和创新人才们&#xff1a; 在2023年6月11日至13日&#xff0c;开放原子全球开源峰会盛大召开&#xff0c;汇集了众多业界精英和开源领域的专家学者&#xff0c;为探索开源创新的辉煌未来共同助力。作为一名开源技术爱好者&am…

开放式耳机的优缺点有哪些?目前有什么开放式耳机推荐?

什么是开放式耳机&#xff1f; 从名字上理解就是开放样式的耳机&#xff0c;其实也确实如此&#xff0c;开放式耳机是不需要封闭耳道来传输声音&#xff0c;主要是通过耳骨振动传递或者声波震动耳膜&#xff0c;两者声音传递的方式都不用完全封闭耳道&#xff0c;可以让耳道对…

COMSOL导入图片几何模型-以二维多孔介质为例

建模教程 首先获取一张多孔介质图片&#xff0c;这里就以COMSOL官网教程图片为例 通过软件将png格式的图片转换为DXF格式文件&#xff0c;也就是AutoCAD支持的文件&#xff1a; 下一步打开COMSOL软件建立二维模型&#xff0c;导入事先准备好的dxf模型&#xff0c;需要注意导入…

C++中String的语法及常用接口的底层实现详解

在C语言中&#xff0c;我们知道处理字符串所用的类型是 char []或者char* 。字符串是以‘\0’结尾的。在C中&#xff0c;string 是一个标准库类&#xff08;class&#xff09;&#xff0c;用于处理字符串。它提供了一种更高级、更便捷的字符串操作方式&#xff0c;string 类提供…

mysql 开启binlog

1&#xff09;检查binlog功能是否有开启 mysql> show variables like log_bin; ---------------------- | Variable_name | Value | ---------------------- | log_bin | OFF | ---------------------- 1 row in set (0.00 sec) &#xff08;2&#xff09;如果显…

用视频制作gif动画的小窍门!知识分享

Gif动画图片是当下最为流行的一种图片格式&#xff0c;它的内容丰富画面生动&#xff0c;能够快速抓住人们的眼球。有非常好信息传递效果&#xff0c;但是很多小伙伴不知道这种gif动画怎么制作的。别担心&#xff0c;接下来小编就跟大家分享一下利用视频制作gif动画的小窍门&am…

Docker Compose资源限制

一、资源限制原因&#xff1a; 防止容器占用过多资源,影响其他容器或宿主机保证容器稳定运行,避免OOM等情况.OOM现象&#xff1a;根据优先机制kill掉宿主机上最高的进程从而来释放空间&#xff0c;只要是宿主机的进程都可能被kill掉的。进行资源隔离,提高安全性 二、Docker Com…

eclipse (C/C++) 常用设置记录

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台&#xff1b;现公司用其作为开发单片机的IDE&#xff1b;因此记录一下常用的配置方法&#xff1b; 文章目录 零、常用默认快捷键一、高亮相同变量二、修改高亮变量颜色三、在整个工程内搜索某个函数四、切换主题五、改变字…

主分区,逻辑分区,扩展分区有什么区别

1.地位不同 逻辑分区属于扩展分区&#xff0c;扩展分区属于主分区。 给新硬盘上建立分区时都要遵循以下的顺序&#xff1a;建立主分区→建立扩展分区→建立逻辑分区→激活主分区→格式化所有分区。 2.位置不同 主分区又叫做引导分区&#xff0c;最多只能创建四个。 扩展分…

软考A计划-系统集成项目管理工程师-信息化知识(四)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

人机接口回路原理(一)

一、人机接口框图 一般来说&#xff0c;微机保护的人机接口回路是指键盘、显示器及接口 CPU插件电路。人机接口回路的主要作用是通过键盘和显示器完成人机对话任务、时钟校对及与各保护CPU插件通信和巡检任务。在单CPU结构的保护中&#xff0c;接口 CPU就由保护CPU兼任。为了减…

什么是IO Pad?

1.什么是IO pad&#xff1f; IO pad是一个芯片管脚处理模块&#xff0c;即可以将芯片管脚的信号经过处理送给芯片内部&#xff0c;又可以将芯片内部输出的信号经过处理送到芯片管脚。输入信号处理包含时钟信号&#xff0c;复位信号等&#xff0c;输出信号包含观察时钟、中断等…

详细介绍Eclipse控制台Console使用说明

1.说明 本文详细介绍Eclipse控制台Console使用说明&#xff0c; 调试时通过控制台查看日志&#xff0c; 有时候日志太多会找不到上面的日志&#xff0c; 有时候几个控制台会不受控制的弹出&#xff0c; 那么请参考本文&#xff0c; 通过调整Eclipse控制台, 更高效方便的查看日志…

高效项目评审的6大注意事项

评审活动是软件项目降低风险的重要手段&#xff0c;但在实际评审活动中往往因主题不明确、现场争执不停&#xff0c;主持人无法有效控制会议流程等问题&#xff0c;最终导致项目评审活动效果大打折扣。 那么在项目评审过程中有什么注意事项&#xff0c;有助于完成高效的评审会议…

软件测试技能,JMeter压力测试教程,正则表达式提取(三)

目录 前言 一、场景案例 二、登录请求 三、正则提取器 四、引用参数 前言 接口关联&#xff0c;上一个接口返回的 token 作为下个接口的入参&#xff0c;除了前面一篇讲到的用 json 提取器提取&#xff0c;也可以用正则提取 json 提取器只能提取 json 格式的数据&#x…

vue3 + TS + elementplus + pinia实现后台管理系统左侧菜单联动实现 tab根据路由切换联动内容

效果图&#xff1a; home.vue页面代码 <template><el-container><el-aside width"collapse ? 200px : 70px"><el-button color"#626aef" click"collapseToggle()"><el-icon><Expand v-if"collapse"…

[进阶]Java:多线程:线程同步

线程同步 解决线程安全问题的方案。 线程同步的思想 让多个线程实现先后一次访问共享资源&#xff0c;这样就解决了安全问题。 线程同步的常见方案&#xff1a; 加锁&#xff1a;每次只允许一个线程加锁&#xff0c;加锁后才能进入访问&#xff0c;访问完毕后自动解锁&…

css 使用杂记

水平居中 条件&#xff1a;必须有宽度&#xff0c;不能无限宽 行内元素&#xff1a;通过设置父元素text-align:center块元素&#xff1a;要设置宽度&#xff0c;然后 左右margin值为“auto” 垂直居中 条件&#xff1a;父元素必须有高度&#xff0c;不能无限高 行内元素&#…

【Solr】体验极速安装solr

目录 前言 安装下载- 方式一&#xff1a;官网下载- 方式二&#xff1a;仓库下载 启动方式 快速使用 前言 solr是基于java开发的&#xff0c;所以solr需要用到jdk环境,并且solr需要在tomcat容器中才能运行,所以需要提前配置好jdk和tomcat环境。 安装下载 需要注意的是&#…