Qt之自绘制饼图
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展示图33、思路分析
上边三张展示图,如果要说从理解难以成都来说,展示图3是比较容易理解。下边我就几个需要注意的细节描述下:图表矩形距离边框距离,影响图表绘制矩形的因素图表绘制方向,默认是逆时针图表文本描述位置legend描述位置,demo中已经提供了接口,可以支持不同legend的展现形式箭头长短空心饼图(圆环图)
饼图绘制关键步骤:添加数据项构造数据缓存绘制图表窗口大小变化构造数据项矩形构造数据缓存绘制图表
点击领取Qt学习资料视频教程链接
4、源码解说
首先来看两个结构体,主要是用来缓存数据,PieItemPrivate存储的是每一个item项的内容,包括item的legend,文本、颜色、值和一些辅助的结构体;PieChartPrivate结构是饼图类的私有数据存储结构,具体含义看注释1structPieItemPrivate2{3PieItemitem;用户插入数据时的结构,包括注释、值和颜色4QPainterPathpath;项绘制时区域5QPointlabelPos;文本位置6QRectmLegendRect;legend的矩形7};89structPieChartPrivate10{11boolmbLegendVisiblefalse;是否显示图例12intmMinx25;左右最小边距13intmMiny25;上下最小边距14intmMinDiameter130;饼图最小直径15intmRingWidth0;如果是环,环的宽度16intmStartRotationAngle0;绘制item项的时候,其实角度17intmLegendWidth100;图表宽度可以在插入新数据项的时候更新,计算展示legend所需要的最小尺寸18intmLegendHeight30;图例高度可以在插入新数据项的时候更新,计算展示legend所需要的最小尺寸19doublemSumValue0;所有item的value和20QRectmPieRect;饼图绘制矩形21QColormLabelColorQColor(0,0,0);百分比文字颜色22QStringmRingLabelQStringLiteral(饼图);图表中心文字描述23QVectorPieItemPrivatemItems;图表项24};
1、当有新数据或者窗口大小发生变化时,计算数据缓存1voidPieChart::ConstructData()2{3intposdptrmStartRotationAngle;4intangle;5QPainterPathsubPath;6subPath。addEllipse(dptrmPieRect。adjusted(dptrmRingWidth,dptrmRingWidth,dptrmRingWidth,dptrmRingWidth));78for(autoiterdptrmItems。begin();iter!dptrmItems。end();iter)9{10angle16iteritem。valuedptrmSumValue360;1112QPainterPathpath;13path。moveTo(dptrmPieRect。center());14path。arcTo(dptrmPieRect。x(),dptrmPieRect。y(),dptrmPieRect。width(),dptrmPieRect。height(),pos16。0,angle16。0);15path。closeSubpath();1617if(dptrmRingWidth0dptrmRingWidthdptrmPieRect。width()2)18{19pathsubPath;20}2122iterpathpath;2324doublelabelAngle(posangle2)16;25doubletx(dptrmPieRect。width()dptrmRingWidth)2qCos(labelAngle36023。1415926);26doublety(dptrmPieRect。width()dptrmRingWidth)2qSin(labelAngle36023。1415926);2728iterlabelPosQPoint(tx,ty)dptrmPieRect。center();2930posangle;31}32}
2、当窗口大小发生变化时,重新计算各项所在矩形,ConstructRect方式是用来计算各子项矩形区域的,内部调用ConstructCornerLayout方法是生产制定的布局,有兴趣的小伙伴可以写自己的矩形区域计算方式,开达到不同的绘制效果。1voidPieChart::ConstructRect(constQSizesize)2{3switch(dptrmItems。size())4{5case4:6ConstructCornerLayout(size);7default:8break;9}10}11该方法是针对4个legend,并且在四角的位置所计算的布局方式,小伙伴也可以实现自己的布局计算,然后在ConstructRect接口中调用12voidPieChart::ConstructCornerLayout(constQSizesize)13{14intcurrentRdptrmMinDiameter;15intdiameter;16inthoriWidthsize。width();17if(dptrmbLegendVisible)18{19horiWidthdptrmLegendWidth2;20}2122if(horiWidthsize。height())23{24diametersize。height();25}26else27{28diameterhoriWidth;29}3031intx,y;32intrdiameterdptrmMinx2;33currentRrcurrentR?r:currentR;34if(dptrmbLegendVisible)35{36xdptrmMinxdptrmLegendWidth;37y(size。height()currentR)2;38计算4个legend位置39dptrmItems〔1〕。mLegendRectQRect(dptrmMinx,dptrmMiny,dptrmLegendWidth,dptrmLegendHeight);40dptrmItems〔0〕。mLegendRectQRect(xr,dptrmMiny,dptrmLegendWidth,dptrmLegendHeight);41dptrmItems〔3〕。mLegendRectQRect(xr,size。height()dptrmMiny30,dptrmLegendWidth,dptrmLegendHeight);42dptrmItems〔2〕。mLegendRectQRect(dptrmMinx,size。height()dptrmMiny30,dptrmLegendWidth,dptrmLegendHeight);43}44else45{46xdptrmMinx;47ydptrmMiny;48}4950dptrmPieRectQRect(x,y,currentR,currentR);计算饼图位置51}5、测试代码1intmain(intargc,charargv〔〕)2{3QApplicationa(argc,argv);45PieChartw;6w。AddData(100,Qt::red,red);7w。AddData(100,Qt::green,green);8w。AddData(100,Qt::blue,blue);9w。AddData(100,Qt::gray,gray);10w。show();1112returna。exec();13}