Error message

  • Notice: Trying to get property of non-object in filter_default_format() (line 532 of /home/ntroutman/webapps/nt_drupal/modules/filter/filter.module).
  • Notice: Undefined variable: options in filter_process_format() (line 911 of /home/ntroutman/webapps/nt_drupal/modules/filter/filter.module).

The Power of Generators: Simple Animation Example for MatPlotLib

I have for some time now been using matplotlib to do all my graphing in python. Its a great package! Recently I've been wanting to do basic animation of graphs, while you'd think it would be simple has turned into a bit of a trek. There is a simple animation example found here: it works okay, but if you try to move the window it freezes, and you can't use anything on the toolbar. Sometimes merely clicking on the window causes it to freeze! Really annoying, and why not quite a bug, its definitely undesirable behavior.

So I've found a very simple fix that leaves the gui reasonably interactive and the window is freely movable. It centers around using a python generator. Calling "yield some_variable" in a function makes the function a generator and causes execution to stop at that point until the generator's next() method is called, this allows us to "pause" the execution of the function.For instance:

def foo()
    i = 0
    while True:
        i += 1
        yield i

gen = foo()
while True:

This prints 1,2,3,4,5, . . . . . forever until you kill the program.

Now, for doing animation in matplotlib the yield statement can be used in the animate() function to "pause" it and return control the gui thread. As it currently stands, once animate() is called it executes until completion, never allowing gui events to be handled, hence the freezing. The solution is changing two lines of code, note that this is for the GTK example but the same principle should be applicable to the other backends. And here is the end result

A simple example of an animated plot using a gtk backend
import time
import numpy as np
import matplotlib
matplotlib.use('GTKAgg') # do this before importing pylab

import matplotlib.pyplot as plt

fig = plt.figure()

ax = fig.add_subplot(111)

def animate():
    tstart = time.time()                  # for profiling
    x = np.arange(0, 2*np.pi, 0.01)       # x-array
    line, = ax.plot(x, np.sin(x))

    for i in np.arange(1,200):
        line.set_ydata(np.sin(x+i/10.0))  # update the data
        fig.canvas.draw()                 # redraw the canvas

        # Here is generator magic that stops execution of animate()
        # till the generators next() method is called
        yield True                        # continue animating
    print 'FPS:' , 200/(time.time()-tstart)
    raise SystemExit

import gobject
print 'adding idle'

# This is some lambda and python magic:
# Python only evaluates defaults for arguments once,
# so the first call to this lambda function sets iter to the generator made (automagically)
# from the animate() function.
# Each subsequent call to the lambda function calls which restarts(unpauses)
# execution of the animate() function,
# until it yeilds again, at which point control goes back to the gui
# These calls are done when the gui is idle, hence why we are registering an idle callback,
gobject.idle_add(lambda iter=animate():

print 'showing'

Add new comment