title: Best way to chart streamed data using PyQtChart or pyqtgraph with PyQt5 on Python?
body: <p>I am streaming TimeSeries that I want to chart efficiently (20+ chart live on a small computer).
  I have tried PyQtChart and pyqtgraph on PyQt5, but with both libs, I am ending up redrawing the whole chart for each data that I receive, which doesn't feel optimal.
  I settled for PyQtChart because it was handling better DatetimeSeries, but happy to be proven wrong (and share the  pyqtgraph code, just didn't want to make the post too big).</p>
  <p>Bellow is my working code with PyQtChart using random datas, so that you can run it:</p>
  <pre><code>import sys
  from random import randint
  from typing import Union
  
  from PyQt5.QtChart import (QChart, QChartView, QLineSeries, QDateTimeAxis, QValueAxis)
  from PyQt5.QtCore import Qt, QDateTime, QTimer
  from PyQt5.QtWidgets import QApplication
  from PyQt5.QtWidgets import (QWidget, QGridLayout)
  
  
  class Window(QWidget):
      def __init__(self, window_name: str = 'Ticker'):
          QWidget.__init__(self)
          # GUI
          self.setGeometry(200, 200, 600, 400)
          self.window_name: str = window_name
          self.setWindowTitle(self.window_name)
          layout = QGridLayout(self)
  
          # change the color of the window
          self.setStyleSheet('background-color:black')
  
          # Series
          self.high_dataset = QLineSeries()
          self.low_dataset = QLineSeries()
          self.mid_dataset = QLineSeries()
  
          self.low_of_day: Union[float, None] = 5
          self.high_of_day: Union[float, None] = 15
  
          # Y Axis
          self.time_axis_y = QValueAxis()
          self.time_axis_y.setLabelFormat(&quot;%.2f&quot;)
          self.time_axis_y.setTitleText(&quot;Price&quot;)
          # X Axis
          self.time_axis_x = QDateTimeAxis()
          self.time_axis_x.setFormat(&quot;hh:mm:ss&quot;)
          self.time_axis_x.setTitleText(&quot;Datetime&quot;)
  
          # Events
          self.qt_timer = QTimer()
          # QChart
          self.chart = QChart()
          self.chart.addSeries(self.mid_dataset)
          self.chart.addSeries(self.high_dataset)
          self.chart.addSeries(self.low_dataset)
  
          self.chart.setTitle(&quot;Barchart Percent Example&quot;)
          self.chart.setTheme(QChart.ChartThemeDark)
  
          # https://linuxtut.com/fr/35fb93c7ca35f9665d9f/
  
          self.chart.legend().setVisible(True)
          self.chart.legend().setAlignment(Qt.AlignBottom)
  
          self.chartview = QChartView(self.chart)
  
          # using -1 to span through all rows available in the window
          layout.addWidget(self.chartview, 2, 0, -1, 3)
  
          self.chartview.setChart(self.chart)
  
      def set_yaxis(self):
          # Y Axis Settings
          self.time_axis_y.setRange(int(self.low_of_day * .9), int(self.high_of_day * 1.1))
  
          self.chart.addAxis(self.time_axis_y, Qt.AlignLeft)
  
          self.mid_dataset.attachAxis(self.time_axis_y)
          self.high_dataset.attachAxis(self.time_axis_y)
          self.low_dataset.attachAxis(self.time_axis_y)
  
      def set_xaxis(self):
          # X Axis Settings
          self.chart.removeAxis(self.time_axis_x)
  
          self.time_axis_x = QDateTimeAxis()
          self.time_axis_x.setFormat(&quot;hh:mm:ss&quot;)
          self.time_axis_x.setTitleText(&quot;Datetime&quot;)
          self.chart.addAxis(self.time_axis_x, Qt.AlignBottom)
  
          self.mid_dataset.attachAxis(self.time_axis_x)
          self.high_dataset.attachAxis(self.time_axis_x)
          self.low_dataset.attachAxis(self.time_axis_x)
  
      def start_app(self):
          self.qt_timer.timeout.connect(self.retrieveStream, )
          time_to_wait: int = 500  # milliseconds
          self.qt_timer.start(time_to_wait)
  
      def retrieveStream(self):
          date_px = QDateTime()
          date_px = date_px.currentDateTime().toMSecsSinceEpoch()
          print(date_px)
  
          mid_px = randint(int((self.low_of_day + 2) * 100), int((self.high_of_day - 2) * 100)) / 100
  
          self.mid_dataset.append(date_px, mid_px)
          self.low_dataset.append(date_px, self.low_of_day)
          self.high_dataset.append(date_px, self.high_of_day)
  
          print(f&quot;epoch: {date_px}, mid: {mid_px:.2f}&quot;)
  
          self.update()
  
      def update(self):
          print(&quot;updating chart&quot;)
  
          self.chart.removeSeries(self.mid_dataset)
          self.chart.removeSeries(self.low_dataset)
          self.chart.removeSeries(self.high_dataset)
  
          self.chart.addSeries(self.mid_dataset)
          self.chart.addSeries(self.high_dataset)
          self.chart.addSeries(self.low_dataset)
  
          self.set_yaxis()
          self.set_xaxis()
  
  
  if __name__ == '__main__':
      app = QApplication(sys.argv)
      window = Window()
      window.show()
      window.start_app()
  
      sys.exit(app.exec_())
  </code></pre>
  <p>Biggest worries with this code are:</p>
  <ol>
  <li>the 'update' method which basically re-draw every element of the chart=&gt;I would have prefered a deque, refresh/update/refire type of solution</li>
  <li>QLineSeries do not seem to have a maxLen like deque collection, so I could end up with loads of data (ideally looking to run more than three QLineSeries)</li>
  </ol>
  <p>Besides that, I would be grateful to receive any insigths about how to optimize this code.
  I am new to Qt/Asyncio/Threading and really keen to learn.</p>
  <p>Best</p>
  <p><strong>EDIT chart now updating without redrawing everything</strong>
  <strong>Let me know if there is a better way, or code needing improvement</strong> as i am new to Qt.</p>
  <p>Thanks to answer bellow (@domarm) I corrected the way I was updating chart and link bellow made me aware I needed to set a min max for axis at each refresh so that data are within scope.</p>
  <p><a href="https://stackoverflow.com/questions/57079698/qdatetimeaxis-series-are-not-displayed">Qchart update with axis</a></p>
  <pre><code>
  import sys
  from datetime import datetime
  from random import randint
  from typing import Union, Optional
  
  from PyQt5.QtChart import (QChart, QChartView, QLineSeries, QDateTimeAxis, QValueAxis)
  from PyQt5.QtCore import (Qt, QDateTime, QTimer, QPointF)
  from PyQt5.QtGui import QFont
  from PyQt5.QtWidgets import (QWidget, QGridLayout, QLabel, QApplication)
  
  
  # https://doc.qt.io/qt-5/qtcharts-modeldata-example.html
  
  class Window(QWidget):
      running = False
  
      def __init__(self, window_name: str = 'Chart',
                   chart_title: Optional[str] = None,
                   geometry_ratio: int = 2,
                   histo_tick_size: int = 200):
          QWidget.__init__(self)
          # GUI
          self.window_wideness: int = 300
          self.histo_tick_size: int = histo_tick_size
          self.setGeometry(200,
                           200,
                           int(self.window_wideness * geometry_ratio),
                           self.window_wideness
                           )
          self.window_name: str = window_name
          self.setWindowTitle(self.window_name)
          self.label_color: str = 'grey'
          self.text_color: str = 'white'
          # Layout
          layout = QGridLayout(self)
  
          # Gui components
          bold_font = QFont()
          bold_font.setBold(True)
  
          self.label_last_px = QLabel('-', self)
          self.label_last_px.setFont(bold_font)
          self.label_last_px.setStyleSheet(&quot;QLabel { color : blue; }&quot;)
          layout.addWidget(self.label_last_px)
  
          # change the color of the window
          self.setStyleSheet('background-color:black')
          # QChart
          self.chart = QChart()
          if chart_title:
              self.chart.setTitle(chart_title)
          # Series
          self.high_dataset = QLineSeries(self.chart)
          self.high_dataset.setName(&quot;High&quot;)
  
          self.low_dataset = QLineSeries(self.chart)
          self.low_dataset.setName(&quot;Low&quot;)
  
          self.mid_dataset = QLineSeries(self.chart)
          self.mid_dataset.setName(&quot;Mid&quot;)
  
          self.low_of_day: Union[float, None] = 5
          self.high_of_day: Union[float, None] = 15
          self.last_data_point: dict = {&quot;last_date&quot;: None, &quot;mid_px&quot;: None, &quot;low_px&quot;: None, &quot;high_px&quot;: None}
  
          # Y Axis
          self.time_axis_y = QValueAxis()
          self.time_axis_y.setLabelFormat(&quot;%.2f&quot;)
          self.time_axis_y.setTitleText(&quot;Price&quot;)
  
          # X Axis
          self.time_axis_x = QDateTimeAxis()
          self.time_axis_x.setTitleText(&quot;Datetime&quot;)
  
          # Events
          self.qt_timer = QTimer()
  
          self.chart.setTheme(QChart.ChartThemeDark)
          self.chart.addSeries(self.mid_dataset)
          self.chart.addSeries(self.low_dataset)
          self.chart.addSeries(self.high_dataset)
          # https://linuxtut.com/fr/35fb93c7ca35f9665d9f/
  
          self.chart.legend().setVisible(True)
          # self.chart.legend().setAlignment(Qt.AlignBottom)
  
          self.chartview = QChartView(self.chart)
          # self.chartview.chart().setAxisX(self.axisX, self.mid_dataset)
  
          # using -1 to span through all rows available in the window
          layout.addWidget(self.chartview, 2, 0, -1, 3)
  
          self.chartview.setChart(self.chart)
  
      def set_yaxis(self):
          # Y Axis Settings
          self.time_axis_y.setRange(int(self.low_of_day * .9), int(self.high_of_day * 1.1))
  
          self.chart.addAxis(self.time_axis_y, Qt.AlignLeft)
  
          self.mid_dataset.attachAxis(self.time_axis_y)
          self.high_dataset.attachAxis(self.time_axis_y)
          self.low_dataset.attachAxis(self.time_axis_y)
  
      def set_xaxis(self):
          # X Axis Settings
          self.chart.removeAxis(self.time_axis_x)
          # X Axis
          self.time_axis_x = QDateTimeAxis()
          self.time_axis_x.setFormat(&quot;hh:mm:ss&quot;)
          self.time_axis_x.setTitleText(&quot;Datetime&quot;)
  
          point_first: QPointF = self.mid_dataset.at(0)
          point_last: QPointF = self.mid_dataset.at(len(self.mid_dataset) - 1)
  
          # needs to be updated each time for chart to render
          # https://stackoverflow.com/questions/57079698/qdatetimeaxis-series-are-not-displayed
          self.time_axis_x.setMin(QDateTime().fromMSecsSinceEpoch(point_first.x()).addSecs(0))
          self.time_axis_x.setMax(QDateTime().fromMSecsSinceEpoch(point_last.x()).addSecs(0))
  
          self.chart.addAxis(self.time_axis_x, Qt.AlignBottom)
  
          self.mid_dataset.attachAxis(self.time_axis_x)
          self.high_dataset.attachAxis(self.time_axis_x)
          self.low_dataset.attachAxis(self.time_axis_x)
  
      def _update_label_last_px(self):
          last_point: QPointF = self.mid_dataset.at(self.mid_dataset.count() - 1)
          last_date: datetime = datetime.fromtimestamp(last_point.x() / 1000)
          last_price = last_point.y()
          self.label_last_px.setText(f&quot;Date time: {last_date.strftime('%d-%m-%y %H:%M %S')}  &quot;
                                     f&quot;Price: {last_price:.2f}&quot;)
  
      def start_app(self):
          &quot;&quot;&quot;Start Thread generator&quot;&quot;&quot;
          # This method is supposed to stream data but not the issue, problem is that chart is not updating
          self.qt_timer.timeout.connect(self.update, )
          time_to_wait: int = 250  # milliseconds
          self.qt_timer.start(time_to_wait)
  
      def update(self):
          &quot;&quot;&quot; Update chart and Label with the latest data in Series&quot;&quot;&quot;
          print(&quot;updating chart&quot;)
          self._update_label_last_px()
          # date_px = QDateTime()
          # self.last_data_point['last_date'] = date_px.currentDateTime().toMSecsSinceEpoch()
  
          date_px = datetime.now().timestamp() * 1000
          self.last_data_point['last_date'] = date_px
          # Make up a price
          self.last_data_point['mid_px'] = randint(int((self.low_of_day + 2) * 100),
                                                   int((self.high_of_day - 2) * 100)) / 100
          self.last_data_point['low_date'] = self.low_of_day
          self.last_data_point['high_date'] = self.high_of_day
          print(self.last_data_point)
  
          # Feed datasets and simulate deque
          # https://www.qtcentre.org/threads/67774-Dynamically-updating-QChart
          if self.mid_dataset.count() &gt; self.histo_tick_size:
              self.mid_dataset.remove(0)
              self.low_dataset.remove(0)
              self.high_dataset.remove(0)
  
          self.mid_dataset.append(self.last_data_point['last_date'], self.last_data_point['mid_px'])
          self.low_dataset.append(self.last_data_point['last_date'], self.last_data_point['low_date'])
          self.high_dataset.append(self.last_data_point['last_date'], self.last_data_point['high_date'])
          self.set_xaxis()
          self.set_yaxis()
  
  
  if __name__ == '__main__':
      app = QApplication(sys.argv)
      window = Window()
      window.show()
      window.start_app()
  
      sys.exit(app.exec())
  </code></pre>