简单的条形图
在 QML 应用程序中使用Bars3D 。
“简单条形图”演示了如何使用Bars3D 和QML制作一个简单的3D条形图。

以下各节将介绍如何切换数据系列以及如何同时显示多个数据系列。有关 QML 应用程序基本功能的更多信息,请参阅《简单的散点图》。
运行示例
您可以通过以下方式运行示例:
- Qt Creator
打开Welcome 模式,并从Examples 中选择该示例。有关更多信息,请参阅Qt Creator :教程:构建和运行。
- Qt Extension for Visual Studio Code
在Command Palette 中运行Qt: Open Qt examples 命令,然后从列表中选择该示例。有关更多信息,请参阅Qt Extension for Visual Studio Code :教程:构建和运行。
数据
示例数据集是一家虚构公司数年间的月度收支情况。该数据在Data.qml 中以列表模型定义如下:
ListModel {
id: dataModel
ListElement{ timestamp: "2016-01"; expenses: "-4"; income: "5" }
ListElement{ timestamp: "2016-02"; expenses: "-5"; income: "6" }
ListElement{ timestamp: "2016-03"; expenses: "-7"; income: "4" }
...每个数据项具有三个角色:时间戳、收入和支出。时间戳的格式为:<four digit year>-<two digit month> 。通常,您会将年和月映射到柱状图的行和列,但作为数值显示的只能是收入或支出其中之一。
现在,将数据添加到Bars3D 图表中。在其内部创建两个Bar3DSeries ,首先创建一个用于收入的系列:
Bar3DSeries {
id: barSeries
itemLabelFormat: "Income, @colLabel, @rowLabel: @valueLabel"
baseGradient: barGradient
ItemModelBarDataProxy {
id: modelProxy
itemModel: graphData.model
rowRole: "timestamp"
columnRole: "timestamp"
valueRole: "income"
rowRolePattern: /^(\d\d\d\d).*$/
columnRolePattern: /^.*-(\d\d)$/
rowRoleReplace: "\\1"
columnRoleReplace: "\\1"
multiMatchBehavior: ItemModelBarDataProxy.MultiMatchBehavior.Cumulative
}
...数据绑定到该系列内ItemModelBarDataProxy 的itemModel 属性上。对于valueRole ,请指定income 字段,因为该字段包含所需的值。获取年份和月份则稍显复杂,因为它们都位于同一个字段中。 要提取这些值,请分别针对rowRole 和columnRole 指定timestamp 字段。此外,还需为这些角色指定搜索模式和替换规则,以便为每个角色提取字段内容中的正确部分。搜索模式是标准的JavaScript正则表达式,而替换规则则指定了匹配该正则表达式的字段内容将被替换为什么。 在此情况下,将整个字段内容替换为仅包含年份或月份的部分,即行和列中捕获的第一个子字符串。有关使用正则表达式进行替换功能的更多信息,请参阅QString::replace(const QRegExp &rx, constQString &after) 函数的文档。
multiMatchBehavior 属性指定了当多个项目模型项匹配同一行/列组合时应如何处理。在此情况下,将它们的值相加。在显示各月的值时,该属性无效,因为我们的项目模型中不存在重复的月份。但在后续需要显示年度总计时,该属性便变得重要。
接着,再添加一个代表支出的系列:
Bar3DSeries {
id: secondarySeries
visible: false
itemLabelFormat: "Expenses, @colLabel, @rowLabel: -@valueLabel"
baseGradient: secondaryGradient
ItemModelBarDataProxy {
id: secondaryProxy
itemModel: graphData.model
rowRole: "timestamp"
columnRole: "timestamp"
valueRole: "expenses"
rowRolePattern: /^(\d\d\d\d).*$/
columnRolePattern: /^.*-(\d\d)$/
valueRolePattern: /-/
rowRoleReplace: "\\1"
columnRoleReplace: "\\1"
multiMatchBehavior: ItemModelBarDataProxy.MultiMatchBehavior.Cumulative
}
...模型中的支出以负值形式存储,但为了便于与收入柱状图进行比较,可以将其显示为正值柱状图。使用 `valueRolePattern ` 属性去除负号,且无需指定替换字符串,因为默认替换字符串为空字符串。
暂时使用该系列的visible 属性来隐藏第二个系列。
自定义坐标轴标签
Axes.qml 重新定义了柱形轴的类别标签,因为数据中包含月份的数字,这会导致标签过于拥挤:
Category3DAxis {
id: columnAxis
labels: ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"]
labelAutoAngle: 30
}为了在低视角下提高坐标轴标签的可读性,请设置自动坐标轴标签旋转。
切换数据系列
在 `main.qml` 中,设置图表及各种 UI 元素。这里有三个值得关注的有趣代码块。第一个代码块展示了如何通过简单地更改两个系列的可见性,在收入、支出以及两者兼有之间切换可视化数据:
onClicked: {
if (text === "Show Expenses") {
barSeries.visible = false
secondarySeries.visible = true
barGraph.valueAxis.labelFormat = "-%.2f M\u20AC"
secondarySeries.itemLabelFormat = "Expenses, @colLabel, @rowLabel: @valueLabel"
text = "Show Both"
} else if (text === "Show Both") {
barSeries.visible = true
barGraph.valueAxis.labelFormat = "%.2f M\u20AC"
secondarySeries.itemLabelFormat = "Expenses, @colLabel, @rowLabel: -@valueLabel"
text = "Show Income"
} else { // text === "Show Income"
secondarySeries.visible = false
text = "Show Expenses"
}
}对坐标轴标签格式和项目选择标签格式进行了微调,以确保支出(实际上被解析为正值)的负号能正确显示。
第二个值得关注的代码块展示了如何通过调整代理属性来更改可视化数据:
onClicked: {
if (text === "Show yearly totals") {
modelProxy.autoRowCategories = true
secondaryProxy.autoRowCategories = true
modelProxy.columnRolePattern = /^.*$/
secondaryProxy.columnRolePattern = /^.*$/
graphAxes.value.autoAdjustRange = true
barGraph.columnAxis = graphAxes.total
text = "Show all years"
} else if (text === "Show all years") {
modelProxy.autoRowCategories = true
secondaryProxy.autoRowCategories = true
modelProxy.columnRolePattern = /^.*-(\d\d)$/
secondaryProxy.columnRolePattern = /^.*-(\d\d)$/
graphAxes.value.min = 0
graphAxes.value.max = 35
barGraph.columnAxis = graphAxes.column
text = "Show 2020 - 2022"
} else { // text === "Show 2020 - 2022"
// Explicitly defining row categories, since we do not want to show data for
// all years in the model, just for the selected ones.
modelProxy.autoRowCategories = false
secondaryProxy.autoRowCategories = false
modelProxy.rowCategories = ["2020", "2021", "2022"]
secondaryProxy.rowCategories = ["2020", "2021", "2022"]
text = "Show yearly totals"
}
}要显示年度总计,需将每年的十二个月合并为单根柱状条。这通过指定与所有模型项匹配的“columnRolePattern ”来实现。这样,该系列将仅包含一列。此前为代理指定的累加属性“multiMatchBehavior ”此时便发挥作用,使每年十二个月的数值相加,形成单根柱状条。
若要仅显示部分年份,请将“ItemModelBarDataProxy ”项的autoRowCategories 设置为false,并显式定义行类别。这样,只有指定行类别中的项目才会被可视化。
第三个有趣的代码块演示了如何在已知行和列值的情况下,通过ItemModelBarDataProxy 方法rowCategoryIndex() 和columnCategoryIndex() 获取项目的行和列索引:
var timestamp = graphData.model.get(mainview.currentRow).timestamp
var pattern = /(\d\d\d\d)-(\d\d)/
var matches = pattern.exec(timestamp)
var rowIndex = modelProxy.rowCategoryIndex(matches[1])
var colIndex
if (barGraph.columnAxis == graphAxes.total)
colIndex = 0 // Just one column when showing yearly totals
else
colIndex = modelProxy.columnCategoryIndex(matches[2])
if (selectedSeries.visible)
mainview.selectedSeries.selectedBar = Qt.point(rowIndex, colIndex)
else if (barSeries.visible)
barSeries.selectedBar = Qt.point(rowIndex, colIndex)
else
secondarySeries.selectedBar = Qt.point(rowIndex, colIndex)© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.