0.前言
虽然js的ECharts很好看,但是得嵌一个webengineview,如果是Loader加载的话会很慢,所以我试着用QCharts来自定义一些简单的图表。
UI原型图如下:
最终实现效果如下:
相对来说,没有ECharts的好看,花样也少,而且要定制效果更好的话得参考源码从C++入手改。比较坑的是hover只有true和false两个状态,没有鼠标坐标,没法做标签跟随,而且MouseArea也没法传递hover只能传点击。
(刚看了下代码,发现要获取鼠标移动事件鼠标的坐标也很容易,C++继承QChartView然后重写鼠标移动事件把坐标emit出来就行了,也不知道为什么官方没有提供这个信号。我在C++里试了下是可行的,在调用父类该事件函数后也不会影响之前已有的信号,下一步我该定制为QML类型了。看来QCharts还是挺有意思的)
1.QML代码注:编写时用的Qt版本为5.9和5.12
[1/3]
//DoughnutPie.qml
import QtQuick 2.0
import QtCharts 2.2
import QtQuick.Controls 2.2
//外面套一层,方便放标签
Item{
height: 140
width: 140
ChartView {
id:chart
anchors.fill: parent
//anchors.centerIn: parent
animationDuration: 500
animationOptions: ChartView.AllAnimations
//view背景色
backgroundColor: "transparent"
//pie区域背景色
//plotAreaColor: "cyan"
//theme: ChartView.ChartThemeQt
//padding-plotarea区域外边圆角
backgroundRoundness:0
margins{
left:0
right:0
top:0
bottom:0
}
legend{
visible: false
}
//抗锯齿
antialiasing: true
PieSeries {
id:pie
size: 0.9
holeSize: 0.6
startAngle: 10
endAngle: 360+10
property string defaultNameText: ""
property string defaultValueText: ""
//hover选中
//问题:不能配合mousearea使用,而且mousearea只能穿透/传递点击没有hover
onHovered: {
if(state){
//slice,state
name_text.text=slice.label;
//占比保留一位小数
value_text.text=Number(slice.value*100/pie.sum).toFixed(1)+"%";
}else{
name_text.text=defaultNameText;
//占比保留一位小数
value_text.text=defaultValueText;
}
}
}
Column{
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Text {
id: name_text
anchors.horizontalCenter: parent.horizontalCenter
//text: pie.defaultNameText
font.pixelSize: 12
font.family: "Microsoft YaHei UI"
//font.weight: Font.DemiBold
color: "white"
}
Text {
id: value_text
anchors.horizontalCenter: parent.horizontalCenter
//text: pie.defaultValueText
font.pixelSize: 16
font.family: "Microsoft YaHei UI"
//font.weight: Font.DemiBold
color: "white"
}
}
}
//根据{json}数据刷新
//"data"数据为slice数组
//"default"数据为默认展示对应的"name"
function updateChart(args){
pie.clear(); //清除之前数据
if(args["data"]){
var arg_data=args["data"];
for(var index in arg_data){
appendSlice(arg_data[index]);
}
}
if(args["default"]){
defaultSlice(args["default"]);
}
}
//添加slice,参数{json}
function appendSlice(args){
//添加新slice(label,value)
var newslice=pie.append(args["name"]||"noname",args["value"]||0);
newslice.color=args["color"]||"white";
newslice.borderWidth= 0;
newslice.borderColor= args["borderColor"]||args["color"]||"black";
//exploded距离
newslice.explodeDistanceFactor= args["explodeDistanceFactor"]||0.1;
newslice.hovered.connect(function(state){
newslice.exploded=state;
});
}
//设置默认展示的slice(slice.label)
function defaultSlice(name){
var theslice=pie.find(name);
if(theslice){
pie.defaultNameText=name_text.text=theslice.label;
pie.defaultValueText=value_text.text=Number(theslice.value*100/pie.sum).toFixed(1)+"%";
}
}
//测试用数据
Component.onCompleted: {
updateChart({
"data":[{
"name":"已完成",
"value":23,
"color":"#40a9af"
},{
"name":"其他",
"value":77,
"color":"#57d8de"
}],
"default":"已完成"
});
}
}
[2/3]
//NightingaleRose.qml
import QtQuick 2.0
import QtCharts 2.2
Item {
height: 140
width: 140
ChartView{
id: chart
anchors.fill: parent
backgroundColor: "transparent"
//theme: ChartView.ChartThemeBrownSand
animationDuration: 500
animationOptions:ChartView.SeriesAnimations
antialiasing: true
legend{
visible: false
}
backgroundRoundness:0
margins{
left:0
right:0
top:0
bottom:0
}
Rectangle {
//不用透明图片方便复制粘贴代码
id:value_area
visible: value_text.visible
anchors.centerIn: parent
width: 60
height: 30
color: "black"
opacity: 0.4
radius: 3
}
Text {
id: value_text
visible: false
anchors.centerIn: parent
font.pixelSize: 14
font.family: "Microsoft YaHei UI"
//font.underline: true
//font.weight: Font.DemiBold
color: "white"
}
}
//偷个懒,不用自己计算
//保留一个隐藏的pie来计算角度
ChartView{
//anchors.fill: parent
visible: false
PieSeries {
id: pie
/*onHovered: {
size=state?0.8:0.5
}
PieSlice {
labelColor: "red"
labelVisible: true
//labelPosition: PieSlice.LabelInsideNormal
label: "eaten"
value: 94.9
}
*/
}
}
//根据{json}数据刷新
//"data"数据为slice数组
function updateChart(args){
chart.removeAllSeries(); //清除之前数据
if(args["data"]){
var arg_data=args["data"];
//console.log(arg_data);
arg_data.sort(compare("value"));
updatePie(arg_data);
arg_data.reverse();
for(var index in arg_data){
//console.log(arg_data[index]["name"],arg_data[index]["value"])
appendSlice(arg_data[index],index);
}
}
}
//先把参数给隐藏的pie计算角度
function updatePie(args){
pie.clear();
for(var index in args){
var newslice=pie.append(args[index]["name"]||"noname",args[index]["value"]||0);
}
}
//添加slice,参数{json}
function appendSlice(args,cut){
//默认的pie不能每个slice一个半径,so每个slice单独一个pie
var newpie = chart.createSeries(ChartView.SeriesTypePie, args["name"]||"noname");
newpie.size=0.4+cut/20 //0.6
newpie.holeSize=0
var spanpie=pie.find(args["name"]||"noname")
//startAngle没效果,且生成得slice得startAngle打印位undefine
//so添加一个slice来挤过去
//但是这个slice设置透明会使黑色,所以得先添加角度最大得slice
//newpie.sartAngle=spanpie.startAngle
newpie.endAngle=spanpie.startAngle+spanpie.angleSpan
newpie.hovered.connect(function(aslice,state){
//console.log(state)
if(state){
newpie.size+=0.03;
}else{
newpie.size-=0.03;
}
});
var normallice=newpie.append(args["name"]||"noname",spanpie.startAngle);
normallice.color="black";
normallice.borderColor= "black";
var newslice=newpie.append(args["name"]||"noname",spanpie.angleSpan);
newslice.color=args["color"]||"white";
newslice.borderWidth= 0;
newslice.borderColor= args["borderColor"]||args["color"]||"black";
//newslice.label=(args["name"]||"noname")+":"+Number(spanpie.percentage*100).toFixed(1)+"%";
//newslice.labelPosition=PieSlice.LabelInsideHorizontal;
newslice.labelArmLengthFactor=0;
newslice.labelFont.pixelSize=12;
newslice.labelVisible=true;
newslice.labelColor= "white";
newslice.hovered.connect(function(state){
value_text.visible=state
if(state){
value_text.text=Number(spanpie.percentage*100).toFixed(1)+"%";
newslice.borderColor=Qt.lighter(args["color"]||"white");
newslice.labelColor= args["color"]||"white";
}else{
newslice.borderColor=args["color"]||"white";
newslice.labelColor= "white";
}
});
}
//数组排序比较
function compare(propertyname){
return function(a,b){
var value1 = a[propertyname]||0;
var value2 = b[propertyname]||0;
return value2 - value1;
}
}
测试用数据
Component.onCompleted: {
updateChart({
"data":[{
"name":"巴山监测站",
"value":45,
"color":"#db497d"
},{
"name":"宁阳监测站",
"value":35,
"color":"#ffc95f"
},{
"name":"高山监测站",
"value":20,
"color":"#83f5f5"
}]
});
}
}
[3/3]
//main.qml
import QtQuick 2.2
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
DoughnutPie{
anchors.fill: parent
}
/*NightingaleRose{
anchors.fill: parent
}*/
}