Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Support recording movie with animate decorator.
If one has a scene with the movie_maker.record set to True, a movie will
be recorded when the animation is run.
  • Loading branch information
prabhuramachandran committed Jul 3, 2016
commit f3fd83b57137b3ac09309ffbb4d3c529352265b2
116 changes: 74 additions & 42 deletions mayavi/tools/animator.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,60 +126,68 @@ def _delay_changed(self, value):
###############################################################################
# Decorators.

def animate(func=None, delay=500, ui=True):
""" A convenient decorator to animate a generator that performs an
animation. The `delay` parameter specifies the delay (in
milliseconds) between calls to the decorated function. If `ui` is
True, then a simple UI for the animator is also popped up. The
decorated function will return the `Animator` instance used and a
user may call its `Stop` method to stop the animation.
def animate(func=None, delay=500, ui=True, support_movie=True):
"""A convenient decorator to animate a generator that performs an
animation.

If an ordinary function is decorated a `TypeError` will be raised.
The `delay` parameter specifies the delay (in milliseconds) between calls
to the decorated function. If `ui` is True, then a simple UI for the
animator is also popped up. The decorated function will return the
`Animator` instance used and a user may call its `Stop` method to stop the
animation. The `support_movie` parameter is True by default and this
makes it easy to record a movie with the decorator. If this is turned
off, one cannot record a movie of the animation.

**Parameters**
If an ordinary function is decorated a `TypeError` will be raised.

:delay: int specifying the time interval in milliseconds between
calls to the function.
**Parameters**

:ui: bool specifying if a UI controlling the animation is to be
provided.
:delay: int specifying the time interval in milliseconds between
calls to the function.

**Returns**
:ui: bool specifying if a UI controlling the animation is to be
provided.

The decorated function returns an `Animator` instance.
:support_movie: bool specifying if the animation will support
recording of a movie.

**Examples**
**Returns**

Here is the example provided in the Animator class documentation::
The decorated function returns an `Animator` instance.

>>> from mayavi import mlab
>>> @mlab.animate
... def anim():
... f = mlab.gcf()
... while 1:
... f.scene.camera.azimuth(10)
... f.scene.render()
... yield
...
>>> a = anim() # Starts the animation.
**Examples**

For more specialized use you can pass arguments to the decorator::
Here is the example provided in the Animator class documentation::

>>> from mayavi import mlab
>>> @mlab.animate(delay=500, ui=False)
... def anim():
... f = mlab.gcf()
... while 1:
... f.scene.camera.azimuth(10)
... f.scene.render()
... yield
...
>>> a = anim() # Starts the animation without a UI.
>>> from mayavi import mlab
>>> @mlab.animate
... def anim():
... f = mlab.gcf()
... while 1:
... f.scene.camera.azimuth(10)
... f.scene.render()
... yield
...
>>> a = anim() # Starts the animation.

**Notes**
For more specialized use you can pass arguments to the decorator::

>>> from mayavi import mlab
>>> @mlab.animate(delay=500, ui=False)
... def anim():
... f = mlab.gcf()
... while 1:
... f.scene.camera.azimuth(10)
... f.scene.render()
... yield
...
>>> a = anim() # Starts the animation without a UI.

**Notes**

If you want to modify the data plotted by an `mlab` function call,
please refer to the section on: :ref:`mlab-animating-data`.

If you want to modify the data plotted by an `mlab` function call,
please refer to the section on: :ref:`mlab-animating-data`.
"""

class Wrapper(object):
Expand All @@ -189,6 +197,9 @@ def __init__(self, function):
self.func = function
self.ui = ui
self.delay = delay
self._support_movie = support_movie
self._movie_maker = None
self._next = None

def __call__(self, *args, **kw):
if isinstance(self.func, types.GeneratorType):
Expand All @@ -197,7 +208,9 @@ def __call__(self, *args, **kw):
f = self.func(*args, **kw)
if isinstance(f, types.GeneratorType):
_next = f.next if hasattr(f, 'next') else f.__next__
a = Animator(self.delay, _next)
self._next = _next
self._movie_maker = None
a = Animator(self.delay, self._step)
if self.ui:
a.show()
return a
Expand All @@ -206,6 +219,25 @@ def __call__(self, *args, **kw):
'(use yield)!' % (self.func.__name__)
raise TypeError(msg)

def _step(self):
try:
self._next()
if self._support_movie:
self._update_movie_maker()
except StopIteration:
if self._support_movie:
self._movie_maker.animation_stop()
raise

def _update_movie_maker(self):
if self._movie_maker is None:
from .engine_manager import get_engine
scene = get_engine().current_scene.scene
self._movie_maker = scene.movie_maker
self._movie_maker.animation_start()
else:
self._movie_maker.animation_step()

def decorator_call(self, func, *args, **kw):
return self(*args, **kw)

Expand Down
34 changes: 18 additions & 16 deletions tvtk/pyface/movie_maker.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,19 @@ def default_traits_view(self):
return view

def animation_start(self):
self._count = 0
self._save_scene(self._count)
if self.record:
if self.use_default_directory:
self.directory = self._directory_default()
self._count = 0
self._save_scene(self._count)

def animation_step(self):
self._count += 1
self._save_scene(self._count)
if self.record:
self._count += 1
self._save_scene(self._count)

def animation_stop(self):
if self.use_default_directory:
self.directory = self._directory_default()
pass

@contextmanager
def record_movie(self):
Expand All @@ -66,17 +69,16 @@ def record_movie(self):
self.record = False

def _save_scene(self, count):
if self.record:
dir = self.directory
if not os.path.exists(dir):
os.makedirs(dir)
dir = self.directory
if not os.path.exists(dir):
os.makedirs(dir)

fname = os.path.join(dir, self.filename%count)
if not self.anti_alias:
orig_aa = self.scene.anti_aliasing_frames
self.scene.save(fname)
if not self.anti_alias:
self.scene.anti_aliasing_frames = orig_aa
fname = os.path.join(dir, self.filename%count)
if not self.anti_alias:
orig_aa = self.scene.anti_aliasing_frames
self.scene.save(fname)
if not self.anti_alias:
self.scene.anti_aliasing_frames = orig_aa

def _directory_default(self):
home = get_home_directory()
Expand Down