1、说明

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

  • qtcharts:qt5.7之后才开源的模块,支持绘制各种图标,并且功能相当丰富,但是可扩展性差,如果自己想高度定制,比较困难,主要是和qt的源码风格有决定性的关系。
  • ChartDirector:开源的第三方绘图库,使用方便,推荐使用
  • qwt:主要绘制仪表盘类似的东西(这个库可以编译后加入qt帮助文档)
  • kdchart:不仅可以绘制图表,而且可以绘制甘特图,功能也都挺好使,我个人之前在qt4.7的时候使用过
  • QCustomPlot:简答的绘图库,因为只有两个文件,如果想高度定制我个人推荐这个靠谱,毕竟理解起来容易些

2、效果展示

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

图1 展示图1

图2 展示2

图3 展示图3

3、思路分析

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

  • 图表矩形距离边框距离,影响图表绘制矩形的因素
  • 图表绘制方向,默认是逆时针
  • 图表文本描述位置
  • legend描述位置,demo中已经提供了接口,可以支持不同legend的展现形式
  • 箭头长短
  • 空心饼图(圆环图)

饼图绘制关键步骤:

  • 添加数据项->构造数据缓存->绘制图表
  • 窗口大小变化->构造数据项矩形->构造数据缓存->绘制图表

4、源码解说

首先来看两个结构体,主要是用来缓存数据,PieItemPrivate存储的是每一个item项的内容,包括item的legend,文本、颜色、值和一些辅助的结构体;PieChartPrivate结构是饼图类的私有数据存储结构,具体含义看注释

 struct PieItemPrivate
{
PieItem item;//用户插入数据时的结构,包括注释、值和颜色
QPainterPath path;//项绘制时区域
QPoint labelPos;//文本位置
QRect m_LegendRect;//legend的矩形
}; struct PieChartPrivate
{
bool m_bLegendVisible = false;//是否显示图例
int m_Minx = ;//左右最小边距
int m_Miny = ;//上下最小边距
int m_MinDiameter = ;//饼图最小直径
int m_RingWidth = ;//如果是环,环的宽度
int m_StartRotationAngle = ;//绘制item项的时候,其实角度
int m_LegendWidth = ;//图表宽度 可以在插入新数据项的时候更新,计算展示legend所需要的最小尺寸
int m_LegendHeight = ;//图例高度 可以在插入新数据项的时候更新,计算展示legend所需要的最小尺寸
double m_SumValue = ;//所有item的value和
QRect m_PieRect;//饼图绘制矩形
QColor m_LabelColor = QColor(, , );//百分比文字颜色
QString m_RingLabel = QStringLiteral("饼图");//图表中心文字描述
QVector<PieItemPrivate> m_Items;//图表项
};

1、当有新数据或者窗口大小发生变化时,计算数据缓存

 void PieChart::ConstructData()
{
int pos = d_ptr->m_StartRotationAngle;
int angle;
QPainterPath subPath;
subPath.addEllipse(d_ptr->m_PieRect.adjusted(d_ptr->m_RingWidth, d_ptr->m_RingWidth, -d_ptr->m_RingWidth, -d_ptr->m_RingWidth)); for (auto iter = d_ptr->m_Items.begin(); iter != d_ptr->m_Items.end(); ++iter)
{
angle = * iter->item.value / d_ptr->m_SumValue * ; QPainterPath path;
path.moveTo(d_ptr->m_PieRect.center());
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);
path.closeSubpath(); if (d_ptr->m_RingWidth > && d_ptr->m_RingWidth <= d_ptr->m_PieRect.width() / )
{
path -= subPath;
} iter->path = path; double labelAngle = (pos + angle / ) / ;
double tx = (d_ptr->m_PieRect.width() - d_ptr->m_RingWidth) / * qCos(labelAngle / * * 3.1415926);
double ty = -(d_ptr->m_PieRect.width() - d_ptr->m_RingWidth) / * qSin(labelAngle / * * 3.1415926); iter->labelPos = QPoint(tx, ty) + d_ptr->m_PieRect.center(); pos += angle;
}
}

2、当窗口大小发生变化时,重新计算各项所在矩形,ConstructRect方式是用来计算各子项矩形区域的,内部调用ConstructCornerLayout方法是生产制定的布局,有兴趣的小伙伴可以写自己的矩形区域计算方式,开达到不同的绘制效果。

 void PieChart::ConstructRect(const QSize & size)
{
switch (d_ptr->m_Items.size())
{
case :
ConstructCornerLayout(size);
default:
break;
}
}
//该方法是针对4个legend,并且在四角的位置所计算的布局方式,小伙伴也可以实现自己的布局计算,然后在ConstructRect接口中调用
void PieChart::ConstructCornerLayout(const QSize & size)
{
int currentR = d_ptr->m_MinDiameter;
int diameter;
int horiWidth = size.width();
if (d_ptr->m_bLegendVisible)
{
horiWidth -= d_ptr->m_LegendWidth * ;
} if (horiWidth > size.height())
{
diameter = size.height();
}
else
{
diameter = horiWidth;
} int x, y;
int r = diameter - d_ptr->m_Minx * ;
currentR = r > currentR ? r : currentR;
if (d_ptr->m_bLegendVisible)
{
x = d_ptr->m_Minx + d_ptr->m_LegendWidth;
y = (size.height() - currentR) / ;
      //计算4个legend位置
d_ptr->m_Items[].m_LegendRect = QRect(d_ptr->m_Minx, d_ptr->m_Miny, d_ptr->m_LegendWidth, d_ptr->m_LegendHeight);
d_ptr->m_Items[].m_LegendRect = QRect(x + r, d_ptr->m_Miny, d_ptr->m_LegendWidth, d_ptr->m_LegendHeight);
d_ptr->m_Items[].m_LegendRect = QRect(x + r, size.height() - d_ptr->m_Miny - , d_ptr->m_LegendWidth, d_ptr->m_LegendHeight);
d_ptr->m_Items[].m_LegendRect = QRect(d_ptr->m_Minx, size.height() - d_ptr->m_Miny - , d_ptr->m_LegendWidth, d_ptr->m_LegendHeight);
}
else
{
x = d_ptr->m_Minx;
y = d_ptr->m_Miny;
} d_ptr->m_PieRect = QRect(x, y, currentR, currentR);//计算饼图位置
}

5、测试代码

 int main(int argc, char *argv[])
{
QApplication a(argc, argv); PieChart w;
w.AddData(, Qt::red, "red");
w.AddData(, Qt::green, "green");
w.AddData(, Qt::blue, "blue");
w.AddData(, Qt::gray, "gray");
w.show(); return a.exec();
}

6、示例下载

Qt之自绘制饼图

如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!! 

 

很重要--转载声明

  1. 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
  2. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。

Qt之自绘制饼图的更多相关文章

  1. QT绘制饼图

    QT版本:QT5.6.1 QT绘制饼图,出问题的代码如下 void DrawPieDialog::paintEvent(QPaintEvent *event) { float startAngle=0 ...

  2. Html5绘制饼图统计图

    这里要介绍的是一个jQuery插件:jquery.easysector.js Html5提供了强大的绘图API,让我们能够使用javascript轻松绘制各种图形.本文将主要讲解使用HTML5绘制饼图 ...

  3. PHP中用GD绘制饼图

    PHP中用GD绘制饼图,绘制的类见代码: Class Chart{ private $image; // 定义图像 private $title; // 定义标题 private $ydata; // ...

  4. 【Highcharts】 绘制饼图和漏斗图

    1.outModel类设计 设计outModel类首先研究下Highcharts中series的data数据格式,发现饼图和漏斗图都可以使用这样格式的数据 series: [{ name: 'Uniq ...

  5. 【带着canvas去流浪】 (3)绘制饼图

    目录 一. 任务说明 二. 重点提示 三. 示例代码 四. hover高亮的实现思路 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:< ...

  6. Python使用Plotly绘图工具,绘制饼图

    今天我们来学习一下如何使用Python的Plotly绘图工具,绘制饼图 使用Plotly绘制饼图的方法,我们需要使用graph_objs中的Pie函数 函数中最常用的两个属性values,用于赋值给需 ...

  7. HTML5绘制饼图示例(一)

    原文地址:http://www.2cto.com/kf/201108/100251.html HTML5引入Canvas元素,用于图形的绘制,我们可以仅仅基于HTML和JavaScript就能绘制出原 ...

  8. R绘图 第八篇:绘制饼图(ggplot2)

    geom_bar()函数不仅可以绘制条形图,还能绘制饼图,跟绘制条形图的区别是坐标系不同,绘制饼图使用的坐标系polar,并且设置theta="y": coord_polar(th ...

  9. c# 通过.net自带的chart控件绘制饼图pie chart

    c# 通过.net自带的chart控件绘制饼图pie chart   需要实现的目标是: 1.将数据绑定到pie的后台数据中,自动生成饼图. 2.生成的饼图有详细文字的说明. 具体的实现步骤: > ...

随机推荐

  1. CMD运行命令每次都要进入很麻烦

    20:35:52 win+r  输入regedit 进入注册表 找到HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor项 创建AutoRun ...

  2. 极简科普 1:什么是 VOIP

    VoIP 的全称是 Voice over Internet Protocol.简单说,就是用过 IP 网络进行即时的语音/视频通信.注意,这里只强调了在传输过程中有使用 IP 网络,并没有说只通过 I ...

  3. matlab 图像平移操作

    目标:对原图I进行[80,50]的偏移操作得到图B. 首先读入图像,以matlab自带的pout.tif为例. strel是创建形态学结构元素的. translate函数在原结构上进行[80,50]的 ...

  4. 04flask_scripts使用

    1,flask_script作用 flask_script的作用是可以通过命令行的实行来操作Flask,例如通过命令跑一个开发版本的服务器,设置数据库,定时任务等, 我们把脚本命令代码放在一个叫做ma ...

  5. elasticsearch简单操作

    现在,启动一个节点和kibana,接下来的一切操作都在kibana中Dev Tools下的Console里完成 创建一篇文档 将小黑的小姨妈的个人信息录入elasticsearch.我们只要输入 PU ...

  6. ajax常见的面试题

    1. 什么是ajax? AJAX = Asynchronous (异步) JavaScript and XML,是一种用于创建快速动态网页的技术. 2. ajax接受到的数据类型是什么? 2.1 st ...

  7. vs2010 sp1 安装Silverlight5 语言版本不匹配的问题

    好久之前用silverlight写了个程序,今天心血来潮想给朋友看一下,朋友更新了sl5,但是运行不起来. 所以有点郁闷,于是打算更新项目到silverlight5. 装sp1后,下载silverli ...

  8. 数位DP -启示录

    http://poj.org/problem?id=3208 一个魔鬼数为包含连续三个666的的数字,给个n(n<5e7)求第n个魔鬼数. 预处理f[i][j],f[i][3]表示由前i位数字构 ...

  9. python 用正则处理日志实例

    前提:     了解正则基本语法   import re with open('top10_xiaozhuang_net.log','r') as f1: #读取日志文件 subject=f1.rea ...

  10. mybatis逆向工程的注意事项,以及数据库表

    1.选择性更新,如果有新参数就更换成新参数,如果参数是null就不更新,还是原来的参数 2.mybatis使用逆向工程,数据库建表的字段user_id必须用下滑线隔开,这样生成的对象private L ...