Strange, strange. iPython is apparently having some effect here: I tried
ipython -pylab animation.py
and I got a figure with the initial plot, but nothing further
happened. After closing the plot, I typed
run animation.py
at the ipython prompt, and saw the initial plot, erasure, and
animation that I expected. Can you try running with a plain
python animation.py
I've attached a new version of the script with some changes suggested
offline by Ryan May - he was seeing platform or wx-specific behavior
related to event handling. All bugs remaining are mine :)
-Eric
On Sun, Nov 23, 2008 at 6:20 PM, Cohen-Tanugi Johann
<[EMAIL PROTECTED]> wrote:
>
>> hi, I tried your script, commenting/uncommenting the backend line, but I
>> still get:
>> [EMAIL PROTECTED] ~]$ ipython -pylab MACROS/animation.py
>>
>> ---------------------------------------------------------------------------
>> AttributeError Traceback (most recent call
>> last)
>>
>> /home/cohen/MACROS/animation.py in <module>()
>> 274 p.show()
>> 275
>> --> 276 t = test()
>> 277
>> 278
>>
>> /home/cohen/MACROS/animation.py in __init__(self)
>> 270
>> 271 # cid = p.gcf().canvas.mpl_connect('idle_event',
>> self.update)
>> --> 272 wx.GetApp().Bind(wx.EVT_IDLE, self.update)
>> 273
>> 274 p.show()
>>
>> AttributeError: 'NoneType' object has no attribute 'Bind'
>> WARNING: Failure executing file: <MACROS/animation.py>
>>
>> I am using MPL revision 6440.
>> cheers,
>> Johann
>>
>> Eric Bruning wrote:
>>>
>>> Hi Eric,
>>>
>>>>
>>>> On Fri, Nov 21, 2008 at 11:19 AM, Eric Jonas <[EMAIL PROTECTED]> wrote:
>>>>
>>>>>
>>>>> I've looked through the latest examples as well as google and the list
>>>>> archives, and am still at a loss -- can anyone point me to an example
>>>>> of
>>>>> how to animate a scatter plot?
>>>>>
>>>
>>> I've attached a somewhat unpolished but functional example that shows
>>> time-synchronized animation of multiple scatter plots. It uses the wx
>>> backend explicitly, due to some issues with the idle event handling.
>>>
>>>
>>>>>
>>>>> The collection returned by scatter() lacks anything akin to a set_data
>>>>> method.
>>>>>
>>>
>>> Yup, you want this instead:
>>> collection.set_offsets(xy)
>>> collection.set_array(s)
>>>
>>> -Eric
>>> ------------------------------------------------------------------------
>>>
>>> -------------------------------------------------------------------------
>>> This SF.Net email is sponsored by the Moblin Your Move Developer's
>>> challenge
>>> Build the coolest Linux based applications with Moblin SDK & win great
>>> prizes
>>> Grand prize is a trip for two to an Open Source event anywhere in the
>>> world
>>> http://moblin-contest.org/redirect.php?banner_id=100&url=/
>>> ------------------------------------------------------------------------
>>>
>>> _______________________________________________
>>> Matplotlib-users mailing list
>>> Matplotlib-users@lists.sourceforge.net
>>> https://lists.sourceforge.net/lists/listinfo/matplotlib-users
>>
>
import matplotlib
# matplotlib.use('WxAgg')
import time
import wx
import sys
import numpy as np
class AnimationController(object):
def __init__(self, duration, animations, leader=None):
""" Single controller of animations. Manages binding to wx.
Binding to wx in each individual animator blocks drawing of all
but the last animator.
duration: Total time to taken to draw the animation. Approximate, but
won't be exceeded by more than one frame redraw interval.
animations: sequence of Animate objects that are to be animated
simultaneously.
"""
self.animations = animations
self.duration = duration
if leader == None:
self.leader = animations[0]
else:
self.leader = leader
# When animating by points, only the lead artist should animate by points,
# and the others should animate by data intervals up to the appropriate time.
if self.leader.framing == 'point_intervals':
for animation in self.animations:
if animation != self.leader:
animation.framing = 'data_intervals'
self.tstart = time.time()
wx.GetApp().Bind(wx.EVT_IDLE, self.control_update)
# use a timer to generate new idle_events, so that the frame rate is limited.
# calling WakeUpIdle at the end of
self.idle_kick = wx.Timer()
wx.GetApp().Bind(wx.EVT_TIMER, lambda e: wx.WakeUpIdle(), self.idle_kick)
self.idle_kick.Start(40, oneShot=False)
# self.cid = self.animations[0].artist.axes.figure.canvas.mpl_connect('idle_event', self.control_update)
def control_update(self, *args):
time_fraction = (time.time() - self.tstart) / self.duration
# only restore the background for the first frame drawn.
# otherwise, only the last animation will appear to animate
restore = True
# handle the animation leader first
animation = self.leader
selection = self.leader.get_selection(time_fraction)
current_native_order_value = self.leader._order[selection].max()
animation.update(time_fraction, selection=None, restore=restore)
# did first blit, so don't restore background again
restore = False
for animation in self.animations:
# already did the leader
if animation != self.leader:
selection = animation._order <= current_native_order_value
animation.update(time_fraction, selection=selection, restore=restore)
# restore=False
if time_fraction >= 1.0:
self.control_cleanup()
event = args[0]
# the two calls below should have the same effect, now handled by the timer in the init.
# event.RequestMore()
# wx.WakeUpIdle()
def control_cleanup(self):
wx.GetApp().Unbind(wx.EVT_IDLE)
wx.GetApp().Unbind(wx.EVT_TIMER)
# self.animations[0].artist.axes.figure.canvas.mpl_disconnect(self.cid)
for animation in self.animations:
animation.cleanup()
class Animate(object):
""" Base class that provides a framework for animating Artists.
Artist-specific subclasses should implement setup and update_artist
methods that set artist specific instance variables and do artist-specifc
drawing, respectively.
"""
def update_artist(self, selection):
""" Must be implemented by an artist-specific subclass.
It is called with a boolean selection array that is used
to select data pertinent to each frame of the animation.
Subclasses should update the data in the artist instance.
"""
raise NotImplementedError
def setup(self):
""" Must be implemented by an artist-specific subclass.
Used to set up artist-specific instance variables.
"""
raise NotImplementedError
def __init__(self, artist, order, framing='data_intervals', order_min=None, order_max=None):
""" artist: An artist instance, such as from ax.scatter
order: Array of values of same length as collection items.
The range of values in order is divided up evenly
among frames.
framing: one of ('data_intervals', 'point_intervals'). Defaults
to 'data_intervals'. These options are most easily understood
by considering order as time. Framing by data_intervals displays
data with the same relative spacing in time. The effect is
of simulating what the eye would have seen compressed into
4.0 seconds. Framing by point_intervals displays the same
number of events per frame, which is useful for emphasizing
more data-dense regions.
order_min, order_max: Defaults to min/max of order. Override if drawing
multiple artists with different min/max of order that
need to be simultaneously animated,
"""
self.artist = artist
self.animated_cache = self.artist.get_animated()
self.framing = framing
total_points = order.shape[0]
if framing == 'data_intervals':
self.order = order
self._order = order
elif framing == 'point_intervals':
self._order = order # hold on to the order in data coordintes
# construct a new order vector like so:
# self._order = order = [3.0, 1.0, 2.5, 1.7, 2.6, 2.9]
# self.order = [ 5, 0, 2, 1, 3, 4]
self.order = np.empty(total_points, dtype=int)
self.order[order.argsort()] = np.arange(total_points)
order_min=None
order_max=None
else:
self.order = order
if order_min == None:
self.order_min = self.order.min()
else:
self.order_min = order_min
if order_max == None:
self.order_max = self.order.max()
else:
self.order_max = order_max
self.order_range = (self.order_max - self.order_min)
self.tstart = time.time()
self.background = None
self.artist.set_animated(True)
# hide the collection
self.artist.set_visible(False)
self.need_first_idle = True
self.setup()
self.artist.axes.figure.canvas.draw()
def get_selection(self, time_fraction):
# some percentage of the total time has passed,
# so draw up to that many points.
order_current_value = self.order_min + self.order_range * time_fraction
# no requirement that order be sorted.
sel = self.order <= order_current_value
return sel
def update(self, time_fraction, selection=None, restore=True):
ax = self.artist.axes
canvas = ax.figure.canvas
# First idle event: do nothing. allows all drawing to flush through MPL.
# Allows the set_visible=False from __init__ to take effect.
if self.need_first_idle == True:
self.need_first_idle=False
return
if (self.background == None):
# do nothing on first idle event
# should be copying a blank background
self.background = canvas.copy_from_bbox(ax.bbox)
# now, allow the collection to show up.
self.artist.set_visible(True)
# restore everything minus the scatter plot
if restore:
canvas.restore_region(self.background)
if selection == None:
sel = self.get_selection(time_fraction)
else:
sel = selection
self.update_artist(sel)
ax.draw_artist(self.artist)
canvas.blit(ax.bbox)
def cleanup(self):
self.artist.set_animated(self.animated_cache)
class CollectionAnimate(Animate):
""" Animate a scatter plot, taking some duration to draw the points. """
def setup(self):
"""set up instance variables specific to the collection artist"""
self.offsets = self.artist.get_offsets()
self.array = self.artist.get_array()
def update_artist(self, selection):
"""update the collection with plot data specifc to this frame."""
xy = self.offsets[selection,:]
s = self.array[selection]
self.artist.set_offsets(xy)
self.artist.set_array(s)
if __name__ == '__main__':
import pylab as p
class test(object):
def update(self, *args):
if not self.ran_update:
print "Allowing time to admire the original plot..."
time.sleep(2)
print "proceeding."
sc, sc2 = self.scatters
# ----- ADJUST -----
framing='data_intervals'
# framing='point_intervals'
anim = CollectionAnimate(sc, self.order, framing=framing)
anim2 = CollectionAnimate(sc2, self.order, framing=framing)
anim_ctrl = AnimationController(4.0, [anim, anim2])
self.ran_update = True
def __init__(self):
NPTS = 1000
ax = p.subplot(111)
x = np.arange(NPTS)
y = np.arange(NPTS)
# ----- ADJUST -----
# demonstrates that the colors fill in in the correct order
# t = np.random.rand(NPTS)
# demonstrates the difference between plotting by points and plotting by time.
t = np.asarray([0.2]*int(0.5*NPTS) + [0.8]*int(0.4*NPTS) + [0.9]*int(0.1*NPTS))
sc = p.scatter(x,y,c=t, vmin=0, vmax=1, animated=False, edgecolor='none')
sc2 = p.scatter(x, -y, c=t, vmin=0, vmax=1, animated=False, edgecolor='none')
ax.axis((0,NPTS,-NPTS,NPTS))
self.order = t
self.scatters = (sc, sc2)
# could have contents of the update method here, and it will animate fine.
# Delaying the animation to the first idle event allows us to
# demonstrate that the animation will work after an initial draw of
# the complete plot.
self.ran_update = False
# cid = p.gcf().canvas.mpl_connect('idle_event', self.update)
wx.GetApp().Bind(wx.EVT_IDLE, self.update)
p.show()
t = test()
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users