Animations
Animations#
The capabilities of dantro
and its plotting framework make it really simple to
let your plot function support animation.
Say you have defined the following plot function, making use of the PlotHelper
:
from utopya import DataManager, UniverseGroup
from utopya.plotting import UniversePlotCreator, is_plot_func, PlotHelper
@is_plot_func(creator_type=UniversePlotCreator)
def plot_some_data(dm: DataManager, *, hlpr: PlotHelper,
uni: UniverseGroup,
data_path: str,
time: int,
**plot_kwargs):
"""Plots the data at `data_path` for the selected `time`."""
# Get the y-data
y_data = uni[data_path][time]
# Via plot helper, perform a line plot
hlpr.ax.plot(y_data, **plot_kwargs)
# Dynamically provide some information to the plot helper
hlpr.provide_defaults('set_title', title=data_path)
hlpr.provide_defaults('set_labels', y=dict(label=data_path))
All you need to do to make this function support animation is to mark it as supporting animation and add an update function:
from utopya import DataManager, UniverseGroup
from utopya.plotting import UniversePlotCreator, is_plot_func, PlotHelper
@is_plot_func(creator_type=UniversePlotCreator, supports_animation=True)
def plot_some_data(dm: DataManager, *, hlpr: PlotHelper,
uni: UniverseGroup,
data_path: str,
time: int,
**plot_kwargs):
"""Plots the data at `data_path` for the selected `time`."""
# Get the y-data
y_data = uni[data_path][time]
# Via plot helper, perform a line plot
hlpr.ax.plot(y_data, **plot_kwargs)
# Dynamically provide some information to the plot helper
hlpr.provide_defaults('set_title', title=data_path)
hlpr.provide_defaults('set_labels', y=dict(label=data_path))
# End of regular plot function
# Define update function
def update():
"""The animation update function: a python generator"""
# Go over all available times
for idx, y_data in enumerate(uni[data_path]):
# Clear the plot and plot anew
hlpr.ax.clear()
hlpr.ax.plot(y_data, **plot_kwargs)
# Set the title
hlpr.invoke_helper('set_title', title="Time {}".format(idx))
# Done with this frame. Yield control to the plot framework,
# which will take care of grabbing the frame.
yield
# Register the animation update with the helper
hlpr.register_animation_update(update)
The following things changed:
an
update
function was defined,the
update
function was passed to the helper viaregister_animation_update
, andthe plot function was marked as
supports_animation
.
- class dantro.plot_creators.PlotHelper(*, out_path: str, helper_defaults: Optional[dict] = None, update_helper_cfg: Optional[dict] = None, raise_on_error: bool = True, animation_enabled: bool = False)[source]
Bases:
object
The PlotHelper takes care of the figure setup and saving and allows accessing matplotlib utilities through the plot configuration.
Initialize a Plot Helper with a certain configuration.
This configuration is the so-called “base” configuration and is not axis-specific. There is the possibility to specify axis-specific configuration entries.
All entries in the helper configuration are deemed ‘enabled’ unless they explicitly specify enabled: false in their configuration.
- Parameters
out_path (str) – path to store the created figure. This may be an absolute path or a relative path; the latter is regarded as relative to the current working directory. The home directory indicator
~
is expanded.helper_defaults (dict, optional) – The basic configuration of the helpers.
update_helper_cfg (dict, optional) – A configuration used to update the existing helper defaults
raise_on_error (bool, optional) – Whether to raise on an exception created on helper invocation or just log the error
animation_enabled (bool, optional) – Whether animation mode is enabled.
- __init__(*, out_path: str, helper_defaults: Optional[dict] = None, update_helper_cfg: Optional[dict] = None, raise_on_error: bool = True, animation_enabled: bool = False)[source]
Initialize a Plot Helper with a certain configuration.
This configuration is the so-called “base” configuration and is not axis-specific. There is the possibility to specify axis-specific configuration entries.
All entries in the helper configuration are deemed ‘enabled’ unless they explicitly specify enabled: false in their configuration.
- Parameters
out_path (str) – path to store the created figure. This may be an absolute path or a relative path; the latter is regarded as relative to the current working directory. The home directory indicator
~
is expanded.helper_defaults (dict, optional) – The basic configuration of the helpers.
update_helper_cfg (dict, optional) – A configuration used to update the existing helper defaults
raise_on_error (bool, optional) – Whether to raise on an exception created on helper invocation or just log the error
animation_enabled (bool, optional) – Whether animation mode is enabled.
- property axis_cfg: dict
Returns a deepcopy of the current axis’ configuration.
- property base_cfg: dict
Returns a deepcopy of the base configuration, i.e. the configuration that is not axis-specific.
- property fig
Returns the current figure
- property ax
Returns the current axis of the associated figure
- property ax_coords: Tuple[int]
Returns the current axis coordinates within a subfigure in shape
(col, row)
.For example, the
(0, 0)
coordinate refers to the top left subplot of the figure.(1, 2)
is the axis object in the second column, third row.
- property axes: numpy.ndarray
Returns the axes array, which is of shape (#cols, #rows).
The (0, 0) axis refers to the top left subplot of the figure.
- property available_helpers: Tuple[str]
List of available helper names
- property enabled_helpers: list
Returns a list of enabled helpers for the current axis
- property enabled_figure_helpers: list
Returns a list of enabled figure-level helpers
- property out_path: str
Returns the output path of the plot
- property animation_enabled: bool
Whether animation mode is currently enabled or not
- property animation_update: Callable
Returns the animation update generator callable
- property invoke_before_grab: bool
Whether the helpers are to be invoked before grabbing each frame of an animation.
- property raise_on_error: bool
Whether the PlotHelper was configured to raise exceptions
- property axis_handles_labels: Tuple[list, list]
Returns the tracked axis handles and labels for the current axis
- property all_handles_labels: Tuple[list, list]
Returns all associated handles and labels
- property axis_is_empty: bool
Returns true if the current axis is empty, i.e. has no artists added to it. This is basically the inverse of
mpl.axes.Axes.has_data
.
- attach_figure_and_axes(*, fig, axes, skip_if_identical: bool = False) None [source]
Attaches the given figure and axes to the PlotHelper. This method replaces an existing figure and existing axes with the ones given.
As the PlotHelper relies on axes being accessible via coordinate pairs, multiple axes must be passed as two-dimensional array-like. Since the axes are internally stored as numpy array, the axes-grid must be complete.
Note that by closing the old figure the existing axis-specific config and all existing axes are destroyed. In other words: All information previously provided via the provide_defaults and the mark_* methods is lost. Therefore, if needed, it is recommended to call this method at the beginning of the plotting function.
Note
This function assumes multiple axes to be passed in (y,x) format (as e.g. returned by matplotlib.pyplot.subplots with squeeze set to False) and internally transposes the axes-grid such that afterwards it is accessible via (x,y) coordinates.
- Parameters
fig – The new figure which replaces the existing.
axes – single axis or 2d array-like containing the axes
skip_if_identical (bool, optional) – If True, will check if the given
fig
is identical to the already associated figure; if so, will do nothing. This can be useful if one cannot be sure if the figure was already associated. In such a case, note that theaxes
argument is completely ignored.
- Raises
ValueError – On multiple axes not being passed in 2d format.
- Returns
None
- setup_figure(**update_fig_kwargs)[source]
Sets up a matplotlib figure instance and axes with the given configuration (by calling matplotlib.pyplot.subplots) and attaches both to the PlotHelper.
If the
scale_figsize_with_subplots_shape
option is enabled here, this method will also take care of scaling the figure accordingly.- Parameters
**update_fig_kwargs – Parameters that are used to update the figure setup parameters stored in setup_figure.
- save_figure(*, close: bool = True)[source]
Saves and (optionally, but default) closes the current figure
- Parameters
close (bool, optional) – Whether to close the figure after saving.
- close_figure()[source]
Closes the figure and disassociates it from the helper. This method has no effect if no figure is currently associated.
This also removes the axes objects and deletes the axis-specific configuration. All information provided via provide_defaults and the mark_* methods is lost.
- select_axis(col: Optional[int] = None, row: Optional[int] = None, *, ax=None)[source]
Sets the current axis.
Setting the axis can happen in three ways, depending on the arguments:
The axis object at the given
col
androw
coordinates.An explicitly given axis object (if
ax
is given)The current axis (if all arguments are None)
This method can be used to change to a different associated axis to continue plotting on that axis.
Calling this method may also become necessary if the current axis is changed in a part of the program where the plot helper is not involved; in such a case, the currently selected axis may have been changed directly via the matplotlib interface. This method can then be used to synchronize the two again.
Note
The
col
androw
values are wrapped around according to the shape of the associatedaxes
array, thereby allowing to specify them as negative values for indexing from the back.- Parameters
col (int, optional) – The column to select, i.e. the x-coordinate. Can be negative, in which case it indexes backwards from the last column.
row (int, optional) – The row to select, i.e. the y-coordinate. Can be negative, in which case it indexes backwards from the last row.
ax (optional) – If given this axis object, tries to look it up from the associated axes array.
- Raises
ValueError – On failing to set the current axis or if the given axis object or the result of
plt.gca()
was not part of the associated axes array. To associate the correct figure and axes, useattach_figure_and_axes()
.
- coords_iter(*, match: Optional[Union[tuple, str]] = None) Generator[Tuple[int], None, None] [source]
Returns a generator to iterate over all coordinates that match match.
- Parameters
match (Union[tuple, str]) – The coordinates to match; those that do not match this pattern (evaluated by coords_match function) will not be yielded. If not given, will iterate only over the currently selected axis.
- Yields
Generator[Tuple[int], None, None] – The axis coordinates generator
- invoke_helper(helper_name: str, *, axes: Optional[Union[tuple, str]] = None, mark_disabled_after_use: bool = True, raise_on_error: Optional[bool] = None, **update_kwargs)[source]
Invokes a single helper on the specified axes.
- Parameters
helper_name (str) – The name of the helper to invoke
axes (Union[tuple, str], optional) – A coordinate match tuple of the axes to invoke this helper on. If not given, will invoke only on the current axes.
mark_disabled_after_use (bool, optional) – If True, the helper is marked as disabled after the invocation.
raise_on_error (bool, optional) – If given, overwrites the default controlled via the
raise_on_error
attribute.**update_kwargs – Update parameters for this specific plot helper. Note that these do not persist, but are only used for this invocation.
- Raises
PlotHelperErrors – On failing plot helper invocation
- invoke_helpers(*helper_names, axes: Optional[Union[tuple, str]] = None, mark_disabled_after_use: bool = True, raise_on_error: Optional[bool] = None, **update_helpers)[source]
Invoke all specified helpers on the specified axes.
- Parameters
*helper_names – The helper names to invoke
axes (Union[tuple, str], optional) – A coordinate match tuple of the axes to invoke this helper on. If not given, will invoke only on the current axes.
mark_disabled_after_use (bool, optional) – Whether to mark helpers disabled after they were used.
raise_on_error (bool, optional) – If given, overwrites the default controlled via the
raise_on_error
attribute.**update_helpers – Update parameters for all plot helpers. These have to be grouped under the name of the helper in order to be correctly associated. Note that these do not persist, but are only used for this invocation.
- Raises
PlotHelperErrors – On failing plot helper invocations
- invoke_enabled(*, axes: Optional[Union[tuple, str]] = None, mark_disabled_after_use: bool = True, raise_on_error: Optional[bool] = None, **update_helpers)[source]
Invokes all enabled helpers with their current configuration on the matching axes and all enabled figure-level helpers on the figure.
Internally, this first invokes all figure-level helpers and then calls
invoke_helpers()
with all enabled helpers for all axes matching theaxes
argument.Note
When setting
mark_disabled_after_use = False
, this will lead to figure-level helpers being invoked multiple times. As some of these helpers do not allow multiple invocation, invoking this method a second time might fail if not disabling them as part of the first call to this method.- Parameters
axes (Union[tuple, str], optional) – A coordinate match tuple of the axes to invoke this helper on. If not given, will invoke only on the current axes.
mark_disabled_after_use (bool, optional) – If True, the helper is marked as disabled after the invocation.
raise_on_error (bool, optional) – If given, overwrites the default controlled via the
raise_on_error
attribute.**update_helpers – Update parameters for all plot helpers. These have to be grouped under the name of the helper in order to be correctly associated. Note that these do not persist, but are only used for this invocation.
- Raises
PlotHelperErrors – On failing plot helper invocations
- provide_defaults(helper_name: str, *, axes: Optional[Union[tuple, str]] = None, mark_enabled: bool = True, **update_kwargs)[source]
Update or add a single entry to a helper’s configuration.
- Parameters
helper_name (str) – The name of the helper whose config is to change
axes (Union[tuple, str], optional) – A coordinate match tuple of the axes to invoke this helper on. If not given, will invoke only on the current axes.
mark_enabled (bool, optional) – Whether to mark the helper enabled by providing defaults
**update_kwargs – dict containing the helper parameters with which the config is updated recursively
- mark_enabled(*helper_names, axes: Optional[Union[tuple, str]] = None)[source]
Marks the specified helpers as enabled for the specified axes.
- Parameters
*helper_names – Helpers to be enabled
axes (Union[tuple, str], optional) – A coordinate match tuple of the axes to invoke this helper on. If not given, will invoke only on the current axes.
- mark_disabled(*helper_names, axes: Optional[Union[tuple, str]] = None)[source]
Marks the specified helpers as disabled for the specified axes.
- Parameters
*helper_names – Helpers to be disabled
axes (Union[tuple, str], optional) – A coordinate match tuple of the axes to invoke this helper on. If not given, will invoke only on the current axes.
- track_handles_labels(handles: list, labels)[source]
Keep track of legend handles and/or labels for the current axis.
- Parameters
handles (list) – The handles to keep track of
labels (list) – The associated labels
- register_animation_update(update_func: Callable, *, invoke_helpers_before_grab: bool = False)[source]
Registers a generator used for animations.
- Parameters
update_func (Callable) – Generator object over which is iterated over to create an animation. This needs
invoke_helpers_before_grab (bool, optional) – Whether to invoke all enabled plot helpers before grabbing a frame. This should be set to
True
if the animation update function overwrites the effects of the previously applied helpers.
- enable_animation()[source]
Can be invoked to enter animation mode. An action is only performed if the helper is not currently in animation mode.
- Raises
EnterAnimationMode – Conveys to the plot creator that animation mode is to be entered.
- disable_animation()[source]
Can be invoked to exit animation mode. An action is only performed if the helper is currently in animation mode.
- Raises
ExitAnimationMode – Conveys to the plot creator that animation mode should be left.
- __dict__ = mappingproxy({'__module__': 'dantro.plot_creators._plot_helper', '__doc__': 'The PlotHelper takes care of the figure setup and saving and allows\n accessing matplotlib utilities through the plot configuration.\n ', '_SPECIAL_CFG_KEYS': ('setup_figure', 'save_figure'), '_FIGURE_HELPERS': ('align_labels', 'set_suptitle', 'set_figlegend', 'subplots_adjust', 'figcall'), '__init__': <function PlotHelper.__init__>, '_axis_cfg': <property object>, 'axis_cfg': <property object>, 'base_cfg': <property object>, 'fig': <property object>, 'ax': <property object>, 'ax_coords': <property object>, 'axes': <property object>, 'available_helpers': <property object>, 'enabled_helpers': <property object>, 'enabled_figure_helpers': <property object>, 'out_path': <property object>, 'animation_enabled': <property object>, 'animation_update': <property object>, 'invoke_before_grab': <property object>, 'raise_on_error': <property object>, 'axis_handles_labels': <property object>, 'all_handles_labels': <property object>, 'axis_is_empty': <property object>, 'attach_figure_and_axes': <function PlotHelper.attach_figure_and_axes>, 'setup_figure': <function PlotHelper.setup_figure>, 'save_figure': <function PlotHelper.save_figure>, 'close_figure': <function PlotHelper.close_figure>, 'select_axis': <function PlotHelper.select_axis>, 'coords_iter': <function PlotHelper.coords_iter>, '_invoke_helper': <function PlotHelper._invoke_helper>, 'invoke_helper': <function PlotHelper.invoke_helper>, 'invoke_helpers': <function PlotHelper.invoke_helpers>, 'invoke_enabled': <function PlotHelper.invoke_enabled>, 'provide_defaults': <function PlotHelper.provide_defaults>, 'mark_enabled': <function PlotHelper.mark_enabled>, 'mark_disabled': <function PlotHelper.mark_disabled>, 'track_handles_labels': <function PlotHelper.track_handles_labels>, 'register_animation_update': <function PlotHelper.register_animation_update>, 'enable_animation': <function PlotHelper.enable_animation>, 'disable_animation': <function PlotHelper.disable_animation>, '_compile_axis_specific_cfg': <function PlotHelper._compile_axis_specific_cfg>, '_raise_on_invalid_helper_name': <function PlotHelper._raise_on_invalid_helper_name>, '_handle_errors': <function PlotHelper._handle_errors>, '_find_axis_coords': <function PlotHelper._find_axis_coords>, '_hlpr_align_labels': <function PlotHelper._hlpr_align_labels>, '_hlpr_set_suptitle': <function PlotHelper._hlpr_set_suptitle>, '_hlpr_set_figlegend': <function PlotHelper._hlpr_set_figlegend>, '_hlpr_subplots_adjust': <function PlotHelper._hlpr_subplots_adjust>, '_hlpr_figcall': <function PlotHelper._hlpr_figcall>, '_hlpr_set_title': <function PlotHelper._hlpr_set_title>, '_hlpr_set_labels': <function PlotHelper._hlpr_set_labels>, '_hlpr_set_limits': <function PlotHelper._hlpr_set_limits>, '_hlpr_set_legend': <function PlotHelper._hlpr_set_legend>, '_hlpr_set_texts': <function PlotHelper._hlpr_set_texts>, '_hlpr_annotate': <function PlotHelper._hlpr_annotate>, '_hlpr_set_hv_lines': <function PlotHelper._hlpr_set_hv_lines>, '_hlpr_set_scales': <function PlotHelper._hlpr_set_scales>, '_hlpr_set_ticks': <function PlotHelper._hlpr_set_ticks>, '_hlpr_set_tick_locators': <function PlotHelper._hlpr_set_tick_locators>, '_hlpr_set_tick_formatters': <function PlotHelper._hlpr_set_tick_formatters>, '_hlpr_call': <function PlotHelper._hlpr_call>, '_hlpr_despine': <function PlotHelper._hlpr_despine>, '__dict__': <attribute '__dict__' of 'PlotHelper' objects>, '__weakref__': <attribute '__weakref__' of 'PlotHelper' objects>, '__annotations__': {}})
- __module__ = 'dantro.plot_creators._plot_helper'
- __weakref__
list of weak references to the object (if defined)
- There are a few things to look out for:
In order for the animation update to actually be used, the feature needs to be enabled in the plot configuration. The behaviour of the animation is controlled via the
animation
key; in it, set the entryenabled: true
.While whatever happens before the registration of the animation function is also executed, the animation update function should be built such as to also include the initial frame of the animation. This is to allow the plot function itself to be more flexible, and the animation update need not distinguish between initial frame and other frames.
The animation update function is expected to be a so-called Python Generator, thus using the yield keyword. For more information, have a look here.
The file extension is taken care of by the
PlotManager
, which is why it needs to be adjusted at the top level of the plot configuration, e.g. when storing the animation as a movie.
Here is an example for an animation configuration:
my_plot:
# Regular plot configuration
# ...
# Specify file extension to use, with leading dot (handled by PlotManager)
file_ext: .png # change to mp4 if using ffmpeg writer
# Animation configuration
animation:
enabled: true # false by default
writer: frames # which writer to use: frames, ffmpeg, ...
writer_kwargs: # additional configuration for each writer
frames: # passed to 'frames' writer
saving: # passed to Writer.saving method
dpi: 254
ffmpeg:
init: # passed to Writer.__init__ method
fps: 15
saving:
dpi: 254
grab_frame: {} # passed to Writer.grab_frame and from there to savefig
animation_update_kwargs: {} # passed to the animation update function
Note
For high-resolution plots, e.g. from a cellular automaton state, take care to choose a sufficiently high dpi
value.
Otherwise, you might encounter interpolation issues.