如何将 qml ScatterSeries 添加到现有的 qml 定义的 ChartView?
How to add qml ScatterSeries to existing qml defined ChartView?
我正在尝试使用 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 中找不到执行此操作的方法。以下是我迄今为止尝试的一些片段:
QML 片段(最初只有 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)
Python addChartSeries 片段:
@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 函数在我的 class "dataModel" 中,我像这样传递给 QML(数据模型 class 对我做的很多其他事情都很好有了它所以没问题):
dataModel = DataModel()
self.rootContext().setContextProperty("dataModel", dataModel)
QML 图表 API 是基于 C++ 使用的 API 编写的,但它并不相同,例如 ChartView 是一个 QQuickItem,它不公开 QChart,不像 QChartView 是一个QGraphicsView(QWidget),如果它公开了 QChart,那么系列是一样的。总之,您将无法使用 C++(Python) 类 与 QML 进行交互。
你开头的例子不是C++的例子,而是QML的例子,所以不能直接翻译成QML。也不可能在 C++/Python 中直接使用 QtCharts 类 创建 QML 系列,一种可能的策略是使用可以评估 QML 元素的 QQmlExpression 和 return 它到 C++/Python。另外,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)
}
}
}
我正在尝试使用 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 中找不到执行此操作的方法。以下是我迄今为止尝试的一些片段:
QML 片段(最初只有 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)
Python addChartSeries 片段:
@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 函数在我的 class "dataModel" 中,我像这样传递给 QML(数据模型 class 对我做的很多其他事情都很好有了它所以没问题):
dataModel = DataModel()
self.rootContext().setContextProperty("dataModel", dataModel)
QML 图表 API 是基于 C++ 使用的 API 编写的,但它并不相同,例如 ChartView 是一个 QQuickItem,它不公开 QChart,不像 QChartView 是一个QGraphicsView(QWidget),如果它公开了 QChart,那么系列是一样的。总之,您将无法使用 C++(Python) 类 与 QML 进行交互。
你开头的例子不是C++的例子,而是QML的例子,所以不能直接翻译成QML。也不可能在 C++/Python 中直接使用 QtCharts 类 创建 QML 系列,一种可能的策略是使用可以评估 QML 元素的 QQmlExpression 和 return 它到 C++/Python。另外,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)
}
}
}