Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Continuously Updating BarPlot (QtCharts)
Forum Updated to NodeBB v4.3 + New Features

Continuously Updating BarPlot (QtCharts)

Scheduled Pinned Locked Moved Unsolved Qt for Python
pyside2
8 Posts 3 Posters 2.5k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • T Offline
    T Offline
    TommasoFurieri
    wrote on 19 Mar 2020, 08:10 last edited by
    #1

    Hi to everybody, i'm new in this forum and i'm relatively new to Qt for Python.

    Up untill now i have been always capable of solving the problems googling or using the official documentation, but now i'm facing an obstacle than I cannot solve by myself.

    I'm using a QTimer() with a 20Hz update clock.
    At every timeout, some operations are executed. Among them i need to plot a barplot that takes the values from the Numpy array (converted to a list) self.z_coeff.

    I designed the interface with Qt Designer and I draw a QWidget for the barplot (object name: zernike_bar_plot).

    The .ui is converted to python code using the pyside2-uic command, and what i'm doing in the separate file that I use to add all the functionality is pretty standard:

    class MainWindow(QMainWindow):
        def __init__(self):
            super(MainWindow, self).__init__()
            self.ui = Ui_MainWindow()
            self.ui.setupUi(self)
    

    When the program starts i draw the bar chart for the first time using this function:

    def set_zernike_plot(self):
    
        self.zernike_barset = QtCharts.QBarSet('zernike_bp')
        self.zernike_barset.append(self.z_coeff.tolist())
        self.zernike_barset.setColor(QColor(0, 230, 0))
    
        self.zernike_series = QtCharts.QBarSeries()
        self.zernike_series.append(self.zernike_barset)
        self.zernike_series.setBarWidth(1)
    
        self.zernike_plot_chart = QtCharts.QChart()
        self.zernike_plot_chart.addSeries(self.zernike_series) 
    
        self.zernike_plot_chart.createDefaultAxes()
        self.zernike_plot_chart.legend().hide()
    
        self.zernike_plot_chart.axisX(self.zernike_series).setVisible(False)
        self.zernike_plot_chart.axisY(self.zernike_series).setVisible(False)
    
        self.zernike_chartview = QtCharts.QChartView(self.zernike_plot_chart)
    
        self.ui.zernike_bar_plot.setContentsMargins(0, 0, 0, 0)
        self.zernike_layout = QHBoxLayout(self.ui.zernike_bar_plot)
    
        self.zernike_layout.setContentsMargins(0, 0, 0, 0)
        self.zernike_layout.addWidget(self.zernike_chartview)
    

    Here the first problem: I'm not able to limit the y axis to be in some range, say [-1,1].

    Then what I want is to update this plot with che continuously changing values of self.z_coeff.
    For this purpose, i connected the QTimer timeout to another function:

    def update_zernike_coefficient_plot(self):
        self.zernike_plot_chart.removeSeries(self.zernike_series)
    
        self.zernike_barset = QtCharts.QBarSet('zernike_set')
        self.zernike_barset.append(self.z_coeff.tolist())
        self.zernike_barset.setColor(QColor(0, 230, 0))
    
        self.zernike_series = QtCharts.QBarSeries()
        self.zernike_series.append(self.zernike_barset)
        self.zernike_series.setBarWidth(1)
    
        self.zernike_plot_chart.addSeries(self.zernike_series) 
    
        self.zernike_chartview.setChart(self.zernike_plot_chart)
    
        self.zernike_layout.insertWidget(0,self.zernike_chartview)
    

    This is working, but i think that is overkilling to reassign all this variables and rewrite all this code just to update the plot. Moreover to me this adds unnecessary load for the processor.

    I will thank everyone that can help me ❤️

    1 Reply Last reply
    0
    • J Offline
      J Offline
      jazzycamel
      wrote on 19 Mar 2020, 09:43 last edited by jazzycamel
      #2

      Hi there,

      See the minimal example below (uses PyQt5 or PySide2) that hopefully addresses your issues/questions:

      try:
          from PyQt5.QtCore import QTimerEvent
          from PyQt5.QtGui import QColor
          from PyQt5.QtWidgets import QMainWindow
          from PyQt5.QtChart import QChartView, QBarSet, QBarSeries, QChart
      
      except ImportError:
          from PySide2.QtCore import QTimerEvent
          from PySide2.QtGui import QColor
          from PySide2.QtWidgets import QMainWindow
          from PySide2.QtCharts import QtCharts
      
          QBarSet=QtCharts.QBarSet
          QBarSeries=QtCharts.QBarSeries
          QChart=QtCharts.QChart
          QChartView=QtCharts.QChartView
      
      class MainWindow(QMainWindow):
          def __init__(self, parent=None, **kwargs):
              super().__init__(parent, **kwargs)
      
              self._data=[
                  [1,2,3,4,5,4,3,2,1],
                  [5,4,3,2,1,2,3,4,5]
              ]
              self._currentDataIdx=0
      
              self._barSet=QBarSet("Bar Set")
              self._barSet.setColor(QColor(0,230,0))
              self._barSet.append(self._data[self._currentDataIdx])
      
              self._barSeries=QBarSeries()
              self._barSeries.setBarWidth(1)
              self._barSeries.append(self._barSet)
      
              self._chart=QChart()
              self._chart.addSeries(self._barSeries)
              self._chart.createDefaultAxes()
              self._chart.legend().hide()
              self._chart.axisX(self._barSeries).setVisible(False)
              self._chart.axisY(self._barSeries).setVisible(False)
      
              # Set the Y-axis range/limits 0 to 6
              self._chart.axisY(self._barSeries).setRange(0,6)
      
              self._chartView=QChartView(self._chart)
      
              self.setCentralWidget(self._chartView)
      
              self._timerId=self.startTimer(1000)
      
          def timerEvent(self, event:QTimerEvent):
              if self._timerId!=event.timerId(): return
      
              # Replace the data in the existing series
              self._currentDataIdx=1 if not self._currentDataIdx else 0
              for i,n in enumerate(self._data[self._currentDataIdx]):
                  self._barSet.replace(i,n)
      
      if __name__=="__main__":
          from sys import arg, exit
          try: from PyQt5.QtWidgets import QApplication
          except ImportError: from PySide2.QtWidgets import QApplication
      
          a=QApplication(argv)
          m=MainWindow()
          m.show()
          m.resize(640,480)
          exit(a.exec_())
      

      Hope this helps :o)

      For the avoidance of doubt:

      1. All my code samples (C++ or Python) are tested before posting
      2. As of 23/03/20, my Python code is formatted to PEP-8 standards using black from the PSF (https://github.com/psf/black)
      1 Reply Last reply
      4
      • T Offline
        T Offline
        TommasoFurieri
        wrote on 19 Mar 2020, 13:20 last edited by
        #3

        This works! THANKS!

        But, i don't know why, if i call the function set_zernike_plot inside the __init__ of the class MainWindow, it doen't work, while if i put the same code of the function in the __init__, than it works 🤔

        1 Reply Last reply
        0
        • J Offline
          J Offline
          jazzycamel
          wrote on 19 Mar 2020, 13:59 last edited by
          #4

          Glad it works.

          I'm not sure about your other issue, I would need a more complete minimal example to try and trace the issue.

          For the avoidance of doubt:

          1. All my code samples (C++ or Python) are tested before posting
          2. As of 23/03/20, my Python code is formatted to PEP-8 standards using black from the PSF (https://github.com/psf/black)
          1 Reply Last reply
          3
          • J Offline
            J Offline
            jazzycamel
            wrote on 20 Mar 2020, 09:08 last edited by
            #5

            @Denni-0

            +1 for stepping away from designer, causes way more problems than it solves!

            I used startTimer out of habit more than anything and because it makes for (IMHO) cleaner code, but I take your point and here, for completeness, is my example using QTimer instead:

            try:
                from PyQt5.QtCore import QTimer, pyqtSlot
                from PyQt5.QtGui import QColor
                from PyQt5.QtWidgets import QMainWindow
                from PyQt5.QtChart import QChartView, QBarSet, QBarSeries, QChart
            
            except ImportError:
                from PySide2.QtCore import QTimer, Slot as pyqtSlot
                from PySide2.QtGui import QColor
                from PySide2.QtWidgets import QMainWindow
                from PySide2.QtCharts import QtCharts
            
                QBarSet=QtCharts.QBarSet
                QBarSeries=QtCharts.QBarSeries
                QChart=QtCharts.QChart
                QChartView=QtCharts.QChartView
            
            class MainWindow(QMainWindow):
                def __init__(self, parent=None, **kwargs):
                    super().__init__(parent, **kwargs)
            
                    self._data=[
                        [1,2,3,4,5,4,3,2,1],
                        [5,4,3,2,1,2,3,4,5]
                    ]
                    self._currentDataIdx=0
            
                    self._barSet=QBarSet("Bar Set")
                    self._barSet.setColor(QColor(0,230,0))
                    self._barSet.append(self._data[self._currentDataIdx])
            
                    self._barSeries=QBarSeries()
                    self._barSeries.setBarWidth(1)
                    self._barSeries.append(self._barSet)
            
                    self._chart=QChart()
                    self._chart.addSeries(self._barSeries)
                    self._chart.createDefaultAxes()
                    self._chart.legend().hide()
                    self._chart.axisX(self._barSeries).setVisible(False)
                    self._chart.axisY(self._barSeries).setVisible(False)
                    self._chart.axisY(self._barSeries).setRange(0,6)
            
                    self._chartView=QChartView(self._chart)
            
                    self.setCentralWidget(self._chartView)
            
                    self._timer=QTimer()
                    self._timer.timeout.connect(self.onTimeout)
                    self._timer.start(1000)
            
                @pyqtSlot()
                def onTimeout(self):
                    self._currentDataIdx=1 if not self._currentDataIdx else 0
                    for i,n in enumerate(self._data[self._currentDataIdx]):
                        self._barSet.replace(i,n)
            
            if __name__=="__main__":
                from sys import argv, exit
                try: from PyQt5.QtWidgets import QApplication
                except ImportError: from PySide2.QtWidgets import QApplication
            
                a=QApplication(argv)
                m=MainWindow()
                m.show()
                m.resize(640,480)
                exit(a.exec_())
            

            For the avoidance of doubt:

            1. All my code samples (C++ or Python) are tested before posting
            2. As of 23/03/20, my Python code is formatted to PEP-8 standards using black from the PSF (https://github.com/psf/black)
            1 Reply Last reply
            2
            • J Offline
              J Offline
              jazzycamel
              wrote on 20 Mar 2020, 16:07 last edited by jazzycamel
              #6

              @Denni-0
              With all due respect, please don't be condescending.

              My code worked perfectly for both PyQt5 and PySide2, I tested it before posting as I always do.

              I don't know why you feel the need to correct my example, most of what you said is either personal taste or in fact incorrect. I am a python dev with decades of experience, I know what I'm doing. Your post will not help people learn, it is simply insulting.

              For the avoidance of doubt:

              1. All my code samples (C++ or Python) are tested before posting
              2. As of 23/03/20, my Python code is formatted to PEP-8 standards using black from the PSF (https://github.com/psf/black)
              1 Reply Last reply
              4
              • J Offline
                J Offline
                jazzycamel
                wrote on 20 Mar 2020, 17:19 last edited by
                #7

                @Denni-0
                In which case, I would be interested to see the traceback from the failure for my own learning, as I have it working right here now in front of me (both with PyQt5 and PySide2).

                I'm not sure why moderators are required; I have in no way insulted you or made any 'accusations' and I did my best to be respectful. However, if I have unintentionally caused you offence, then please accept my apologies. However, my comments regarding the opinions you inserted as comments in to my code stand; most of it is personal taste and some of it is incorrect.

                For example, here are just some of the ways you have broken PEP-8 conventions in your modified version of my example.

                • Imports should not be aligned (extraneous whitespace): https://www.python.org/dev/peps/pep-0008/#id26.
                • Indentation should be 4 spaces not 2: https://www.python.org/dev/peps/pep-0008/#id17
                • Instance variable names should start with lowercase: https://www.python.org/dev/peps/pep-0008/#id37

                Further, I pass argv to QApplication because there are somethings that can me modified from the command line in every (Py)Qt5 program (should the user wish to) such as the application style (-style=plastique).

                The long and the short of it is, I really don't know why you felt it necessary to comment so heavily on someone elses post. It is condescending to assume you have the right to mark everyone elses work and, as you were quick to point out, we have moderators to moderate forums, we don't need you to police them too.

                For the avoidance of doubt:

                1. All my code samples (C++ or Python) are tested before posting
                2. As of 23/03/20, my Python code is formatted to PEP-8 standards using black from the PSF (https://github.com/psf/black)
                1 Reply Last reply
                4
                • J Offline
                  J Offline
                  jazzycamel
                  wrote on 20 Mar 2020, 19:42 last edited by jazzycamel
                  #8

                  @Denni-0
                  Cool, I’m glad we’re on a square footing :o) and thanks for the clarity around the suspected error.

                  PEP-8 is the gold standard for Python code, that’s why I referenced it and because you’d made comments regarding good practice etc., not because you had mentioned these things specifically, sorry if I wasn’t clear. I was trying to highlight how a lot of the things you had changed and made quite in-depth comments about were actually due to your taste/style and technically could be considered incorrect as they go against the generally accepted standard for our language of choice. And sorry about the indent one, your code looked like it was using 2 spaces on the forum, but I was obviously wrong. Also, as I don’t agree with all of your points regarding good practice or readability/ease of understanding (idx is no better than i, abs no clearer than enumerate to a newb IMO) I was trying to reference an independent 3rd party of standing, i.e. the Python Software Foundation. The bits about super(), the reasons I pass parent and **kwargs and where imports belong I will leave; we’re not going to agree, but my reasons are as informed, heartfelt and, IMO, valid as yours, I just don’t think there’s anything to be gained by going on about it here.

                  I don’t think we are going to agree on all the points raised and the nice thing is we don’t have to. I guess my objection was based on my code being classed as bad or wrong based on what I saw as semantics and preference rather than technical errors.

                  Anyway, I’d rather extend an olive branch, agree to disagree and move on. If the original OP was helped by one of both of us then that is why we’re here.

                  See you around the forums dude, live long and prosper.

                  For the avoidance of doubt:

                  1. All my code samples (C++ or Python) are tested before posting
                  2. As of 23/03/20, my Python code is formatted to PEP-8 standards using black from the PSF (https://github.com/psf/black)
                  1 Reply Last reply
                  3

                  1/8

                  19 Mar 2020, 08:10

                  • Login

                  • Login or register to search.
                  1 out of 8
                  • First post
                    1/8
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved