Matplotlib plot canvas¶
A anim.plot.canva embeds a Matplotlib figure in an
anim.window. It follows the same animation cycle as a
anim.plane.canva, while exposing Matplotlib’s object-oriented
interface. Users do not need to import Qt or matplotlib.pyplot.
Creating a plot canvas¶
A new plot canvas contains one empty axes by default:
import anim
W = anim.window('Plot')
C = anim.plot.canva(W)
W.add(C)
C.axes.plot([0, 1, 2], [0, 1, 4])
W.show()
The main Matplotlib objects are available directly on the canvas:
C.figureThe complete Matplotlib figure. Use it to create figure-level titles or additional axes.
C.axesThe default axes, or an array of axes when several subplots are requested.
C.canvasThe Matplotlib rendering canvas. Calling it directly is rarely necessary, because
anim.plot.canva.update()redraws it automatically.
Configuring axes¶
Use the exposed axes just as in Matplotlib’s object-oriented API:
C = anim.plot.canva(W)
C.axes.set_title('Position over time')
C.axes.set_xlabel('Time (s)')
C.axes.set_ylabel('Position (m)')
C.axes.set_xlim(0, 10)
C.axes.set_ylim(-1, 1)
C.axes.grid(True, alpha=0.3)
line, = C.axes.plot([], [], label='x(t)')
C.axes.legend()
Keeping the returned artist, such as line above, makes later updates
simple and efficient. Modify the artist rather than clearing and rebuilding
the complete axes on every frame.
For standard axes, matplotlib.axes.Axes.legend() automatically uses
the axes background and foreground colors. A plain C.axes.legend() is
therefore consistent with both dark and light plot styles. Explicit legend
options remain possible and take priority over these defaults:
C.axes.legend(facecolor='navy', labelcolor='white')
Plot style¶
The plot inherits its parent window style by default. The supported values are
'dark', 'light' and 'white':
W = anim.window('Dark plot', style='dark')
inherited = anim.plot.canva(W)
light_plot = anim.plot.canva(W, style='light')
The style is applied only while the embedded figure is created. It does not change Matplotlib’s global settings and therefore does not affect other plot canvas instances.
Relative canvas size¶
width and height are positive integer weights used by the window grid.
They are proportions, not pixels and not data-axis units. Their default value
is 1, which gives a plot the same layout weight as other canvas types.
W.add(anim.plane.canva(W), row=0, col=0)
W.add(anim.plot.canva(W, width=2), row=0, col=1)
Here the right column receives twice as much horizontal space as the left
column. Likewise, height=2 gives a row twice the vertical weight of a row
whose weight is 1.
Creating several axes¶
Use nrows and ncols for a regular grid of subplots. Any other keyword
argument is forwarded directly to Matplotlib’s subplot creation method:
C = anim.plot.canva(
W,
nrows=2,
ncols=1,
sharex=True
)
C.axes[0].set_ylabel('x')
C.axes[1].set_ylabel('y')
C.axes[1].set_xlabel('Time (s)')
Advanced options such as sharey, squeeze, subplot_kw and
gridspec_kw can be passed in the same way. By Matplotlib convention,
C.axes is a single axes for a default 1 by 1 layout, but usually becomes a
NumPy array when several subplots are created. The squeeze option can
change that behavior.
Updating a plot dynamically¶
Define a child class of anim.plot.canva, keep references to the
artists created in the constructor, then change their data in update.
Always finish with anim.plot.canva.update() so the completed frame is
drawn and reported to the window:
import numpy as np
import anim
class LivePlot(anim.plot.canva):
def __init__(self, window):
super().__init__(window)
self.line, = self.axes.plot([], [])
self.axes.set_xlim(0, 10)
self.axes.set_ylim(-1, 1)
def update(self, t):
times = np.arange(t.step + 1)*self.window.dt
self.line.set_data(times, np.sin(times))
super().update(t)
W = anim.window('Live plot')
W.add(LivePlot)
W.show()
Warning
Do not blindly append one data point on every call to update unless the
animation is guaranteed to move strictly forward and every step is visited
exactly once. A backward step, a direct jump to another step, or a repeated
update of the same step can otherwise create duplicate, out-of-order or
stale plot data.
Prefer deriving the displayed arrays from t.step or t.time on every
update, as in the example above. If rebuilding all data is too expensive,
use an explicitly reversible and idempotent history structure: updating the
same step twice must produce the same result, and moving backward must undo
or discard samples beyond the current step.
The final call to anim.plot.canva.update() performs a synchronous
Matplotlib draw. This keeps the plot synchronized with other canvas panels and
ensures that movie captures contain the fully updated frame.