如何将 qml ScatterSeries 添加到现有的 qml 定义的 ChartView?
Posted
技术标签:
【中文标题】如何将 qml ScatterSeries 添加到现有的 qml 定义的 ChartView?【英文标题】:How to add qml ScatterSeries to existing qml defined ChartView? 【发布时间】:2019-08-17 12:47:01 【问题描述】:我正在尝试将系列按需添加到使用 python 在 qml 中定义的现有 ChartView 中。我找到了一个示例,它展示了如何在 C++ 中执行此操作(取自 https://doc.qt.io/archives/qt-5.11/qml-qtcharts-chartview.html#createSeries-method):
// lineSeries is a LineSeries object that has already been added to the ChartView; re-use its axes
var myAxisX = chartView.axisX(lineSeries);
var myAxisY = chartView.axisY(lineSeries);
var scatter = chartView.createSeries(ChartView.SeriesTypeScatter, "scatter series", myAxisX, myAxisY);
但我找不到在 python 中执行此操作的方法。以下是我目前尝试的一些 sn-ps:
QML sn-p(最初只有 1 个散点系列):
ChartView
id: bscan0
ScatterSeries
id: hits0
axisX: ValueAxis
id: bscan0_xAxix
min: 0
max: 10
axisY: ValueAxis
id: bscan0_yAxis
min: -105
max: 1
QML js 函数将 chartView 传递给 python,以便它可以添加另一个系列:
dataModel.addChartSeries(bscan0, hits0)
addChartSeries 的 Python sn-p:
@Slot(QObject, QObject)
def addChartSeries(self, chartView, chart_series):
myAxisX = chartView.axisX(chart_series) # reuse the axis from existing series
myAxisY = chartView.axisY(chart_series) # reuse the axis from existing series
# This function equivalent to the c++ one doesn't exit
# myChartSeries = chartView.createSeries(QtCharts.SeriesTypeScatter, "scatter series", myAxisX, myAxisY)
# So try another way:
myChartSeries = QtCharts.QScatterSeries()
myChartSeries.setName("scatter series")
myChartSeries.attachAxis(myAxisX)
myChartSeries.attachAxis(myAxisY)
myChartSeries.append(5, -10)
myChartSeries.append(5, -20)
myChartSeries.append(5, -30)
# Try to get chart from existing series. Doesn't work
# Error says that chart_series is not in a chart (it is!)
# myChart = chart_series.chart()
# Series not in the chart. Please addSeries to chart first.
# Try to get chart from chartview passed across. Doesn't work
# Error says that object has no .chart attribute (same for .chart and .chart()):
# myChart = chartView.chart
# Error: 'PySide2.QtQuick.QQuickItem' object has no attribute 'chart'
# It seems I must add series to chart like this, not to chartView,
# but I can't find a way to get the chart for the chartView.
# myChart.addSeries(myChartSeries)
上面的python函数在我的类“dataModel”中,我像这样传递给QML(数据模型类可以很好地用于我用它做的许多其他事情,所以没有问题):
dataModel = DataModel()
self.rootContext().setContextProperty("dataModel", dataModel)
【问题讨论】:
【参考方案1】:QML Charts API 是基于 C++ 使用的 API 编写的,但并不相同,例如 ChartView 是一个不暴露 QChart 的 QQuickItem,不像 QChartView 是一个 QGraphicsView(QWidget),如果它暴露 QChart ,该系列的内容是什么。总之,您将无法使用 C++(Python) 类与 QML 进行交互。
您在开头显示的示例不是针对 C++ 的示例,而是针对 QML 的示例,因此无法直接将其转换为 QML。也不可能直接使用 QtCharts 类在 C++/Python 中创建 QML 系列,一种可能的策略是使用可以评估 QML 元素并将其返回给 C++/Python 的 QQmlExpression。此外,createSeries() 方法不仅添加了系列,还连接了信号。
from enum import Enum, auto
from PySide2 import QtCore, QtGui, QtWidgets, QtQml
# https://code.qt.io/cgit/qt/qtcharts.git/tree/src/chartsqml2/declarativechart_p.h#n105
class SeriesType(Enum):
SeriesTypeLine = 0
SeriesTypeArea = auto()
SeriesTypeBar = auto()
SeriesTypeStackedBar = auto()
SeriesTypePercentBar = auto()
SeriesTypePie = auto()
SeriesTypeScatter = auto()
SeriesTypeSpline = auto()
SeriesTypeHorizontalBar = auto()
SeriesTypeHorizontalStackedBar = auto()
SeriesTypeHorizontalPercentBar = auto()
SeriesTypeBoxPlot = auto()
SeriesTypeCandlestick = auto()
class DataModel(QtCore.QObject):
def __init__(self, engine, parent=None):
super().__init__(parent)
self.m_engine = engine
@QtCore.Slot(QtCore.QObject, QtCore.QObject, QtCore.QObject, result=QtCore.QObject)
def addChartSeries(self, chart_view, chart_axis_x, chart_axis_y):
context = QtQml.QQmlContext(self.m_engine.rootContext())
context.setContextProperty("chart_view", chart_view)
context.setContextProperty("axis_x", chart_axis_x)
context.setContextProperty("axis_y", chart_axis_y)
context.setContextProperty("type", SeriesType.SeriesTypeScatter.value)
script = """chart_view.createSeries(type, "scatter series", axis_x, axis_y);"""
expression = QtQml.QQmlExpression(context, chart_view, script)
serie, valueIsUndefined = expression.evaluate()
if expression.hasError():
print(expression.error())
return
import random
mx, Mx = chart_axis_x.property("min"), chart_axis_x.property("max")
my, My = chart_axis_y.property("min"), chart_axis_y.property("max")
if not valueIsUndefined:
for _ in range(100):
x = random.uniform(mx, Mx)
y = random.uniform(my, My)
serie.append(x, y)
# https://doc.qt.io/qt-5/qml-qtcharts-scatterseries.html#borderColor-prop
serie.setProperty("borderColor", QtGui.QColor("salmon"))
# https://doc.qt.io/qt-5/qml-qtcharts-scatterseries.html#brush-prop
serie.setProperty("brush", QtGui.QBrush(QtGui.QColor("green")))
# https://doc.qt.io/qt-5/qml-qtcharts-scatterseries.html#borderColor-prop
serie.setProperty("borderWidth", 4.0)
return serie
if __name__ == "__main__":
import os
import sys
current_dir = os.path.dirname(os.path.realpath(__file__))
app = QtWidgets.QApplication(sys.argv)
engine = QtQml.QQmlApplicationEngine()
dataModel = DataModel(engine)
engine.rootContext().setContextProperty("dataModel", dataModel)
file = os.path.join(current_dir, "main.qml")
engine.load(QtCore.QUrl.fromLocalFile(file))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtCharts 2.2
ApplicationWindow
visible: true
width: 640
height: 480
ChartView
anchors.fill: parent
id: bscan0
ValueAxis
id: bscan0_xAxix
min: 0
max: 10
ValueAxis
id: bscan0_yAxis
min: -100
max: 100
Component.onCompleted:
var serie = dataModel.addChartSeries(bscan0, bscan0_xAxix, bscan0_yAxis)
虽然一般的策略是用QML创建系列,用C++/Python填充,例如:
import random
from PySide2 import QtCore, QtGui, QtWidgets, QtQml, QtCharts
class DataModel(QtCore.QObject):
@QtCore.Slot(QtCharts.QtCharts.QAbstractSeries)
def fill_serie(self, serie):
mx, Mx = 0, 10
my, My = -100, 100
for _ in range(100):
x = random.uniform(mx, Mx)
y = random.uniform(my, My)
serie.append(x, y)
# https://doc.qt.io/qt-5/qml-qtcharts-scatterseries.html#borderColor-prop
serie.setProperty("borderColor", QtGui.QColor("salmon"))
# https://doc.qt.io/qt-5/qml-qtcharts-scatterseries.html#brush-prop
serie.setProperty("brush", QtGui.QBrush(QtGui.QColor("green")))
# https://doc.qt.io/qt-5/qml-qtcharts-scatterseries.html#borderColor-prop
serie.setProperty("borderWidth", 4.0)
if __name__ == "__main__":
import os
import sys
current_dir = os.path.dirname(os.path.realpath(__file__))
app = QtWidgets.QApplication(sys.argv)
engine = QtQml.QQmlApplicationEngine()
dataModel = DataModel(engine)
engine.rootContext().setContextProperty("dataModel", dataModel)
file = os.path.join(current_dir, "main.qml")
engine.load(QtCore.QUrl.fromLocalFile(file))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtCharts 2.2
ApplicationWindow
visible: true
width: 640
height: 480
ChartView
anchors.fill: parent
id: bscan0
ValueAxis
id: bscan0_xAxix
min: 0
max: 10
ValueAxis
id: bscan0_yAxis
min: -100
max: 100
Component.onCompleted:
var serie = bscan0.createSeries(ChartView.SeriesTypeScatter, "scatter series", bscan0_xAxix, bscan0_yAxis);
dataModel.fill_serie(serie)
【讨论】:
这看起来很有用,谢谢。我将尝试按照您的示例在 qml 中创建系列。在加载数据模型之前,我不知道需要多少个系列,但看起来我可以选择在 qml 中使用您的示例的变体创建 n 个新系列。理想情况下,如果用户重新加载新数据,我需要删除并重新创建它们。我猜有一种方法可以删除它们,然后将其附加到加载按钮?一旦我得到它的工作,会告诉你我是怎么做的,并将它标记为接受的答案。 @BobbyG 我的答案是针对您当前的问题,而不是针对您的项目,我不知道您的项目还有什么要求/限制,所以我不能向您保证任何事情。请记住,在 SO 中,我们不会在项目中提供帮助(我们对此非常有限),仅针对特定问题,恕我直言,接受我的答案不应该取决于您的项目。 同意。只是想尝试一下并确保我理解。以上是关于如何将 qml ScatterSeries 添加到现有的 qml 定义的 ChartView?的主要内容,如果未能解决你的问题,请参考以下文章