qml 动态创建复杂饼图

来源:互联网 发布:java编译执行命令 编辑:程序博客网 时间:2024/05/14 20:16

数据的展示自然离不开图表,qt默认安装没有将QtCharts模块集成并社区版中,导致只能自己编译或使用第三方图库,如chartDirector或qwtplot等等。

本次介绍Qt在qml中使用QtCharts绘制稍复杂的饼图示例,并且该饼图提供一个接口传入数据并非一个简单的demo程序。

首先在安装Qt时要选中QtCharts模块,这点就不介绍了。

先来看一下效果


中间为第一环绘制了第一层的数据比例,第二层是以第一层为基础,将第一层的继续分比例显示。这里我们先来熟悉一下画饼图的控件

PieSeries{    startAngle: 起始角度    endAngle: 终止角度    holeSize: 内狐离圆心的距离    size: 外狐离圆心的距离}
以上只列举了本次博客用到的主要属性,如果还想了解其它属性请查看Qt帮助文档。下一个控件是PieSlice
PieSlice{    angleSpan: 狐所占角度    labelVisible: 是否显示标签    label: 标签显示内容    value: 狐所代表的值}
知道以上两个控件后,我们大概可以知道,饼图是由扇形组成的,而要想出现环状效果,只要使扇形内儿离圆心的距离大于0,且外儿离圆心的距离
大于内狐离圆心的距离就可以了。所以我们 可以构想出上图的思路,在一个ChartView中放一个PieSeries画里面的一圈,这个PieSeies里有两PieSlice;
设置它的startAngle =0, endAngle=360;里面一圈就画好了。接着在ChartView中再放一个PieSeries用来画网银止付的外环,这个PieSeries的起始角度为statAngle=0,
终止角度为endAngle= "网银止付的slice对象".angleSpan,内狐离圆心的距离为最里环的外狐离圆心的距离,由于最里的一环没有设置Size(默认为0.7),所以这一个PieSeries
的holeSize=0.7,外狐离圆心的距离比0.7大就可以。这里我取了size=0.8;第二个PieSeries就画好了。最后一个原理和第二个一样。我们的代码可以是下面这样
     ChartView{        id: chartView        title: "2017-05-12 至 2017-08-29电信诈骗统计饼图"        anchors.fill: parent        legend.alignment: Qt.AlignRight        antialiasing: true        animationOptions: ChartView.AllAnimations        property real sumAngle: 0        PieSeries{            PieSlice{                id: sliceBank                labelVisible: true                labelPosition: PieSlice.LabelInsideHorizontal                label: qsTr("网银止付 ") + (new Array(2).join('0') + percentage * 100).slice(-2) + "%"                value: pieBankSeries.sum            }            PieSlice{                id: sliceCall                labelVisible: true                labelPosition: PieSlice.LabelInsideHorizontal                label: qsTr("智能追呼 ") + (new Array(2).join('0') + percentage * 100).slice(-2) + "%"                value: pieCallSeries.sum            }        }        PieSeries{            id: pieBankSeries            size: 0.8            holeSize: 0.7            startAngle: 0            endAngle: sliceBank.angleSpan            PieSlice{                labelVisible: true                label: qsTr("中国银行")                value: 120            }            PieSlice{                labelVisible: true                label: qsTr("工商银行")                value: 130            }            PieSlice{                labelVisible: true                label: qsTr("招商银行")                value: 142            }        }        PieSeries{            id: pieCallSeries            size: 0.8            holeSize: 0.7            startAngle: sliceBank.angleSpan            endAngle: 360            PieSlice{                labelVisible: true                label: qsTr("中国电信")                value: 123            }            PieSlice{                labelVisible: true                label: qsTr("中国联通")                value: 98            }            PieSlice{                labelVisible: true                label: qsTr("中国移动")                value: 400            }        }    }
像上面这样我们便可以画出上图一样的饼图,但是这样很明显,饼图的数据很死,能表示的数据也有限。所以我们应该把它做成接口。
这里我先用了json做为数据的接口,给出上面图的数据如下
[{model: "网银止付",data: [{label: "招商银行",total_count: 115},                           {label: "工商银行",total_count: 200},                           {label: "中国银行",total_count: 220}]}, {model: "智能追呼",data: [{label: "中国移动",total_count: 100},                           {label: "中国联通",total_count: 89},                           {label: "中国电信",total_count: 400}]}]
解析数据的方法因人而异,这里我分成了两步,一是写了一个画饼图的方法,二是写了一个解析json并调用画饼图的方法的方法。
画饼图的方法如下
         function createPieSeries(pieArgObj){             var str = "import QtQuick 2.7;import QtCharts 2.0;PieSeries{}";             var pieSeriesObj = Qt.createQmlObject(str,chartView,"dynamicSnippet1"); //创建一个PieSeries以chartView为父对象             pieSeriesObj.startAngle = pieArgObj.startAngle; //起始角度             pieSeriesObj.endAngle = pieArgObj.endAngle; //终止角度             var dataArray = pieArgObj.data;             var dataCount = dataArray.length;             for(var i=0;i<dataCount;i++){                 var sliceObj = pieSeriesObj.append(dataArray[i].label,dataArray[i].total_count); //通过PieSeries的append方法添加PieSlice                 sliceObj.labelVisible = true;        //设置PieSlice的标签可见                 sliceObj.label = dataArray[i].label;  //设置PieSlice的标签内容             }             if(pieArgObj.position == 0){       //0代表是内环,此时显示百分比                 var sliceCount = pieSeriesObj.count;                 for(var j =0; j<sliceCount;j++){                     sliceObj = pieSeriesObj.at(j);                     sliceObj.labelPosition = PieSlice.LabelInsideHorizontal //设置PieSlice的label显示位置                     sliceObj.label = sliceObj.label + " " + (new Array(2).join('0') + sliceObj.percentage * 100).slice(-2) + "%"; //保留两位小数,并以百                 }         //分比显示             }             return pieSeriesObj;         }
解析数据的方法如下
         function loadData(jsObj){             if(!(jsObj instanceof Array)){    //如果不是array,直接返回                 console.log("errror argument!")                 return;             }             var dataLength = jsObj.length;             var modelArray = new Array();             var modelObject = new Object();             for(var i=0;i<dataLength;i++){ //获取网银止付和智能追呼的总值,通过计算子块的和;                 var nodeData = jsObj[i];                 var nodeDataArray = nodeData.data;                 var nodeDataArrayLength = nodeDataArray.length;                 var pieSeriesSumValue = 0                 for(var j=0;j<nodeDataArrayLength;j++){                     pieSeriesSumValue += nodeDataArray[j].total_count;                 }                 modelArray[i] = {label: nodeData.model,total_count: pieSeriesSumValue};             }             var obj = createPieSeries({startAngle:0,endAngle:360,data: modelArray,position: 0}); //先创建最里环             var endAngle = new Number();             for(var j=0;j<dataLength;j++){                 var curAngle = obj.at(j).angleSpan; //                 endAngle += curAngle;                 var subObj = createPieSeries({startAngle: endAngle - curAngle,endAngle: endAngle,data: jsObj[j].data,position: 1}); //statAngle为最里环
                 subObj.holeSize = 0.7; //的每个PieSlice的angleSpan之各减去第二环当前的角度,endAngle为最里环每个PieSlice的angleSpan和                 subObj.size = 0.8;    //设置内狐和外狐离圆心的距离。             }         }

上面我做的这个接口只能表示两环,表示的数据块理论上是无限个,这样我们就可以传入数据动态创建了。贴一个完整代码。
import QtQuick 2.8import QtCharts 2.0import QtQuick.Window 2.2Window {    visible: true    width: 640    height: 480    title: qsTr("Hello World")    Item {         anchors.fill: parent         ChartView{            id: chartView            title: "2017-05-12 至 2017-08-29电信诈骗统计饼图"            anchors.fill: parent            legend.alignment: Qt.AlignRight            antialiasing: true            animationOptions: ChartView.AllAnimations            property real sumAngle: 0        }         function createPieSeries(pieArgObj){             var str = "import QtQuick 2.7;import QtCharts 2.0;PieSeries{}";             var pieSeriesObj = Qt.createQmlObject(str,chartView,"dynamicSnippet1");             pieSeriesObj.startAngle = pieArgObj.startAngle;             pieSeriesObj.endAngle = pieArgObj.endAngle;             var dataArray = pieArgObj.data;             var dataCount = dataArray.length;             for(var i=0;i<dataCount;i++){                 var sliceObj = pieSeriesObj.append(dataArray[i].label,dataArray[i].total_count);                 sliceObj.labelVisible = true;                 sliceObj.label = dataArray[i].label;             }             if(pieArgObj.position == 0){                 var sliceCount = pieSeriesObj.count;                 for(var j =0; j<sliceCount;j++){                     sliceObj = pieSeriesObj.at(j);                     sliceObj.labelPosition = PieSlice.LabelInsideHorizontal                     sliceObj.label = sliceObj.label + " " + (new Array(2).join('0') + sliceObj.percentage * 100).slice(-2) + "%";                 }             }             return pieSeriesObj;         }         function loadData(jsObj){             if(!(jsObj instanceof Array)){                 console.log("errror argument!")                 return;             }             var dataLength = jsObj.length;             var modelArray = new Array();             var modelObject = new Object();             for(var i=0;i<dataLength;i++){                 var nodeData = jsObj[i];                 var nodeDataArray = nodeData.data;                 var nodeDataArrayLength = nodeDataArray.length;                 var pieSeriesSumValue = 0                 for(var j=0;j<nodeDataArrayLength;j++){                     pieSeriesSumValue += nodeDataArray[j].total_count;                 }                 modelArray[i] = {label: nodeData.model,total_count: pieSeriesSumValue};             }             var obj = createPieSeries({startAngle:0,endAngle:360,data: modelArray,position: 0});             var endAngle = new Number();             for(var j=0;j<dataLength;j++){                 var curAngle = obj.at(j).angleSpan;                 endAngle += curAngle;                 var subObj = createPieSeries({startAngle: endAngle - curAngle,endAngle: endAngle,data: jsObj[j].data,position: 1});                 subObj.holeSize = 0.7;                 subObj.size = 0.8;             }         }         Component.onCompleted: loadData([{model: "网银止付",data: [{label: "招商银行",total_count: 115},                                                                   {label: "工商银行",total_count: 200},                                                                   {label: "中国银行",total_count: 220}]},                                          {model: "智能追呼",data: [{label: "中国移动",total_count: 100},                                                                   {label: "中国联通",total_count: 89},                                                                   {label: "中国电信",total_count: 400}]}])    }}
这个结果就是最顶上的饼图,我修改数据如下之后
         Component.onCompleted: loadData([{model: "网银止付",data: [{label: "招商银行",total_count: 115},                                                                   {label: "工商银行",total_count: 200},                                                                   {label: "中国银行",total_count: 220}]},                                          {model: "智能追呼",data: [{label: "中国移动",total_count: 100},                                                                   {label: "中国联通",total_count: 89},                                                                   {label: "中国电信",total_count: 400}]},                                          {model: "其它",data: [{label: "报纸",total_count: 100},                                                               {label: "杂志",total_count: 89},                                                               {label: "书刊",total_count: 98}]}])

结果为

到此结束,祝大家工作愉快。



原创粉丝点击