Multiple updating plot with pyqtgraph in Python

cyberdyne picture cyberdyne · Nov 18, 2014 · Viewed 10.9k times · Source

I have to plot 3 updating curves of data I read from a sensor. The updating plot is very fast when I use just a curve but when I try to plot them all each of them is drastically slower. The code I use is following:

#!/usr/bin/python


from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg

import time
import numpy as np


app = QtGui.QApplication([])



win = pg.GraphicsWindow()

p1 = win.addPlot()
p2 = win.addPlot()
p3 = win.addPlot()


curve1 = p1.plot()

curve2 = p2.plot()

curve3 = p3.plot()

readData = [0.0, 0.0, 0.0]
y1=[0.0]
y2=[0.0]
y3=[0.0]

temp = [0.0]

start = time.time()

def update():
    global curve1, curve2, curve3
    t = time.time()-start         # measure of time as x-coordinate
    readData= readfun()        #function that reads data from the sensor it returns a list of 3 elements as the y-coordinates for the updating plots
    y1.append(readData[0])
    y2.append(readData[1])
    y3.append(readData[2])
    temp.append(t)

    curve1.setData(temp,y1)
    curve2.setData(temp,y2)
    curve3.setData(temp,y3)
    app.processEvents()



timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)




if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_'):
        QtGui.QApplication.instance().exec_()

How can I speed up the updating plotting for the three curves ? Thanks

EDIT: Inspired by dirkjot's solution I want edit my above code in the case someone will need it for the same purpose. It works fine:

#!/usr/bin/python


from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg

import time
import numpy as np


app = QtGui.QApplication([])

win = pg.GraphicsWindow()

p1 = win.addPlot()
p2 = win.addPlot()
p3 = win.addPlot()


curve1 = p1.plot()

curve2 = p2.plot()

curve3 = p3.plot()

readData = [0.0, 0.0, 0.0]
y1=np.zeros(1000,dtype=float)
y2=np.zeros(1000,dtype=float)
y3=np.zeros(1000,dtype=float)

indx = 0
def update():
    global curve1, curve2, curve3, indx, y1,y2,y3

    readData= readfun()        #function that reads data from the sensor it returns a list of 3 elements as the y-coordinates for the updating plots
    y1[indx]=readData[0]
    y2[indx]=readData[1]
    y3[indx]=readData[2]

    if indx==99:
       y1=np.zeros(1000,dtype=float)
       y2=np.zeros(1000,dtype=float)
       y3=np.zeros(1000,dtype=float)
    else:
       indx+=1
    curve1.setData(y1)
    curve2.setData(y2)
    curve3.setData(y3)
    app.processEvents()



timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)




if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_'):
        QtGui.QApplication.instance().exec_()

Answer

dirkjot picture dirkjot · Dec 19, 2014

One problem is that you are appending to lists. This gets to be very slow when the lists grow large, as Python has to copy the full list to a new location that is one cell larger than the previous one. You could test this by running your code again with the appends removed, but continuing to read the data (which you throw away for that test).

If that is the problem, there are several solutions:

  1. preallocate a big list (say [None] * 1000) and keep a counter where you are writing. Wrap around when you reach 1k. This way, you get a continuous updating display like you see that on an old fashioned scope.
  2. Use numpy and preallocate a fixed buffer of say 1k samples. Add samples to the end (position -10 to -1). When you reach -1, move the buffer content 10 to the left in an efficient numpy data statement (something like buffer[0:-10] = buffer[10:], but there may be a dedicated numpy shift instruction) and start writing at -10 again.