Qt之自绘制饼图

1、说明

最近在搞绘图方面的工作,说实话C++的第三方绘图库并不算多,总之我了解的有:qtcharts、ChartDirector、qwt、kdchart和QCustomPlot。这几个库各有利弊。

2、效果展示

下边是绘制的饼图展示效果,当然了不能满足大多数人的需要,我主要是在这里提供一种思路,如果需要在绘制上有所调整的小伙伴可以下载demo自行修改。

图1 展示图1

图2 展示2

图3 展示图3

3、思路分析

上边三张展示图,如果要说从理解难以成都来说,展示图3是比较容易理解。下边我就几个需要注意的细节描述下:

饼图绘制关键步骤:

点击领取Qt学习资料+视频教程~「链接」

4、源码解说

首先来看两个结构体,主要是用来缓存数据,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 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 }

5、测试代码

 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 }
展开阅读全文

页面更新:2024-02-29

标签:数据项   矩形   图表   缓存   布局   最小   文本   位置   结构   数据

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top