QML:ChartView 系列的自定义可拖动点委托
QML: Custom draggable point delegate for ChartView series
是否可以在 Qt 图表或其他第 3 方库中为可拖动点(即 Item
s、图标、Rectangle
s 等)使用自定义委托,就像在 Qt 中很容易实现一样MapItemView
、MapQuickItem
和他们的 delegate
属性 的位置?或者可以为此目的使用 PathView
和 ChartView
的某些组合? ChartView
内的 PathView
可能是一个解决方案,但可能需要将图表坐标转换为屏幕坐标:也不确定 ChartView
是否有此方法。需要制作一些模型来检查这一点。没有找到任何有关的文档或样本。应该是显而易见和简单的,就像它在 Qt Location 中实现的一样,但是由于某些原因它没有为 Qt Charts 实现。
理想的方式可能是 Map
使用 Qt Location 中我需要的所有元素:MapItemView
、MapQuickItem
和 MapPolygon
或 MapPolyline
图形,因为我有要绘制的制图信息。但又是 2 个问题:
- 如何画轴:X - 距离公里,Y - 高度
- 如何绘制图表网格
- 如何绘制自定义 2D BarSeries 模拟地形高程(但是可以
MapPolygon
)。
是否可以在这里动态绘制一些自定义地图?有什么想法吗?
使用 ChartView
和 Repeater
内部和 ChartView
映射函数找到简单而优雅的解决方案:mapToPosition
和 mapToValue
映射图表<->屏幕轻松协调。
模型:
import QtQuick 2.12
import QtCharts 2.3
Item {
visible: true
width: 640
height: 480
ChartView {
id: chart
anchors.fill: parent
antialiasing: true
ValueAxis {
id: xAxis
min: 0
max: 1100
tickCount: 12
labelFormat: "%.0f"
}
ValueAxis {
id: yAxis
min: -50
max: 200
tickInterval: 50
labelFormat: "%.0f"
}
ListModel {
id: lineModel
ListElement { x: 50; y: 155; }
ListElement { x: 138; y: 175 }
ListElement { x: 193; y: 50 }
ListElement { x: 271; y: 90 }
ListElement { x: 295; y: 90 }
ListElement { x: 383; y: 150 }
ListElement { x: 529; y: 100 }
ListElement { x: 665; y: 150 }
ListElement { x: 768; y: 90 }
ListElement { x: 794; y: 90 }
ListElement { x: 851; y: 50 }
ListElement { x: 875; y: 50 }
ListElement { x: 925; y: 175 }
ListElement { x: 1060; y: 125 }
}
ListModel {
id: areaModel
ListElement { x: 0; y: 100 }
ListElement { x: 138; y: 125 }
ListElement { x: 193; y: 0 }
ListElement { x: 271; y: 40 }
ListElement { x: 295; y: 40 }
ListElement { x: 383; y: 100 }
ListElement { x: 529; y: 50 }
ListElement { x: 665; y: 100 }
ListElement { x: 768; y: 40 }
ListElement { x: 794; y: 40 }
ListElement { x: 851; y: 0 }
ListElement { x: 875; y: 0 }
ListElement { x: 925; y: 125 }
ListElement { x: 1060; y: 75 }
ListElement { x: 1100; y: 60 }
}
AreaSeries {
name: "Terrain"
axisX: xAxis
axisY: yAxis
borderColor: color
upperSeries: LineSeries {
id: areaSeries
}
}
LineSeries {
id: lineSeries
name: "Flying path"
axisX: xAxis
axisY: yAxis
}
function adjustPosition(item, index) {
let point = Qt.point(lineModel.get(index).x, lineModel.get(index).y)
let position = chart.mapToPosition(point, lineSeries)
item.x = position.x - item.width / 2
item.y = position.y - item.height / 2
}
function adjustValue(item, index) {
let position = Qt.point(item.x + item.width / 2, item.y + item.height / 2)
let point = chart.mapToValue(position, lineSeries)
lineModel.setProperty(index, "y", point.y) // Change only Y-coordinate
lineSeries.replace(lineSeries.at(index).x, lineSeries.at(index).y, // old
lineSeries.at(index).x, point.y) // new
}
Repeater {
model: lineModel
Rectangle {
id: indicator
radius: 100
width: radius / 2
height: width
color: "red"
property real parentWidth: chart.width
property real parentHeight: chart.height
onParentWidthChanged: chart.adjustPosition(this, index)
onParentHeightChanged: chart.adjustPosition(this, index)
onYChanged: {
if(mouseArea.drag.active) {
chart.adjustValue(this, index)
}
}
Image {
id: waypoint
anchors.centerIn: parent
source: index ? "qrc:/waypoint.svg" : "qrc:/home.svg"
}
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: indicator
drag.axis: Drag.YAxis
preventStealing: true
}
}
}
Component.onCompleted: {
lineSeries.clear()
areaSeries.clear()
for(let i = 0; i < lineModel.count; i++) {
lineSeries.append(lineModel.get(i).x, lineModel.get(i).y)
}
for(let j = 0; j < areaModel.count; j++) {
areaSeries.append(areaModel.get(j).x, areaModel.get(j).y)
}
}
}
}
欢迎任何改进、优化和建议,例如通过 HXYModelMapper
/VXYModelMapper
而非 JS 模型填充进行模型绑定。将修正答案。
结果截图:
是否可以在 Qt 图表或其他第 3 方库中为可拖动点(即 Item
s、图标、Rectangle
s 等)使用自定义委托,就像在 Qt 中很容易实现一样MapItemView
、MapQuickItem
和他们的 delegate
属性 的位置?或者可以为此目的使用 PathView
和 ChartView
的某些组合? ChartView
内的 PathView
可能是一个解决方案,但可能需要将图表坐标转换为屏幕坐标:也不确定 ChartView
是否有此方法。需要制作一些模型来检查这一点。没有找到任何有关的文档或样本。应该是显而易见和简单的,就像它在 Qt Location 中实现的一样,但是由于某些原因它没有为 Qt Charts 实现。
理想的方式可能是 Map
使用 Qt Location 中我需要的所有元素:MapItemView
、MapQuickItem
和 MapPolygon
或 MapPolyline
图形,因为我有要绘制的制图信息。但又是 2 个问题:
- 如何画轴:X - 距离公里,Y - 高度
- 如何绘制图表网格
- 如何绘制自定义 2D BarSeries 模拟地形高程(但是可以
MapPolygon
)。
是否可以在这里动态绘制一些自定义地图?有什么想法吗?
使用 ChartView
和 Repeater
内部和 ChartView
映射函数找到简单而优雅的解决方案:mapToPosition
和 mapToValue
映射图表<->屏幕轻松协调。
模型:
import QtQuick 2.12
import QtCharts 2.3
Item {
visible: true
width: 640
height: 480
ChartView {
id: chart
anchors.fill: parent
antialiasing: true
ValueAxis {
id: xAxis
min: 0
max: 1100
tickCount: 12
labelFormat: "%.0f"
}
ValueAxis {
id: yAxis
min: -50
max: 200
tickInterval: 50
labelFormat: "%.0f"
}
ListModel {
id: lineModel
ListElement { x: 50; y: 155; }
ListElement { x: 138; y: 175 }
ListElement { x: 193; y: 50 }
ListElement { x: 271; y: 90 }
ListElement { x: 295; y: 90 }
ListElement { x: 383; y: 150 }
ListElement { x: 529; y: 100 }
ListElement { x: 665; y: 150 }
ListElement { x: 768; y: 90 }
ListElement { x: 794; y: 90 }
ListElement { x: 851; y: 50 }
ListElement { x: 875; y: 50 }
ListElement { x: 925; y: 175 }
ListElement { x: 1060; y: 125 }
}
ListModel {
id: areaModel
ListElement { x: 0; y: 100 }
ListElement { x: 138; y: 125 }
ListElement { x: 193; y: 0 }
ListElement { x: 271; y: 40 }
ListElement { x: 295; y: 40 }
ListElement { x: 383; y: 100 }
ListElement { x: 529; y: 50 }
ListElement { x: 665; y: 100 }
ListElement { x: 768; y: 40 }
ListElement { x: 794; y: 40 }
ListElement { x: 851; y: 0 }
ListElement { x: 875; y: 0 }
ListElement { x: 925; y: 125 }
ListElement { x: 1060; y: 75 }
ListElement { x: 1100; y: 60 }
}
AreaSeries {
name: "Terrain"
axisX: xAxis
axisY: yAxis
borderColor: color
upperSeries: LineSeries {
id: areaSeries
}
}
LineSeries {
id: lineSeries
name: "Flying path"
axisX: xAxis
axisY: yAxis
}
function adjustPosition(item, index) {
let point = Qt.point(lineModel.get(index).x, lineModel.get(index).y)
let position = chart.mapToPosition(point, lineSeries)
item.x = position.x - item.width / 2
item.y = position.y - item.height / 2
}
function adjustValue(item, index) {
let position = Qt.point(item.x + item.width / 2, item.y + item.height / 2)
let point = chart.mapToValue(position, lineSeries)
lineModel.setProperty(index, "y", point.y) // Change only Y-coordinate
lineSeries.replace(lineSeries.at(index).x, lineSeries.at(index).y, // old
lineSeries.at(index).x, point.y) // new
}
Repeater {
model: lineModel
Rectangle {
id: indicator
radius: 100
width: radius / 2
height: width
color: "red"
property real parentWidth: chart.width
property real parentHeight: chart.height
onParentWidthChanged: chart.adjustPosition(this, index)
onParentHeightChanged: chart.adjustPosition(this, index)
onYChanged: {
if(mouseArea.drag.active) {
chart.adjustValue(this, index)
}
}
Image {
id: waypoint
anchors.centerIn: parent
source: index ? "qrc:/waypoint.svg" : "qrc:/home.svg"
}
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: indicator
drag.axis: Drag.YAxis
preventStealing: true
}
}
}
Component.onCompleted: {
lineSeries.clear()
areaSeries.clear()
for(let i = 0; i < lineModel.count; i++) {
lineSeries.append(lineModel.get(i).x, lineModel.get(i).y)
}
for(let j = 0; j < areaModel.count; j++) {
areaSeries.append(areaModel.get(j).x, areaModel.get(j).y)
}
}
}
}
欢迎任何改进、优化和建议,例如通过 HXYModelMapper
/VXYModelMapper
而非 JS 模型填充进行模型绑定。将修正答案。
结果截图: