很重要--转载声明
- 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
- 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。
Posted 朝十晚八
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt之自绘制饼图相关的知识,希望对你有一定的参考价值。
最近在搞绘图方面的工作,说实话C++的第三方绘图库并不算多,总之我了解的有:qtcharts、ChartDirector、qwt、kdchart和QCustomPlot。这几个库各有利弊。
下边是绘制的饼图展示效果,当然了不能满足大多数人的需要,我主要是在这里提供一种思路,如果需要在绘制上有所调整的小伙伴可以下载demo自行修改。
图1 展示图1
图2 展示2
图3 展示图3
上边三张展示图,如果要说从理解难以成都来说,展示图3是比较容易理解。下边我就几个需要注意的细节描述下:
饼图绘制关键步骤:
首先来看两个结构体,主要是用来缓存数据,PieItemPrivate存储的是每一个item项的内容,包括item的legend,文本、颜色、值和一些辅助的结构体;PieChartPrivate结构是饼图类的私有数据存储结构,具体含义看注释
1 struct PieItemPrivate 2 { 3 PieItem item;//用户插入数据时的结构,包括注释、值和颜色 4 QPainterPath path;//项绘制时区域 5 QPoint labelPos;//文本位置 6 QRect m_LegendRect;//legend的矩形 7 }; 8 9 struct PieChartPrivate 10 { 11 bool m_bLegendVisible = false;//是否显示图例 12 int m_Minx = 25;//左右最小边距 13 int m_Miny = 25;//上下最小边距 14 int m_MinDiameter = 130;//饼图最小直径 15 int m_RingWidth = 0;//如果是环,环的宽度 16 int m_StartRotationAngle = 0;//绘制item项的时候,其实角度 17 int m_LegendWidth = 100;//图表宽度 可以在插入新数据项的时候更新,计算展示legend所需要的最小尺寸 18 int m_LegendHeight = 30;//图例高度 可以在插入新数据项的时候更新,计算展示legend所需要的最小尺寸 19 double m_SumValue = 0;//所有item的value和 20 QRect m_PieRect;//饼图绘制矩形 21 QColor m_LabelColor = QColor(0, 0, 0);//百分比文字颜色 22 QString m_RingLabel = QStringLiteral("饼图");//图表中心文字描述 23 QVector<PieItemPrivate> m_Items;//图表项 24 };
1、当有新数据或者窗口大小发生变化时,计算数据缓存
1 void PieChart::ConstructData() 2 { 3 int pos = d_ptr->m_StartRotationAngle; 4 int angle; 5 QPainterPath subPath; 6 subPath.addEllipse(d_ptr->m_PieRect.adjusted(d_ptr->m_RingWidth, d_ptr->m_RingWidth, -d_ptr->m_RingWidth, -d_ptr->m_RingWidth)); 7 8 for (auto iter = d_ptr->m_Items.begin(); iter != d_ptr->m_Items.end(); ++iter) 9 { 10 angle = 16 * iter->item.value / d_ptr->m_SumValue * 360; 11 12 QPainterPath path; 13 path.moveTo(d_ptr->m_PieRect.center()); 14 path.arcTo(d_ptr->m_PieRect.x(), d_ptr->m_PieRect.y(), d_ptr->m_PieRect.width(), d_ptr->m_PieRect.height(), pos / 16.0, angle / 16.0); 15 path.closeSubpath(); 16 17 if (d_ptr->m_RingWidth > 0 && d_ptr->m_RingWidth <= d_ptr->m_PieRect.width() / 2) 18 { 19 path -= subPath; 20 } 21 22 iter->path = path; 23 24 double labelAngle = (pos + angle / 2) / 16; 25 double tx = (d_ptr->m_PieRect.width() - d_ptr->m_RingWidth) / 2 * qCos(labelAngle / 360 * 2 * 3.1415926); 26 double ty = -(d_ptr->m_PieRect.width() - d_ptr->m_RingWidth) / 2 * qSin(labelAngle / 360 * 2 * 3.1415926); 27 28 iter->labelPos = QPoint(tx, ty) + d_ptr->m_PieRect.center(); 29 30 pos += angle; 31 } 32 }
2、当窗口大小发生变化时,重新计算各项所在矩形,ConstructRect方式是用来计算各子项矩形区域的,内部调用ConstructCornerLayout方法是生产制定的布局,有兴趣的小伙伴可以写自己的矩形区域计算方式,开达到不同的绘制效果。
1 void PieChart::ConstructRect(const QSize & size) 2 { 3 switch (d_ptr->m_Items.size()) 4 { 5 case 4: 6 ConstructCornerLayout(size); 7 default: 8 break; 9 } 10 } 11 //该方法是针对4个legend,并且在四角的位置所计算的布局方式,小伙伴也可以实现自己的布局计算,然后在ConstructRect接口中调用 12 void PieChart::ConstructCornerLayout(const QSize & size) 13 { 14 int currentR = d_ptr->m_MinDiameter; 15 int diameter; 16 int horiWidth = size.width(); 17 if (d_ptr->m_bLegendVisible) 18 { 19 horiWidth -= d_ptr->m_LegendWidth * 2; 20 } 21 22 if (horiWidth > size.height()) 23 { 24 diameter = size.height(); 25 } 26 else 27 { 28 diameter = horiWidth; 29 } 30 31 int x, y; 32 int r = diameter - d_ptr->m_Minx * 2; 33 currentR = r > currentR ? r : currentR; 34 if (d_ptr->m_bLegendVisible) 35 { 36 x = d_ptr->m_Minx + d_ptr->m_LegendWidth; 37 y = (size.height() - currentR) / 2; 38 //计算4个legend位置 39 d_ptr->m_Items[1].m_LegendRect = QRect(d_ptr->m_Minx, d_ptr->m_Miny, d_ptr->m_LegendWidth, d_ptr->m_LegendHeight); 40 d_ptr->m_Items[0].m_LegendRect = QRect(x + r, d_ptr->m_Miny, d_ptr->m_LegendWidth, d_ptr->m_LegendHeight); 41 d_ptr->m_Items[3].m_LegendRect = QRect(x + r, size.height() - d_ptr->m_Miny - 30, d_ptr->m_LegendWidth, d_ptr->m_LegendHeight); 42 d_ptr->m_Items[2].m_LegendRect = QRect(d_ptr->m_Minx, size.height() - d_ptr->m_Miny - 30, d_ptr->m_LegendWidth, d_ptr->m_LegendHeight); 43 } 44 else 45 { 46 x = d_ptr->m_Minx; 47 y = d_ptr->m_Miny; 48 } 49 50 d_ptr->m_PieRect = QRect(x, y, currentR, currentR);//计算饼图位置 51 }
1 int main(int argc, char *argv[]) 2 { 3 QApplication a(argc, argv); 4 5 PieChart w; 6 w.AddData(100, Qt::red, "red"); 7 w.AddData(100, Qt::green, "green"); 8 w.AddData(100, Qt::blue, "blue"); 9 w.AddData(100, Qt::gray, "gray"); 10 w.show(); 11 12 return a.exec(); 13 }
以上是关于Qt之自绘制饼图的主要内容,如果未能解决你的问题,请参考以下文章
Qt+ECharts开发笔记:ECharts的饼图介绍基础使用和Qt封装百分比图Demo
100天精通Python(可视化篇)——第81天:matplotlib绘制不同种类炫酷饼图参数说明+代码实战(自定义百分比多个子图圆环嵌套饼图)