本想用qtchart来做,但是用了一下发现限制太多不太满足需求,所以使用自定义的方式实现,支持图例数量变化,标签数量变化,值为0时不显示
内容过长可滑动查看
用的都是基础控件,思路是把一个标签中的每个柱子视为重复项,用repeater构建,把柱状图中每个标签的所有内容(柱子)整体视为一个重复项,用repeater构建.
主要难点在计算位置上,对柱子xy值的计算,标签位置居中的计算.以及整个内容所占的宽度,具体解释在代码注释中.
注意约束,vDataModel中data里的元素的数量要和vLegendModel的元素数量相同:
import QtQuick 2.9
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2
Rectangle{
property var vDataModel: [
{"text":"公交", "data":[20, 6, 0, 4]},
{"text":"地铁", "data":[5, 6, 1, 4]},
{"text":"飞机", "data":[5, 6, 1, 4]},
{"text":"高铁", "data":[5, 6, 1, 4]},
{"text":"出租", "data":[5, 6, 1, 4]},
{"text":"火箭", "data":[5, 6, 1, 4]},
{"text":"飞船", "data":[5, 6, 1, 4]},
{"text":"其它", "data":[5, 6, 1, 4]}
]
property var vLegendModel: [
{"text":"首次出行选择", "color":"#ff8040"},
{"text":"第二次出行选择", "color":"#008000"},
{"text":"第三次出行选择", "color":"#0080ff"},
{"text":"未选择", "color":"#ff0000"}
]
width: parent.width
height: 480 - 45 - 40 + 10
color: "transparent"
Rectangle{
id:verticalLine
anchors.left: parent.left
anchors.leftMargin: 20
anchors.top: parent.top
anchors.topMargin: 20
width: 1
height: 330
color: "black"
}
Rectangle{
id:horizontalLine
anchors.left: verticalLine.right
anchors.top: verticalLine.bottom
width: parent.width - legnedRect.width - 10 - 40
height: 1
color: "black"
}
ScrollView{//标签 柱状图中有很多标签, 一个标签(30 * 柱子数 - 10) 标签之间70, 因此内容宽度 = 标签数 * (30 * 柱子数 + 60) - 70
anchors.left: verticalLine.right
anchors.leftMargin: 20
anchors.top: verticalLine.top
width: horizontalLine.width - 20
height: verticalLine.height + 36
contentWidth: vDataModel.length * (30 * vDataModel[0].data.length + 60) - 70
contentHeight: height
clip: true
Row{
spacing: 70
Repeater{
model:vDataModel
//柱子 一个标签中有很多柱子, 一个柱子20 柱子之间10间隙, 因此宽度 = 30 * 柱子数 - 10
Rectangle{
width: 30 * vDataModel[index].data.length - 10
height: verticalLine.height
x: index * (width + 70)//初始x:0 index:0, 每加一个标签右移width + 70
y: 0
color: "transparent"
Row{
anchors.fill: parent
spacing: 10
Repeater{
model: vDataModel[index].data
delegate: Rectangle{
x: index * (width + 10)
y: verticalLine.height - height
width: 20
height: modelData * 15
color: vLegendModel[index].color
visible: modelData != 0
Text{
anchors.bottom: parent.top
anchors.bottomMargin: 5
anchors.horizontalCenter: parent.horizontalCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: 15
font.family: "Droid Sans Fallback"
text: String(modelData)
}
}
}
}
Text{
x: (30 * vDataModel[index].data.length - 10 - width) / 2
y: verticalLine.height + 1 + 5
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 15
font.family: "Droid Sans Fallback"
text: vDataModel[index].text
}
}
}
}
}
//图例, 自动排版
Rectangle{
id:legnedRect
width: 130
height: legnedRepeater.count * 25
anchors.right: parent.right
anchors.rightMargin: 10
anchors.verticalCenter: parent.verticalCenter
color: "transparent"
Column{
anchors.fill: parent
spacing: 5
Repeater{
id:legnedRepeater
model: vLegendModel
Rectangle{
width: 130
height: 20
color: "transparent"
Rectangle{
id:rect
anchors.left: parent.left
anchors.leftMargin: 10
width: 10
height: 10
color: modelData.color
anchors.verticalCenter: parent.verticalCenter
}
Text {
anchors.left: rect.right
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: 13
font.family: "Droid Sans Fallback"
text: modelData.text
}
}
}
}
}
}