Do not store the figure as an attribute to be able to show the same figure multiple times

This commit is contained in:
Lucas Verney 2016-03-25 18:58:57 +01:00
parent a404a37749
commit 623746d5ea
3 changed files with 1612 additions and 45 deletions

File diff suppressed because one or more lines are too long

View File

@ -6,3 +6,10 @@ __VERSION__ = "0.0.1"
# Default subplot group (reserved name) # Default subplot group (reserved name)
DEFAULT_GROUP = "_" DEFAULT_GROUP = "_"
# Default animation arguments
DEFAULT_ANIMATION_KWARGS = {
"frames": 200,
"interval": 20,
"blit": True,
}

View File

@ -91,8 +91,6 @@ class Figure():
self.savepath = savepath self.savepath = savepath
self.custom_mpl_rc = custom_mpl_rc self.custom_mpl_rc = custom_mpl_rc
# Working attributes # Working attributes
self.figure = None
self.axes = None
self.animation = {"type": False, self.animation = {"type": False,
"args": (), "kwargs": {}, "args": (), "kwargs": {},
"persist": []} "persist": []}
@ -126,9 +124,9 @@ class Figure():
if len(args) == 0 and self.savepath is not None: if len(args) == 0 and self.savepath is not None:
args = (self.savepath,) args = (self.savepath,)
self.render() figure = self.render()
if self.figure is not None: if figure is not None:
self.figure.savefig(*args, **kwargs) figure.savefig(*args, **kwargs)
else: else:
raise exc.InvalidFigure("Invalid figure.") raise exc.InvalidFigure("Invalid figure.")
@ -136,37 +134,36 @@ class Figure():
""" """
Render and show the :class:`Figure` object. Render and show the :class:`Figure` object.
""" """
self.render() figure = self.render()
if self.figure is not None: if figure is not None:
self.figure.show() figure.show()
else: else:
raise exc.InvalidFigure("Invalid figure.") raise exc.InvalidFigure("Invalid figure.")
def render(self): def render(self):
""" """
Actually render the figure. Update ``self.figure`` attribute, but do \ Actually render the figure.
not show or save it.
:returns: None. :returns: A :mod:`matplotlib` figure object.
""" """
# Use custom matplotlib context # Use custom matplotlib context
with plt.rc_context(rc=custom_mpl.custom_rc(rc=self.custom_mpl_rc)): with plt.rc_context(rc=custom_mpl.custom_rc(rc=self.custom_mpl_rc)):
if self.figure is None or self.axes is None:
# Create figure if necessary # Create figure if necessary
self._render_grid() figure, axes = self._render_grid()
# Render depending on animation type # Render depending on animation type
if self.animation["type"] is False: if self.animation["type"] is False:
self._render_no_animation() self._render_no_animation(axes)
elif self.animation["type"] == "gif": elif self.animation["type"] == "gif":
self._render_gif_animation() self._render_gif_animation(figure, axes)
elif self.animation["type"] == "animation": elif self.animation["type"] == "animation":
# TODO # TODO
return None return None
else: else:
return None return None
# Use tight_layout to optimize layout, use custom padding # Use tight_layout to optimize layout, use custom padding
self.figure.tight_layout(pad=1) figure.tight_layout(pad=1) # TODO: Messes up animations
return figure
def set_grid(self, grid_description=None, def set_grid(self, grid_description=None,
height=None, width=None, ignore_groups=False, height=None, width=None, ignore_groups=False,
@ -464,7 +461,7 @@ class Figure():
def _render_grid(self): def _render_grid(self):
""" """
Helper method to fill ``self.figure`` and ``self.axes`` with \ Helper method to create figure and axes with \
subplots according to the grid description. subplots according to the grid description.
:returns: A tuple containing the figure object as first element, and \ :returns: A tuple containing the figure object as first element, and \
@ -505,9 +502,7 @@ class Figure():
# Set the axis for every subplot # Set the axis for every subplot
for subplot in self.plots: for subplot in self.plots:
axes[subplot] = axis axes[subplot] = axis
# Set attributes return (figure, axes)
self.figure = figure
self.axes = axes
def _set_axes_properties(self, axis, group_): def _set_axes_properties(self, axis, group_):
""" """
@ -546,17 +541,19 @@ class Figure():
# Set yrange # Set yrange
render_helpers.set_axis_property(group_, axis.set_ylim, self.yrange) render_helpers.set_axis_property(group_, axis.set_ylim, self.yrange)
def _render_gif_animation(self): def _render_gif_animation(self, figure, axes):
""" """
Handle the render of a GIF-like animation, cycling through the plots. Handle the render of a GIF-like animation, cycling through the plots.
:returns: A :mod:`matplotlib` figure. :param figure: A :mod:`matplotlib` figure.
:param axes: A dict mapping the symbols of the groups to matplotlib \
axes as second element.
""" """
# Init # Init
# TODO # TODO
self.axes[constants.DEFAULT_GROUP].set_xlim((-2, 2)) axes[constants.DEFAULT_GROUP].set_xlim((-2, 2))
self.axes[constants.DEFAULT_GROUP].set_ylim((-2, 2)) axes[constants.DEFAULT_GROUP].set_ylim((-2, 2))
line, = self.axes[constants.DEFAULT_GROUP].plot([], []) line, = axes[constants.DEFAULT_GROUP].plot([], [])
# Define an animation function (closure) # Define an animation function (closure)
def animate(i): def animate(i):
# TODO # TODO
@ -565,34 +562,30 @@ class Figure():
line.set_data(x, y) line.set_data(x, y)
return line, return line,
# Set default kwargs # Set default kwargs
default_args = (self.figure, animate) args = (figure, animate)
default_kwargs = { kwargs = constants.DEFAULT_ANIMATION_KWARGS
"frames": 200,
"interval": 20,
"blit": True,
}
# Update with overloaded arguments # Update with overloaded arguments
default_args = default_args + self.animation["args"] args += self.animation["args"]
default_kwargs.update(self.animation["kwargs"]) kwargs.update(self.animation["kwargs"])
# Keep track of animation object, as it has to persist # Keep track of animation object, as it has to persist
self.animation["persist"] = [ self.animation["persist"] = [
animation.FuncAnimation(*default_args, **default_kwargs)] animation.FuncAnimation(*args, **kwargs)]
return self.figure
def _render_no_animation(self): def _render_no_animation(self, axes):
""" """
Handle the render of the figure when no animation is used. Handle the render of the figure when no animation is used.
:returns: A :mod:`matplotlib` figure. :param axes: A dict mapping the symbols of the groups to matplotlib \
axes as second element.
""" """
# Add plots # Add plots
for group_ in self.plots: for group_ in self.plots:
# Get the axis corresponding to current group # Get the axis corresponding to current group
try: try:
axis = self.axes[group_] axis = axes[group_]
except KeyError: except KeyError:
# If not found, plot in the default group # If not found, plot in the default group
axis = self.axes[constants.DEFAULT_GROUP] axis = axes[constants.DEFAULT_GROUP]
# Skip this plot if the axis is None # Skip this plot if the axis is None
if axis is None: if axis is None:
continue continue
@ -618,4 +611,3 @@ class Figure():
tmp_plot.set_clip_on(False) tmp_plot.set_clip_on(False)
# Set ax properties # Set ax properties
self._set_axes_properties(axis, group_) self._set_axes_properties(axis, group_)
return self.figure