hugodurmer
hugodurmer

Reputation: 11

Problem with dynamically updating a QLineSeries

I encounter a problem when I try to make a self updating QLineSeries through the help of QThread.

from PySide2.QtCore import *
from PySide2 import *
from PySide2.QtWidgets import *
from PySide2.QtCharts import *
from PySide2.QtGui import *

import time


baseValuesList = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

randomValuesList = [256, 14, 89, 100, 150, 500, 50, 34, 67, 90, 102, 12, 19, 89, 34, 145, 71, 4, 89, 47]

rangeList = list(range(len(baseValuesList))) 

def listUpdatingFunction(): 
    baseValuesList.pop(0)
    baseValuesList.append(randomValuesList[0])
    randomValuesList.pop(0)

class Worker(QObject):
    def __init__(self, function, interval):
        super(Worker, self).__init__()
        self._step = 0
        self._isRunning = True
        self.function = function
        self.interval = interval

    def task(self):
        if not self._isRunning:
            self._isRunning = True
            self._step = 0

        while self._isRunning == True:
            self.function()
            time.sleep(self.interval)

    def stop(self):
        self._isRunning = False
        
class minimalMonitor(QtWidgets.QWidget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)
          
        # Creating QChart
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)
        self.chart = QtCharts.QChart()
        self.chart.setAnimationOptions(QtCharts.QChart.AllAnimations)
        self.series = QtCharts.QLineSeries()
        for i in rangeList:
            self.series.append(i,baseValuesList[i])
        self.chart.addSeries(self.series)
        self.chart_view = QtCharts.QChartView(self.chart)
        self.chart_view.setRenderHint(QPainter.Antialiasing)
        self.layout.addWidget(self.chart_view)
        self.axis_x = QtCharts.QValueAxis()
        self.chart.addAxis(self.axis_x, Qt.AlignBottom)
        self.axis_y = QtCharts.QValueAxis()
        self.chart.addAxis(self.axis_y, Qt.AlignLeft)
        self.axis_x.setRange(0, 20)
        self.axis_y.setRange(0, 300)
        self.series.attachAxis(self.axis_x)
        self.series.attachAxis(self.axis_y)
        self.thread = QThread()
        self.thread.start()

        self.worker = Worker(self.update, 1)
        self.worker.moveToThread(self.thread)

        self.autoUpdateStart = QPushButton("Start Auto-Update")
        self.autoUpdateStart.setCheckable(False)
        self.autoUpdateStart.toggle()
        self.autoUpdateStart.clicked.connect(self.worker.task)
        
        self.autoUpdateStop = QPushButton("Stop Auto-Update")
        self.autoUpdateStop.setCheckable(False)
        self.autoUpdateStop.toggle()
        self.autoUpdateStop.clicked.connect(lambda: self.worker.stop())
        self.layout.addWidget(self.autoUpdateStart)
        self.layout.addWidget(self.autoUpdateStop)
        self.manualUpdateButton = QPushButton("Manual Update")
        self.manualUpdateButton.setCheckable(False)
        self.manualUpdateButton.toggle()
        self.manualUpdateButton.clicked.connect(self.update)
        self.layout.addWidget(self.manualUpdateButton)
    def update(self):
            listUpdatingFunction()
            self.series.clear()

            for i in rangeList:
                self.series.append(i,baseValuesList[i])

            self.chart_view.update()
            
if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    widget = minimalMonitor()
    widget.show()
    sys.exit(app.exec_())

In a nutshell, if I call directly the update function or press manual update, the QLineSeries will correctly append, but as soon as I use the QThread or Auto Update button it goes haywire and will always try to join with the Axis Origin. Does any one have an idea why it is doing this?

Upvotes: 0

Views: 900

Answers (1)

eyllanesc
eyllanesc

Reputation: 244003

In this case I see the following errors:

  • It is not necessary to use threads to do a repetitive task since a QTImer is enough, for example with a QThread you cannot and should not update the GUI from a secondary thread as you do now, unlike QTimer that allows it.
  • I don't understand why you delete elements from randomValuesList since at one point that list will be empty causing problems.
import random

from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCharts import QtCharts


baseValuesList = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
randomValuesList = [256, 14, 89, 100, 150, 500, 50, 34, 67, 90, 102, 12, 19, 89, 34, 145, 71, 4, 89, 47]


def listUpdatingFunction():
    baseValuesList.pop(0)
    value = random.choice(randomValuesList)
    baseValuesList.append(value)


class Worker(QtCore.QObject):
    def __init__(self, function, interval):
        super(Worker, self).__init__()
        self._funcion = function
        self._timer = QtCore.QTimer(self, interval=interval, timeout=self.execute)

    @property
    def running(self):
        return self._timer.isActive()

    def start(self):
        self._timer.start()

    def stop(self):
        self._timer.stop()

    def execute(self):
        self._funcion()


class minimalMonitor(QtWidgets.QWidget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)

        # Creating QChart
        layout = QtWidgets.QVBoxLayout(self)
        self.chart = QtCharts.QChart()
        self.chart.setAnimationOptions(QtCharts.QChart.AllAnimations)
        self.series = QtCharts.QLineSeries()
        self.chart.addSeries(self.series)
        self.chart_view = QtCharts.QChartView(self.chart)
        self.chart_view.setRenderHint(QtGui.QPainter.Antialiasing)
        layout.addWidget(self.chart_view)
        self.axis_x = QtCharts.QValueAxis()
        self.chart.addAxis(self.axis_x, QtCore.Qt.AlignBottom)
        self.axis_y = QtCharts.QValueAxis()
        self.chart.addAxis(self.axis_y, QtCore.Qt.AlignLeft)
        self.axis_x.setRange(0, 20)
        self.axis_y.setRange(0, 300)
        self.series.attachAxis(self.axis_x)
        self.series.attachAxis(self.axis_y)

        self.worker = Worker(self.update_chart, 1000)

        self.autoUpdateStart = QtWidgets.QPushButton("Start Auto-Update")
        self.autoUpdateStart.setCheckable(False)
        self.autoUpdateStart.toggle()
        self.autoUpdateStart.clicked.connect(self.worker.start)

        self.autoUpdateStop = QtWidgets.QPushButton("Stop Auto-Update")
        self.autoUpdateStop.setCheckable(False)
        self.autoUpdateStop.toggle()
        self.autoUpdateStop.clicked.connect(self.worker.stop)
        layout.addWidget(self.autoUpdateStart)
        layout.addWidget(self.autoUpdateStop)
        self.manualUpdateButton = QtWidgets.QPushButton("Manual Update")
        self.manualUpdateButton.setCheckable(False)
        self.manualUpdateButton.toggle()
        self.manualUpdateButton.clicked.connect(self.update_chart)
        layout.addWidget(self.manualUpdateButton)

        self.update_chart()

    def update_chart(self):
        listUpdatingFunction()
        self.series.clear()

        for i, value in enumerate(baseValuesList):
            self.series.append(i, value)

        self.chart_view.update()


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    widget = minimalMonitor()
    widget.show()
    sys.exit(app.exec_())

Upvotes: 1

Related Questions