Skip to content

Animation

Animation

Animation(
    render: Callable[..., Figure],
    frames: Union[int, Iterable],
    *,
    fps: float = 20.0,
    repeat: bool = True
)

A sequence of figures encoded as an animated image.

Parameters

render: Called once per frame as render(value); must return a pyplotrs.Figure. frames: Either an int (the callback receives 0 .. frames-1) or an iterable of values passed to the callback in order. fps: Frames per second (default 20). repeat: Loop forever (default) or play through once.

Source code in python/pyplotrs/animation.py
def __init__(self, render: Callable[..., Figure],
             frames: Union[int, Iterable], *,
             fps: float = 20.0, repeat: bool = True) -> None:
    if not callable(render):
        raise TypeError("render must be callable: render(value) -> pyplotrs.Figure")
    if isinstance(frames, int):
        if frames <= 0:
            raise ValueError("frames must be a positive int")
        self._items: list = list(range(frames))
    else:
        self._items = list(frames)
        if not self._items:
            raise ValueError("frames iterable is empty")
    self._render = render
    self.fps = float(fps)
    if self.fps <= 0:
        raise ValueError("fps must be positive")
    self.repeat = bool(repeat)

to_bytes

to_bytes(
    format: str = "gif",
    *,
    dpi: float = 100.0,
    fps: Optional[float] = None
) -> bytes

Render every frame and return the encoded animation.

format is "gif" (256-color, plays anywhere) or "apng" (full 8-bit color); a leading dot and any capitalization are accepted, so a file extension can be handed straight through. dpi sets the raster resolution and fps overrides the construction-time frame rate, both as in save.

This is the primitive save is built on. Reach for it when the destination is not a path - an HTTP response, a zip member, a BytesIO - and use save otherwise, since it does not hold the encoded animation and the file at once.

Source code in python/pyplotrs/animation.py
def to_bytes(self, format: str = "gif", *, dpi: float = 100.0,
             fps: Optional[float] = None) -> bytes:
    """Render every frame and return the encoded animation.

    ``format`` is ``"gif"`` (256-color, plays anywhere) or ``"apng"``
    (full 8-bit color); a leading dot and any capitalization are accepted,
    so a file extension can be handed straight through. ``dpi`` sets the
    raster resolution and ``fps`` overrides the construction-time frame
    rate, both as in [`save`][pyplotrs.animation.Animation.save].

    This is the primitive [`save`][pyplotrs.animation.Animation.save] is
    built on. Reach for it when the destination is not a path - an HTTP
    response, a zip member, a ``BytesIO`` - and use ``save`` otherwise,
    since it does not hold the encoded animation and the file at once.
    """
    rate = self.fps if fps is None else float(fps)
    if rate <= 0:
        raise ValueError("fps must be positive")
    key = format.lstrip(".").lower()
    try:
        encoder = _FORMATS[key]
    except KeyError:
        raise ValueError(
            f"unsupported animation format {format!r}; "
            f"use {' or '.join(sorted(set(_FORMATS.values())))}") from None
    scenes = self._scenes()
    if encoder == "gif":
        delay_cs = max(1, round(100.0 / rate))  # GIF delay unit is 10 ms
        return _core.scenes_to_gif(scenes, dpi / 72.0, delay_cs, self.repeat)
    delay_num = max(1, round(1000.0 / rate))  # delay = num/1000 s
    return _core.scenes_to_apng(scenes, dpi, delay_num, 1000, self.repeat)

save

save(
    path: Union[str, PathLike],
    *,
    dpi: float = 100.0,
    fps: Optional[float] = None,
    format: Optional[str] = None
) -> None

Render every frame and encode to path.

The format is taken from the extension: .gif (256-color, broadly viewable) or .apng / .png (full-color). dpi sets the raster resolution; fps overrides the construction-time frame rate; format overrides the extension, for a path that does not carry one.

render is called once per frame per save, so writing two formats from one Animation builds every figure twice - and a callback that draws on random numbers would not even build the same one twice. Encode once with to_bytes and write the bytes yourself when that matters.

Source code in python/pyplotrs/animation.py
def save(self, path: Union[str, os.PathLike], *, dpi: float = 100.0,
         fps: Optional[float] = None,
         format: Optional[str] = None) -> None:
    """Render every frame and encode to ``path``.

    The format is taken from the extension: ``.gif`` (256-color, broadly
    viewable) or ``.apng`` / ``.png`` (full-color). ``dpi`` sets the raster
    resolution; ``fps`` overrides the construction-time frame rate;
    ``format`` overrides the extension, for a path that does not carry one.

    ``render`` is called once per frame *per save*, so writing two formats
    from one ``Animation`` builds every figure twice - and a callback that
    draws on random numbers would not even build the same one twice. Encode
    once with [`to_bytes`][pyplotrs.animation.Animation.to_bytes] and write
    the bytes yourself when that matters.
    """
    path_str = str(path)
    if format is None:
        format = path_str.rsplit(".", 1)[-1] if "." in path_str else ""
    with open(path_str, "wb") as fh:
        fh.write(self.to_bytes(format, dpi=dpi, fps=fps))

animate

animate(
    render: Callable[..., Figure],
    frames: Union[int, Iterable],
    *,
    fps: float = 20.0,
    repeat: bool = True
) -> Animation

Convenience constructor for Animation (see its docstring).

Source code in python/pyplotrs/animation.py
def animate(render: Callable[..., Figure], frames: Union[int, Iterable], *,
            fps: float = 20.0, repeat: bool = True) -> Animation:
    """Convenience constructor for ``Animation`` (see its docstring)."""
    return Animation(render, frames, fps=fps, repeat=repeat)