pythonpyqt5qtcharts

how to refresh data in PyQt5 QtChart?


I'm trying to make a QtChart updatable when I change a combobox whick contains years. For the purpose of this question I made a list with random data and a shorted version of my project. here is my chart_ui.py from QtDesigner

 from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.year_ccb = QtWidgets.QComboBox(self.centralwidget)
        self.year_ccb.setGeometry(QtCore.QRect(10, 10, 111, 22))
        self.year_ccb.setObjectName("year_ccb")
        self.chart_fm = QtWidgets.QFrame(self.centralwidget)
        self.chart_fm.setGeometry(QtCore.QRect(20, 60, 741, 391))
        self.chart_fm.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.chart_fm.setFrameShadow(QtWidgets.QFrame.Raised)
        self.chart_fm.setObjectName("chart_fm")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))

and here is my main.py file with the example code:

import datetime
import sys

from PyQt5.QtChart import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt, QRect, QPropertyAnimation, QParallelAnimationGroup, QDate
from chart_ui import Ui_MainWindow


class LoginWindow(QMainWindow):

    def __init__(self):
        QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        global widgets
        widgets = self.ui
        self.chart_one_create()
        ####################################################
        years = ('2019', '2020', '2021')
        widgets.year_ccb.addItems(years)
        widgets.year_ccb.currentIndexChanged.connect(self.chart_one_create)
        ####################################################
        self.show()

    def chart_one_create(self):
        self.series = QBarSeries()
        self.set_tkf = QBarSet("TAEKWON-DO")
        self.set_fenc = QBarSet("ΞΙΦΑΣΚΙΑ")
        self.set_oplo = QBarSet("ΟΠΛΟΜΑΧΙΑ")
        self.set_spends = QBarSet("ΕΞΟΔΑ")

        if widgets.year_ccb.currentText() == '2019':
            list_of_data = [["1", "2", "3", "4", "5"], [(45, 637, 12, 43), (12, 154, 132, 454),
                                                        (435, 117, 212, 453), (45, 657, 112, 455),
                                                        (451, 667, 12, 475)]]  # manual add data
        elif widgets.year_ccb.currentText() == '2020':
            list_of_data = [["5", "6", "7", "8", "9"], [(425, 67, 172, 43), (192, 584, 132, 454),
                                                        (435, 167, 212, 453), (45, 657, 12, 455),
                                                        (453, 667, 162, 475)]]  # manual add data
        else:
            list_of_data = [["10", "11", "12", "13", "14"], [(45, 67, 12, 43), (127, 54, 132, 454),
                                                             (435, 187, 212, 453), (45, 657, 152, 455),
                                                             (45, 667, 122, 475)]]  # manual add data
        for i in range(len(list_of_data[0])):  # για καθε μηνα που εχει στην πρωτη εσωτερικη λιστα
            if list_of_data[1][i][0] is None:
                tkd = 0
            else:
                tkd = list_of_data[1][i][0]
            self.set_tkf.append(tkd)
            if list_of_data[1][i][1] is None:
                fenc = 0
            else:
                fenc = list_of_data[1][i][1]
            self.set_fenc.append(fenc)
            if list_of_data[1][i][2] is None:
                oplo = 0
            else:
                oplo = list_of_data[1][i][2]
            self.set_oplo.append(oplo)
            if list_of_data[1][i][3] is None:
                spen = 0
            else:
                spen = list_of_data[1][i][3]
            self.set_spends.append(spen)

        self.series.append(self.set_tkf)
        self.series.append(self.set_fenc)
        self.series.append(self.set_oplo)
        self.series.append(self.set_spends)

        ##############################################################
        self.chart = QChart()
        self.chart.addSeries(self.series)
        self.chart.setTitle("Monthly Stats per Year")
        self.chart.setAnimationOptions(QChart.AllAnimations)
        self.chart.setTheme(QChart.ChartThemeBrownSand)
        self.chart.setBackgroundBrush(QBrush(QColor("transparent")))
        self.chart.legend().setVisible(True)
        self.chart.legend().setAlignment(Qt.AlignBottom)
        ##############################################################
        self.axisX = QBarCategoryAxis()
        self.axisY = QValueAxis()
        self.axisY.setRange(0, 3000)
        ##############################################################
        self.categories_months=list_of_data[0]
        self.axisX.append(self.categories_months)
        self.chart.addAxis(self.axisX, Qt.AlignBottom)
        self.chart.addAxis(self.axisY, Qt.AlignLeft)

        self.series.attachAxis(self.axisX)
        self.series.attachAxis(self.axisY)
        ##############################################################
        self.chartview = QChartView(self.chart)
        vbox = QGridLayout(widgets.chart_fm)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.addWidget(self.chartview)
        self.update_chart_one()

    def update_chart_one(self):
        pass


# Create the application object
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = LoginWindow()
    sys.exit(app.exec_())

So please help me in order to understant what I have to write in def update_chart_one(self): to make my chart refresh its data when I change the year from combobox. Maybe something like:

def update_chart_one(self):
    self.series.clear()
    self.categories_months.clear()

and then somehow refresh the data in chart.

Chart: Y axis shows the money and in X axis are the months ( first list in data list_of_data[0])


Solution

  • You have to reuse the series by removing the previous data:

    import sys
    
    from PyQt5.QtCore import Qt
    from PyQt5.QtGui import QBrush, QColor
    from PyQt5.QtWidgets import QApplication, QGridLayout, QMainWindow
    from PyQt5.QtChart import (
        QBarCategoryAxis,
        QBarSeries,
        QBarSet,
        QChart,
        QChartView,
        QValueAxis,
    )
    
    from chart_ui import Ui_MainWindow
    
    
    class LoginWindow(QMainWindow):
        def __init__(self):
            QMainWindow.__init__(self)
            self.ui = Ui_MainWindow()
            self.ui.setupUi(self)
    
            self.build_chart()
    
            years = ("2019", "2020", "2021")
            self.ui.year_ccb.currentIndexChanged.connect(self.handle_index_changed)
            self.ui.year_ccb.addItems(years)
    
        def build_chart(self):
            self.series = QBarSeries()
            self.set_tkf = QBarSet("TAEKWON-DO")
            self.set_fenc = QBarSet("ΞΙΦΑΣΚΙΑ")
            self.set_oplo = QBarSet("ΟΠΛΟΜΑΧΙΑ")
            self.set_spends = QBarSet("ΕΞΟΔΑ")
            self.series.append(self.set_tkf)
            self.series.append(self.set_fenc)
            self.series.append(self.set_oplo)
            self.series.append(self.set_spends)
    
            self.chart = QChart()
            self.chart.addSeries(self.series)
            self.chart.setTitle("Monthly Stats per Year")
            self.chart.setAnimationOptions(QChart.AllAnimations)
            self.chart.setTheme(QChart.ChartThemeBrownSand)
            self.chart.setBackgroundBrush(QBrush(QColor("transparent")))
            self.chart.legend().setVisible(True)
            self.chart.legend().setAlignment(Qt.AlignBottom)
    
            self.axisX = QBarCategoryAxis()
            self.axisY = QValueAxis()
            self.axisY.setRange(0, 3000)
    
            self.chart.addAxis(self.axisX, Qt.AlignBottom)
            self.chart.addAxis(self.axisY, Qt.AlignLeft)
    
            self.series.attachAxis(self.axisX)
            self.series.attachAxis(self.axisY)
    
            self.chartview = QChartView(self.chart)
            vbox = QGridLayout(self.ui.chart_fm)
            vbox.setContentsMargins(0, 0, 0, 0)
            vbox.addWidget(self.chartview)
    
        def handle_index_changed(self):
            default = [
                ["10", "11", "12", "13", "14"],
                [
                    (45, 67, 12, 43),
                    (127, 54, 132, 454),
                    (435, 187, 212, 453),
                    (45, 657, 152, 455),
                    (45, 667, 122, 475),
                ],
            ]
            data = {
                "2019": [
                    ["1", "2", "3", "4", "5"],
                    [
                        (45, 637, 12, 43),
                        (12, 154, 132, 454),
                        (435, 117, 212, 453),
                        (45, 657, 112, 455),
                        (451, 667, 12, 475),
                    ],
                ],
                "2020": [
                    ["5", "6", "7", "8", "9"],
                    [
                        (425, 67, 172, 43),
                        (192, 584, 132, 454),
                        (435, 167, 212, 453),
                        (45, 657, 12, 455),
                        (453, 667, 162, 475),
                    ],
                ],
            }
            values = data.get(self.ui.year_ccb.currentText(), default)
            self.update_chart_one(values)
    
        def update_chart_one(self, datas):
            self.axisX.clear()
            self.set_tkf.remove(0, self.set_tkf.count())
            self.set_fenc.remove(0, self.set_fenc.count())
            self.set_oplo.remove(0, self.set_oplo.count())
            self.set_spends.remove(0, self.set_spends.count())
            categories, data = datas
            self.axisX.append(categories)
            for tkf, fenc, oplo, spends in data:
                self.set_tkf.append(tkf)
                self.set_fenc.append(fenc)
                self.set_oplo.append(oplo)
                self.set_spends.append(spends)
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = LoginWindow()
        window.show()
        sys.exit(app.exec_())