title: Real-time plotting in Python using pyqtgraph
body: <p>I am writing an application in Python using Qt (currently PyQt) that receives telemetry (position data) over UDP and displays that data. It can handle several packet types each with several data fields each. I have a separate thread that handles receiving and decoding packets and they emits a signal to send that data to the Qt GUI for display. The data get displayed in several ways: a current value display, scrolling log display, and a 3D view display. All this runs great and in real time with each packet updating at up to about 30Hz.</p>
  
  <p>I want to add another tab that shows a live plot. The user can select which data streams to plot and it will update in real time, showing a sliding window of the last 60sec of data (maybe configurable at some point). However my first approach is particularly slow. It is barely keeping up plotting only one line with our emulator that runs much slower than 30Hz.</p>
  
  <p>I am using pyqtgraph to do the plotting. I have a PlotWidget instantiated in my .ui file and create a PlotDataItem for each data line that could be drawn on the plot. I am using deques to store the data to be plotted, both the value and the time. This way I can quickly add data as it comes in and remove it as it falls outside of the sliding window. I am storing all this in a dict for each packet type and field:</p>
  
  <pre><code>    self.plotData[pktType][field] = {}
      self.plotData[pktType][field]['curve'] = self.pwPlot.plot()
      self.plotData[pktType][field]['starttime'] = time
      self.plotData[pktType][field]['data'] = coll.deque()
      self.plotData[pktType][field]['realtime'] = coll.deque()
      self.plotData[pktType][field]['time'] = coll.deque()
  </code></pre>
  
  <p>'starttime' stores an initial datetime value for computing elapsed seconds. 'realtime' stores datetime values of when each packet was received (I am not currently using this, so I could drop it if it would save time). 'time' stores elapsed seconds from the 'starttime' for easier plotting and 'data' stores the values.</p>
  
  <p>When a packet that comes in, I store data in the deques for each field I might want to parse. I then trim off any data outside the sliding window. Finally, the deque gets packaged in a numpy array and passed to the PlotDataItem setData method. Here's a simplified version of the code that runs for each received packet:</p>
  
  <pre><code>def updatePlot(self, time, pktData):
      pktType = pktData['ptype']
  
      keys = self.getPlottableFromType(pktType) # list of fields that can be plotted
      if keys == None:
          return
      for k in keys:
          self.plotData[pktType][k]['data'].append(pktData[k])
          self.plotData[pktType][k]['realtime'].append(time)
          runtime = (time - self.plotData[pktType][k]['starttime']).total_seconds()
          if self.plotRate == 0:
              self.plotData[pktType][k]['time'].append(runtime)
          else:
              if self.plotData[pktType][k]['time']: # has items
                  nexttime = self.plotData[pktType][k]['time'][-1] + 1. / self.plotRate
              else:
                  nexttime = 0
  
              self.plotData[pktType][k]['time'].append(nexttime)
  
          while (self.plotData[pktType][k]['time'][-1] - self.plotData[pktType][k]['time'][0]) &gt; self.plotRangeSec:
              self.plotData[pktType][k]['data'].popleft()
              self.plotData[pktType][k]['realtime'].popleft()
              self.plotData[pktType][k]['time'].popleft()
  
          self.drawPlot(pktType, k)
  
  def drawPlot(self, pktType, k):
      if self.plotIsEnabled(pktType, k) and self.plotData[pktType][k]['time']: # has items
          npt = np.array(self.plotData[pktType][k]['time'])
          npd = np.array(self.plotData[pktType][k]['data'])
          self.plotData[pktType][k]['curve'].setData(npt, npd)
      else:
          self.plotData[pktType][k]['curve'].clear()
  </code></pre>
  
  <p>self.plotRate can be used to plot the data either using wall time or force the time axis to a fixed update rate. This is useful for using with the emulator since it runs slower than the real system.</p>
  
  <p>First thing I should probably do is not call .clear() every time for plots that are not being plotted (just complicates the logic a little bit). Other than that, anyone have any tips for improving performance? Is the deque to numpy array a good strategy? Is there a better way to update the data since I am only changing a few values per line (adding a point and possibly removing a point or two)?</p>