title: PyQtGraph stops updating and freezes when grapahing live sensor data
body: <p>I am using PyQt5 and pyqtgraph to plot live sensor data. This graph is part of a bigger PyQt5 application which is used to interact with various hardware and also visualize the sensor data.</p>
  <p><strong>Background:</strong> Below code is a very simplified example for the code that is responsible for querying the sensor for data and then plotting a graph of the instantaneous position and its moving average. The sensor is queried in a separate thread at every x ms intervals.</p>
  <p><strong>Problem:</strong>  The graphing and sensor reading works as expected. However, after running the application for a few seconds, the pyqtgraph stops updating and freezes. Once the graph freezes, the only time I see the graph refresh/update is if I try to resize the window or give focus to another window and refocus back to the graph window. In such cases the graph will be updated only once and it doesn't continue to refresh.</p>
  <p>I have read other user having similar issue in links like below. However the proposed solution was not to update the GUI from a separate thread. In my case, I am NOT updating the graph from a separate thread. I am only using the separate thread to collect sensor data then emit a signal which with the new data. The updating of the graph occurs in the main thread.</p>
  <ul>
  <li><a href="https://groups.google.com/g/pyqtgraph/c/ajykxBvysEc" rel="nofollow noreferrer">https://groups.google.com/g/pyqtgraph/c/ajykxBvysEc</a></li>
  <li><a href="https://stackoverflow.com/questions/57012643/pyqtgraph-imageview-freezes-when-multithreaded">pyqtgraph ImageView Freezes when multithreaded</a></li>
  </ul>
  <p><a href="https://i.stack.imgur.com/kOfA6.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/kOfA6.gif" alt="enter image description here" /></a></p>
  <pre class="lang-py prettyprint-override"><code>import time 
  import numpy as np
  from threading import Thread
  import pyqtgraph as pg
  import bottleneck as bn
  import PyQt5
  
  class MySensor():
      def get_position(self, mean=0.0, standard_dev=0.1):
          # Random sensor data 
          return np.random.normal(mean,standard_dev,1)[0]
  
  class SignalCommunicate(PyQt5.QtCore.QObject):
      # https://stackoverflow.com/a/45620056
      got_new_sensor_data = PyQt5.QtCore.pyqtSignal(float, float)
      position_updated = PyQt5.QtCore.pyqtSignal(float)
  
  class LiveSensorViewer():
  
      def __init__(self, sensor_update_interval=25):
          # super().__init__()
          
          # How frequently to get sensor data and update graph 
          self.sensor_update_interval = sensor_update_interval
  
          # Init sensor object which gives live data 
          self.my_sensor = MySensor()
          
          # Init with default values
          self.current_position = self.my_sensor.get_position(mean=0.0, standard_dev=0.1)
          self.current_position_timestamp = time.time()
  
          # Init array which stores sensor data 
          self.log_time = [self.current_position_timestamp] 
          self.log_position_raw = [self.current_position] 
          self.moving_avg = 5
  
          # Define the array size on max amount of data to store in the list 
          self.log_size = 1 * 60 * 1000/self.sensor_update_interval  
  
          # Setup the graphs which will display sensor data 
          self.plot_widget = pg.GraphicsLayoutWidget(show=True)
          self.my_graph = self.plot_widget.addPlot(axisItems = {'bottom': pg.DateAxisItem()})
          self.my_graph.showGrid(x=True, y=True, alpha=0.25)
          self.my_graph.addLegend()
  
          # Curves to be drawn on the graph 
          self.curve_position_raw = self.my_graph.plot(self.log_time, self.log_position_raw, name='Position raw (mm)', pen=pg.mkPen(color='#525252'))
          self.curve_position_moving_avg = self.my_graph.plot(self.log_time, self.log_position_raw, name='Position avg. 5 periods (mm)', pen=pg.mkPen(color='#FFF'))
  
          # A dialog box which displays the sensor value only. No graph. 
          self.my_dialog = PyQt5.QtWidgets.QWidget()
          self.verticalLayout = PyQt5.QtWidgets.QVBoxLayout(self.my_dialog)
      
          self.my_label = PyQt5.QtWidgets.QLabel()
          self.verticalLayout.addWidget(self.my_label)
          self.my_label.setText('Current sensor position:')
  
          self.my_sensor_value = PyQt5.QtWidgets.QDoubleSpinBox()
          self.verticalLayout.addWidget(self.my_sensor_value)
          self.my_sensor_value.setDecimals(6)
  
          self.my_dialog.show()
  
          # Signals that can be emitted 
          self.signalComm = SignalCommunicate()
           # Connect the signal 'position_updated' to the QDoubleSpinBox 
          self.signalComm.position_updated.connect(self.my_sensor_value.setValue)
          
          # Setup thread which will continuously query the sensor for data 
          self.position_update_thread = Thread(target=self.read_position, args=(self.my_sensor, self.sensor_update_interval))
          self.position_update_thread.daemon = True
          self.position_update_thread.start() # Start the thread to query sensor data 
  
      def read_position(self, sensor_obj, update_interval ):
          # This function continuously runs in a seprate thread to continuously query the sensor for data 
  
          sc = SignalCommunicate()
          sc.got_new_sensor_data.connect(self.handle_sensor_data)
  
          while True:
              # Get data and timestamp from sensor 
              new_pos = sensor_obj.get_position(mean=0.0, standard_dev=0.1)
              new_pos_time = time.time()
  
              # Emit signal with sensor data and  timestamp 
              sc.got_new_sensor_data.emit(new_pos, new_pos_time)
  
              # Wait before querying the sensor again 
              time.sleep(update_interval/1000)
  
      def handle_sensor_data(self, new_pos, new_pos_time ):
  
          # Get the sensor position/timestamp emitted from the separate thread 
          self.current_position_timestamp = new_pos_time
          self.current_position = new_pos
  
          # Emit a singal with new position info 
          self.signalComm.position_updated.emit(self.current_position)
  
          # Add data to log array 
          self.log_time.append(self.current_position_timestamp)
          if len(self.log_time) &gt; self.log_size:
              # Append new data to the log and remove old data to maintain desired log size 
              self.log_time.pop(0)
  
          self.log_position_raw.append(self.current_position)
          if len(self.log_position_raw) &gt; self.log_size:
              # Append new data to the log and remove old data to maintain desired log size 
              self.log_position_raw.pop(0)
  
          if len(self.log_time) &lt;= self.moving_avg:
              # Skip calculating moving avg if only 10 data points collected from sensor to prevent errors 
              return 
          else:
              self.calculate_moving_avg()
          
          # Request a graph update 
          self.update_graph()
  
      def calculate_moving_avg(self):
          # Get moving average of the position 
          self.log_position_moving_avg = bn.move_mean(self.log_position_raw, window=self.moving_avg, min_count=1)
  
      def update_graph(self):
          self.curve_position_raw.setData(self.log_time, self.log_position_raw)
          self.curve_position_moving_avg.setData(self.log_time, self.log_position_moving_avg)
  
  if __name__ == '__main__':
      import sys
      from PyQt5 import QtWidgets
  
      app = QtWidgets.QApplication(sys.argv)
  
      z = LiveSensorViewer()
  
      app.exec_()
      sys.exit(app.exec_())   
  </code></pre>