Realtime plotting

 A nice screen capture video which shows the plotting of a waveform in realtime using python with pylab.

 Now you probably want to know how it is done? The crux with the matplotlib is that you should not call matplotlib functions in any other thread than the main thread. If you dont want to use the  interactive mode of matplotlib (which is another possibility) you have a issue because the main thread normally  keeps running all the time.  Luckily  you can create Timers with the canvas object of the plot, to install periodically called callback functions. In the callback function you can then call whatever matplotlib function you like because the function is executed in the context of the main thread.

 And here  is how it is done:

#!/usr/bin/env python
# Plot a graph of Data which is comming in on the fly
# uses pylab
# Author: Norbert Feurle
# Date: 12.1.2012
# License: if you get any profit from this then please share it with me
import pylab
from pylab import *

xAchse=pylab.arange(0,100,1)
yAchse=pylab.array([0]*100)

fig = pylab.figure(1)
ax = fig.add_subplot(111)
ax.grid(True)
ax.set_title("Realtime Waveform Plot")
ax.set_xlabel("Time")
ax.set_ylabel("Amplitude")
ax.axis([0,100,-1.5,1.5])
line1=ax.plot(xAchse,yAchse,'-')

manager = pylab.get_current_fig_manager()

values=[]
values = [0 for x in range(100)]

Ta=0.01
fa=1.0/Ta
fcos=3.5

Konstant=cos(2*pi*fcos*Ta)
T0=1.0
T1=Konstant

def SinwaveformGenerator(arg):
  global values,T1,Konstant,T0
  #ohmegaCos=arccos(T1)/Ta
  #print "fcos=", ohmegaCos/(2*pi), "Hz"

  Tnext=((Konstant*T1)*2)-T0
  if len(values)%100>70:
    values.append(random()*2-1)
  else:
    values.append(Tnext)
  T0=T1
  T1=Tnext

def RealtimePloter(arg):
  global values
  CurrentXAxis=pylab.arange(len(values)-100,len(values),1)
  line1[0].set_data(CurrentXAxis,pylab.array(values[-100:]))
  ax.axis([CurrentXAxis.min(),CurrentXAxis.max(),-1.5,1.5])
  manager.canvas.draw()
  #manager.show()

timer = fig.canvas.new_timer(interval=20)
timer.add_callback(RealtimePloter, ())
timer2 = fig.canvas.new_timer(interval=20)
timer2.add_callback(SinwaveformGenerator, ())
timer.start()
timer2.start()

pylab.show()

 As a next step i embedded some Tkinter Widgets into the plot to make the number of points to view and the Waveform generation speed more adjustable:

The Code:

#!/usr/bin/env python
# Plot a graph of Data which is comming in on the fly
# uses pylab
# Author: Norbert Feurle
# Date: 12.1.2012
# License: if you get any profit from this then please share it with me  and only use it for good
import pylab
from pylab import *
import Tkinter
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg

root = Tkinter.Tk()
root.wm_title("Extended Realtime Plotter")

xAchse=pylab.arange(0,100,1)
yAchse=pylab.array([0]*100)

fig = pylab.figure(1)
ax = fig.add_subplot(111)
ax.grid(True)
ax.set_title("Realtime Waveform Plot")
ax.set_xlabel("Time")
ax.set_ylabel("Amplitude")
ax.axis([0,100,-1.5,1.5])
line1=ax.plot(xAchse,yAchse,'-')

canvas = FigureCanvasTkAgg(fig, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1)

toolbar = NavigationToolbar2TkAgg( canvas, root )
toolbar.update()
canvas._tkcanvas.pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1)

values=[]
values = [0 for x in range(100)]

Ta=0.01
fa=1.0/Ta
fcos=3.5

Konstant=cos(2*pi*fcos*Ta)
T0=1.0
T1=Konstant

def SinwaveformGenerator():
  global values,T1,Konstant,T0,wScale2
  #ohmegaCos=arccos(T1)/Ta
  #print "fcos=", ohmegaCos/(2*pi), "Hz"

  Tnext=((Konstant*T1)*2)-T0 
  if len(values)%100>70:
    values.append(random()*2-1)
  else:
    values.append(Tnext)
  T0=T1
  T1=Tnext
  root.after(int(wScale2['to'])-wScale2.get(),SinwaveformGenerator)

def RealtimePloter():
  global values,wScale,wScale2
  NumberSamples=min(len(values),wScale.get())
  CurrentXAxis=pylab.arange(len(values)-NumberSamples,len(values),1)
  line1[0].set_data(CurrentXAxis,pylab.array(values[-NumberSamples:]))
  ax.axis([CurrentXAxis.min(),CurrentXAxis.max(),-1.5,1.5])
  canvas.draw()
  root.after(25,RealtimePloter)
  #canvas.draw()

  #manager.show()

def _quit():
    root.quit()     # stops mainloop
    root.destroy()  # this is necessary on Windows to prevent
                    # Fatal Python Error: PyEval_RestoreThread: NULL tstate

button = Tkinter.Button(master=root, text='Quit', command=_quit)
button.pack(side=Tkinter.BOTTOM)

wScale = Tkinter.Scale(master=root,label="View Width:", from_=3, to=1000,sliderlength=30,length=ax.get_frame().get_window_extent().width, orient=Tkinter.HORIZONTAL)
wScale2 = Tkinter.Scale(master=root,label="Generation Speed:", from_=1, to=200,sliderlength=30,length=ax.get_frame().get_window_extent().width, orient=Tkinter.HORIZONTAL)
wScale2.pack(side=Tkinter.BOTTOM)
wScale.pack(side=Tkinter.BOTTOM)

wScale.set(100)
wScale2.set(wScale2['to']-10)

root.protocol("WM_DELETE_WINDOW", _quit)  #thanks aurelienvlg
root.after(100,SinwaveformGenerator)
root.after(100,RealtimePloter)
Tkinter.mainloop()
#pylab.show()
  1. Hi could you post the code of your second program? Thanks!

  2. “there is a small problem with this code if you dont use the extra added quit button to close the plot, maybe someone has a nice solution”

    To solve this problem, add “root.protocol(“WM_DELETE_WINDOW”, _quit)”
    just before: “root.after(100,SinwaveformGenerator)”.

    Thanks you for your code.

  3. Hi Sir

    I m praveen from IIT-Madras.

    I m the beginner of python.
    Actually i need to draw a waveform for given wave file in python. Using that GUI i want to play, cut , copy in new frame and stop like that. I need your help. Can u help me. Do u have any e-books or tutorials for GUI python, can u send it to me.

    Thanks in advance sir.

    Praveen.M
    IIT-Madras

    • hey men,

      There are actually a cuople of GUI frameworks for python you could use and then check out
      the tutorials/documentations of these frameworks. E.g: pyQt, Tkinter, .. etc. I dont think a graph-plotting library like Mathplotlib would fit your needs.
      greetings
      Norbert

  4. Thank u Sir.

  5. Hi sir

    Now i create a GUI for my requirements with Tkinter Framework.
    I can show the waveform of the wave file and i can play,stop, pause like that.
    Now i need ur help/Suggestions to play only selected portion of the waveform like wave surfer(we can select some part of the wave form and play in wave surfer). Please help me sir to do this task.

    Thanks & Regards
    Praveen.M

  6. This is great. Is there a way to use animation to fire on events or on demand instead of a timer? I have a data acquisition device that calls a function after every n collected samples. Currently, I am using a separate thread, but apparently I shouldn’t do this?

    • Im not sure i completly understand, but like in the secont example you could use the root.after(0,RealtimePloter) method to update the plot only when new data is available, you could use it with the first example by writing: fig.canvas.get_tk_widget().after(0,RealtimePloter)

  7. Hi,
    I am Gurinder Singh Gill from IISc , I was able to plot real time Sensor data using matplotlib’s Animation framework.

    I have few problems namely

    1. I am able to plot 1 line graph sucessfully but have no idea of how to have more than one line i.e. multiple line graph in one plot.

    2. I have designed a gui using pygtk & GTK3 and as I am using plot() function, matplotlib draws a seperate window and pulls the graph on to it. I tried creating an GTK canvas and binding the plot to it but couldnt do it. Can you suggest any method to bing it to gtk.

    thanks a lot, your help will be really appericiated . 🙂

  8. Good work. First program ran flawlessly.
    Second one failed at line 80:
    ‘AxesSubplot’ object has no attribute ‘get_frame’…. using Python27

  9. Hi! I’m trying to do the 2nd example but I have the same problem when I start the program:
    File “my location file”, line “my line”
    root.protocol(“WM_DELETE_WINDOW”, _quit)
    ^
    SyntaxError: invalid syntax

    What do you thing that would be the problem?

    Regards

  10. Aidis Stukas

    Inspiring!

  11. Don’t know if this is being monitored any more, but figured I ask…
    I’m trying to apply your slider widgets to multiple sub-plots in the same figure. While I am able to create a ‘master’ slider to control width of all sub-plots simultaneously, I’m unable to have ‘individual sliders’ to control the individual sub-plots in real time. Any suggestions?

  12. Hi, I’m trying to do the second example but it appears that object has not attribute ‘get_frame’. I investigate and I need to change to ‘patch’ but now,it appears a new error
    wScale = Tkinter.Scale(master=root,label=”View Width:”, from_=3, to=1000,sliderlength=30,length=ax.patch().get_window_extent().width, orient=Tkinter.HORIZONTAL)
    TypeError: ‘Rectangle’ object is not callable

    I dont undestand why?

    • the correct value of length is “ax.patch.get_window_extent().width” …. without parentes on “patch” object

  13. Hi there

    Your code was running nicely (the second one), but after updating matplotlib e tinter it now crashes Python! Any hint of a conflict going on here? (I ran that on a Mac, with Python 3.5.0, matplotlib 1.5.0 and tkinter 8.5.18).

    Thanks!

    M-

  1. Pingback: Realtime plotting with matplotlib embedded in Qt » MrLeehs Blog

Leave a reply to Praveen Cancel reply