Buckets:
| """ | |
| `matplotlib.figure` implements the following classes: | |
| `Figure` | |
| Top level `~matplotlib.artist.Artist`, which holds all plot elements. | |
| Many methods are implemented in `FigureBase`. | |
| `SubFigure` | |
| A logical figure inside a figure, usually added to a figure (or parent `SubFigure`) | |
| with `Figure.add_subfigure` or `Figure.subfigures` methods. | |
| Figures are typically created using pyplot methods `~.pyplot.figure`, | |
| `~.pyplot.subplots`, and `~.pyplot.subplot_mosaic`. | |
| .. plot:: | |
| :include-source: | |
| fig, ax = plt.subplots(figsize=(2, 2), facecolor='lightskyblue', | |
| layout='constrained') | |
| fig.suptitle('Figure') | |
| ax.set_title('Axes', loc='left', fontstyle='oblique', fontsize='medium') | |
| Some situations call for directly instantiating a `~.figure.Figure` class, | |
| usually inside an application of some sort (see :ref:`user_interfaces` for a | |
| list of examples) . More information about Figures can be found at | |
| :ref:`figure-intro`. | |
| """ | |
| from contextlib import ExitStack | |
| import inspect | |
| import itertools | |
| import functools | |
| import logging | |
| from numbers import Integral | |
| import threading | |
| import numpy as np | |
| import matplotlib as mpl | |
| from matplotlib import _blocking_input, backend_bases, _docstring, projections | |
| from matplotlib.artist import ( | |
| Artist, allow_rasterization, _finalize_rasterization) | |
| from matplotlib.backend_bases import ( | |
| DrawEvent, FigureCanvasBase, NonGuiException, MouseButton, _get_renderer) | |
| import matplotlib._api as _api | |
| import matplotlib.cbook as cbook | |
| import matplotlib.colorbar as cbar | |
| import matplotlib.image as mimage | |
| from matplotlib.axes import Axes | |
| from matplotlib.gridspec import GridSpec, SubplotParams | |
| from matplotlib.layout_engine import ( | |
| ConstrainedLayoutEngine, TightLayoutEngine, LayoutEngine, | |
| PlaceHolderLayoutEngine | |
| ) | |
| import matplotlib.legend as mlegend | |
| from matplotlib.patches import Rectangle | |
| from matplotlib.text import Text | |
| from matplotlib.transforms import (Affine2D, Bbox, BboxTransformTo, | |
| TransformedBbox) | |
| _log = logging.getLogger(__name__) | |
| def _stale_figure_callback(self, val): | |
| if (fig := self.get_figure(root=False)) is not None: | |
| fig.stale = val | |
| class _AxesStack: | |
| """ | |
| Helper class to track Axes in a figure. | |
| Axes are tracked both in the order in which they have been added | |
| (``self._axes`` insertion/iteration order) and in the separate "gca" stack | |
| (which is the index to which they map in the ``self._axes`` dict). | |
| """ | |
| def __init__(self): | |
| self._axes = {} # Mapping of Axes to "gca" order. | |
| self._counter = itertools.count() | |
| def as_list(self): | |
| """List the Axes that have been added to the figure.""" | |
| return [*self._axes] # This relies on dict preserving order. | |
| def remove(self, a): | |
| """Remove the Axes from the stack.""" | |
| self._axes.pop(a) | |
| def bubble(self, a): | |
| """Move an Axes, which must already exist in the stack, to the top.""" | |
| if a not in self._axes: | |
| raise ValueError("Axes has not been added yet") | |
| self._axes[a] = next(self._counter) | |
| def add(self, a): | |
| """Add an Axes to the stack, ignoring it if already present.""" | |
| if a not in self._axes: | |
| self._axes[a] = next(self._counter) | |
| def current(self): | |
| """Return the active Axes, or None if the stack is empty.""" | |
| return max(self._axes, key=self._axes.__getitem__, default=None) | |
| def __getstate__(self): | |
| return { | |
| **vars(self), | |
| "_counter": max(self._axes.values(), default=0) | |
| } | |
| def __setstate__(self, state): | |
| next_counter = state.pop('_counter') | |
| vars(self).update(state) | |
| self._counter = itertools.count(next_counter) | |
| class FigureBase(Artist): | |
| """ | |
| Base class for `.Figure` and `.SubFigure` containing the methods that add | |
| artists to the figure or subfigure, create Axes, etc. | |
| """ | |
| def __init__(self, **kwargs): | |
| super().__init__() | |
| # remove the non-figure artist _axes property | |
| # as it makes no sense for a figure to be _in_ an Axes | |
| # this is used by the property methods in the artist base class | |
| # which are over-ridden in this class | |
| del self._axes | |
| self._suptitle = None | |
| self._supxlabel = None | |
| self._supylabel = None | |
| # groupers to keep track of x, y labels and title we want to align. | |
| # see self.align_xlabels, self.align_ylabels, | |
| # self.align_titles, and axis._get_tick_boxes_siblings | |
| self._align_label_groups = { | |
| "x": cbook.Grouper(), | |
| "y": cbook.Grouper(), | |
| "title": cbook.Grouper() | |
| } | |
| self._localaxes = [] # track all Axes | |
| self.artists = [] | |
| self.lines = [] | |
| self.patches = [] | |
| self.texts = [] | |
| self.images = [] | |
| self.legends = [] | |
| self.subfigs = [] | |
| self.stale = True | |
| self.suppressComposite = None | |
| self.set(**kwargs) | |
| def _get_draw_artists(self, renderer): | |
| """Also runs apply_aspect""" | |
| artists = self.get_children() | |
| artists.remove(self.patch) | |
| artists = sorted( | |
| (artist for artist in artists if not artist.get_animated()), | |
| key=lambda artist: artist.get_zorder()) | |
| for ax in self._localaxes: | |
| locator = ax.get_axes_locator() | |
| ax.apply_aspect(locator(ax, renderer) if locator else None) | |
| for child in ax.get_children(): | |
| if hasattr(child, 'apply_aspect'): | |
| locator = child.get_axes_locator() | |
| child.apply_aspect( | |
| locator(child, renderer) if locator else None) | |
| return artists | |
| def autofmt_xdate( | |
| self, bottom=0.2, rotation=30, ha='right', which='major'): | |
| """ | |
| Date ticklabels often overlap, so it is useful to rotate them | |
| and right align them. Also, a common use case is a number of | |
| subplots with shared x-axis where the x-axis is date data. The | |
| ticklabels are often long, and it helps to rotate them on the | |
| bottom subplot and turn them off on other subplots, as well as | |
| turn off xlabels. | |
| Parameters | |
| ---------- | |
| bottom : float, default: 0.2 | |
| The bottom of the subplots for `subplots_adjust`. | |
| rotation : float, default: 30 degrees | |
| The rotation angle of the xtick labels in degrees. | |
| ha : {'left', 'center', 'right'}, default: 'right' | |
| The horizontal alignment of the xticklabels. | |
| which : {'major', 'minor', 'both'}, default: 'major' | |
| Selects which ticklabels to rotate. | |
| """ | |
| _api.check_in_list(['major', 'minor', 'both'], which=which) | |
| axes = [ax for ax in self.axes if ax._label != '<colorbar>'] | |
| allsubplots = all(ax.get_subplotspec() for ax in axes) | |
| if len(axes) == 1: | |
| for label in self.axes[0].get_xticklabels(which=which): | |
| label.set_ha(ha) | |
| label.set_rotation(rotation) | |
| else: | |
| if allsubplots: | |
| for ax in axes: | |
| if ax.get_subplotspec().is_last_row(): | |
| for label in ax.get_xticklabels(which=which): | |
| label.set_ha(ha) | |
| label.set_rotation(rotation) | |
| else: | |
| for label in ax.get_xticklabels(which=which): | |
| label.set_visible(False) | |
| ax.set_xlabel('') | |
| engine = self.get_layout_engine() | |
| if allsubplots and (engine is None or engine.adjust_compatible): | |
| self.subplots_adjust(bottom=bottom) | |
| self.stale = True | |
| def get_children(self): | |
| """Get a list of artists contained in the figure.""" | |
| return [self.patch, | |
| *self.artists, | |
| *self._localaxes, | |
| *self.lines, | |
| *self.patches, | |
| *self.texts, | |
| *self.images, | |
| *self.legends, | |
| *self.subfigs] | |
| def get_figure(self, root=None): | |
| """ | |
| Return the `.Figure` or `.SubFigure` instance the (Sub)Figure belongs to. | |
| Parameters | |
| ---------- | |
| root : bool, default=True | |
| If False, return the (Sub)Figure this artist is on. If True, | |
| return the root Figure for a nested tree of SubFigures. | |
| .. deprecated:: 3.10 | |
| From version 3.12 *root* will default to False. | |
| """ | |
| if self._root_figure is self: | |
| # Top level Figure | |
| return self | |
| if self._parent is self._root_figure: | |
| # Return early to prevent the deprecation warning when *root* does not | |
| # matter | |
| return self._parent | |
| if root is None: | |
| # When deprecation expires, consider removing the docstring and just | |
| # inheriting the one from Artist. | |
| message = ('From Matplotlib 3.12 SubFigure.get_figure will by default ' | |
| 'return the direct parent figure, which may be a SubFigure. ' | |
| 'To suppress this warning, pass the root parameter. Pass ' | |
| '`True` to maintain the old behavior and `False` to opt-in to ' | |
| 'the future behavior.') | |
| _api.warn_deprecated('3.10', message=message) | |
| root = True | |
| if root: | |
| return self._root_figure | |
| return self._parent | |
| def set_figure(self, fig): | |
| """ | |
| .. deprecated:: 3.10 | |
| Currently this method will raise an exception if *fig* is anything other | |
| than the root `.Figure` this (Sub)Figure is on. In future it will always | |
| raise an exception. | |
| """ | |
| no_switch = ("The parent and root figures of a (Sub)Figure are set at " | |
| "instantiation and cannot be changed.") | |
| if fig is self._root_figure: | |
| _api.warn_deprecated( | |
| "3.10", | |
| message=(f"{no_switch} From Matplotlib 3.12 this operation will raise " | |
| "an exception.")) | |
| return | |
| raise ValueError(no_switch) | |
| figure = property(functools.partial(get_figure, root=True), set_figure, | |
| doc=("The root `Figure`. To get the parent of a `SubFigure`, " | |
| "use the `get_figure` method.")) | |
| def contains(self, mouseevent): | |
| """ | |
| Test whether the mouse event occurred on the figure. | |
| Returns | |
| ------- | |
| bool, {} | |
| """ | |
| if self._different_canvas(mouseevent): | |
| return False, {} | |
| inside = self.bbox.contains(mouseevent.x, mouseevent.y) | |
| return inside, {} | |
| def get_window_extent(self, renderer=None): | |
| # docstring inherited | |
| return self.bbox | |
| def _suplabels(self, t, info, **kwargs): | |
| """ | |
| Add a centered %(name)s to the figure. | |
| Parameters | |
| ---------- | |
| t : str | |
| The %(name)s text. | |
| x : float, default: %(x0)s | |
| The x location of the text in figure coordinates. | |
| y : float, default: %(y0)s | |
| The y location of the text in figure coordinates. | |
| horizontalalignment, ha : {'center', 'left', 'right'}, default: %(ha)s | |
| The horizontal alignment of the text relative to (*x*, *y*). | |
| verticalalignment, va : {'top', 'center', 'bottom', 'baseline'}, \ | |
| default: %(va)s | |
| The vertical alignment of the text relative to (*x*, *y*). | |
| fontsize, size : default: :rc:`figure.%(rc)ssize` | |
| The font size of the text. See `.Text.set_size` for possible | |
| values. | |
| fontweight, weight : default: :rc:`figure.%(rc)sweight` | |
| The font weight of the text. See `.Text.set_weight` for possible | |
| values. | |
| Returns | |
| ------- | |
| text | |
| The `.Text` instance of the %(name)s. | |
| Other Parameters | |
| ---------------- | |
| fontproperties : None or dict, optional | |
| A dict of font properties. If *fontproperties* is given the | |
| default values for font size and weight are taken from the | |
| `.FontProperties` defaults. :rc:`figure.%(rc)ssize` and | |
| :rc:`figure.%(rc)sweight` are ignored in this case. | |
| **kwargs | |
| Additional kwargs are `matplotlib.text.Text` properties. | |
| """ | |
| x = kwargs.pop('x', None) | |
| y = kwargs.pop('y', None) | |
| if info['name'] in ['_supxlabel', '_suptitle']: | |
| autopos = y is None | |
| elif info['name'] == '_supylabel': | |
| autopos = x is None | |
| if x is None: | |
| x = info['x0'] | |
| if y is None: | |
| y = info['y0'] | |
| kwargs = cbook.normalize_kwargs(kwargs, Text) | |
| kwargs.setdefault('horizontalalignment', info['ha']) | |
| kwargs.setdefault('verticalalignment', info['va']) | |
| kwargs.setdefault('rotation', info['rotation']) | |
| if 'fontproperties' not in kwargs: | |
| kwargs.setdefault('fontsize', mpl.rcParams[info['size']]) | |
| kwargs.setdefault('fontweight', mpl.rcParams[info['weight']]) | |
| suplab = getattr(self, info['name']) | |
| if suplab is not None: | |
| suplab.set_text(t) | |
| suplab.set_position((x, y)) | |
| suplab.set(**kwargs) | |
| else: | |
| suplab = self.text(x, y, t, **kwargs) | |
| setattr(self, info['name'], suplab) | |
| suplab._autopos = autopos | |
| self.stale = True | |
| return suplab | |
| def suptitle(self, t, **kwargs): | |
| # docstring from _suplabels... | |
| info = {'name': '_suptitle', 'x0': 0.5, 'y0': 0.98, | |
| 'ha': 'center', 'va': 'top', 'rotation': 0, | |
| 'size': 'figure.titlesize', 'weight': 'figure.titleweight'} | |
| return self._suplabels(t, info, **kwargs) | |
| def get_suptitle(self): | |
| """Return the suptitle as string or an empty string if not set.""" | |
| text_obj = self._suptitle | |
| return "" if text_obj is None else text_obj.get_text() | |
| def supxlabel(self, t, **kwargs): | |
| # docstring from _suplabels... | |
| info = {'name': '_supxlabel', 'x0': 0.5, 'y0': 0.01, | |
| 'ha': 'center', 'va': 'bottom', 'rotation': 0, | |
| 'size': 'figure.labelsize', 'weight': 'figure.labelweight'} | |
| return self._suplabels(t, info, **kwargs) | |
| def get_supxlabel(self): | |
| """Return the supxlabel as string or an empty string if not set.""" | |
| text_obj = self._supxlabel | |
| return "" if text_obj is None else text_obj.get_text() | |
| def supylabel(self, t, **kwargs): | |
| # docstring from _suplabels... | |
| info = {'name': '_supylabel', 'x0': 0.02, 'y0': 0.5, | |
| 'ha': 'left', 'va': 'center', 'rotation': 'vertical', | |
| 'rotation_mode': 'anchor', 'size': 'figure.labelsize', | |
| 'weight': 'figure.labelweight'} | |
| return self._suplabels(t, info, **kwargs) | |
| def get_supylabel(self): | |
| """Return the supylabel as string or an empty string if not set.""" | |
| text_obj = self._supylabel | |
| return "" if text_obj is None else text_obj.get_text() | |
| def get_edgecolor(self): | |
| """Get the edge color of the Figure rectangle.""" | |
| return self.patch.get_edgecolor() | |
| def get_facecolor(self): | |
| """Get the face color of the Figure rectangle.""" | |
| return self.patch.get_facecolor() | |
| def get_frameon(self): | |
| """ | |
| Return the figure's background patch visibility, i.e. | |
| whether the figure background will be drawn. Equivalent to | |
| ``Figure.patch.get_visible()``. | |
| """ | |
| return self.patch.get_visible() | |
| def set_linewidth(self, linewidth): | |
| """ | |
| Set the line width of the Figure rectangle. | |
| Parameters | |
| ---------- | |
| linewidth : number | |
| """ | |
| self.patch.set_linewidth(linewidth) | |
| def get_linewidth(self): | |
| """ | |
| Get the line width of the Figure rectangle. | |
| """ | |
| return self.patch.get_linewidth() | |
| def set_edgecolor(self, color): | |
| """ | |
| Set the edge color of the Figure rectangle. | |
| Parameters | |
| ---------- | |
| color : :mpltype:`color` | |
| """ | |
| self.patch.set_edgecolor(color) | |
| def set_facecolor(self, color): | |
| """ | |
| Set the face color of the Figure rectangle. | |
| Parameters | |
| ---------- | |
| color : :mpltype:`color` | |
| """ | |
| self.patch.set_facecolor(color) | |
| def set_frameon(self, b): | |
| """ | |
| Set the figure's background patch visibility, i.e. | |
| whether the figure background will be drawn. Equivalent to | |
| ``Figure.patch.set_visible()``. | |
| Parameters | |
| ---------- | |
| b : bool | |
| """ | |
| self.patch.set_visible(b) | |
| self.stale = True | |
| frameon = property(get_frameon, set_frameon) | |
| def add_artist(self, artist, clip=False): | |
| """ | |
| Add an `.Artist` to the figure. | |
| Usually artists are added to `~.axes.Axes` objects using | |
| `.Axes.add_artist`; this method can be used in the rare cases where | |
| one needs to add artists directly to the figure instead. | |
| Parameters | |
| ---------- | |
| artist : `~matplotlib.artist.Artist` | |
| The artist to add to the figure. If the added artist has no | |
| transform previously set, its transform will be set to | |
| ``figure.transSubfigure``. | |
| clip : bool, default: False | |
| Whether the added artist should be clipped by the figure patch. | |
| Returns | |
| ------- | |
| `~matplotlib.artist.Artist` | |
| The added artist. | |
| """ | |
| artist.set_figure(self) | |
| self.artists.append(artist) | |
| artist._remove_method = self.artists.remove | |
| if not artist.is_transform_set(): | |
| artist.set_transform(self.transSubfigure) | |
| if clip and artist.get_clip_path() is None: | |
| artist.set_clip_path(self.patch) | |
| self.stale = True | |
| return artist | |
| def add_axes(self, *args, **kwargs): | |
| """ | |
| Add an `~.axes.Axes` to the figure. | |
| Call signatures:: | |
| add_axes(rect, projection=None, polar=False, **kwargs) | |
| add_axes(ax) | |
| Parameters | |
| ---------- | |
| rect : tuple (left, bottom, width, height) | |
| The dimensions (left, bottom, width, height) of the new | |
| `~.axes.Axes`. All quantities are in fractions of figure width and | |
| height. | |
| projection : {None, 'aitoff', 'hammer', 'lambert', 'mollweide', \ | |
| 'polar', 'rectilinear', str}, optional | |
| The projection type of the `~.axes.Axes`. *str* is the name of | |
| a custom projection, see `~matplotlib.projections`. The default | |
| None results in a 'rectilinear' projection. | |
| polar : bool, default: False | |
| If True, equivalent to projection='polar'. | |
| axes_class : subclass type of `~.axes.Axes`, optional | |
| The `.axes.Axes` subclass that is instantiated. This parameter | |
| is incompatible with *projection* and *polar*. See | |
| :ref:`axisartist_users-guide-index` for examples. | |
| sharex, sharey : `~matplotlib.axes.Axes`, optional | |
| Share the x or y `~matplotlib.axis` with sharex and/or sharey. | |
| The axis will have the same limits, ticks, and scale as the axis | |
| of the shared Axes. | |
| label : str | |
| A label for the returned Axes. | |
| Returns | |
| ------- | |
| `~.axes.Axes`, or a subclass of `~.axes.Axes` | |
| The returned Axes class depends on the projection used. It is | |
| `~.axes.Axes` if rectilinear projection is used and | |
| `.projections.polar.PolarAxes` if polar projection is used. | |
| Other Parameters | |
| ---------------- | |
| **kwargs | |
| This method also takes the keyword arguments for | |
| the returned Axes class. The keyword arguments for the | |
| rectilinear Axes class `~.axes.Axes` can be found in | |
| the following table but there might also be other keyword | |
| arguments if another projection is used, see the actual Axes | |
| class. | |
| %(Axes:kwdoc)s | |
| Notes | |
| ----- | |
| In rare circumstances, `.add_axes` may be called with a single | |
| argument, an Axes instance already created in the present figure but | |
| not in the figure's list of Axes. | |
| See Also | |
| -------- | |
| .Figure.add_subplot | |
| .pyplot.subplot | |
| .pyplot.axes | |
| .Figure.subplots | |
| .pyplot.subplots | |
| Examples | |
| -------- | |
| Some simple examples:: | |
| rect = l, b, w, h | |
| fig = plt.figure() | |
| fig.add_axes(rect) | |
| fig.add_axes(rect, frameon=False, facecolor='g') | |
| fig.add_axes(rect, polar=True) | |
| ax = fig.add_axes(rect, projection='polar') | |
| fig.delaxes(ax) | |
| fig.add_axes(ax) | |
| """ | |
| if not len(args) and 'rect' not in kwargs: | |
| raise TypeError("add_axes() missing 1 required positional argument: 'rect'") | |
| elif 'rect' in kwargs: | |
| if len(args): | |
| raise TypeError("add_axes() got multiple values for argument 'rect'") | |
| args = (kwargs.pop('rect'), ) | |
| if len(args) != 1: | |
| raise _api.nargs_error("add_axes", 1, len(args)) | |
| if isinstance(args[0], Axes): | |
| a, = args | |
| key = a._projection_init | |
| if a.get_figure(root=False) is not self: | |
| raise ValueError( | |
| "The Axes must have been created in the present figure") | |
| else: | |
| rect, = args | |
| if not np.isfinite(rect).all(): | |
| raise ValueError(f'all entries in rect must be finite not {rect}') | |
| projection_class, pkw = self._process_projection_requirements(**kwargs) | |
| # create the new Axes using the Axes class given | |
| a = projection_class(self, rect, **pkw) | |
| key = (projection_class, pkw) | |
| return self._add_axes_internal(a, key) | |
| def add_subplot(self, *args, **kwargs): | |
| """ | |
| Add an `~.axes.Axes` to the figure as part of a subplot arrangement. | |
| Call signatures:: | |
| add_subplot(nrows, ncols, index, **kwargs) | |
| add_subplot(pos, **kwargs) | |
| add_subplot(ax) | |
| add_subplot() | |
| Parameters | |
| ---------- | |
| *args : int, (int, int, *index*), or `.SubplotSpec`, default: (1, 1, 1) | |
| The position of the subplot described by one of | |
| - Three integers (*nrows*, *ncols*, *index*). The subplot will | |
| take the *index* position on a grid with *nrows* rows and | |
| *ncols* columns. *index* starts at 1 in the upper left corner | |
| and increases to the right. *index* can also be a two-tuple | |
| specifying the (*first*, *last*) indices (1-based, and including | |
| *last*) of the subplot, e.g., ``fig.add_subplot(3, 1, (1, 2))`` | |
| makes a subplot that spans the upper 2/3 of the figure. | |
| - A 3-digit integer. The digits are interpreted as if given | |
| separately as three single-digit integers, i.e. | |
| ``fig.add_subplot(235)`` is the same as | |
| ``fig.add_subplot(2, 3, 5)``. Note that this can only be used | |
| if there are no more than 9 subplots. | |
| - A `.SubplotSpec`. | |
| In rare circumstances, `.add_subplot` may be called with a single | |
| argument, a subplot Axes instance already created in the | |
| present figure but not in the figure's list of Axes. | |
| projection : {None, 'aitoff', 'hammer', 'lambert', 'mollweide', \ | |
| 'polar', 'rectilinear', str}, optional | |
| The projection type of the subplot (`~.axes.Axes`). *str* is the | |
| name of a custom projection, see `~matplotlib.projections`. The | |
| default None results in a 'rectilinear' projection. | |
| polar : bool, default: False | |
| If True, equivalent to projection='polar'. | |
| axes_class : subclass type of `~.axes.Axes`, optional | |
| The `.axes.Axes` subclass that is instantiated. This parameter | |
| is incompatible with *projection* and *polar*. See | |
| :ref:`axisartist_users-guide-index` for examples. | |
| sharex, sharey : `~matplotlib.axes.Axes`, optional | |
| Share the x or y `~matplotlib.axis` with sharex and/or sharey. | |
| The axis will have the same limits, ticks, and scale as the axis | |
| of the shared Axes. | |
| label : str | |
| A label for the returned Axes. | |
| Returns | |
| ------- | |
| `~.axes.Axes` | |
| The Axes of the subplot. The returned Axes can actually be an | |
| instance of a subclass, such as `.projections.polar.PolarAxes` for | |
| polar projections. | |
| Other Parameters | |
| ---------------- | |
| **kwargs | |
| This method also takes the keyword arguments for the returned Axes | |
| base class; except for the *figure* argument. The keyword arguments | |
| for the rectilinear base class `~.axes.Axes` can be found in | |
| the following table but there might also be other keyword | |
| arguments if another projection is used. | |
| %(Axes:kwdoc)s | |
| See Also | |
| -------- | |
| .Figure.add_axes | |
| .pyplot.subplot | |
| .pyplot.axes | |
| .Figure.subplots | |
| .pyplot.subplots | |
| Examples | |
| -------- | |
| :: | |
| fig = plt.figure() | |
| fig.add_subplot(231) | |
| ax1 = fig.add_subplot(2, 3, 1) # equivalent but more general | |
| fig.add_subplot(232, frameon=False) # subplot with no frame | |
| fig.add_subplot(233, projection='polar') # polar subplot | |
| fig.add_subplot(234, sharex=ax1) # subplot sharing x-axis with ax1 | |
| fig.add_subplot(235, facecolor="red") # red subplot | |
| ax1.remove() # delete ax1 from the figure | |
| fig.add_subplot(ax1) # add ax1 back to the figure | |
| """ | |
| if 'figure' in kwargs: | |
| # Axes itself allows for a 'figure' kwarg, but since we want to | |
| # bind the created Axes to self, it is not allowed here. | |
| raise _api.kwarg_error("add_subplot", "figure") | |
| if (len(args) == 1 | |
| and isinstance(args[0], mpl.axes._base._AxesBase) | |
| and args[0].get_subplotspec()): | |
| ax = args[0] | |
| key = ax._projection_init | |
| if ax.get_figure(root=False) is not self: | |
| raise ValueError("The Axes must have been created in " | |
| "the present figure") | |
| else: | |
| if not args: | |
| args = (1, 1, 1) | |
| # Normalize correct ijk values to (i, j, k) here so that | |
| # add_subplot(211) == add_subplot(2, 1, 1). Invalid values will | |
| # trigger errors later (via SubplotSpec._from_subplot_args). | |
| if (len(args) == 1 and isinstance(args[0], Integral) | |
| and 100 <= args[0] <= 999): | |
| args = tuple(map(int, str(args[0]))) | |
| projection_class, pkw = self._process_projection_requirements(**kwargs) | |
| ax = projection_class(self, *args, **pkw) | |
| key = (projection_class, pkw) | |
| return self._add_axes_internal(ax, key) | |
| def _add_axes_internal(self, ax, key): | |
| """Private helper for `add_axes` and `add_subplot`.""" | |
| self._axstack.add(ax) | |
| if ax not in self._localaxes: | |
| self._localaxes.append(ax) | |
| self.sca(ax) | |
| ax._remove_method = self.delaxes | |
| # this is to support plt.subplot's re-selection logic | |
| ax._projection_init = key | |
| self.stale = True | |
| ax.stale_callback = _stale_figure_callback | |
| return ax | |
| def subplots(self, nrows=1, ncols=1, *, sharex=False, sharey=False, | |
| squeeze=True, width_ratios=None, height_ratios=None, | |
| subplot_kw=None, gridspec_kw=None): | |
| """ | |
| Add a set of subplots to this figure. | |
| This utility wrapper makes it convenient to create common layouts of | |
| subplots in a single call. | |
| Parameters | |
| ---------- | |
| nrows, ncols : int, default: 1 | |
| Number of rows/columns of the subplot grid. | |
| sharex, sharey : bool or {'none', 'all', 'row', 'col'}, default: False | |
| Controls sharing of x-axis (*sharex*) or y-axis (*sharey*): | |
| - True or 'all': x- or y-axis will be shared among all subplots. | |
| - False or 'none': each subplot x- or y-axis will be independent. | |
| - 'row': each subplot row will share an x- or y-axis. | |
| - 'col': each subplot column will share an x- or y-axis. | |
| When subplots have a shared x-axis along a column, only the x tick | |
| labels of the bottom subplot are created. Similarly, when subplots | |
| have a shared y-axis along a row, only the y tick labels of the | |
| first column subplot are created. To later turn other subplots' | |
| ticklabels on, use `~matplotlib.axes.Axes.tick_params`. | |
| When subplots have a shared axis that has units, calling | |
| `.Axis.set_units` will update each axis with the new units. | |
| Note that it is not possible to unshare axes. | |
| squeeze : bool, default: True | |
| - If True, extra dimensions are squeezed out from the returned | |
| array of Axes: | |
| - if only one subplot is constructed (nrows=ncols=1), the | |
| resulting single Axes object is returned as a scalar. | |
| - for Nx1 or 1xM subplots, the returned object is a 1D numpy | |
| object array of Axes objects. | |
| - for NxM, subplots with N>1 and M>1 are returned as a 2D array. | |
| - If False, no squeezing at all is done: the returned Axes object | |
| is always a 2D array containing Axes instances, even if it ends | |
| up being 1x1. | |
| width_ratios : array-like of length *ncols*, optional | |
| Defines the relative widths of the columns. Each column gets a | |
| relative width of ``width_ratios[i] / sum(width_ratios)``. | |
| If not given, all columns will have the same width. Equivalent | |
| to ``gridspec_kw={'width_ratios': [...]}``. | |
| height_ratios : array-like of length *nrows*, optional | |
| Defines the relative heights of the rows. Each row gets a | |
| relative height of ``height_ratios[i] / sum(height_ratios)``. | |
| If not given, all rows will have the same height. Equivalent | |
| to ``gridspec_kw={'height_ratios': [...]}``. | |
| subplot_kw : dict, optional | |
| Dict with keywords passed to the `.Figure.add_subplot` call used to | |
| create each subplot. | |
| gridspec_kw : dict, optional | |
| Dict with keywords passed to the | |
| `~matplotlib.gridspec.GridSpec` constructor used to create | |
| the grid the subplots are placed on. | |
| Returns | |
| ------- | |
| `~.axes.Axes` or array of Axes | |
| Either a single `~matplotlib.axes.Axes` object or an array of Axes | |
| objects if more than one subplot was created. The dimensions of the | |
| resulting array can be controlled with the *squeeze* keyword, see | |
| above. | |
| See Also | |
| -------- | |
| .pyplot.subplots | |
| .Figure.add_subplot | |
| .pyplot.subplot | |
| Examples | |
| -------- | |
| :: | |
| # First create some toy data: | |
| x = np.linspace(0, 2*np.pi, 400) | |
| y = np.sin(x**2) | |
| # Create a figure | |
| fig = plt.figure() | |
| # Create a subplot | |
| ax = fig.subplots() | |
| ax.plot(x, y) | |
| ax.set_title('Simple plot') | |
| # Create two subplots and unpack the output array immediately | |
| ax1, ax2 = fig.subplots(1, 2, sharey=True) | |
| ax1.plot(x, y) | |
| ax1.set_title('Sharing Y axis') | |
| ax2.scatter(x, y) | |
| # Create four polar Axes and access them through the returned array | |
| axes = fig.subplots(2, 2, subplot_kw=dict(projection='polar')) | |
| axes[0, 0].plot(x, y) | |
| axes[1, 1].scatter(x, y) | |
| # Share an X-axis with each column of subplots | |
| fig.subplots(2, 2, sharex='col') | |
| # Share a Y-axis with each row of subplots | |
| fig.subplots(2, 2, sharey='row') | |
| # Share both X- and Y-axes with all subplots | |
| fig.subplots(2, 2, sharex='all', sharey='all') | |
| # Note that this is the same as | |
| fig.subplots(2, 2, sharex=True, sharey=True) | |
| """ | |
| gridspec_kw = dict(gridspec_kw or {}) | |
| if height_ratios is not None: | |
| if 'height_ratios' in gridspec_kw: | |
| raise ValueError("'height_ratios' must not be defined both as " | |
| "parameter and as key in 'gridspec_kw'") | |
| gridspec_kw['height_ratios'] = height_ratios | |
| if width_ratios is not None: | |
| if 'width_ratios' in gridspec_kw: | |
| raise ValueError("'width_ratios' must not be defined both as " | |
| "parameter and as key in 'gridspec_kw'") | |
| gridspec_kw['width_ratios'] = width_ratios | |
| gs = self.add_gridspec(nrows, ncols, figure=self, **gridspec_kw) | |
| axs = gs.subplots(sharex=sharex, sharey=sharey, squeeze=squeeze, | |
| subplot_kw=subplot_kw) | |
| return axs | |
| def delaxes(self, ax): | |
| """ | |
| Remove the `~.axes.Axes` *ax* from the figure; update the current Axes. | |
| """ | |
| self._remove_axes(ax, owners=[self._axstack, self._localaxes]) | |
| def _remove_axes(self, ax, owners): | |
| """ | |
| Common helper for removal of standard Axes (via delaxes) and of child Axes. | |
| Parameters | |
| ---------- | |
| ax : `~.AxesBase` | |
| The Axes to remove. | |
| owners | |
| List of objects (list or _AxesStack) "owning" the Axes, from which the Axes | |
| will be remove()d. | |
| """ | |
| for owner in owners: | |
| owner.remove(ax) | |
| self._axobservers.process("_axes_change_event", self) | |
| self.stale = True | |
| self._root_figure.canvas.release_mouse(ax) | |
| for name in ax._axis_names: # Break link between any shared Axes | |
| grouper = ax._shared_axes[name] | |
| siblings = [other for other in grouper.get_siblings(ax) if other is not ax] | |
| if not siblings: # Axes was not shared along this axis; we're done. | |
| continue | |
| grouper.remove(ax) | |
| # Formatters and locators may previously have been associated with the now | |
| # removed axis. Update them to point to an axis still there (we can pick | |
| # any of them, and use the first sibling). | |
| remaining_axis = siblings[0]._axis_map[name] | |
| remaining_axis.get_major_formatter().set_axis(remaining_axis) | |
| remaining_axis.get_major_locator().set_axis(remaining_axis) | |
| remaining_axis.get_minor_formatter().set_axis(remaining_axis) | |
| remaining_axis.get_minor_locator().set_axis(remaining_axis) | |
| ax._twinned_axes.remove(ax) # Break link between any twinned Axes. | |
| def clear(self, keep_observers=False): | |
| """ | |
| Clear the figure. | |
| Parameters | |
| ---------- | |
| keep_observers : bool, default: False | |
| Set *keep_observers* to True if, for example, | |
| a gui widget is tracking the Axes in the figure. | |
| """ | |
| self.suppressComposite = None | |
| # first clear the Axes in any subfigures | |
| for subfig in self.subfigs: | |
| subfig.clear(keep_observers=keep_observers) | |
| self.subfigs = [] | |
| for ax in tuple(self.axes): # Iterate over the copy. | |
| ax.clear() | |
| self.delaxes(ax) # Remove ax from self._axstack. | |
| self.artists = [] | |
| self.lines = [] | |
| self.patches = [] | |
| self.texts = [] | |
| self.images = [] | |
| self.legends = [] | |
| if not keep_observers: | |
| self._axobservers = cbook.CallbackRegistry() | |
| self._suptitle = None | |
| self._supxlabel = None | |
| self._supylabel = None | |
| self.stale = True | |
| # synonym for `clear`. | |
| def clf(self, keep_observers=False): | |
| """ | |
| [*Discouraged*] Alias for the `clear()` method. | |
| .. admonition:: Discouraged | |
| The use of ``clf()`` is discouraged. Use ``clear()`` instead. | |
| Parameters | |
| ---------- | |
| keep_observers : bool, default: False | |
| Set *keep_observers* to True if, for example, | |
| a gui widget is tracking the Axes in the figure. | |
| """ | |
| return self.clear(keep_observers=keep_observers) | |
| # Note: the docstring below is modified with replace for the pyplot | |
| # version of this function because the method name differs (plt.figlegend) | |
| # the replacements are: | |
| # " legend(" -> " figlegend(" for the signatures | |
| # "fig.legend(" -> "plt.figlegend" for the code examples | |
| # "ax.plot" -> "plt.plot" for consistency in using pyplot when able | |
| def legend(self, *args, **kwargs): | |
| """ | |
| Place a legend on the figure. | |
| Call signatures:: | |
| legend() | |
| legend(handles, labels) | |
| legend(handles=handles) | |
| legend(labels) | |
| The call signatures correspond to the following different ways to use | |
| this method: | |
| **1. Automatic detection of elements to be shown in the legend** | |
| The elements to be added to the legend are automatically determined, | |
| when you do not pass in any extra arguments. | |
| In this case, the labels are taken from the artist. You can specify | |
| them either at artist creation or by calling the | |
| :meth:`~.Artist.set_label` method on the artist:: | |
| ax.plot([1, 2, 3], label='Inline label') | |
| fig.legend() | |
| or:: | |
| line, = ax.plot([1, 2, 3]) | |
| line.set_label('Label via method') | |
| fig.legend() | |
| Specific lines can be excluded from the automatic legend element | |
| selection by defining a label starting with an underscore. | |
| This is default for all artists, so calling `.Figure.legend` without | |
| any arguments and without setting the labels manually will result in | |
| no legend being drawn. | |
| **2. Explicitly listing the artists and labels in the legend** | |
| For full control of which artists have a legend entry, it is possible | |
| to pass an iterable of legend artists followed by an iterable of | |
| legend labels respectively:: | |
| fig.legend([line1, line2, line3], ['label1', 'label2', 'label3']) | |
| **3. Explicitly listing the artists in the legend** | |
| This is similar to 2, but the labels are taken from the artists' | |
| label properties. Example:: | |
| line1, = ax1.plot([1, 2, 3], label='label1') | |
| line2, = ax2.plot([1, 2, 3], label='label2') | |
| fig.legend(handles=[line1, line2]) | |
| **4. Labeling existing plot elements** | |
| .. admonition:: Discouraged | |
| This call signature is discouraged, because the relation between | |
| plot elements and labels is only implicit by their order and can | |
| easily be mixed up. | |
| To make a legend for all artists on all Axes, call this function with | |
| an iterable of strings, one for each legend item. For example:: | |
| fig, (ax1, ax2) = plt.subplots(1, 2) | |
| ax1.plot([1, 3, 5], color='blue') | |
| ax2.plot([2, 4, 6], color='red') | |
| fig.legend(['the blues', 'the reds']) | |
| Parameters | |
| ---------- | |
| handles : list of `.Artist`, optional | |
| A list of Artists (lines, patches) to be added to the legend. | |
| Use this together with *labels*, if you need full control on what | |
| is shown in the legend and the automatic mechanism described above | |
| is not sufficient. | |
| The length of handles and labels should be the same in this | |
| case. If they are not, they are truncated to the smaller length. | |
| labels : list of str, optional | |
| A list of labels to show next to the artists. | |
| Use this together with *handles*, if you need full control on what | |
| is shown in the legend and the automatic mechanism described above | |
| is not sufficient. | |
| Returns | |
| ------- | |
| `~matplotlib.legend.Legend` | |
| Other Parameters | |
| ---------------- | |
| %(_legend_kw_figure)s | |
| See Also | |
| -------- | |
| .Axes.legend | |
| Notes | |
| ----- | |
| Some artists are not supported by this function. See | |
| :ref:`legend_guide` for details. | |
| """ | |
| handles, labels, kwargs = mlegend._parse_legend_args(self.axes, *args, **kwargs) | |
| # explicitly set the bbox transform if the user hasn't. | |
| kwargs.setdefault("bbox_transform", self.transSubfigure) | |
| l = mlegend.Legend(self, handles, labels, **kwargs) | |
| self.legends.append(l) | |
| l._remove_method = self.legends.remove | |
| self.stale = True | |
| return l | |
| def text(self, x, y, s, fontdict=None, **kwargs): | |
| """ | |
| Add text to figure. | |
| Parameters | |
| ---------- | |
| x, y : float | |
| The position to place the text. By default, this is in figure | |
| coordinates, floats in [0, 1]. The coordinate system can be changed | |
| using the *transform* keyword. | |
| s : str | |
| The text string. | |
| fontdict : dict, optional | |
| A dictionary to override the default text properties. If not given, | |
| the defaults are determined by :rc:`font.*`. Properties passed as | |
| *kwargs* override the corresponding ones given in *fontdict*. | |
| Returns | |
| ------- | |
| `~.text.Text` | |
| Other Parameters | |
| ---------------- | |
| **kwargs : `~matplotlib.text.Text` properties | |
| Other miscellaneous text parameters. | |
| %(Text:kwdoc)s | |
| See Also | |
| -------- | |
| .Axes.text | |
| .pyplot.text | |
| """ | |
| effective_kwargs = { | |
| 'transform': self.transSubfigure, | |
| **(fontdict if fontdict is not None else {}), | |
| **kwargs, | |
| } | |
| text = Text(x=x, y=y, text=s, **effective_kwargs) | |
| text.set_figure(self) | |
| text.stale_callback = _stale_figure_callback | |
| self.texts.append(text) | |
| text._remove_method = self.texts.remove | |
| self.stale = True | |
| return text | |
| def colorbar( | |
| self, mappable, cax=None, ax=None, use_gridspec=True, **kwargs): | |
| """ | |
| Add a colorbar to a plot. | |
| Parameters | |
| ---------- | |
| mappable | |
| The `matplotlib.cm.ScalarMappable` (i.e., `.AxesImage`, | |
| `.ContourSet`, etc.) described by this colorbar. This argument is | |
| mandatory for the `.Figure.colorbar` method but optional for the | |
| `.pyplot.colorbar` function, which sets the default to the current | |
| image. | |
| Note that one can create a `.ScalarMappable` "on-the-fly" to | |
| generate colorbars not attached to a previously drawn artist, e.g. | |
| :: | |
| fig.colorbar(cm.ScalarMappable(norm=norm, cmap=cmap), ax=ax) | |
| cax : `~matplotlib.axes.Axes`, optional | |
| Axes into which the colorbar will be drawn. If `None`, then a new | |
| Axes is created and the space for it will be stolen from the Axes(s) | |
| specified in *ax*. | |
| ax : `~matplotlib.axes.Axes` or iterable or `numpy.ndarray` of Axes, optional | |
| The one or more parent Axes from which space for a new colorbar Axes | |
| will be stolen. This parameter is only used if *cax* is not set. | |
| Defaults to the Axes that contains the mappable used to create the | |
| colorbar. | |
| use_gridspec : bool, optional | |
| If *cax* is ``None``, a new *cax* is created as an instance of | |
| Axes. If *ax* is positioned with a subplotspec and *use_gridspec* | |
| is ``True``, then *cax* is also positioned with a subplotspec. | |
| Returns | |
| ------- | |
| colorbar : `~matplotlib.colorbar.Colorbar` | |
| Other Parameters | |
| ---------------- | |
| %(_make_axes_kw_doc)s | |
| %(_colormap_kw_doc)s | |
| Notes | |
| ----- | |
| If *mappable* is a `~.contour.ContourSet`, its *extend* kwarg is | |
| included automatically. | |
| The *shrink* kwarg provides a simple way to scale the colorbar with | |
| respect to the Axes. Note that if *cax* is specified, it determines the | |
| size of the colorbar, and *shrink* and *aspect* are ignored. | |
| For more precise control, you can manually specify the positions of the | |
| axes objects in which the mappable and the colorbar are drawn. In this | |
| case, do not use any of the Axes properties kwargs. | |
| It is known that some vector graphics viewers (svg and pdf) render | |
| white gaps between segments of the colorbar. This is due to bugs in | |
| the viewers, not Matplotlib. As a workaround, the colorbar can be | |
| rendered with overlapping segments:: | |
| cbar = colorbar() | |
| cbar.solids.set_edgecolor("face") | |
| draw() | |
| However, this has negative consequences in other circumstances, e.g. | |
| with semi-transparent images (alpha < 1) and colorbar extensions; | |
| therefore, this workaround is not used by default (see issue #1188). | |
| """ | |
| if ax is None: | |
| ax = getattr(mappable, "axes", None) | |
| if cax is None: | |
| if ax is None: | |
| raise ValueError( | |
| 'Unable to determine Axes to steal space for Colorbar. ' | |
| 'Either provide the *cax* argument to use as the Axes for ' | |
| 'the Colorbar, provide the *ax* argument to steal space ' | |
| 'from it, or add *mappable* to an Axes.') | |
| fig = ( # Figure of first Axes; logic copied from make_axes. | |
| [*ax.flat] if isinstance(ax, np.ndarray) | |
| else [*ax] if np.iterable(ax) | |
| else [ax])[0].get_figure(root=False) | |
| current_ax = fig.gca() | |
| if (fig.get_layout_engine() is not None and | |
| not fig.get_layout_engine().colorbar_gridspec): | |
| use_gridspec = False | |
| if (use_gridspec | |
| and isinstance(ax, mpl.axes._base._AxesBase) | |
| and ax.get_subplotspec()): | |
| cax, kwargs = cbar.make_axes_gridspec(ax, **kwargs) | |
| else: | |
| cax, kwargs = cbar.make_axes(ax, **kwargs) | |
| # make_axes calls add_{axes,subplot} which changes gca; undo that. | |
| fig.sca(current_ax) | |
| cax.grid(visible=False, which='both', axis='both') | |
| if (hasattr(mappable, "get_figure") and | |
| (mappable_host_fig := mappable.get_figure(root=True)) is not None): | |
| # Warn in case of mismatch | |
| if mappable_host_fig is not self._root_figure: | |
| _api.warn_external( | |
| f'Adding colorbar to a different Figure ' | |
| f'{repr(mappable_host_fig)} than ' | |
| f'{repr(self._root_figure)} which ' | |
| f'fig.colorbar is called on.') | |
| NON_COLORBAR_KEYS = [ # remove kws that cannot be passed to Colorbar | |
| 'fraction', 'pad', 'shrink', 'aspect', 'anchor', 'panchor'] | |
| cb = cbar.Colorbar(cax, mappable, **{ | |
| k: v for k, v in kwargs.items() if k not in NON_COLORBAR_KEYS}) | |
| cax.get_figure(root=False).stale = True | |
| return cb | |
| def subplots_adjust(self, left=None, bottom=None, right=None, top=None, | |
| wspace=None, hspace=None): | |
| """ | |
| Adjust the subplot layout parameters. | |
| Unset parameters are left unmodified; initial values are given by | |
| :rc:`figure.subplot.[name]`. | |
| .. plot:: _embedded_plots/figure_subplots_adjust.py | |
| Parameters | |
| ---------- | |
| left : float, optional | |
| The position of the left edge of the subplots, | |
| as a fraction of the figure width. | |
| right : float, optional | |
| The position of the right edge of the subplots, | |
| as a fraction of the figure width. | |
| bottom : float, optional | |
| The position of the bottom edge of the subplots, | |
| as a fraction of the figure height. | |
| top : float, optional | |
| The position of the top edge of the subplots, | |
| as a fraction of the figure height. | |
| wspace : float, optional | |
| The width of the padding between subplots, | |
| as a fraction of the average Axes width. | |
| hspace : float, optional | |
| The height of the padding between subplots, | |
| as a fraction of the average Axes height. | |
| """ | |
| if (self.get_layout_engine() is not None and | |
| not self.get_layout_engine().adjust_compatible): | |
| _api.warn_external( | |
| "This figure was using a layout engine that is " | |
| "incompatible with subplots_adjust and/or tight_layout; " | |
| "not calling subplots_adjust.") | |
| return | |
| self.subplotpars.update(left, bottom, right, top, wspace, hspace) | |
| for ax in self.axes: | |
| if ax.get_subplotspec() is not None: | |
| ax._set_position(ax.get_subplotspec().get_position(self)) | |
| self.stale = True | |
| def align_xlabels(self, axs=None): | |
| """ | |
| Align the xlabels of subplots in the same subplot row if label | |
| alignment is being done automatically (i.e. the label position is | |
| not manually set). | |
| Alignment persists for draw events after this is called. | |
| If a label is on the bottom, it is aligned with labels on Axes that | |
| also have their label on the bottom and that have the same | |
| bottom-most subplot row. If the label is on the top, | |
| it is aligned with labels on Axes with the same top-most row. | |
| Parameters | |
| ---------- | |
| axs : list of `~matplotlib.axes.Axes` | |
| Optional list of (or `~numpy.ndarray`) `~matplotlib.axes.Axes` | |
| to align the xlabels. | |
| Default is to align all Axes on the figure. | |
| See Also | |
| -------- | |
| matplotlib.figure.Figure.align_ylabels | |
| matplotlib.figure.Figure.align_titles | |
| matplotlib.figure.Figure.align_labels | |
| Notes | |
| ----- | |
| This assumes that all Axes in ``axs`` are from the same `.GridSpec`, | |
| so that their `.SubplotSpec` positions correspond to figure positions. | |
| Examples | |
| -------- | |
| Example with rotated xtick labels:: | |
| fig, axs = plt.subplots(1, 2) | |
| for tick in axs[0].get_xticklabels(): | |
| tick.set_rotation(55) | |
| axs[0].set_xlabel('XLabel 0') | |
| axs[1].set_xlabel('XLabel 1') | |
| fig.align_xlabels() | |
| """ | |
| if axs is None: | |
| axs = self.axes | |
| axs = [ax for ax in np.ravel(axs) if ax.get_subplotspec() is not None] | |
| for ax in axs: | |
| _log.debug(' Working on: %s', ax.get_xlabel()) | |
| rowspan = ax.get_subplotspec().rowspan | |
| pos = ax.xaxis.get_label_position() # top or bottom | |
| # Search through other Axes for label positions that are same as | |
| # this one and that share the appropriate row number. | |
| # Add to a grouper associated with each Axes of siblings. | |
| # This list is inspected in `axis.draw` by | |
| # `axis._update_label_position`. | |
| for axc in axs: | |
| if axc.xaxis.get_label_position() == pos: | |
| rowspanc = axc.get_subplotspec().rowspan | |
| if (pos == 'top' and rowspan.start == rowspanc.start or | |
| pos == 'bottom' and rowspan.stop == rowspanc.stop): | |
| # grouper for groups of xlabels to align | |
| self._align_label_groups['x'].join(ax, axc) | |
| def align_ylabels(self, axs=None): | |
| """ | |
| Align the ylabels of subplots in the same subplot column if label | |
| alignment is being done automatically (i.e. the label position is | |
| not manually set). | |
| Alignment persists for draw events after this is called. | |
| If a label is on the left, it is aligned with labels on Axes that | |
| also have their label on the left and that have the same | |
| left-most subplot column. If the label is on the right, | |
| it is aligned with labels on Axes with the same right-most column. | |
| Parameters | |
| ---------- | |
| axs : list of `~matplotlib.axes.Axes` | |
| Optional list (or `~numpy.ndarray`) of `~matplotlib.axes.Axes` | |
| to align the ylabels. | |
| Default is to align all Axes on the figure. | |
| See Also | |
| -------- | |
| matplotlib.figure.Figure.align_xlabels | |
| matplotlib.figure.Figure.align_titles | |
| matplotlib.figure.Figure.align_labels | |
| Notes | |
| ----- | |
| This assumes that all Axes in ``axs`` are from the same `.GridSpec`, | |
| so that their `.SubplotSpec` positions correspond to figure positions. | |
| Examples | |
| -------- | |
| Example with large yticks labels:: | |
| fig, axs = plt.subplots(2, 1) | |
| axs[0].plot(np.arange(0, 1000, 50)) | |
| axs[0].set_ylabel('YLabel 0') | |
| axs[1].set_ylabel('YLabel 1') | |
| fig.align_ylabels() | |
| """ | |
| if axs is None: | |
| axs = self.axes | |
| axs = [ax for ax in np.ravel(axs) if ax.get_subplotspec() is not None] | |
| for ax in axs: | |
| _log.debug(' Working on: %s', ax.get_ylabel()) | |
| colspan = ax.get_subplotspec().colspan | |
| pos = ax.yaxis.get_label_position() # left or right | |
| # Search through other Axes for label positions that are same as | |
| # this one and that share the appropriate column number. | |
| # Add to a list associated with each Axes of siblings. | |
| # This list is inspected in `axis.draw` by | |
| # `axis._update_label_position`. | |
| for axc in axs: | |
| if axc.yaxis.get_label_position() == pos: | |
| colspanc = axc.get_subplotspec().colspan | |
| if (pos == 'left' and colspan.start == colspanc.start or | |
| pos == 'right' and colspan.stop == colspanc.stop): | |
| # grouper for groups of ylabels to align | |
| self._align_label_groups['y'].join(ax, axc) | |
| def align_titles(self, axs=None): | |
| """ | |
| Align the titles of subplots in the same subplot row if title | |
| alignment is being done automatically (i.e. the title position is | |
| not manually set). | |
| Alignment persists for draw events after this is called. | |
| Parameters | |
| ---------- | |
| axs : list of `~matplotlib.axes.Axes` | |
| Optional list of (or ndarray) `~matplotlib.axes.Axes` | |
| to align the titles. | |
| Default is to align all Axes on the figure. | |
| See Also | |
| -------- | |
| matplotlib.figure.Figure.align_xlabels | |
| matplotlib.figure.Figure.align_ylabels | |
| matplotlib.figure.Figure.align_labels | |
| Notes | |
| ----- | |
| This assumes that all Axes in ``axs`` are from the same `.GridSpec`, | |
| so that their `.SubplotSpec` positions correspond to figure positions. | |
| Examples | |
| -------- | |
| Example with titles:: | |
| fig, axs = plt.subplots(1, 2) | |
| axs[0].set_aspect('equal') | |
| axs[0].set_title('Title 0') | |
| axs[1].set_title('Title 1') | |
| fig.align_titles() | |
| """ | |
| if axs is None: | |
| axs = self.axes | |
| axs = [ax for ax in np.ravel(axs) if ax.get_subplotspec() is not None] | |
| for ax in axs: | |
| _log.debug(' Working on: %s', ax.get_title()) | |
| rowspan = ax.get_subplotspec().rowspan | |
| for axc in axs: | |
| rowspanc = axc.get_subplotspec().rowspan | |
| if (rowspan.start == rowspanc.start): | |
| self._align_label_groups['title'].join(ax, axc) | |
| def align_labels(self, axs=None): | |
| """ | |
| Align the xlabels and ylabels of subplots with the same subplots | |
| row or column (respectively) if label alignment is being | |
| done automatically (i.e. the label position is not manually set). | |
| Alignment persists for draw events after this is called. | |
| Parameters | |
| ---------- | |
| axs : list of `~matplotlib.axes.Axes` | |
| Optional list (or `~numpy.ndarray`) of `~matplotlib.axes.Axes` | |
| to align the labels. | |
| Default is to align all Axes on the figure. | |
| See Also | |
| -------- | |
| matplotlib.figure.Figure.align_xlabels | |
| matplotlib.figure.Figure.align_ylabels | |
| matplotlib.figure.Figure.align_titles | |
| Notes | |
| ----- | |
| This assumes that all Axes in ``axs`` are from the same `.GridSpec`, | |
| so that their `.SubplotSpec` positions correspond to figure positions. | |
| """ | |
| self.align_xlabels(axs=axs) | |
| self.align_ylabels(axs=axs) | |
| def add_gridspec(self, nrows=1, ncols=1, **kwargs): | |
| """ | |
| Low-level API for creating a `.GridSpec` that has this figure as a parent. | |
| This is a low-level API, allowing you to create a gridspec and | |
| subsequently add subplots based on the gridspec. Most users do | |
| not need that freedom and should use the higher-level methods | |
| `~.Figure.subplots` or `~.Figure.subplot_mosaic`. | |
| Parameters | |
| ---------- | |
| nrows : int, default: 1 | |
| Number of rows in grid. | |
| ncols : int, default: 1 | |
| Number of columns in grid. | |
| Returns | |
| ------- | |
| `.GridSpec` | |
| Other Parameters | |
| ---------------- | |
| **kwargs | |
| Keyword arguments are passed to `.GridSpec`. | |
| See Also | |
| -------- | |
| matplotlib.pyplot.subplots | |
| Examples | |
| -------- | |
| Adding a subplot that spans two rows:: | |
| fig = plt.figure() | |
| gs = fig.add_gridspec(2, 2) | |
| ax1 = fig.add_subplot(gs[0, 0]) | |
| ax2 = fig.add_subplot(gs[1, 0]) | |
| # spans two rows: | |
| ax3 = fig.add_subplot(gs[:, 1]) | |
| """ | |
| _ = kwargs.pop('figure', None) # pop in case user has added this... | |
| gs = GridSpec(nrows=nrows, ncols=ncols, figure=self, **kwargs) | |
| return gs | |
| def subfigures(self, nrows=1, ncols=1, squeeze=True, | |
| wspace=None, hspace=None, | |
| width_ratios=None, height_ratios=None, | |
| **kwargs): | |
| """ | |
| Add a set of subfigures to this figure or subfigure. | |
| A subfigure has the same artist methods as a figure, and is logically | |
| the same as a figure, but cannot print itself. | |
| See :doc:`/gallery/subplots_axes_and_figures/subfigures`. | |
| .. versionchanged:: 3.10 | |
| subfigures are now added in row-major order. | |
| Parameters | |
| ---------- | |
| nrows, ncols : int, default: 1 | |
| Number of rows/columns of the subfigure grid. | |
| squeeze : bool, default: True | |
| If True, extra dimensions are squeezed out from the returned | |
| array of subfigures. | |
| wspace, hspace : float, default: None | |
| The amount of width/height reserved for space between subfigures, | |
| expressed as a fraction of the average subfigure width/height. | |
| If not given, the values will be inferred from rcParams if using | |
| constrained layout (see `~.ConstrainedLayoutEngine`), or zero if | |
| not using a layout engine. | |
| width_ratios : array-like of length *ncols*, optional | |
| Defines the relative widths of the columns. Each column gets a | |
| relative width of ``width_ratios[i] / sum(width_ratios)``. | |
| If not given, all columns will have the same width. | |
| height_ratios : array-like of length *nrows*, optional | |
| Defines the relative heights of the rows. Each row gets a | |
| relative height of ``height_ratios[i] / sum(height_ratios)``. | |
| If not given, all rows will have the same height. | |
| """ | |
| gs = GridSpec(nrows=nrows, ncols=ncols, figure=self, | |
| wspace=wspace, hspace=hspace, | |
| width_ratios=width_ratios, | |
| height_ratios=height_ratios, | |
| left=0, right=1, bottom=0, top=1) | |
| sfarr = np.empty((nrows, ncols), dtype=object) | |
| for i in range(nrows): | |
| for j in range(ncols): | |
| sfarr[i, j] = self.add_subfigure(gs[i, j], **kwargs) | |
| if self.get_layout_engine() is None and (wspace is not None or | |
| hspace is not None): | |
| # Gridspec wspace and hspace is ignored on subfigure instantiation, | |
| # and no space is left. So need to account for it here if required. | |
| bottoms, tops, lefts, rights = gs.get_grid_positions(self) | |
| for sfrow, bottom, top in zip(sfarr, bottoms, tops): | |
| for sf, left, right in zip(sfrow, lefts, rights): | |
| bbox = Bbox.from_extents(left, bottom, right, top) | |
| sf._redo_transform_rel_fig(bbox=bbox) | |
| if squeeze: | |
| # Discarding unneeded dimensions that equal 1. If we only have one | |
| # subfigure, just return it instead of a 1-element array. | |
| return sfarr.item() if sfarr.size == 1 else sfarr.squeeze() | |
| else: | |
| # Returned axis array will be always 2-d, even if nrows=ncols=1. | |
| return sfarr | |
| def add_subfigure(self, subplotspec, **kwargs): | |
| """ | |
| Add a `.SubFigure` to the figure as part of a subplot arrangement. | |
| Parameters | |
| ---------- | |
| subplotspec : `.gridspec.SubplotSpec` | |
| Defines the region in a parent gridspec where the subfigure will | |
| be placed. | |
| Returns | |
| ------- | |
| `.SubFigure` | |
| Other Parameters | |
| ---------------- | |
| **kwargs | |
| Are passed to the `.SubFigure` object. | |
| See Also | |
| -------- | |
| .Figure.subfigures | |
| """ | |
| sf = SubFigure(self, subplotspec, **kwargs) | |
| self.subfigs += [sf] | |
| sf._remove_method = self.subfigs.remove | |
| sf.stale_callback = _stale_figure_callback | |
| self.stale = True | |
| return sf | |
| def sca(self, a): | |
| """Set the current Axes to be *a* and return *a*.""" | |
| self._axstack.bubble(a) | |
| self._axobservers.process("_axes_change_event", self) | |
| return a | |
| def gca(self): | |
| """ | |
| Get the current Axes. | |
| If there is currently no Axes on this Figure, a new one is created | |
| using `.Figure.add_subplot`. (To test whether there is currently an | |
| Axes on a Figure, check whether ``figure.axes`` is empty. To test | |
| whether there is currently a Figure on the pyplot figure stack, check | |
| whether `.pyplot.get_fignums()` is empty.) | |
| """ | |
| ax = self._axstack.current() | |
| return ax if ax is not None else self.add_subplot() | |
| def _gci(self): | |
| # Helper for `~matplotlib.pyplot.gci`. Do not use elsewhere. | |
| """ | |
| Get the current colorable artist. | |
| Specifically, returns the current `.ScalarMappable` instance (`.Image` | |
| created by `imshow` or `figimage`, `.Collection` created by `pcolor` or | |
| `scatter`, etc.), or *None* if no such instance has been defined. | |
| The current image is an attribute of the current Axes, or the nearest | |
| earlier Axes in the current figure that contains an image. | |
| Notes | |
| ----- | |
| Historically, the only colorable artists were images; hence the name | |
| ``gci`` (get current image). | |
| """ | |
| # Look first for an image in the current Axes. | |
| ax = self._axstack.current() | |
| if ax is None: | |
| return None | |
| im = ax._gci() | |
| if im is not None: | |
| return im | |
| # If there is no image in the current Axes, search for | |
| # one in a previously created Axes. Whether this makes | |
| # sense is debatable, but it is the documented behavior. | |
| for ax in reversed(self.axes): | |
| im = ax._gci() | |
| if im is not None: | |
| return im | |
| return None | |
| def _process_projection_requirements(self, *, axes_class=None, polar=False, | |
| projection=None, **kwargs): | |
| """ | |
| Handle the args/kwargs to add_axes/add_subplot/gca, returning:: | |
| (axes_proj_class, proj_class_kwargs) | |
| which can be used for new Axes initialization/identification. | |
| """ | |
| if axes_class is not None: | |
| if polar or projection is not None: | |
| raise ValueError( | |
| "Cannot combine 'axes_class' and 'projection' or 'polar'") | |
| projection_class = axes_class | |
| else: | |
| if polar: | |
| if projection is not None and projection != 'polar': | |
| raise ValueError( | |
| f"polar={polar}, yet projection={projection!r}. " | |
| "Only one of these arguments should be supplied." | |
| ) | |
| projection = 'polar' | |
| if isinstance(projection, str) or projection is None: | |
| projection_class = projections.get_projection_class(projection) | |
| elif hasattr(projection, '_as_mpl_axes'): | |
| projection_class, extra_kwargs = projection._as_mpl_axes() | |
| kwargs.update(**extra_kwargs) | |
| else: | |
| raise TypeError( | |
| f"projection must be a string, None or implement a " | |
| f"_as_mpl_axes method, not {projection!r}") | |
| return projection_class, kwargs | |
| def get_default_bbox_extra_artists(self): | |
| """ | |
| Return a list of Artists typically used in `.Figure.get_tightbbox`. | |
| """ | |
| bbox_artists = [artist for artist in self.get_children() | |
| if (artist.get_visible() and artist.get_in_layout())] | |
| for ax in self.axes: | |
| if ax.get_visible(): | |
| bbox_artists.extend(ax.get_default_bbox_extra_artists()) | |
| return bbox_artists | |
| def get_tightbbox(self, renderer=None, *, bbox_extra_artists=None): | |
| """ | |
| Return a (tight) bounding box of the figure *in inches*. | |
| Note that `.FigureBase` differs from all other artists, which return | |
| their `.Bbox` in pixels. | |
| Artists that have ``artist.set_in_layout(False)`` are not included | |
| in the bbox. | |
| Parameters | |
| ---------- | |
| renderer : `.RendererBase` subclass | |
| Renderer that will be used to draw the figures (i.e. | |
| ``fig.canvas.get_renderer()``) | |
| bbox_extra_artists : list of `.Artist` or ``None`` | |
| List of artists to include in the tight bounding box. If | |
| ``None`` (default), then all artist children of each Axes are | |
| included in the tight bounding box. | |
| Returns | |
| ------- | |
| `.BboxBase` | |
| containing the bounding box (in figure inches). | |
| """ | |
| if renderer is None: | |
| renderer = self.get_figure(root=True)._get_renderer() | |
| bb = [] | |
| if bbox_extra_artists is None: | |
| artists = [artist for artist in self.get_children() | |
| if (artist not in self.axes and artist.get_visible() | |
| and artist.get_in_layout())] | |
| else: | |
| artists = bbox_extra_artists | |
| for a in artists: | |
| bbox = a.get_tightbbox(renderer) | |
| if bbox is not None: | |
| bb.append(bbox) | |
| for ax in self.axes: | |
| if ax.get_visible(): | |
| # some Axes don't take the bbox_extra_artists kwarg so we | |
| # need this conditional.... | |
| try: | |
| bbox = ax.get_tightbbox( | |
| renderer, bbox_extra_artists=bbox_extra_artists) | |
| except TypeError: | |
| bbox = ax.get_tightbbox(renderer) | |
| bb.append(bbox) | |
| bb = [b for b in bb | |
| if (np.isfinite(b.width) and np.isfinite(b.height) | |
| and (b.width != 0 or b.height != 0))] | |
| isfigure = hasattr(self, 'bbox_inches') | |
| if len(bb) == 0: | |
| if isfigure: | |
| return self.bbox_inches | |
| else: | |
| # subfigures do not have bbox_inches, but do have a bbox | |
| bb = [self.bbox] | |
| _bbox = Bbox.union(bb) | |
| if isfigure: | |
| # transform from pixels to inches... | |
| _bbox = TransformedBbox(_bbox, self.dpi_scale_trans.inverted()) | |
| return _bbox | |
| def _norm_per_subplot_kw(per_subplot_kw): | |
| expanded = {} | |
| for k, v in per_subplot_kw.items(): | |
| if isinstance(k, tuple): | |
| for sub_key in k: | |
| if sub_key in expanded: | |
| raise ValueError(f'The key {sub_key!r} appears multiple times.') | |
| expanded[sub_key] = v | |
| else: | |
| if k in expanded: | |
| raise ValueError(f'The key {k!r} appears multiple times.') | |
| expanded[k] = v | |
| return expanded | |
| def _normalize_grid_string(layout): | |
| if '\n' not in layout: | |
| # single-line string | |
| return [list(ln) for ln in layout.split(';')] | |
| else: | |
| # multi-line string | |
| layout = inspect.cleandoc(layout) | |
| return [list(ln) for ln in layout.strip('\n').split('\n')] | |
| def subplot_mosaic(self, mosaic, *, sharex=False, sharey=False, | |
| width_ratios=None, height_ratios=None, | |
| empty_sentinel='.', | |
| subplot_kw=None, per_subplot_kw=None, gridspec_kw=None): | |
| """ | |
| Build a layout of Axes based on ASCII art or nested lists. | |
| This is a helper function to build complex GridSpec layouts visually. | |
| See :ref:`mosaic` | |
| for an example and full API documentation | |
| Parameters | |
| ---------- | |
| mosaic : list of list of {hashable or nested} or str | |
| A visual layout of how you want your Axes to be arranged | |
| labeled as strings. For example :: | |
| x = [['A panel', 'A panel', 'edge'], | |
| ['C panel', '.', 'edge']] | |
| produces 4 Axes: | |
| - 'A panel' which is 1 row high and spans the first two columns | |
| - 'edge' which is 2 rows high and is on the right edge | |
| - 'C panel' which in 1 row and 1 column wide in the bottom left | |
| - a blank space 1 row and 1 column wide in the bottom center | |
| Any of the entries in the layout can be a list of lists | |
| of the same form to create nested layouts. | |
| If input is a str, then it can either be a multi-line string of | |
| the form :: | |
| ''' | |
| AAE | |
| C.E | |
| ''' | |
| where each character is a column and each line is a row. Or it | |
| can be a single-line string where rows are separated by ``;``:: | |
| 'AB;CC' | |
| The string notation allows only single character Axes labels and | |
| does not support nesting but is very terse. | |
| The Axes identifiers may be `str` or a non-iterable hashable | |
| object (e.g. `tuple` s may not be used). | |
| sharex, sharey : bool, default: False | |
| If True, the x-axis (*sharex*) or y-axis (*sharey*) will be shared | |
| among all subplots. In that case, tick label visibility and axis | |
| units behave as for `subplots`. If False, each subplot's x- or | |
| y-axis will be independent. | |
| width_ratios : array-like of length *ncols*, optional | |
| Defines the relative widths of the columns. Each column gets a | |
| relative width of ``width_ratios[i] / sum(width_ratios)``. | |
| If not given, all columns will have the same width. Equivalent | |
| to ``gridspec_kw={'width_ratios': [...]}``. In the case of nested | |
| layouts, this argument applies only to the outer layout. | |
| height_ratios : array-like of length *nrows*, optional | |
| Defines the relative heights of the rows. Each row gets a | |
| relative height of ``height_ratios[i] / sum(height_ratios)``. | |
| If not given, all rows will have the same height. Equivalent | |
| to ``gridspec_kw={'height_ratios': [...]}``. In the case of nested | |
| layouts, this argument applies only to the outer layout. | |
| subplot_kw : dict, optional | |
| Dictionary with keywords passed to the `.Figure.add_subplot` call | |
| used to create each subplot. These values may be overridden by | |
| values in *per_subplot_kw*. | |
| per_subplot_kw : dict, optional | |
| A dictionary mapping the Axes identifiers or tuples of identifiers | |
| to a dictionary of keyword arguments to be passed to the | |
| `.Figure.add_subplot` call used to create each subplot. The values | |
| in these dictionaries have precedence over the values in | |
| *subplot_kw*. | |
| If *mosaic* is a string, and thus all keys are single characters, | |
| it is possible to use a single string instead of a tuple as keys; | |
| i.e. ``"AB"`` is equivalent to ``("A", "B")``. | |
| .. versionadded:: 3.7 | |
| gridspec_kw : dict, optional | |
| Dictionary with keywords passed to the `.GridSpec` constructor used | |
| to create the grid the subplots are placed on. In the case of | |
| nested layouts, this argument applies only to the outer layout. | |
| For more complex layouts, users should use `.Figure.subfigures` | |
| to create the nesting. | |
| empty_sentinel : object, optional | |
| Entry in the layout to mean "leave this space empty". Defaults | |
| to ``'.'``. Note, if *layout* is a string, it is processed via | |
| `inspect.cleandoc` to remove leading white space, which may | |
| interfere with using white-space as the empty sentinel. | |
| Returns | |
| ------- | |
| dict[label, Axes] | |
| A dictionary mapping the labels to the Axes objects. The order of | |
| the Axes is left-to-right and top-to-bottom of their position in the | |
| total layout. | |
| """ | |
| subplot_kw = subplot_kw or {} | |
| gridspec_kw = dict(gridspec_kw or {}) | |
| per_subplot_kw = per_subplot_kw or {} | |
| if height_ratios is not None: | |
| if 'height_ratios' in gridspec_kw: | |
| raise ValueError("'height_ratios' must not be defined both as " | |
| "parameter and as key in 'gridspec_kw'") | |
| gridspec_kw['height_ratios'] = height_ratios | |
| if width_ratios is not None: | |
| if 'width_ratios' in gridspec_kw: | |
| raise ValueError("'width_ratios' must not be defined both as " | |
| "parameter and as key in 'gridspec_kw'") | |
| gridspec_kw['width_ratios'] = width_ratios | |
| # special-case string input | |
| if isinstance(mosaic, str): | |
| mosaic = self._normalize_grid_string(mosaic) | |
| per_subplot_kw = { | |
| tuple(k): v for k, v in per_subplot_kw.items() | |
| } | |
| per_subplot_kw = self._norm_per_subplot_kw(per_subplot_kw) | |
| # Only accept strict bools to allow a possible future API expansion. | |
| _api.check_isinstance(bool, sharex=sharex, sharey=sharey) | |
| def _make_array(inp): | |
| """ | |
| Convert input into 2D array | |
| We need to have this internal function rather than | |
| ``np.asarray(..., dtype=object)`` so that a list of lists | |
| of lists does not get converted to an array of dimension > 2. | |
| Returns | |
| ------- | |
| 2D object array | |
| """ | |
| r0, *rest = inp | |
| if isinstance(r0, str): | |
| raise ValueError('List mosaic specification must be 2D') | |
| for j, r in enumerate(rest, start=1): | |
| if isinstance(r, str): | |
| raise ValueError('List mosaic specification must be 2D') | |
| if len(r0) != len(r): | |
| raise ValueError( | |
| "All of the rows must be the same length, however " | |
| f"the first row ({r0!r}) has length {len(r0)} " | |
| f"and row {j} ({r!r}) has length {len(r)}." | |
| ) | |
| out = np.zeros((len(inp), len(r0)), dtype=object) | |
| for j, r in enumerate(inp): | |
| for k, v in enumerate(r): | |
| out[j, k] = v | |
| return out | |
| def _identify_keys_and_nested(mosaic): | |
| """ | |
| Given a 2D object array, identify unique IDs and nested mosaics | |
| Parameters | |
| ---------- | |
| mosaic : 2D object array | |
| Returns | |
| ------- | |
| unique_ids : tuple | |
| The unique non-sub mosaic entries in this mosaic | |
| nested : dict[tuple[int, int], 2D object array] | |
| """ | |
| # make sure we preserve the user supplied order | |
| unique_ids = cbook._OrderedSet() | |
| nested = {} | |
| for j, row in enumerate(mosaic): | |
| for k, v in enumerate(row): | |
| if v == empty_sentinel: | |
| continue | |
| elif not cbook.is_scalar_or_string(v): | |
| nested[(j, k)] = _make_array(v) | |
| else: | |
| unique_ids.add(v) | |
| return tuple(unique_ids), nested | |
| def _do_layout(gs, mosaic, unique_ids, nested): | |
| """ | |
| Recursively do the mosaic. | |
| Parameters | |
| ---------- | |
| gs : GridSpec | |
| mosaic : 2D object array | |
| The input converted to a 2D array for this level. | |
| unique_ids : tuple | |
| The identified scalar labels at this level of nesting. | |
| nested : dict[tuple[int, int]], 2D object array | |
| The identified nested mosaics, if any. | |
| Returns | |
| ------- | |
| dict[label, Axes] | |
| A flat dict of all of the Axes created. | |
| """ | |
| output = dict() | |
| # we need to merge together the Axes at this level and the Axes | |
| # in the (recursively) nested sub-mosaics so that we can add | |
| # them to the figure in the "natural" order if you were to | |
| # ravel in c-order all of the Axes that will be created | |
| # | |
| # This will stash the upper left index of each object (axes or | |
| # nested mosaic) at this level | |
| this_level = dict() | |
| # go through the unique keys, | |
| for name in unique_ids: | |
| # sort out where each axes starts/ends | |
| indx = np.argwhere(mosaic == name) | |
| start_row, start_col = np.min(indx, axis=0) | |
| end_row, end_col = np.max(indx, axis=0) + 1 | |
| # and construct the slice object | |
| slc = (slice(start_row, end_row), slice(start_col, end_col)) | |
| # some light error checking | |
| if (mosaic[slc] != name).any(): | |
| raise ValueError( | |
| f"While trying to layout\n{mosaic!r}\n" | |
| f"we found that the label {name!r} specifies a " | |
| "non-rectangular or non-contiguous area.") | |
| # and stash this slice for later | |
| this_level[(start_row, start_col)] = (name, slc, 'axes') | |
| # do the same thing for the nested mosaics (simpler because these | |
| # cannot be spans yet!) | |
| for (j, k), nested_mosaic in nested.items(): | |
| this_level[(j, k)] = (None, nested_mosaic, 'nested') | |
| # now go through the things in this level and add them | |
| # in order left-to-right top-to-bottom | |
| for key in sorted(this_level): | |
| name, arg, method = this_level[key] | |
| # we are doing some hokey function dispatch here based | |
| # on the 'method' string stashed above to sort out if this | |
| # element is an Axes or a nested mosaic. | |
| if method == 'axes': | |
| slc = arg | |
| # add a single Axes | |
| if name in output: | |
| raise ValueError(f"There are duplicate keys {name} " | |
| f"in the layout\n{mosaic!r}") | |
| ax = self.add_subplot( | |
| gs[slc], **{ | |
| 'label': str(name), | |
| **subplot_kw, | |
| **per_subplot_kw.get(name, {}) | |
| } | |
| ) | |
| output[name] = ax | |
| elif method == 'nested': | |
| nested_mosaic = arg | |
| j, k = key | |
| # recursively add the nested mosaic | |
| rows, cols = nested_mosaic.shape | |
| nested_output = _do_layout( | |
| gs[j, k].subgridspec(rows, cols), | |
| nested_mosaic, | |
| *_identify_keys_and_nested(nested_mosaic) | |
| ) | |
| overlap = set(output) & set(nested_output) | |
| if overlap: | |
| raise ValueError( | |
| f"There are duplicate keys {overlap} " | |
| f"between the outer layout\n{mosaic!r}\n" | |
| f"and the nested layout\n{nested_mosaic}" | |
| ) | |
| output.update(nested_output) | |
| else: | |
| raise RuntimeError("This should never happen") | |
| return output | |
| mosaic = _make_array(mosaic) | |
| rows, cols = mosaic.shape | |
| gs = self.add_gridspec(rows, cols, **gridspec_kw) | |
| ret = _do_layout(gs, mosaic, *_identify_keys_and_nested(mosaic)) | |
| ax0 = next(iter(ret.values())) | |
| for ax in ret.values(): | |
| if sharex: | |
| ax.sharex(ax0) | |
| ax._label_outer_xaxis(skip_non_rectangular_axes=True) | |
| if sharey: | |
| ax.sharey(ax0) | |
| ax._label_outer_yaxis(skip_non_rectangular_axes=True) | |
| if extra := set(per_subplot_kw) - set(ret): | |
| raise ValueError( | |
| f"The keys {extra} are in *per_subplot_kw* " | |
| "but not in the mosaic." | |
| ) | |
| return ret | |
| def _set_artist_props(self, a): | |
| if a != self: | |
| a.set_figure(self) | |
| a.stale_callback = _stale_figure_callback | |
| a.set_transform(self.transSubfigure) | |
| class SubFigure(FigureBase): | |
| """ | |
| Logical figure that can be placed inside a figure. | |
| See :ref:`figure-api-subfigure` for an index of methods on this class. | |
| Typically instantiated using `.Figure.add_subfigure` or | |
| `.SubFigure.add_subfigure`, or `.SubFigure.subfigures`. A subfigure has | |
| the same methods as a figure except for those particularly tied to the size | |
| or dpi of the figure, and is confined to a prescribed region of the figure. | |
| For example the following puts two subfigures side-by-side:: | |
| fig = plt.figure() | |
| sfigs = fig.subfigures(1, 2) | |
| axsL = sfigs[0].subplots(1, 2) | |
| axsR = sfigs[1].subplots(2, 1) | |
| See :doc:`/gallery/subplots_axes_and_figures/subfigures` | |
| """ | |
| def __init__(self, parent, subplotspec, *, | |
| facecolor=None, | |
| edgecolor=None, | |
| linewidth=0.0, | |
| frameon=None, | |
| **kwargs): | |
| """ | |
| Parameters | |
| ---------- | |
| parent : `.Figure` or `.SubFigure` | |
| Figure or subfigure that contains the SubFigure. SubFigures | |
| can be nested. | |
| subplotspec : `.gridspec.SubplotSpec` | |
| Defines the region in a parent gridspec where the subfigure will | |
| be placed. | |
| facecolor : default: ``"none"`` | |
| The figure patch face color; transparent by default. | |
| edgecolor : default: :rc:`figure.edgecolor` | |
| The figure patch edge color. | |
| linewidth : float | |
| The linewidth of the frame (i.e. the edge linewidth of the figure | |
| patch). | |
| frameon : bool, default: :rc:`figure.frameon` | |
| If ``False``, suppress drawing the figure background patch. | |
| Other Parameters | |
| ---------------- | |
| **kwargs : `.SubFigure` properties, optional | |
| %(SubFigure:kwdoc)s | |
| """ | |
| super().__init__(**kwargs) | |
| if facecolor is None: | |
| facecolor = "none" | |
| if edgecolor is None: | |
| edgecolor = mpl.rcParams['figure.edgecolor'] | |
| if frameon is None: | |
| frameon = mpl.rcParams['figure.frameon'] | |
| self._subplotspec = subplotspec | |
| self._parent = parent | |
| self._root_figure = parent._root_figure | |
| # subfigures use the parent axstack | |
| self._axstack = parent._axstack | |
| self.subplotpars = parent.subplotpars | |
| self.dpi_scale_trans = parent.dpi_scale_trans | |
| self._axobservers = parent._axobservers | |
| self.transFigure = parent.transFigure | |
| self.bbox_relative = Bbox.null() | |
| self._redo_transform_rel_fig() | |
| self.figbbox = self._parent.figbbox | |
| self.bbox = TransformedBbox(self.bbox_relative, | |
| self._parent.transSubfigure) | |
| self.transSubfigure = BboxTransformTo(self.bbox) | |
| self.patch = Rectangle( | |
| xy=(0, 0), width=1, height=1, visible=frameon, | |
| facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth, | |
| # Don't let the figure patch influence bbox calculation. | |
| in_layout=False, transform=self.transSubfigure) | |
| self._set_artist_props(self.patch) | |
| self.patch.set_antialiased(False) | |
| def canvas(self): | |
| return self._parent.canvas | |
| def dpi(self): | |
| return self._parent.dpi | |
| def dpi(self, value): | |
| self._parent.dpi = value | |
| def get_dpi(self): | |
| """ | |
| Return the resolution of the parent figure in dots-per-inch as a float. | |
| """ | |
| return self._parent.dpi | |
| def set_dpi(self, val): | |
| """ | |
| Set the resolution of parent figure in dots-per-inch. | |
| Parameters | |
| ---------- | |
| val : float | |
| """ | |
| self._parent.dpi = val | |
| self.stale = True | |
| def _get_renderer(self): | |
| return self._parent._get_renderer() | |
| def _redo_transform_rel_fig(self, bbox=None): | |
| """ | |
| Make the transSubfigure bbox relative to Figure transform. | |
| Parameters | |
| ---------- | |
| bbox : bbox or None | |
| If not None, then the bbox is used for relative bounding box. | |
| Otherwise, it is calculated from the subplotspec. | |
| """ | |
| if bbox is not None: | |
| self.bbox_relative.p0 = bbox.p0 | |
| self.bbox_relative.p1 = bbox.p1 | |
| return | |
| # need to figure out *where* this subplotspec is. | |
| gs = self._subplotspec.get_gridspec() | |
| wr = np.asarray(gs.get_width_ratios()) | |
| hr = np.asarray(gs.get_height_ratios()) | |
| dx = wr[self._subplotspec.colspan].sum() / wr.sum() | |
| dy = hr[self._subplotspec.rowspan].sum() / hr.sum() | |
| x0 = wr[:self._subplotspec.colspan.start].sum() / wr.sum() | |
| y0 = 1 - hr[:self._subplotspec.rowspan.stop].sum() / hr.sum() | |
| self.bbox_relative.p0 = (x0, y0) | |
| self.bbox_relative.p1 = (x0 + dx, y0 + dy) | |
| def get_constrained_layout(self): | |
| """ | |
| Return whether constrained layout is being used. | |
| See :ref:`constrainedlayout_guide`. | |
| """ | |
| return self._parent.get_constrained_layout() | |
| def get_constrained_layout_pads(self, relative=False): | |
| """ | |
| Get padding for ``constrained_layout``. | |
| Returns a list of ``w_pad, h_pad`` in inches and | |
| ``wspace`` and ``hspace`` as fractions of the subplot. | |
| See :ref:`constrainedlayout_guide`. | |
| Parameters | |
| ---------- | |
| relative : bool | |
| If `True`, then convert from inches to figure relative. | |
| """ | |
| return self._parent.get_constrained_layout_pads(relative=relative) | |
| def get_layout_engine(self): | |
| return self._parent.get_layout_engine() | |
| def axes(self): | |
| """ | |
| List of Axes in the SubFigure. You can access and modify the Axes | |
| in the SubFigure through this list. | |
| Modifying this list has no effect. Instead, use `~.SubFigure.add_axes`, | |
| `~.SubFigure.add_subplot` or `~.SubFigure.delaxes` to add or remove an | |
| Axes. | |
| Note: The `.SubFigure.axes` property and `~.SubFigure.get_axes` method | |
| are equivalent. | |
| """ | |
| return self._localaxes[:] | |
| get_axes = axes.fget | |
| def draw(self, renderer): | |
| # docstring inherited | |
| # draw the figure bounding box, perhaps none for white figure | |
| if not self.get_visible(): | |
| return | |
| artists = self._get_draw_artists(renderer) | |
| try: | |
| renderer.open_group('subfigure', gid=self.get_gid()) | |
| self.patch.draw(renderer) | |
| mimage._draw_list_compositing_images( | |
| renderer, self, artists, self.get_figure(root=True).suppressComposite) | |
| renderer.close_group('subfigure') | |
| finally: | |
| self.stale = False | |
| class Figure(FigureBase): | |
| """ | |
| The top level container for all the plot elements. | |
| See `matplotlib.figure` for an index of class methods. | |
| Attributes | |
| ---------- | |
| patch | |
| The `.Rectangle` instance representing the figure background patch. | |
| suppressComposite | |
| For multiple images, the figure will make composite images | |
| depending on the renderer option_image_nocomposite function. If | |
| *suppressComposite* is a boolean, this will override the renderer. | |
| """ | |
| # we want to cache the fonts and mathtext at a global level so that when | |
| # multiple figures are created we can reuse them. This helps with a bug on | |
| # windows where the creation of too many figures leads to too many open | |
| # file handles and improves the performance of parsing mathtext. However, | |
| # these global caches are not thread safe. The solution here is to let the | |
| # Figure acquire a shared lock at the start of the draw, and release it when it | |
| # is done. This allows multiple renderers to share the cached fonts and | |
| # parsed text, but only one figure can draw at a time and so the font cache | |
| # and mathtext cache are used by only one renderer at a time. | |
| _render_lock = threading.RLock() | |
| def __str__(self): | |
| return "Figure(%gx%g)" % tuple(self.bbox.size) | |
| def __repr__(self): | |
| return "<{clsname} size {h:g}x{w:g} with {naxes} Axes>".format( | |
| clsname=self.__class__.__name__, | |
| h=self.bbox.size[0], w=self.bbox.size[1], | |
| naxes=len(self.axes), | |
| ) | |
| def __init__(self, | |
| figsize=None, | |
| dpi=None, | |
| *, | |
| facecolor=None, | |
| edgecolor=None, | |
| linewidth=0.0, | |
| frameon=None, | |
| subplotpars=None, # rc figure.subplot.* | |
| tight_layout=None, # rc figure.autolayout | |
| constrained_layout=None, # rc figure.constrained_layout.use | |
| layout=None, | |
| **kwargs | |
| ): | |
| """ | |
| Parameters | |
| ---------- | |
| figsize : 2-tuple of floats, default: :rc:`figure.figsize` | |
| Figure dimension ``(width, height)`` in inches. | |
| dpi : float, default: :rc:`figure.dpi` | |
| Dots per inch. | |
| facecolor : default: :rc:`figure.facecolor` | |
| The figure patch facecolor. | |
| edgecolor : default: :rc:`figure.edgecolor` | |
| The figure patch edge color. | |
| linewidth : float | |
| The linewidth of the frame (i.e. the edge linewidth of the figure | |
| patch). | |
| frameon : bool, default: :rc:`figure.frameon` | |
| If ``False``, suppress drawing the figure background patch. | |
| subplotpars : `~matplotlib.gridspec.SubplotParams` | |
| Subplot parameters. If not given, the default subplot | |
| parameters :rc:`figure.subplot.*` are used. | |
| tight_layout : bool or dict, default: :rc:`figure.autolayout` | |
| Whether to use the tight layout mechanism. See `.set_tight_layout`. | |
| .. admonition:: Discouraged | |
| The use of this parameter is discouraged. Please use | |
| ``layout='tight'`` instead for the common case of | |
| ``tight_layout=True`` and use `.set_tight_layout` otherwise. | |
| constrained_layout : bool, default: :rc:`figure.constrained_layout.use` | |
| This is equal to ``layout='constrained'``. | |
| .. admonition:: Discouraged | |
| The use of this parameter is discouraged. Please use | |
| ``layout='constrained'`` instead. | |
| layout : {'constrained', 'compressed', 'tight', 'none', `.LayoutEngine`, \ | |
| None}, default: None | |
| The layout mechanism for positioning of plot elements to avoid | |
| overlapping Axes decorations (labels, ticks, etc). Note that | |
| layout managers can have significant performance penalties. | |
| - 'constrained': The constrained layout solver adjusts Axes sizes | |
| to avoid overlapping Axes decorations. Can handle complex plot | |
| layouts and colorbars, and is thus recommended. | |
| See :ref:`constrainedlayout_guide` for examples. | |
| - 'compressed': uses the same algorithm as 'constrained', but | |
| removes extra space between fixed-aspect-ratio Axes. Best for | |
| simple grids of Axes. | |
| - 'tight': Use the tight layout mechanism. This is a relatively | |
| simple algorithm that adjusts the subplot parameters so that | |
| decorations do not overlap. | |
| See :ref:`tight_layout_guide` for examples. | |
| - 'none': Do not use a layout engine. | |
| - A `.LayoutEngine` instance. Builtin layout classes are | |
| `.ConstrainedLayoutEngine` and `.TightLayoutEngine`, more easily | |
| accessible by 'constrained' and 'tight'. Passing an instance | |
| allows third parties to provide their own layout engine. | |
| If not given, fall back to using the parameters *tight_layout* and | |
| *constrained_layout*, including their config defaults | |
| :rc:`figure.autolayout` and :rc:`figure.constrained_layout.use`. | |
| Other Parameters | |
| ---------------- | |
| **kwargs : `.Figure` properties, optional | |
| %(Figure:kwdoc)s | |
| """ | |
| super().__init__(**kwargs) | |
| self._root_figure = self | |
| self._layout_engine = None | |
| if layout is not None: | |
| if (tight_layout is not None): | |
| _api.warn_external( | |
| "The Figure parameters 'layout' and 'tight_layout' cannot " | |
| "be used together. Please use 'layout' only.") | |
| if (constrained_layout is not None): | |
| _api.warn_external( | |
| "The Figure parameters 'layout' and 'constrained_layout' " | |
| "cannot be used together. Please use 'layout' only.") | |
| self.set_layout_engine(layout=layout) | |
| elif tight_layout is not None: | |
| if constrained_layout is not None: | |
| _api.warn_external( | |
| "The Figure parameters 'tight_layout' and " | |
| "'constrained_layout' cannot be used together. Please use " | |
| "'layout' parameter") | |
| self.set_layout_engine(layout='tight') | |
| if isinstance(tight_layout, dict): | |
| self.get_layout_engine().set(**tight_layout) | |
| elif constrained_layout is not None: | |
| if isinstance(constrained_layout, dict): | |
| self.set_layout_engine(layout='constrained') | |
| self.get_layout_engine().set(**constrained_layout) | |
| elif constrained_layout: | |
| self.set_layout_engine(layout='constrained') | |
| else: | |
| # everything is None, so use default: | |
| self.set_layout_engine(layout=layout) | |
| # Callbacks traditionally associated with the canvas (and exposed with | |
| # a proxy property), but that actually need to be on the figure for | |
| # pickling. | |
| self._canvas_callbacks = cbook.CallbackRegistry( | |
| signals=FigureCanvasBase.events) | |
| connect = self._canvas_callbacks._connect_picklable | |
| self._mouse_key_ids = [ | |
| connect('key_press_event', backend_bases._key_handler), | |
| connect('key_release_event', backend_bases._key_handler), | |
| connect('key_release_event', backend_bases._key_handler), | |
| connect('button_press_event', backend_bases._mouse_handler), | |
| connect('button_release_event', backend_bases._mouse_handler), | |
| connect('scroll_event', backend_bases._mouse_handler), | |
| connect('motion_notify_event', backend_bases._mouse_handler), | |
| ] | |
| self._button_pick_id = connect('button_press_event', self.pick) | |
| self._scroll_pick_id = connect('scroll_event', self.pick) | |
| if figsize is None: | |
| figsize = mpl.rcParams['figure.figsize'] | |
| if dpi is None: | |
| dpi = mpl.rcParams['figure.dpi'] | |
| if facecolor is None: | |
| facecolor = mpl.rcParams['figure.facecolor'] | |
| if edgecolor is None: | |
| edgecolor = mpl.rcParams['figure.edgecolor'] | |
| if frameon is None: | |
| frameon = mpl.rcParams['figure.frameon'] | |
| if not np.isfinite(figsize).all() or (np.array(figsize) < 0).any(): | |
| raise ValueError('figure size must be positive finite not ' | |
| f'{figsize}') | |
| self.bbox_inches = Bbox.from_bounds(0, 0, *figsize) | |
| self.dpi_scale_trans = Affine2D().scale(dpi) | |
| # do not use property as it will trigger | |
| self._dpi = dpi | |
| self.bbox = TransformedBbox(self.bbox_inches, self.dpi_scale_trans) | |
| self.figbbox = self.bbox | |
| self.transFigure = BboxTransformTo(self.bbox) | |
| self.transSubfigure = self.transFigure | |
| self.patch = Rectangle( | |
| xy=(0, 0), width=1, height=1, visible=frameon, | |
| facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth, | |
| # Don't let the figure patch influence bbox calculation. | |
| in_layout=False) | |
| self._set_artist_props(self.patch) | |
| self.patch.set_antialiased(False) | |
| FigureCanvasBase(self) # Set self.canvas. | |
| if subplotpars is None: | |
| subplotpars = SubplotParams() | |
| self.subplotpars = subplotpars | |
| self._axstack = _AxesStack() # track all figure Axes and current Axes | |
| self.clear() | |
| def pick(self, mouseevent): | |
| if not self.canvas.widgetlock.locked(): | |
| super().pick(mouseevent) | |
| def _check_layout_engines_compat(self, old, new): | |
| """ | |
| Helper for set_layout engine | |
| If the figure has used the old engine and added a colorbar then the | |
| value of colorbar_gridspec must be the same on the new engine. | |
| """ | |
| if old is None or new is None: | |
| return True | |
| if old.colorbar_gridspec == new.colorbar_gridspec: | |
| return True | |
| # colorbar layout different, so check if any colorbars are on the | |
| # figure... | |
| for ax in self.axes: | |
| if hasattr(ax, '_colorbar'): | |
| # colorbars list themselves as a colorbar. | |
| return False | |
| return True | |
| def set_layout_engine(self, layout=None, **kwargs): | |
| """ | |
| Set the layout engine for this figure. | |
| Parameters | |
| ---------- | |
| layout : {'constrained', 'compressed', 'tight', 'none', `.LayoutEngine`, None} | |
| - 'constrained' will use `~.ConstrainedLayoutEngine` | |
| - 'compressed' will also use `~.ConstrainedLayoutEngine`, but with | |
| a correction that attempts to make a good layout for fixed-aspect | |
| ratio Axes. | |
| - 'tight' uses `~.TightLayoutEngine` | |
| - 'none' removes layout engine. | |
| If a `.LayoutEngine` instance, that instance will be used. | |
| If `None`, the behavior is controlled by :rc:`figure.autolayout` | |
| (which if `True` behaves as if 'tight' was passed) and | |
| :rc:`figure.constrained_layout.use` (which if `True` behaves as if | |
| 'constrained' was passed). If both are `True`, | |
| :rc:`figure.autolayout` takes priority. | |
| Users and libraries can define their own layout engines and pass | |
| the instance directly as well. | |
| **kwargs | |
| The keyword arguments are passed to the layout engine to set things | |
| like padding and margin sizes. Only used if *layout* is a string. | |
| """ | |
| if layout is None: | |
| if mpl.rcParams['figure.autolayout']: | |
| layout = 'tight' | |
| elif mpl.rcParams['figure.constrained_layout.use']: | |
| layout = 'constrained' | |
| else: | |
| self._layout_engine = None | |
| return | |
| if layout == 'tight': | |
| new_layout_engine = TightLayoutEngine(**kwargs) | |
| elif layout == 'constrained': | |
| new_layout_engine = ConstrainedLayoutEngine(**kwargs) | |
| elif layout == 'compressed': | |
| new_layout_engine = ConstrainedLayoutEngine(compress=True, | |
| **kwargs) | |
| elif layout == 'none': | |
| if self._layout_engine is not None: | |
| new_layout_engine = PlaceHolderLayoutEngine( | |
| self._layout_engine.adjust_compatible, | |
| self._layout_engine.colorbar_gridspec | |
| ) | |
| else: | |
| new_layout_engine = None | |
| elif isinstance(layout, LayoutEngine): | |
| new_layout_engine = layout | |
| else: | |
| raise ValueError(f"Invalid value for 'layout': {layout!r}") | |
| if self._check_layout_engines_compat(self._layout_engine, | |
| new_layout_engine): | |
| self._layout_engine = new_layout_engine | |
| else: | |
| raise RuntimeError('Colorbar layout of new layout engine not ' | |
| 'compatible with old engine, and a colorbar ' | |
| 'has been created. Engine not changed.') | |
| def get_layout_engine(self): | |
| return self._layout_engine | |
| # TODO: I'd like to dynamically add the _repr_html_ method | |
| # to the figure in the right context, but then IPython doesn't | |
| # use it, for some reason. | |
| def _repr_html_(self): | |
| # We can't use "isinstance" here, because then we'd end up importing | |
| # webagg unconditionally. | |
| if 'WebAgg' in type(self.canvas).__name__: | |
| from matplotlib.backends import backend_webagg | |
| return backend_webagg.ipython_inline_display(self) | |
| def show(self, warn=True): | |
| """ | |
| If using a GUI backend with pyplot, display the figure window. | |
| If the figure was not created using `~.pyplot.figure`, it will lack | |
| a `~.backend_bases.FigureManagerBase`, and this method will raise an | |
| AttributeError. | |
| .. warning:: | |
| This does not manage an GUI event loop. Consequently, the figure | |
| may only be shown briefly or not shown at all if you or your | |
| environment are not managing an event loop. | |
| Use cases for `.Figure.show` include running this from a GUI | |
| application (where there is persistently an event loop running) or | |
| from a shell, like IPython, that install an input hook to allow the | |
| interactive shell to accept input while the figure is also being | |
| shown and interactive. Some, but not all, GUI toolkits will | |
| register an input hook on import. See :ref:`cp_integration` for | |
| more details. | |
| If you're in a shell without input hook integration or executing a | |
| python script, you should use `matplotlib.pyplot.show` with | |
| ``block=True`` instead, which takes care of starting and running | |
| the event loop for you. | |
| Parameters | |
| ---------- | |
| warn : bool, default: True | |
| If ``True`` and we are not running headless (i.e. on Linux with an | |
| unset DISPLAY), issue warning when called on a non-GUI backend. | |
| """ | |
| if self.canvas.manager is None: | |
| raise AttributeError( | |
| "Figure.show works only for figures managed by pyplot, " | |
| "normally created by pyplot.figure()") | |
| try: | |
| self.canvas.manager.show() | |
| except NonGuiException as exc: | |
| if warn: | |
| _api.warn_external(str(exc)) | |
| def axes(self): | |
| """ | |
| List of Axes in the Figure. You can access and modify the Axes in the | |
| Figure through this list. | |
| Do not modify the list itself. Instead, use `~Figure.add_axes`, | |
| `~.Figure.add_subplot` or `~.Figure.delaxes` to add or remove an Axes. | |
| Note: The `.Figure.axes` property and `~.Figure.get_axes` method are | |
| equivalent. | |
| """ | |
| return self._axstack.as_list() | |
| get_axes = axes.fget | |
| def number(self): | |
| """The figure id, used to identify figures in `.pyplot`.""" | |
| # Historically, pyplot dynamically added a number attribute to figure. | |
| # However, this number must stay in sync with the figure manager. | |
| # AFAICS overwriting the number attribute does not have the desired | |
| # effect for pyplot. But there are some repos in GitHub that do change | |
| # number. So let's take it slow and properly migrate away from writing. | |
| # | |
| # Making the dynamic attribute private and wrapping it in a property | |
| # allows to maintain current behavior and deprecate write-access. | |
| # | |
| # When the deprecation expires, there's no need for duplicate state | |
| # anymore and the private _number attribute can be replaced by | |
| # `self.canvas.manager.num` if that exists and None otherwise. | |
| if hasattr(self, '_number'): | |
| return self._number | |
| else: | |
| raise AttributeError( | |
| "'Figure' object has no attribute 'number'. In the future this" | |
| "will change to returning 'None' instead.") | |
| def number(self, num): | |
| _api.warn_deprecated( | |
| "3.10", | |
| message="Changing 'Figure.number' is deprecated since %(since)s and " | |
| "will raise an error starting %(removal)s") | |
| self._number = num | |
| def _get_renderer(self): | |
| if hasattr(self.canvas, 'get_renderer'): | |
| return self.canvas.get_renderer() | |
| else: | |
| return _get_renderer(self) | |
| def _get_dpi(self): | |
| return self._dpi | |
| def _set_dpi(self, dpi, forward=True): | |
| """ | |
| Parameters | |
| ---------- | |
| dpi : float | |
| forward : bool | |
| Passed on to `~.Figure.set_size_inches` | |
| """ | |
| if dpi == self._dpi: | |
| # We don't want to cause undue events in backends. | |
| return | |
| self._dpi = dpi | |
| self.dpi_scale_trans.clear().scale(dpi) | |
| w, h = self.get_size_inches() | |
| self.set_size_inches(w, h, forward=forward) | |
| dpi = property(_get_dpi, _set_dpi, doc="The resolution in dots per inch.") | |
| def get_tight_layout(self): | |
| """Return whether `.Figure.tight_layout` is called when drawing.""" | |
| return isinstance(self.get_layout_engine(), TightLayoutEngine) | |
| def set_tight_layout(self, tight): | |
| """ | |
| Set whether and how `.Figure.tight_layout` is called when drawing. | |
| Parameters | |
| ---------- | |
| tight : bool or dict with keys "pad", "w_pad", "h_pad", "rect" or None | |
| If a bool, sets whether to call `.Figure.tight_layout` upon drawing. | |
| If ``None``, use :rc:`figure.autolayout` instead. | |
| If a dict, pass it as kwargs to `.Figure.tight_layout`, overriding the | |
| default paddings. | |
| """ | |
| if tight is None: | |
| tight = mpl.rcParams['figure.autolayout'] | |
| _tight = 'tight' if bool(tight) else 'none' | |
| _tight_parameters = tight if isinstance(tight, dict) else {} | |
| self.set_layout_engine(_tight, **_tight_parameters) | |
| self.stale = True | |
| def get_constrained_layout(self): | |
| """ | |
| Return whether constrained layout is being used. | |
| See :ref:`constrainedlayout_guide`. | |
| """ | |
| return isinstance(self.get_layout_engine(), ConstrainedLayoutEngine) | |
| def set_constrained_layout(self, constrained): | |
| """ | |
| Set whether ``constrained_layout`` is used upon drawing. | |
| If None, :rc:`figure.constrained_layout.use` value will be used. | |
| When providing a dict containing the keys ``w_pad``, ``h_pad`` | |
| the default ``constrained_layout`` paddings will be | |
| overridden. These pads are in inches and default to 3.0/72.0. | |
| ``w_pad`` is the width padding and ``h_pad`` is the height padding. | |
| Parameters | |
| ---------- | |
| constrained : bool or dict or None | |
| """ | |
| if constrained is None: | |
| constrained = mpl.rcParams['figure.constrained_layout.use'] | |
| _constrained = 'constrained' if bool(constrained) else 'none' | |
| _parameters = constrained if isinstance(constrained, dict) else {} | |
| self.set_layout_engine(_constrained, **_parameters) | |
| self.stale = True | |
| def set_constrained_layout_pads(self, **kwargs): | |
| """ | |
| Set padding for ``constrained_layout``. | |
| Tip: The parameters can be passed from a dictionary by using | |
| ``fig.set_constrained_layout(**pad_dict)``. | |
| See :ref:`constrainedlayout_guide`. | |
| Parameters | |
| ---------- | |
| w_pad : float, default: :rc:`figure.constrained_layout.w_pad` | |
| Width padding in inches. This is the pad around Axes | |
| and is meant to make sure there is enough room for fonts to | |
| look good. Defaults to 3 pts = 0.04167 inches | |
| h_pad : float, default: :rc:`figure.constrained_layout.h_pad` | |
| Height padding in inches. Defaults to 3 pts. | |
| wspace : float, default: :rc:`figure.constrained_layout.wspace` | |
| Width padding between subplots, expressed as a fraction of the | |
| subplot width. The total padding ends up being w_pad + wspace. | |
| hspace : float, default: :rc:`figure.constrained_layout.hspace` | |
| Height padding between subplots, expressed as a fraction of the | |
| subplot width. The total padding ends up being h_pad + hspace. | |
| """ | |
| if isinstance(self.get_layout_engine(), ConstrainedLayoutEngine): | |
| self.get_layout_engine().set(**kwargs) | |
| def get_constrained_layout_pads(self, relative=False): | |
| """ | |
| Get padding for ``constrained_layout``. | |
| Returns a list of ``w_pad, h_pad`` in inches and | |
| ``wspace`` and ``hspace`` as fractions of the subplot. | |
| All values are None if ``constrained_layout`` is not used. | |
| See :ref:`constrainedlayout_guide`. | |
| Parameters | |
| ---------- | |
| relative : bool | |
| If `True`, then convert from inches to figure relative. | |
| """ | |
| if not isinstance(self.get_layout_engine(), ConstrainedLayoutEngine): | |
| return None, None, None, None | |
| info = self.get_layout_engine().get() | |
| w_pad = info['w_pad'] | |
| h_pad = info['h_pad'] | |
| wspace = info['wspace'] | |
| hspace = info['hspace'] | |
| if relative and (w_pad is not None or h_pad is not None): | |
| renderer = self._get_renderer() | |
| dpi = renderer.dpi | |
| w_pad = w_pad * dpi / renderer.width | |
| h_pad = h_pad * dpi / renderer.height | |
| return w_pad, h_pad, wspace, hspace | |
| def set_canvas(self, canvas): | |
| """ | |
| Set the canvas that contains the figure | |
| Parameters | |
| ---------- | |
| canvas : FigureCanvas | |
| """ | |
| self.canvas = canvas | |
| def figimage(self, X, xo=0, yo=0, alpha=None, norm=None, cmap=None, | |
| vmin=None, vmax=None, origin=None, resize=False, *, | |
| colorizer=None, **kwargs): | |
| """ | |
| Add a non-resampled image to the figure. | |
| The image is attached to the lower or upper left corner depending on | |
| *origin*. | |
| Parameters | |
| ---------- | |
| X | |
| The image data. This is an array of one of the following shapes: | |
| - (M, N): an image with scalar data. Color-mapping is controlled | |
| by *cmap*, *norm*, *vmin*, and *vmax*. | |
| - (M, N, 3): an image with RGB values (0-1 float or 0-255 int). | |
| - (M, N, 4): an image with RGBA values (0-1 float or 0-255 int), | |
| i.e. including transparency. | |
| xo, yo : int | |
| The *x*/*y* image offset in pixels. | |
| alpha : None or float | |
| The alpha blending value. | |
| %(cmap_doc)s | |
| This parameter is ignored if *X* is RGB(A). | |
| %(norm_doc)s | |
| This parameter is ignored if *X* is RGB(A). | |
| %(vmin_vmax_doc)s | |
| This parameter is ignored if *X* is RGB(A). | |
| origin : {'upper', 'lower'}, default: :rc:`image.origin` | |
| Indicates where the [0, 0] index of the array is in the upper left | |
| or lower left corner of the Axes. | |
| resize : bool | |
| If *True*, resize the figure to match the given image size. | |
| %(colorizer_doc)s | |
| This parameter is ignored if *X* is RGB(A). | |
| Returns | |
| ------- | |
| `matplotlib.image.FigureImage` | |
| Other Parameters | |
| ---------------- | |
| **kwargs | |
| Additional kwargs are `.Artist` kwargs passed on to `.FigureImage`. | |
| Notes | |
| ----- | |
| figimage complements the Axes image (`~matplotlib.axes.Axes.imshow`) | |
| which will be resampled to fit the current Axes. If you want | |
| a resampled image to fill the entire figure, you can define an | |
| `~matplotlib.axes.Axes` with extent [0, 0, 1, 1]. | |
| Examples | |
| -------- | |
| :: | |
| f = plt.figure() | |
| nx = int(f.get_figwidth() * f.dpi) | |
| ny = int(f.get_figheight() * f.dpi) | |
| data = np.random.random((ny, nx)) | |
| f.figimage(data) | |
| plt.show() | |
| """ | |
| if resize: | |
| dpi = self.get_dpi() | |
| figsize = [x / dpi for x in (X.shape[1], X.shape[0])] | |
| self.set_size_inches(figsize, forward=True) | |
| im = mimage.FigureImage(self, cmap=cmap, norm=norm, | |
| colorizer=colorizer, | |
| offsetx=xo, offsety=yo, | |
| origin=origin, **kwargs) | |
| im.stale_callback = _stale_figure_callback | |
| im.set_array(X) | |
| im.set_alpha(alpha) | |
| if norm is None: | |
| im._check_exclusionary_keywords(colorizer, vmin=vmin, vmax=vmax) | |
| im.set_clim(vmin, vmax) | |
| self.images.append(im) | |
| im._remove_method = self.images.remove | |
| self.stale = True | |
| return im | |
| def set_size_inches(self, w, h=None, forward=True): | |
| """ | |
| Set the figure size in inches. | |
| Call signatures:: | |
| fig.set_size_inches(w, h) # OR | |
| fig.set_size_inches((w, h)) | |
| Parameters | |
| ---------- | |
| w : (float, float) or float | |
| Width and height in inches (if height not specified as a separate | |
| argument) or width. | |
| h : float | |
| Height in inches. | |
| forward : bool, default: True | |
| If ``True``, the canvas size is automatically updated, e.g., | |
| you can resize the figure window from the shell. | |
| See Also | |
| -------- | |
| matplotlib.figure.Figure.get_size_inches | |
| matplotlib.figure.Figure.set_figwidth | |
| matplotlib.figure.Figure.set_figheight | |
| Notes | |
| ----- | |
| To transform from pixels to inches divide by `Figure.dpi`. | |
| """ | |
| if h is None: # Got called with a single pair as argument. | |
| w, h = w | |
| size = np.array([w, h]) | |
| if not np.isfinite(size).all() or (size < 0).any(): | |
| raise ValueError(f'figure size must be positive finite not {size}') | |
| self.bbox_inches.p1 = size | |
| if forward: | |
| manager = self.canvas.manager | |
| if manager is not None: | |
| manager.resize(*(size * self.dpi).astype(int)) | |
| self.stale = True | |
| def get_size_inches(self): | |
| """ | |
| Return the current size of the figure in inches. | |
| Returns | |
| ------- | |
| ndarray | |
| The size (width, height) of the figure in inches. | |
| See Also | |
| -------- | |
| matplotlib.figure.Figure.set_size_inches | |
| matplotlib.figure.Figure.get_figwidth | |
| matplotlib.figure.Figure.get_figheight | |
| Notes | |
| ----- | |
| The size in pixels can be obtained by multiplying with `Figure.dpi`. | |
| """ | |
| return np.array(self.bbox_inches.p1) | |
| def get_figwidth(self): | |
| """Return the figure width in inches.""" | |
| return self.bbox_inches.width | |
| def get_figheight(self): | |
| """Return the figure height in inches.""" | |
| return self.bbox_inches.height | |
| def get_dpi(self): | |
| """Return the resolution in dots per inch as a float.""" | |
| return self.dpi | |
| def set_dpi(self, val): | |
| """ | |
| Set the resolution of the figure in dots-per-inch. | |
| Parameters | |
| ---------- | |
| val : float | |
| """ | |
| self.dpi = val | |
| self.stale = True | |
| def set_figwidth(self, val, forward=True): | |
| """ | |
| Set the width of the figure in inches. | |
| Parameters | |
| ---------- | |
| val : float | |
| forward : bool | |
| See `set_size_inches`. | |
| See Also | |
| -------- | |
| matplotlib.figure.Figure.set_figheight | |
| matplotlib.figure.Figure.set_size_inches | |
| """ | |
| self.set_size_inches(val, self.get_figheight(), forward=forward) | |
| def set_figheight(self, val, forward=True): | |
| """ | |
| Set the height of the figure in inches. | |
| Parameters | |
| ---------- | |
| val : float | |
| forward : bool | |
| See `set_size_inches`. | |
| See Also | |
| -------- | |
| matplotlib.figure.Figure.set_figwidth | |
| matplotlib.figure.Figure.set_size_inches | |
| """ | |
| self.set_size_inches(self.get_figwidth(), val, forward=forward) | |
| def clear(self, keep_observers=False): | |
| # docstring inherited | |
| super().clear(keep_observers=keep_observers) | |
| # FigureBase.clear does not clear toolbars, as | |
| # only Figure can have toolbars | |
| toolbar = self.canvas.toolbar | |
| if toolbar is not None: | |
| toolbar.update() | |
| def draw(self, renderer): | |
| # docstring inherited | |
| if not self.get_visible(): | |
| return | |
| with self._render_lock: | |
| artists = self._get_draw_artists(renderer) | |
| try: | |
| renderer.open_group('figure', gid=self.get_gid()) | |
| if self.axes and self.get_layout_engine() is not None: | |
| try: | |
| self.get_layout_engine().execute(self) | |
| except ValueError: | |
| pass | |
| # ValueError can occur when resizing a window. | |
| self.patch.draw(renderer) | |
| mimage._draw_list_compositing_images( | |
| renderer, self, artists, self.suppressComposite) | |
| renderer.close_group('figure') | |
| finally: | |
| self.stale = False | |
| DrawEvent("draw_event", self.canvas, renderer)._process() | |
| def draw_without_rendering(self): | |
| """ | |
| Draw the figure with no output. Useful to get the final size of | |
| artists that require a draw before their size is known (e.g. text). | |
| """ | |
| renderer = _get_renderer(self) | |
| with renderer._draw_disabled(): | |
| self.draw(renderer) | |
| def draw_artist(self, a): | |
| """ | |
| Draw `.Artist` *a* only. | |
| """ | |
| a.draw(self.canvas.get_renderer()) | |
| def __getstate__(self): | |
| state = super().__getstate__() | |
| # The canvas cannot currently be pickled, but this has the benefit | |
| # of meaning that a figure can be detached from one canvas, and | |
| # re-attached to another. | |
| state.pop("canvas") | |
| # discard any changes to the dpi due to pixel ratio changes | |
| state["_dpi"] = state.get('_original_dpi', state['_dpi']) | |
| # add version information to the state | |
| state['__mpl_version__'] = mpl.__version__ | |
| # check whether the figure manager (if any) is registered with pyplot | |
| from matplotlib import _pylab_helpers | |
| if self.canvas.manager in _pylab_helpers.Gcf.figs.values(): | |
| state['_restore_to_pylab'] = True | |
| return state | |
| def __setstate__(self, state): | |
| version = state.pop('__mpl_version__') | |
| restore_to_pylab = state.pop('_restore_to_pylab', False) | |
| if version != mpl.__version__: | |
| _api.warn_external( | |
| f"This figure was saved with matplotlib version {version} and " | |
| f"loaded with {mpl.__version__} so may not function correctly." | |
| ) | |
| self.__dict__ = state | |
| # re-initialise some of the unstored state information | |
| FigureCanvasBase(self) # Set self.canvas. | |
| if restore_to_pylab: | |
| # lazy import to avoid circularity | |
| import matplotlib.pyplot as plt | |
| import matplotlib._pylab_helpers as pylab_helpers | |
| allnums = plt.get_fignums() | |
| num = max(allnums) + 1 if allnums else 1 | |
| backend = plt._get_backend_mod() | |
| mgr = backend.new_figure_manager_given_figure(num, self) | |
| pylab_helpers.Gcf._set_new_active_manager(mgr) | |
| plt.draw_if_interactive() | |
| self.stale = True | |
| def add_axobserver(self, func): | |
| """Whenever the Axes state change, ``func(self)`` will be called.""" | |
| # Connect a wrapper lambda and not func itself, to avoid it being | |
| # weakref-collected. | |
| self._axobservers.connect("_axes_change_event", lambda arg: func(arg)) | |
| def savefig(self, fname, *, transparent=None, **kwargs): | |
| """ | |
| Save the current figure as an image or vector graphic to a file. | |
| Call signature:: | |
| savefig(fname, *, transparent=None, dpi='figure', format=None, | |
| metadata=None, bbox_inches=None, pad_inches=0.1, | |
| facecolor='auto', edgecolor='auto', backend=None, | |
| **kwargs | |
| ) | |
| The available output formats depend on the backend being used. | |
| Parameters | |
| ---------- | |
| fname : str or path-like or binary file-like | |
| A path, or a Python file-like object, or | |
| possibly some backend-dependent object such as | |
| `matplotlib.backends.backend_pdf.PdfPages`. | |
| If *format* is set, it determines the output format, and the file | |
| is saved as *fname*. Note that *fname* is used verbatim, and there | |
| is no attempt to make the extension, if any, of *fname* match | |
| *format*, and no extension is appended. | |
| If *format* is not set, then the format is inferred from the | |
| extension of *fname*, if there is one. If *format* is not | |
| set and *fname* has no extension, then the file is saved with | |
| :rc:`savefig.format` and the appropriate extension is appended to | |
| *fname*. | |
| Other Parameters | |
| ---------------- | |
| transparent : bool, default: :rc:`savefig.transparent` | |
| If *True*, the Axes patches will all be transparent; the | |
| Figure patch will also be transparent unless *facecolor* | |
| and/or *edgecolor* are specified via kwargs. | |
| If *False* has no effect and the color of the Axes and | |
| Figure patches are unchanged (unless the Figure patch | |
| is specified via the *facecolor* and/or *edgecolor* keyword | |
| arguments in which case those colors are used). | |
| The transparency of these patches will be restored to their | |
| original values upon exit of this function. | |
| This is useful, for example, for displaying | |
| a plot on top of a colored background on a web page. | |
| dpi : float or 'figure', default: :rc:`savefig.dpi` | |
| The resolution in dots per inch. If 'figure', use the figure's | |
| dpi value. | |
| format : str | |
| The file format, e.g. 'png', 'pdf', 'svg', ... The behavior when | |
| this is unset is documented under *fname*. | |
| metadata : dict, optional | |
| Key/value pairs to store in the image metadata. The supported keys | |
| and defaults depend on the image format and backend: | |
| - 'png' with Agg backend: See the parameter ``metadata`` of | |
| `~.FigureCanvasAgg.print_png`. | |
| - 'pdf' with pdf backend: See the parameter ``metadata`` of | |
| `~.backend_pdf.PdfPages`. | |
| - 'svg' with svg backend: See the parameter ``metadata`` of | |
| `~.FigureCanvasSVG.print_svg`. | |
| - 'eps' and 'ps' with PS backend: Only 'Creator' is supported. | |
| Not supported for 'pgf', 'raw', and 'rgba' as those formats do not support | |
| embedding metadata. | |
| Does not currently support 'jpg', 'tiff', or 'webp', but may include | |
| embedding EXIF metadata in the future. | |
| bbox_inches : str or `.Bbox`, default: :rc:`savefig.bbox` | |
| Bounding box in inches: only the given portion of the figure is | |
| saved. If 'tight', try to figure out the tight bbox of the figure. | |
| pad_inches : float or 'layout', default: :rc:`savefig.pad_inches` | |
| Amount of padding in inches around the figure when bbox_inches is | |
| 'tight'. If 'layout' use the padding from the constrained or | |
| compressed layout engine; ignored if one of those engines is not in | |
| use. | |
| facecolor : :mpltype:`color` or 'auto', default: :rc:`savefig.facecolor` | |
| The facecolor of the figure. If 'auto', use the current figure | |
| facecolor. | |
| edgecolor : :mpltype:`color` or 'auto', default: :rc:`savefig.edgecolor` | |
| The edgecolor of the figure. If 'auto', use the current figure | |
| edgecolor. | |
| backend : str, optional | |
| Use a non-default backend to render the file, e.g. to render a | |
| png file with the "cairo" backend rather than the default "agg", | |
| or a pdf file with the "pgf" backend rather than the default | |
| "pdf". Note that the default backend is normally sufficient. See | |
| :ref:`the-builtin-backends` for a list of valid backends for each | |
| file format. Custom backends can be referenced as "module://...". | |
| orientation : {'landscape', 'portrait'} | |
| Currently only supported by the postscript backend. | |
| papertype : str | |
| One of 'letter', 'legal', 'executive', 'ledger', 'a0' through | |
| 'a10', 'b0' through 'b10'. Only supported for postscript | |
| output. | |
| bbox_extra_artists : list of `~matplotlib.artist.Artist`, optional | |
| A list of extra artists that will be considered when the | |
| tight bbox is calculated. | |
| pil_kwargs : dict, optional | |
| Additional keyword arguments that are passed to | |
| `PIL.Image.Image.save` when saving the figure. | |
| """ | |
| kwargs.setdefault('dpi', mpl.rcParams['savefig.dpi']) | |
| if transparent is None: | |
| transparent = mpl.rcParams['savefig.transparent'] | |
| with ExitStack() as stack: | |
| if transparent: | |
| def _recursively_make_subfig_transparent(exit_stack, subfig): | |
| exit_stack.enter_context( | |
| subfig.patch._cm_set( | |
| facecolor="none", edgecolor="none")) | |
| for ax in subfig.axes: | |
| exit_stack.enter_context( | |
| ax.patch._cm_set( | |
| facecolor="none", edgecolor="none")) | |
| for sub_subfig in subfig.subfigs: | |
| _recursively_make_subfig_transparent( | |
| exit_stack, sub_subfig) | |
| def _recursively_make_axes_transparent(exit_stack, ax): | |
| exit_stack.enter_context( | |
| ax.patch._cm_set(facecolor="none", edgecolor="none")) | |
| for child_ax in ax.child_axes: | |
| exit_stack.enter_context( | |
| child_ax.patch._cm_set( | |
| facecolor="none", edgecolor="none")) | |
| for child_childax in ax.child_axes: | |
| _recursively_make_axes_transparent( | |
| exit_stack, child_childax) | |
| kwargs.setdefault('facecolor', 'none') | |
| kwargs.setdefault('edgecolor', 'none') | |
| # set subfigure to appear transparent in printed image | |
| for subfig in self.subfigs: | |
| _recursively_make_subfig_transparent(stack, subfig) | |
| # set Axes to be transparent | |
| for ax in self.axes: | |
| _recursively_make_axes_transparent(stack, ax) | |
| self.canvas.print_figure(fname, **kwargs) | |
| def ginput(self, n=1, timeout=30, show_clicks=True, | |
| mouse_add=MouseButton.LEFT, | |
| mouse_pop=MouseButton.RIGHT, | |
| mouse_stop=MouseButton.MIDDLE): | |
| """ | |
| Blocking call to interact with a figure. | |
| Wait until the user clicks *n* times on the figure, and return the | |
| coordinates of each click in a list. | |
| There are three possible interactions: | |
| - Add a point. | |
| - Remove the most recently added point. | |
| - Stop the interaction and return the points added so far. | |
| The actions are assigned to mouse buttons via the arguments | |
| *mouse_add*, *mouse_pop* and *mouse_stop*. | |
| Parameters | |
| ---------- | |
| n : int, default: 1 | |
| Number of mouse clicks to accumulate. If negative, accumulate | |
| clicks until the input is terminated manually. | |
| timeout : float, default: 30 seconds | |
| Number of seconds to wait before timing out. If zero or negative | |
| will never time out. | |
| show_clicks : bool, default: True | |
| If True, show a red cross at the location of each click. | |
| mouse_add : `.MouseButton` or None, default: `.MouseButton.LEFT` | |
| Mouse button used to add points. | |
| mouse_pop : `.MouseButton` or None, default: `.MouseButton.RIGHT` | |
| Mouse button used to remove the most recently added point. | |
| mouse_stop : `.MouseButton` or None, default: `.MouseButton.MIDDLE` | |
| Mouse button used to stop input. | |
| Returns | |
| ------- | |
| list of tuples | |
| A list of the clicked (x, y) coordinates. | |
| Notes | |
| ----- | |
| The keyboard can also be used to select points in case your mouse | |
| does not have one or more of the buttons. The delete and backspace | |
| keys act like right-clicking (i.e., remove last point), the enter key | |
| terminates input and any other key (not already used by the window | |
| manager) selects a point. | |
| """ | |
| clicks = [] | |
| marks = [] | |
| def handler(event): | |
| is_button = event.name == "button_press_event" | |
| is_key = event.name == "key_press_event" | |
| # Quit (even if not in infinite mode; this is consistent with | |
| # MATLAB and sometimes quite useful, but will require the user to | |
| # test how many points were actually returned before using data). | |
| if (is_button and event.button == mouse_stop | |
| or is_key and event.key in ["escape", "enter"]): | |
| self.canvas.stop_event_loop() | |
| # Pop last click. | |
| elif (is_button and event.button == mouse_pop | |
| or is_key and event.key in ["backspace", "delete"]): | |
| if clicks: | |
| clicks.pop() | |
| if show_clicks: | |
| marks.pop().remove() | |
| self.canvas.draw() | |
| # Add new click. | |
| elif (is_button and event.button == mouse_add | |
| # On macOS/gtk, some keys return None. | |
| or is_key and event.key is not None): | |
| if event.inaxes: | |
| clicks.append((event.xdata, event.ydata)) | |
| _log.info("input %i: %f, %f", | |
| len(clicks), event.xdata, event.ydata) | |
| if show_clicks: | |
| line = mpl.lines.Line2D([event.xdata], [event.ydata], | |
| marker="+", color="r") | |
| event.inaxes.add_line(line) | |
| marks.append(line) | |
| self.canvas.draw() | |
| if len(clicks) == n and n > 0: | |
| self.canvas.stop_event_loop() | |
| _blocking_input.blocking_input_loop( | |
| self, ["button_press_event", "key_press_event"], timeout, handler) | |
| # Cleanup. | |
| for mark in marks: | |
| mark.remove() | |
| self.canvas.draw() | |
| return clicks | |
| def waitforbuttonpress(self, timeout=-1): | |
| """ | |
| Blocking call to interact with the figure. | |
| Wait for user input and return True if a key was pressed, False if a | |
| mouse button was pressed and None if no input was given within | |
| *timeout* seconds. Negative values deactivate *timeout*. | |
| """ | |
| event = None | |
| def handler(ev): | |
| nonlocal event | |
| event = ev | |
| self.canvas.stop_event_loop() | |
| _blocking_input.blocking_input_loop( | |
| self, ["button_press_event", "key_press_event"], timeout, handler) | |
| return None if event is None else event.name == "key_press_event" | |
| def tight_layout(self, *, pad=1.08, h_pad=None, w_pad=None, rect=None): | |
| """ | |
| Adjust the padding between and around subplots. | |
| To exclude an artist on the Axes from the bounding box calculation | |
| that determines the subplot parameters (i.e. legend, or annotation), | |
| set ``a.set_in_layout(False)`` for that artist. | |
| Parameters | |
| ---------- | |
| pad : float, default: 1.08 | |
| Padding between the figure edge and the edges of subplots, | |
| as a fraction of the font size. | |
| h_pad, w_pad : float, default: *pad* | |
| Padding (height/width) between edges of adjacent subplots, | |
| as a fraction of the font size. | |
| rect : tuple (left, bottom, right, top), default: (0, 0, 1, 1) | |
| A rectangle in normalized figure coordinates into which the whole | |
| subplots area (including labels) will fit. | |
| See Also | |
| -------- | |
| .Figure.set_layout_engine | |
| .pyplot.tight_layout | |
| """ | |
| # note that here we do not permanently set the figures engine to | |
| # tight_layout but rather just perform the layout in place and remove | |
| # any previous engines. | |
| engine = TightLayoutEngine(pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) | |
| try: | |
| previous_engine = self.get_layout_engine() | |
| self.set_layout_engine(engine) | |
| engine.execute(self) | |
| if previous_engine is not None and not isinstance( | |
| previous_engine, (TightLayoutEngine, PlaceHolderLayoutEngine) | |
| ): | |
| _api.warn_external('The figure layout has changed to tight') | |
| finally: | |
| self.set_layout_engine('none') | |
| def figaspect(arg): | |
| """ | |
| Calculate the width and height for a figure with a specified aspect ratio. | |
| While the height is taken from :rc:`figure.figsize`, the width is | |
| adjusted to match the desired aspect ratio. Additionally, it is ensured | |
| that the width is in the range [4., 16.] and the height is in the range | |
| [2., 16.]. If necessary, the default height is adjusted to ensure this. | |
| Parameters | |
| ---------- | |
| arg : float or 2D array | |
| If a float, this defines the aspect ratio (i.e. the ratio height / | |
| width). | |
| In case of an array the aspect ratio is number of rows / number of | |
| columns, so that the array could be fitted in the figure undistorted. | |
| Returns | |
| ------- | |
| size : (2,) array | |
| The width and height of the figure in inches. | |
| Notes | |
| ----- | |
| If you want to create an Axes within the figure, that still preserves the | |
| aspect ratio, be sure to create it with equal width and height. See | |
| examples below. | |
| Thanks to Fernando Perez for this function. | |
| Examples | |
| -------- | |
| Make a figure twice as tall as it is wide:: | |
| w, h = figaspect(2.) | |
| fig = Figure(figsize=(w, h)) | |
| ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]) | |
| ax.imshow(A, **kwargs) | |
| Make a figure with the proper aspect for an array:: | |
| A = rand(5, 3) | |
| w, h = figaspect(A) | |
| fig = Figure(figsize=(w, h)) | |
| ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]) | |
| ax.imshow(A, **kwargs) | |
| """ | |
| isarray = hasattr(arg, 'shape') and not np.isscalar(arg) | |
| # min/max sizes to respect when autoscaling. If John likes the idea, they | |
| # could become rc parameters, for now they're hardwired. | |
| figsize_min = np.array((4.0, 2.0)) # min length for width/height | |
| figsize_max = np.array((16.0, 16.0)) # max length for width/height | |
| # Extract the aspect ratio of the array | |
| if isarray: | |
| nr, nc = arg.shape[:2] | |
| arr_ratio = nr / nc | |
| else: | |
| arr_ratio = arg | |
| # Height of user figure defaults | |
| fig_height = mpl.rcParams['figure.figsize'][1] | |
| # New size for the figure, keeping the aspect ratio of the caller | |
| newsize = np.array((fig_height / arr_ratio, fig_height)) | |
| # Sanity checks, don't drop either dimension below figsize_min | |
| newsize /= min(1.0, *(newsize / figsize_min)) | |
| # Avoid humongous windows as well | |
| newsize /= max(1.0, *(newsize / figsize_max)) | |
| # Finally, if we have a really funky aspect ratio, break it but respect | |
| # the min/max dimensions (we don't want figures 10 feet tall!) | |
| newsize = np.clip(newsize, figsize_min, figsize_max) | |
| return newsize | |
Xet Storage Details
- Size:
- 142 kB
- Xet hash:
- 64ce3e03dfd49a1de018f222af9d121f383654b83eff63817cc1d899fd2d6b68
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.