3

I am running a Qt QML application with Python. I am graphing values using qt charts dynamically. To do this I made a similar code as in the oscilloscope example from Qt documentation, except that instead of C++ I used Python. I first created a line series in QML. Then I exposed a class called "Bridge" as "con" to QML using context property. Inside the class called "Bridge", I generated the initial data. Then I am updating the chart every time the timer counts by passing the series to the "Bridge" class and then using the replace function so that the series gets the data fastly instead of using clear and append.

import QtQuick 2.10
import QtQuick.Window 2.5
import QtQuick.Controls 2.4
import QtCharts 2.0

Window {
    id: window
    title: qsTr("QML and Python graphing dynamically")
    width: 640
    height: 480
    color: "#1b480d"
    visible: true

    Timer{
        id: miTimer
        interval: 1 / 24 * 1000  //update every 200ms
        running: true
        repeat: true
        onTriggered: {
            con.update_series(chart.series(0))
        }
    }

    Label {
        id: label
        x: 298
        color: "#f1f3f4"
        text: qsTr("Graphin dynamically with python")
        anchors.horizontalCenterOffset: 0
        anchors.top: parent.top
        anchors.topMargin: 10
        font.bold: true
        font.pointSize: 25
        anchors.horizontalCenter: parent.horizontalCenter
    }

    ChartView {
        id: chart
        x: 180
        y: 90
        width: 500
        height: 300
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter

        ValueAxis{
            id: axisX
            min: 0
            max: 200
        }

        ValueAxis{
            id: axisY
            min: 0
            max: 100
        }

        }

    Component.onCompleted: {
        console.log("Se ha iniciado QML\n")
        var series = chart.createSeries(ChartView.SeriesTypeLine,"My grafico",axisX,axisY)
        con.generateData()
    }
}

This QML essentially is a chart in the center. In Component.onCompleted, I create a line series which is using a context property class, I update it using python.

# This Python file uses the following encoding: utf-8
import sys
from os.path import abspath, dirname, join
import random

from PySide2.QtCore import QObject, Slot, QPoint
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCharts import QtCharts
from PySide2.QtWidgets import QApplication # <---

class Bridge(QObject):
    def __init__(self, parent=None):
        super(Bridge, self).__init__(parent)
        self.my_data = []
        self.index = -1

    @Slot(QtCharts.QAbstractSeries)
    def update_series(self, series):
        self.index += 1
        if(self.index > 4):
            self.index = 0
        series.replace(self.my_data[self.index])

    @Slot()
    def generateData(self):
        my_data = []

        for i in range (5):
            my_list = []
            for j in range(200):
                my_list.append(QPoint(j,random.uniform(0,100)))
            self.my_data.append(my_list)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    engine = QQmlApplicationEngine()

    bridge = Bridge()

    # Expose the Python object to QML
    context = engine.rootContext()
    context.setContextProperty("con", bridge)

    #engine.load(os.path.join(os.path.dirname(__file__), "main.qml"))
    # Get the path of the current directory, and then add the name
    # of the QML file, to load it.
    qmlFile = join(dirname(__file__), 'main.qml')
    engine.load(abspath(qmlFile))

    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

It works nicely.

enter image description here

The problem is that the exact same program doesn't work in the raspberry pi 3. The error is on series.replace(self.my_data[self.index])

It says TypeError:

replace(double,double,double,double) needs 4 argument(s), 1 given!

To run the code on the raspberry pi I installed Pyside2 libraries from: https://forum.qt.io/topic/112813/installing-pyside2-on-raspberry-pi/7 Its version is PySide2 5.11.2.

And for QML modules I used sudo apt-get install qml-module-xxxxxxx for each needed library

Gary Bao 鲍昱彤
  • 2,608
  • 20
  • 31
  • Can you point me out what you get when you run `help(QtCharts.QLineSeries.replace)`? – eyllanesc Sep 02 '20 at 21:50
  • I tried right now. It is showing like this: >>>> help(QtCharts.QLineSeries.replace) Help on method_descriptor: replace(...) – Manuel Anatoliy Arcia Salmeron Sep 02 '20 at 22:31
  • Are you sure it just says "method_descriptor: replace(...)"? I was hoping there was more information, try running `python -c "from PySide2.QtCharts import QtCharts; help(QtCharts.QLineSeries.replace)" ` in the raspberry console: – eyllanesc Sep 02 '20 at 22:36
  • Basically the same. Help on method_descriptor: replace(...) (END) I am also using python3 instead of python. The raspberry pi has python 3.7.3. Also I was further checking and realized that happens the same with the append function. TypeError: append(double,double) needs 2 argument(s), 1 given! This same code works nicely on desktop. – Manuel Anatoliy Arcia Salmeron Sep 02 '20 at 23:02
  • try with my solution – eyllanesc Sep 02 '20 at 23:20

1 Answers1

1

One possible solution is to replace

series.replace(self.my_data[self.index])

with:

series.clear()
for p in self.my_data[self.index]:
    series.append(p.x(), p.y())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • This workaround made the code run on the raspberry pi. Now I can update the chart dynamically on the raspberry pi 3. Thank you very much for the help. Now, the reason I was trying to use replace instead of clear + append is because according to Qt documentation, replace is a lot faster. In desktop, I measured the time between the timer triggers. Using replace took less time. I did the same on the pi, with a list of 200 elements,I could make it the least 130ms between timer triggers. Maybe if I run it on the pi4, it will run faster. I don't know why these pyside2 functions don't work on the pi. – Manuel Anatoliy Arcia Salmeron Sep 03 '20 at 00:41
  • @ManuelAnatoliyArciaSalmeron 1) The problem is not the RPI but the very old version of PySide2 that you use. I installed PySide2 5.11.2 and I get the same error on my desktop, probably a future release of raspbian (or the distro you use) will provide a more updated version. 2) What it says is correct: replace is faster than clear + append. 3) If my answer helped you then don't forget to mark as correct, if you don't know how to do it then check the [tour] – eyllanesc Sep 03 '20 at 00:47
  • Yes, I do use Raspbian. Do you know if there is a way to install a newer PySide2 for the raspberry pi or would it be better if I cross compile(have to go and learn how to do that). – Manuel Anatoliy Arcia Salmeron Sep 03 '20 at 00:53
  • @ManuelAnatoliyArciaSalmeron The binaries offered by pip are not for ARM architecture so they are not valid for RPI so if you want a more updated version of PySide2 you will have to compile PySide2 manually (either with a cross-compiler or other means) – eyllanesc Sep 03 '20 at 01:00