How can I change all the points in an XYSeries in

2020-04-19 05:11发布

问题:

I'm kinda new to PySide2 and QML and I really need a way to replace all the points in an XYSeries at once. Since the QML item does not have a function that does so, I thought I had to create a custom class (that would inherits from QtCharts.QXYSeries), implement the function I need and then register the new type with PySide2.QtQml.qmlRegisterType, but I don't know how that should be done and I haven't been able to find an answer online (or at least one that I could understand).

So, to cut a long story short, what I need to know is if there's a way to change all the points of an XYSeries and how can it be done (e.g. creating a custom class and registering it, accessing the Item declarend in the .qml file from python and chaning its properties, etc...).
I know my question is really vague but I do not know where to look and what to do...

EDIT

I have a python class that acquires data from an instruments and generates an array of X and Y points. Since this arrays are made from at least 1000 points and since I need to have a refresh rate of at least 1Hz, it's impossible to do it adding one point at a time (I have an signal that sends the whole array to the qml interface and there, for the moment, I simply clear the series and add one XY couple at a time. It works but it's too damn slow).

回答1:

One possible solution is to create a class that allows access to a QML object from Python, in this case I create the helper class that I export to QML through setContextProperty by linking the series with a qproperty.

main.py

import random
from PySide2 import QtCore, QtWidgets, QtQml
from PySide2.QtCharts import QtCharts

class Helper(QtCore.QObject):
    serieChanged = QtCore.Signal()

    def __init__(self, parent=None):
        super(Helper, self).__init__(parent)
        self._serie = None

    def serie(self):
        return self._serie

    def setSerie(self, serie):
        if self._serie == serie:
            return
        self._serie = serie
        self.serieChanged.emit()

    serie = QtCore.Property(QtCharts.QXYSeries, fget=serie, fset=setSerie, notify=serieChanged)

    @QtCore.Slot(list)
    def replace_points(self, points):
        if self._serie is not None:
            self._serie.replace(points)

class Provider(QtCore.QObject):
    pointsChanged = QtCore.Signal(list)

    def __init__(self, parent=None):
        super(Provider, self).__init__(parent)
        timer = QtCore.QTimer(
            self, 
            interval=100,
            timeout=self.generate_points
        )
        timer.start()

    @QtCore.Slot()
    def generate_points(self):
        points = []
        for i in range(101):
            point = QtCore.QPointF(i, random.uniform(-10, 10))
            points.append(point)
        self.pointsChanged.emit(points)

if __name__ == '__main__':
    import os
    import sys
    app = QtWidgets.QApplication(sys.argv)
    helper = Helper()
    provider = Provider()
    provider.pointsChanged.connect(helper.replace_points)
    engine = QtQml.QQmlApplicationEngine()
    engine.rootContext().setContextProperty("helper", helper)
    file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "main.qml")
    engine.load(QtCore.QUrl.fromLocalFile(file))
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

main.qml

import QtQuick 2.9
import QtQuick.Window 2.2
import QtCharts 2.3

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    ChartView{
        anchors.fill: parent
        LineSeries{
            id: serie
            axisX: axisX
            axisY: axisY
        }
        ValueAxis {
            id: axisX
            min: 0
            max: 100
        }

        ValueAxis {
            id: axisY
            min: -10
            max: 10
        }
        Component.onCompleted: helper.serie = serie
    }
}