1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
.. redirect-from:: /users/explain/writing_a_backend_pyplot_interface
.. _writing_backend_interface:
=========================================
Writing a backend -- the pyplot interface
=========================================
This page assumes general understanding of the information in the
:ref:`backends` page, and is instead intended as reference for
third-party backend implementers. It also only deals with the interaction
between backends and `.pyplot`, not with the rendering side, which is described
in `.backend_template`.
There are two APIs for defining backends: a new canvas-based API (introduced in
Matplotlib 3.6), and an older function-based API. The new API is simpler to
implement because many methods can be inherited from "parent backends". It is
recommended if back-compatibility for Matplotlib < 3.6 is not a concern.
However, the old API remains supported.
Fundamentally, a backend module needs to provide information to `.pyplot`, so
that
1. `.pyplot.figure()` can create a new `.Figure` instance and associate it with
an instance of a backend-provided canvas class, itself hosted in an instance
of a backend-provided manager class.
2. `.pyplot.show()` can show all figures and start the GUI event loop (if any).
To do so, the backend module must define a ``backend_module.FigureCanvas``
subclass of `.FigureCanvasBase`. In the canvas-based API, this is the only
strict requirement for backend modules. The function-based API additionally
requires many module-level functions to be defined.
Canvas-based API (Matplotlib >= 3.6)
------------------------------------
1. **Creating a figure**: `.pyplot.figure()` calls
``figure = Figure(); FigureCanvas.new_manager(figure, num)``
(``new_manager`` is a classmethod) to instantiate a canvas and a manager and
set up the ``figure.canvas`` and ``figure.canvas.manager`` attributes.
Figure unpickling uses the same approach, but replaces the newly
instantiated ``Figure()`` by the unpickled figure.
Interactive backends should customize the effect of ``new_manager`` by
setting the ``FigureCanvas.manager_class`` attribute to the desired manager
class, and additionally (if the canvas cannot be created before the manager,
as in the case of the wx backends) by overriding the
``FigureManager.create_with_canvas`` classmethod. (Non-interactive backends
can normally use a trivial ``FigureManagerBase`` and can therefore skip this
step.)
After a new figure is registered with `.pyplot` (either via
`.pyplot.figure()` or via unpickling), if in interactive mode, `.pyplot`
will call its canvas' ``draw_idle()`` method, which can be overridden as
desired.
2. **Showing figures**: `.pyplot.show()` calls
``FigureCanvas.manager_class.pyplot_show()`` (a classmethod), forwarding any
arguments, to start the main event loop.
By default, ``pyplot_show()`` checks whether there are any ``managers``
registered with `.pyplot` (exiting early if not), calls ``manager.show()``
on all such managers, and then, if called with ``block=True`` (or with
the default ``block=None`` and out of IPython's pylab mode and not in
interactive mode), calls ``FigureCanvas.manager_class.start_main_loop()``
(a classmethod) to start the main event loop. Interactive backends should
therefore override the ``FigureCanvas.manager_class.start_main_loop``
classmethod accordingly (or alternatively, they may also directly override
``FigureCanvas.manager_class.pyplot_show`` directly).
Function-based API
------------------
1. **Creating a figure**: `.pyplot.figure()` calls
``new_figure_manager(num, *args, **kwargs)`` (which also takes care of
creating the new figure as ``Figure(*args, **kwargs)``); unpickling calls
``new_figure_manager_given_figure(num, figure)``.
Furthermore, in interactive mode, the first draw of the newly registered
figure can be customized by providing a module-level
``draw_if_interactive()`` function. (In the new canvas-based API, this
function is not taken into account anymore.)
2. **Showing figures**: `.pyplot.show()` calls a module-level ``show()``
function, which is typically generated via the ``ShowBase`` class and its
``mainloop`` method.
Registering a backend
---------------------
For a new backend to be usable via ``matplotlib.use()`` or IPython
``%matplotlib`` magic command, it must be compatible with one of the three ways
supported by the :class:`~matplotlib.backends.registry.BackendRegistry`:
Built-in
^^^^^^^^
A backend built into Matplotlib must have its name and
``FigureCanvas.required_interactive_framework`` hard-coded in the
:class:`~matplotlib.backends.registry.BackendRegistry`. If the backend module
is not ``f"matplotlib.backends.backend_{backend_name.lower()}"`` then there
must also be an entry in the ``BackendRegistry._name_to_module``.
module:// syntax
^^^^^^^^^^^^^^^^
Any backend in a separate module (not built into Matplotlib) can be used by
specifying the path to the module in the form ``module://some.backend.module``.
An example is ``module://mplcairo.qt`` for
`mplcairo <https://github.com/matplotlib/mplcairo>`_. The backend's
interactive framework will be taken from its
``FigureCanvas.required_interactive_framework``.
Entry point
^^^^^^^^^^^
An external backend module can self-register as a backend using an
``entry point`` in its ``pyproject.toml`` such as the one used by
``matplotlib-inline``:
.. code-block:: toml
[project.entry-points."matplotlib.backend"]
inline = "matplotlib_inline.backend_inline"
The backend's interactive framework will be taken from its
``FigureCanvas.required_interactive_framework``. All entry points are loaded
together but only when first needed, such as when a backend name is not
recognised as a built-in backend, or when
:meth:`~matplotlib.backends.registry.BackendRegistry.list_all` is first called.
|