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 via register_animation_update, and

  • the 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 the axes 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 and row 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 and row values are wrapped around according to the shape of the associated axes 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, use attach_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 the axes 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 entry enabled: 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.