matplotlib library is a - if not the - classic plotting package in the Python ecosystem. For further details, please consult the documentation of matplotlib. A good basic introduction can also be found in the Python Data Science Handbook (2016), chapter 4, by Jake VanderPlas. The author has made the manuscript freely available in the form of Jupyter notebooks on Github.
from bewegung import Video v = Video(width = 480, height = 270, seconds = 1.0) @v.sequence() class Foo: @v.layer( canvas = v.canvas( backend = 'matplotlib', facecolor = '#FFFFFFFF', dpi = 150, ) ) def bar(self, canvas): # a matplotlib figure object ax = canvas.subplots() ax.plot([1, 2, 3], [5, 4, 7]) return canvas v.reset() v.render_frame(v.time(0))
matplotlib.pyplot.figure, the function call
v.canvas(backend = 'matplotlib') accepts the following additional keyword arguments, among others (see documentation of matplotlib.pyplot.figure and matplotlib.figure.Figure):
dpi, resolution or dots per inch. 300 by default.
figsize, a tuple of width and height in inches. Width and height of the video by default, converted to inches based on the value of
width, width in pixels. Mapped to
figsizeif provided together with
height. Converted to inches based on the value of
height, height in pixels. Mapped to
figsizeif provided together with
width. Converted to inches based on the value of
tight_layout, by default
facecolor, a background color.
background_color, mapped to
managed, a boolean, by default
True. This value indicates whether the the
matplotlib.figure.Figureobject is “managed” by
bewegungwill close, i.e. destroy, a figure that is returned by a layer method.
Layer methods are expected to return
bewegung will “manage”
matplotlib.figure.Figure objects for saving resources, i.e. the returned
matplotlib.figure.Figure objects are automatically closed once returned. This can be avoided by setting
Aside from its rich set of features,
matplotlib is known for its mediocre performance. Not to be confused with
matplotlib also has different backends for rendering. Within
matplotlib is automatically configured to use mplcairo, “A (new) cairo backend for Matplotlib”. Compared to
matplotlib’s own built-in backends, its output quality is significantly better while the rendering speed is also higher. Unfortunately,
mplcairo is just “half” of the story of
mplcairo can not be installed or is not present for whatever reason,
bewegung will show a warning and fall back to
In animation frameworks for
matplotlib, such as the “official” matplotlib.animation sub-package, it is common practice to re-use and update existing figure and subplot / axes objects. This speeds up the rendering process considerably. This strategy is also supported by
For optimal results, the suggested approach requires some deeper understanding of
The following code illustrates the approach.
from bewegung import Video v = Video(width = 480, height = 270, seconds = 1.0) @v.sequence() class Foo: def __init__(self): self._fig = v.canvas( backend = 'matplotlib', facecolor = '#FFFFFFFF', dpi = 150, managed = False, # ensure that bewegung does not close figure )() # calls the factory once, generates a single figure self._ax = self._fig.subplots() # generates a single subplot @v.layer() # no backend configuration required def bar(self): # no canvas requested self._ax.clear() # optional: update content directly instead self._ax.plot([1, 2, 3], [5, 4, 7]) # draw new content or change old content return self._fig v.reset() image0 = v.render_frame(v.time(0)) image1 = v.render_frame(v.time(1))
The less a figure changes, the faster the above code becomes. Depending on the degree of complexity and optimization, anything from a few percent to an order of magnitude of performance gain can be achieved.