在项目中,根据需求需要开发双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;
}
}
Qt利用paintEvent绘制双Y轴坐标折线图
于 2023-06-20 15:12:42 首次发布