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()
Hi could you post the code of your second program? Thanks!
“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.
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
Thank u Sir.
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
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)
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 . 🙂
Good work. First program ran flawlessly.
Second one failed at line 80:
‘AxesSubplot’ object has no attribute ‘get_frame’…. using Python27
i am also using python27. Maybe we have different mathplottlib versions?
I am getting the same error. I am running matplotlib 1.3.0 on python 2.7. I noticed there is a new version…
Update: I installed matplotlib version 1.3.1 and got the same error.
By the way, thanks for posting your code!
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
what Versions are you using? It works fine here. Mabe it is a indentation problem?
Inspiring!
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?
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
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-
Pingback: Realtime plotting with matplotlib embedded in Qt » MrLeehs Blog