plotting directly using the renderer

Then I wondered 'Why is this so?' and figured out that the

    > plot()-command (in interactive mode) plots the previous
    > points all over again with each new point. Therefore
    > using setp(line, data=('old data + new point')) made
    > everything faster because now was only one object to
    > handle (which of course is getting complexer and
    > complexer). Up to this point it is exactly the same as
    > described in 'anim.py'.

    > My question is now: Is there a way just to add points to a
    > plot? Because even in the append-method MPL draws

This is one area of the code I have been actively developing in the
last few months -- the ability to selectively draw certain artists and
reblit just a region of the figure. For a tutorial introduction, see
http://www.scipy.org/wikis/topical_software/MatplotlibCookbook

I'll include an example script below. For each marker, an object is
created, but we only draw each marker once so it is much faster than
the typical case when you get an accumulation of independent objects
and all have to be redrawn. I realize the code is complex and not
suitable for use by your students. This was only meant to be a low
level API that we could then provide a user friendly wrapper around.
But since these features are very new (only in CVS and only for TKAgg,
GTKAgg and WXAgg) we haven't gotten to the stage of making it easy to
use. If you have some ideas on an API to make this more friendly,
please share them.

In the example code below on my system (GTKAgg, 3GHz P4, linux) I get
about 134 PPS (points per second) for both N=100 and N=10000 so it
scales fine. However, for 10000 points, 134 PPS is still pretty slow,
because it takes 74 seconds to finish. For large numbers of points,
you can speed it up significantly by only blitting when a certain
number of points have been drawn, eg replacing the blit code with

  if update.cnt%50==0: ax.figure.canvas.blit(ax.bbox)

With this approach, I draw about 1000 PPS on my machine

Here is the example

import time
import matplotlib.numerix as nx
from matplotlib.patches import Circle
from matplotlib.transforms import identity_transform
from pylab import figure, show

N = 100
x = nx.arange(float(N))
y = nx.mlab.rand(N)

fig = figure()
ax = fig.add_subplot(111, autoscale_on=False)
ax.set_xlim(0,N)
ax.set_ylim(-.1, 1.1)

radius = 6/72.*ax.figure.dpi.get() # 6 points in pixels

def update(*args):
  # we want to draw a circle at x,y in data coordinates but have the
  # radius in pyhysical size (points). To accomplish this we use an
  # identity transform and draw a circle at 0,0 with a radius in
  # pixels. We apply an translation offset of x,y in data coords

  if update.cnt==0:
    update.start = time.time()

  trans = identity_transform()
  trans.set_offset( (x[update.cnt], y[update.cnt]), ax.transData)
  circ = Circle( (0,0), radius=radius, transform=trans )
  circ.set_clip_box(ax.bbox)
  #selectively draw the artist with the draw_artist command
  #and just reblit the axes bounding box
  ax.draw_artist(circ)
  ax.figure.canvas.blit(ax.bbox)
  update.cnt+=1
  if update.cnt==len(x):
    print 'PPS', update.cnt/(time.time()-update.start)
    return False
  else: return True
update.cnt=0

# use any GUI timer or idle function you want
import gobject
gobject.idle_add(update)

show()

If you do want to blit every marker when it is created, you can reblit
just the small region that bounds the marker. The "get_window_extent"
call is a bbox that bounds the circle vertices, but you have to pad it
a little to account for the line width. The relevant update is

  l,b,w,h = circ.get_window_extent().get_bounds()
  pad = 3
  bbox = lbwh_to_bbox(l-pad, b-pad, w+2*pad, h+2*pad)
  ...snip...
  ax.figure.canvas.blit(bbox)

which gives me 777 PPS even when blitting every marker.

Here is the complete example

import time
import matplotlib.numerix as nx
from matplotlib.patches import Circle
from matplotlib.transforms import identity_transform, lbwh_to_bbox
from pylab import figure, show

N = 10000
x = nx.arange(float(N))
y = nx.mlab.rand(N)

fig = figure()
ax = fig.add_subplot(111, autoscale_on=False)
ax.set_xlim(0,N)
ax.set_ylim(-.1, 1.1)

radius = 6/72.*ax.figure.dpi.get() # 6 points in pixels

def update(*args):

  if update.cnt==0:
    update.start = time.time()

  trans = identity_transform()
  trans.set_offset( (x[update.cnt], y[update.cnt]), ax.transData)
  circ = Circle( (0,0), radius=radius, transform=trans )
  circ.set_clip_box(ax.bbox)
  l,b,w,h = circ.get_window_extent().get_bounds()
  pad = 3
  bbox = lbwh_to_bbox(l-pad, b-pad, w+2*pad, h+2*pad)
  ax.draw_artist(circ)
  ax.figure.canvas.blit(bbox)
  update.cnt+=1
  if update.cnt==len(x):
    print 'PPS', update.cnt/(time.time()-update.start)
    return False
  else: return True
update.cnt=0

import gobject
gobject.idle_add(update)

show()