Skip to content

Commit d30b372

Browse files
committed
Document what pyplot expects from a backend.
1 parent 7d3046f commit d30b372

File tree

3 files changed

+77
-0
lines changed

3 files changed

+77
-0
lines changed

doc/users/explain/backends.rst

+3
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,6 @@ More generally, any importable backend can be selected by using any of the
252252
methods above. If ``name.of.the.backend`` is the module containing the
253253
backend, use ``module://name.of.the.backend`` as the backend name, e.g.
254254
``matplotlib.use('module://name.of.the.backend')``.
255+
256+
Information for backend implementers is available at
257+
:doc:`/users/explain/writing_a_backend`.

doc/users/explain/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Explanations
1010

1111
api_interfaces.rst
1212
backends.rst
13+
writing_a_backend.rst
1314
interactive.rst
1415
fonts.rst
1516
event_handling.rst
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
=================
2+
Writing a backend
3+
=================
4+
5+
**This is a work in progress.**
6+
7+
This page assumes general understanding of the information in the
8+
:doc:`/users/explain/backends` page, and is instead intended as reference for
9+
third-party backend implementers. It also only deals with the interaction
10+
between backends and `.pyplot`, not with the rendering side, which is described
11+
in `.backend_template`.
12+
13+
There are two APIs for defining backends: a new canvas-based API (introduced
14+
in Matplotlib 3.6), and an older function-based API. The newer API should
15+
be preferred if back-compatibility is not a concern (because of better
16+
composability: many methods can be inherited from "parent backends"), and is
17+
therefore described first, but the older API is also briefly described here.
18+
19+
Fundamentally, a backend module needs to provide information to `.pyplot`, so
20+
that
21+
22+
- `.pyplot.figure()` can attach a newly created `.Figure` to an instance of a
23+
backend-provided canvas class, itself hosted in an instance of a
24+
backend-provided manager class.
25+
- `.pyplot.show()` can show all figures and start the GUI event loop (if any).
26+
27+
To do so, the backend module must define a class accessible as
28+
``backend_module.FigureCanvas``. In the canvas-based API, this is the only
29+
strict requirement for backend modules. (The function-based API additionally
30+
requires many module-level functions to be defined.)
31+
32+
- `.pyplot.figure()` calls ``FigureCanvas.new_manager(Figure(), num)`` (a
33+
classmethod) to attach a canvas and a manager. Figure unpickling uses the
34+
same approach, but replaces the ``Figure()`` instantiation by the unpickled
35+
figure.
36+
37+
Interactive backends should customize the effect of ``new_manager``
38+
by setting a ``manager_class`` attribute on their ``FigureCanvas``,
39+
and additionally (if the canvas cannot be created before the
40+
manager, as in the case of the wx backend) by overriding the
41+
``FigureManager.create_with_canvas`` classmethod. (Non-interactive backends
42+
can normally use a trivial ``FigureManagerBase`` and can therefore skip this
43+
step.)
44+
45+
In the old function-based API, this customization point was provided by
46+
two functions, ``new_figure_manager(num, *args, **kwargs)`` (for
47+
`.pyplot.figure`) and ``new_figure_manager_given_figure`` (for unpickling).
48+
49+
- After a new figure is registered with `.pyplot` (either via
50+
`.pyplot.figure()` or via unpickling), if in interactive mode, `.pyplot` will
51+
call its canvas' ``draw_idle()`` method. In the old function-based API, it
52+
was possible to customize this call by providing a ``draw_if_interactive``
53+
module-level function. In the new canvas-based API, this function is not
54+
taken into account anymore; if needed, consider overriding ``draw_idle()``
55+
instead, keeping track of whether that method is being called for the first
56+
time.
57+
58+
- `.pyplot.show()` calls ``FigureCanvas.manager_class.pyplot_show()`` (a
59+
classmethod), forwarding any arguments, to start the main event loop.
60+
61+
By default, ``pyplot_show()`` checks whether there are any ``managers``
62+
registered with `.pyplot` (exiting early if not), calls ``manager.show()``
63+
on all such managers, and then, if called with ``block=True`` (or with
64+
the default ``block=None`` and out of IPython's pylab mode and not in
65+
interactive mode), calls ``FigureCanvas.manager_class.start_main_loop()``
66+
(a classmethod) to start the main event loop. Interactive backends should
67+
therefore override the ``FigureCanvas.manager_class.start_main_loop``
68+
classmethod accordingly (or alternatively, they may also directly override
69+
``FigureCanvas.manager_class.pyplot_show`` directly).
70+
71+
In the old function-based API, this customization point was provided via a
72+
``show`` callable generated via the ``ShowBase`` class and its ``mainloop``
73+
method.

0 commit comments

Comments
 (0)