Skip to content

Commit 828939f

Browse files
committed
Update documentation
1 parent 987cdc4 commit 828939f

File tree

11 files changed

+109
-44
lines changed

11 files changed

+109
-44
lines changed

.github/workflows/tests.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ jobs:
9595
python-version: '3.12'
9696
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346
9797
pyside6-ver: '!=6.5.1'
98-
# Temporary CI run using pre-release IPython and matplotlib-inline
98+
# Temporary CI run using pre-release IPython
9999
- name-suffix: 'Pre-release IPython'
100100
os: ubuntu-22.04
101101
python-version: '3.12'
@@ -286,13 +286,12 @@ jobs:
286286
--index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple \
287287
--upgrade --only-binary=:all: numpy pandas
288288
289-
- name: Install pre-release IPython and matplotlib-inline
289+
- name: Install pre-release IPython
290290
if: matrix.name-suffix == 'Pre-release IPython'
291291
# Temporary, until release of IPython 8.24
292292
run: |
293293
pip list
294-
pip install git+https://github.com/ianthomas23/ipython@entry_points
295-
pip install git+https://github.com/ipython/matplotlib-inline@main
294+
pip install git+https://github.com/ipython/ipython@main
296295
pip list
297296
298297
- name: Install Matplotlib

doc/users/next_whats_new/backend_registry.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,15 @@ BackendRegistry
33

44
New :class:`~matplotlib.backends.registry.BackendRegistry` class is the single
55
source of truth for available backends. The singleton instance is
6-
``matplotlib.backends.backend_registry``.
6+
``matplotlib.backends.backend_registry``. It is used internally by Matplotlib,
7+
and also IPython (and therefore Jupyter) starting with IPython 8.24.0.
8+
9+
There are three sources of backends: built-in (source code is within the
10+
Matplotlib repository), explicit ``module://some.backend`` syntax (backend is
11+
obtained by loading the module), or via an entry point (self-registering
12+
backend in an external package).
13+
14+
To obtain a list of all registered backends use:
15+
16+
>>> from matplotlib.backends import backend_registry
17+
>>> backend_registry.list_all()

galleries/users_explain/figure/backends.rst

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ QtAgg Agg rendering in a Qt_ canvas (requires PyQt_ or `Qt for Python`_,
175175
more details.
176176
ipympl Agg rendering embedded in a Jupyter widget (requires ipympl_).
177177
This backend can be enabled in a Jupyter notebook with
178-
``%matplotlib ipympl``.
178+
``%matplotlib ipympl`` or ``%matplotlib widget``. Works with
179+
Jupyter ``lab`` and ``notebook>=7``.
179180
GTK3Agg Agg rendering to a GTK_ 3.x canvas (requires PyGObject_ and
180181
pycairo_). This backend can be activated in IPython with
181182
``%matplotlib gtk3``.
@@ -188,7 +189,8 @@ TkAgg Agg rendering to a Tk_ canvas (requires TkInter_). This
188189
backend can be activated in IPython with ``%matplotlib tk``.
189190
nbAgg Embed an interactive figure in a Jupyter classic notebook. This
190191
backend can be enabled in Jupyter notebooks via
191-
``%matplotlib notebook``.
192+
``%matplotlib notebook`` or ``%matplotlib nbagg``. Works with
193+
Jupyter ``notebook<7`` and ``nbclassic``.
192194
WebAgg On ``show()`` will start a tornado server with an interactive
193195
figure.
194196
GTK3Cairo Cairo rendering to a GTK_ 3.x canvas (requires PyGObject_ and
@@ -200,7 +202,7 @@ wxAgg Agg rendering to a wxWidgets_ canvas (requires wxPython_ 4).
200202
========= ================================================================
201203

202204
.. note::
203-
The names of builtin backends case-insensitive; e.g., 'QtAgg' and
205+
The names of builtin backends are case-insensitive; e.g., 'QtAgg' and
204206
'qtagg' are equivalent.
205207

206208
.. _`Anti-Grain Geometry`: http://agg.sourceforge.net/antigrain.com/
@@ -222,11 +224,13 @@ wxAgg Agg rendering to a wxWidgets_ canvas (requires wxPython_ 4).
222224
.. _wxWidgets: https://www.wxwidgets.org/
223225
.. _ipympl: https://www.matplotlib.org/ipympl
224226

227+
.. _ipympl_install:
228+
225229
ipympl
226230
^^^^^^
227231

228-
The Jupyter widget ecosystem is moving too fast to support directly in
229-
Matplotlib. To install ipympl:
232+
The ipympl backend is in a separate package that must be explicitly installed
233+
if you wish to use it, for example:
230234

231235
.. code-block:: bash
232236

galleries/users_explain/figure/figure_intro.rst

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,20 @@ Notebooks and IDEs
5252

5353
If you are using a Notebook (e.g. `Jupyter <https://jupyter.org>`_) or an IDE
5454
that renders Notebooks (PyCharm, VSCode, etc), then they have a backend that
55-
will render the Matplotlib Figure when a code cell is executed. One thing to
56-
be aware of is that the default Jupyter backend (``%matplotlib inline``) will
55+
will render the Matplotlib Figure when a code cell is executed. The default
56+
Jupyter backend (``%matplotlib inline``) creates static plots that
5757
by default trim or expand the figure size to have a tight box around Artists
58-
added to the Figure (see :ref:`saving_figures`, below). If you use a backend
59-
other than the default "inline" backend, you will likely need to use an ipython
60-
"magic" like ``%matplotlib notebook`` for the Matplotlib :ref:`notebook
61-
<jupyter_notebooks_jupyterlab>` or ``%matplotlib widget`` for the `ipympl
62-
<https://matplotlib.org/ipympl/>`_ backend.
58+
added to the Figure (see :ref:`saving_figures`, below). For interactive plots
59+
in Jupyter you will need to use an ipython "magic" like ``%matplotlib widget``
60+
for the `ipympl <https://matplotlib.org/ipympl/>`_ backend in ``jupyter lab``
61+
or ``notebook>=7``, or ``%matplotlib notebook`` for the Matplotlib
62+
:ref:`notebook <jupyter_notebooks_jupyterlab>` in ``notebook<7`` or
63+
``nbclassic``.
64+
65+
.. note::
66+
67+
The `ipympl <https://matplotlib.org/ipympl/>`_ backend is in a separate
68+
package, see :ref:`Installing ipympl <ipympl_install>`.
6369

6470
.. figure:: /_static/FigureNotebook.png
6571
:alt: Image of figure generated in Jupyter Notebook with notebook
@@ -75,15 +81,6 @@ other than the default "inline" backend, you will likely need to use an ipython
7581
.. seealso::
7682
:ref:`interactive_figures`.
7783

78-
.. note::
79-
80-
If you only need to use the classic notebook (i.e. ``notebook<7``),
81-
you can use:
82-
83-
.. sourcecode:: ipython
84-
85-
%matplotlib notebook
86-
8784
.. _standalone-scripts-and-interactive-use:
8885

8986
Standalone scripts and interactive use
@@ -104,7 +101,7 @@ backend. These are typically chosen either in the user's :ref:`matplotlibrc
104101
QtAgg backend.
105102

106103
When run from a script, or interactively (e.g. from an
107-
`iPython shell <https://ipython.readthedocs.io/en/stable/>`_) the Figure
104+
`IPython shell <https://ipython.readthedocs.io/en/stable/>`_) the Figure
108105
will not be shown until we call ``plt.show()``. The Figure will appear in
109106
a new GUI window, and usually will have a toolbar with Zoom, Pan, and other tools
110107
for interacting with the Figure. By default, ``plt.show()`` blocks

galleries/users_explain/figure/writing_a_backend_pyplot_interface.rst

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,47 @@ Function-based API
8484
2. **Showing figures**: `.pyplot.show()` calls a module-level ``show()``
8585
function, which is typically generated via the ``ShowBase`` class and its
8686
``mainloop`` method.
87+
88+
Registering a backend
89+
---------------------
90+
91+
For a new backend to be usable via ``matplotlib.use()`` or IPython
92+
``%matplotlib`` magic command, it must be compatible with one of the three ways
93+
supported by the :class:`~matplotlib.backends.registry.BackendRegistry`:
94+
95+
Built-in
96+
^^^^^^^^
97+
98+
A backend built into Matplotlib must have its name and
99+
``FigureCanvas.required_interactive_framework`` hard-coded in the
100+
:class:`~matplotlib.backends.registry.BackendRegistry`. If the backend module
101+
is not ``f"matplotlib.backends.backend_{backend_name.lower()}"`` then there
102+
must also be an entry in the ``BackendRegistry._name_to_module``.
103+
104+
module:// syntax
105+
^^^^^^^^^^^^^^^^
106+
107+
Any backend in a separate module (not built into Matplotlib) can be used by
108+
specifying the path to the module in the form ``module://some.backend.module``.
109+
An example is ``module://mplcairo.qt`` for
110+
`mplcairo <https:////github.com/matplotlib/mplcairo>`_. The backend's
111+
interactive framework will be taken from its
112+
``FigureCanvas.required_interactive_framework``.
113+
114+
Entry point
115+
^^^^^^^^^^^
116+
117+
An external backend module can self-register as a backend using an
118+
``entry point`` in its ``pyproject.toml`` such as the one used by
119+
``matplotlib-inline``:
120+
121+
.. code-block:: toml
122+
123+
[project.entry-points."matplotlib.backend"]
124+
inline = "matplotlib_inline.backend_inline"
125+
126+
The backend's interactive framework will be taken from its
127+
``FigureCanvas.required_interactive_framework``. All entry points are loaded
128+
together but only when first needed, such as when a backend name is not
129+
recognised as a built-in backend, or when
130+
:meth:`~matplotlib.backends.registry.BackendRegistry.list_all` is first called.

lib/matplotlib/backend_bases.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,6 +1766,10 @@ def _fix_ipython_backend2gui(cls):
17661766
# `ipython --auto`). This cannot be done at import time due to
17671767
# ordering issues, so we do it when creating a canvas, and should only
17681768
# be done once per class (hence the `cache`).
1769+
1770+
# This function will not be needed when Python 3.12, the latest version
1771+
# supported by IPython < 8.24, reaches end-of-life in late 2028.
1772+
# At that time this function can be made a no-op and deprecated.
17691773
mod_ipython = sys.modules.get("IPython")
17701774
if mod_ipython is None or mod_ipython.version_info[:2] >= (8, 24):
17711775
# Use of backend2gui is not needed for IPython >= 8.24 as the

lib/matplotlib/backends/registry.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class BackendRegistry:
2424
Each backend has a name, a module name containing the backend code, and an
2525
optional GUI framework that must be running if the backend is interactive.
2626
There are three sources of backends: built-in (source code is within the
27-
Matplotlib repository), explicit module://some.backend syntax (backend is
27+
Matplotlib repository), explicit ``module://some.backend`` syntax (backend is
2828
obtained by loading the module), or via an entry point (self-registering
2929
backend in an external package).
3030
@@ -208,7 +208,7 @@ def is_valid_backend(self, backend: str) -> bool:
208208
Return True if the backend name is valid, False otherwise.
209209
210210
A backend name is valid if it is one of the built-in backends or has been
211-
dynamically added via an entry point. Those beginning with "module://" are
211+
dynamically added via an entry point. Those beginning with ``module://`` are
212212
always considered valid and are added to the current list of all backends
213213
within this function.
214214
@@ -256,14 +256,14 @@ def list_all(self):
256256
Return list of all known backends.
257257
258258
These include built-in backends and those obtained at runtime either from entry
259-
points or explicit module://some.backend syntax.
259+
points or explicit ``module://some.backend`` syntax.
260+
261+
Entry points will be loaded if they haven't been already.
260262
261263
Returns
262264
-------
263265
list of str
264266
Backend names.
265-
266-
Entry points will be loaded if they haven't been already.
267267
"""
268268
self._ensure_entry_points_loaded()
269269
return self.list_builtin() + list(self._backend_to_gui_framework.keys())
@@ -323,12 +323,15 @@ def load_backend_module(self, backend):
323323

324324
def resolve_backend(self, backend):
325325
"""
326-
Return the backend and GUI framework for the specified backend.
326+
Return the backend and GUI framework for the specified backend name.
327327
328328
If the GUI framework is not yet known then it will be determine by loading the
329-
backend module and checking the FigureCanvas.required_interactive_framework
329+
backend module and checking the ``FigureCanvas.required_interactive_framework``
330330
attribute.
331331
332+
This function only loads entry points if they have not already been loaded and
333+
the backend is not built-in and not of ``module://some.backend`` format.
334+
332335
Parameters
333336
----------
334337
backend : str or None
@@ -339,9 +342,6 @@ def resolve_backend(self, backend):
339342
Tuple of backend (str) and GUI framework (str or None).
340343
A non-interactive backend returns None for its GUI framework rather than
341344
"headless".
342-
343-
This function only loads entry points if they have not already been loaded and
344-
the backend is not built-in and not of module://some.backend format.
345345
"""
346346
if isinstance(backend, str):
347347
backend = backend.lower()
@@ -380,10 +380,11 @@ def resolve_gui_or_backend(self, gui_or_backend):
380380
either a GUI framework or a backend name, tested in that order.
381381
382382
This is for use with the IPython %matplotlib magic command which may be a GUI
383-
framework such as
384-
%matplotlib qt
385-
or a backend name such as
386-
%matplotlib qtagg
383+
framework such as ``%matplotlib qt`` or a backend name such as
384+
``%matplotlib qtagg``.
385+
386+
This function only loads entry points if they have not already been loaded and
387+
the backend is not built-in and not of ``module://some.backend`` format.
387388
388389
Parameters
389390
----------
@@ -395,9 +396,6 @@ def resolve_gui_or_backend(self, gui_or_backend):
395396
Tuple of backend (str) and GUI framework (str or None).
396397
A non-interactive backend returns None for its GUI framework rather than
397398
"headless".
398-
399-
This function only loads entry points if they have not already been loaded and
400-
the backend is not built-in and not of module://some.backend format.
401399
"""
402400
gui_or_backend = gui_or_backend.lower()
403401

lib/matplotlib/pyplot.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ def install_repl_displayhook() -> None:
298298
if mod_ipython.version_info[:2] < (8, 24):
299299
# Use of backend2gui is not needed for IPython >= 8.24 as that functionality
300300
# has been moved to Matplotlib.
301+
# This code can be removed when Python 3.12, the latest version supported by
302+
# IPython < 8.24, reaches end-of-life in late 2028.
301303
from IPython.core.pylabtools import backend2gui
302304
# trigger IPython's eventloop integration, if available
303305
ipython_gui_name = backend2gui.get(get_backend())

lib/matplotlib/testing/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ def ipython_in_subprocess(
190190
if IPython.version_info[:2] >= (8, 24):
191191
expected_backend = expected_backend_new_ipython
192192
else:
193+
# This code can be removed when Python 3.12, the latest version supported by
194+
# IPython < 8.24, reaches end-of-life in late 2028.
193195
expected_backend = expected_backend_old_ipython
194196

195197
code = ("import matplotlib as mpl, matplotlib.pyplot as plt;"

lib/matplotlib/tests/test_backend_inline.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ def test_ipynb():
3535
if IPython.version_info[:2] >= (8, 24):
3636
expected_backend = "inline"
3737
else:
38+
# This code can be removed when Python 3.12, the latest version supported by
39+
# IPython < 8.24, reaches end-of-life in late 2028.
3840
expected_backend = "module://matplotlib_inline.backend_inline"
3941
backend_outputs = nb.cells[2]["outputs"]
4042
assert backend_outputs[0]["data"]["text/plain"] == f"'{expected_backend}'"

0 commit comments

Comments
 (0)