1"""
2`matplotlib.figure` implements the following classes:
3
4`Figure`
5 Top level `~matplotlib.artist.Artist`, which holds all plot elements.
6 Many methods are implemented in `FigureBase`.
7
8`SubFigure`
9 A logical figure inside a figure, usually added to a figure (or parent
10 `SubFigure`) with `Figure.add_subfigure` or `Figure.subfigures` methods
11 (provisional API v3.4).
12
13Figures are typically created using pyplot methods `~.pyplot.figure`,
14`~.pyplot.subplots`, and `~.pyplot.subplot_mosaic`.
15
16.. plot::
17 :include-source:
18
19 fig, ax = plt.subplots(figsize=(2, 2), facecolor='lightskyblue',
20 layout='constrained')
21 fig.suptitle('Figure')
22 ax.set_title('Axes', loc='left', fontstyle='oblique', fontsize='medium')
23
24Some situations call for directly instantiating a `~.figure.Figure` class,
25usually inside an application of some sort (see :ref:`user_interfaces` for a
26list of examples) . More information about Figures can be found at
27:ref:`figure-intro`.
28"""
29
30from contextlib import ExitStack
31import inspect
32import itertools
33import logging
34from numbers import Integral
35import threading
36
37import numpy as np
38
39import matplotlib as mpl
40from matplotlib import _blocking_input, backend_bases, _docstring, projections
41from matplotlib.artist import (
42 Artist, allow_rasterization, _finalize_rasterization)
43from matplotlib.backend_bases import (
44 DrawEvent, FigureCanvasBase, NonGuiException, MouseButton, _get_renderer)
45import matplotlib._api as _api
46import matplotlib.cbook as cbook
47import matplotlib.colorbar as cbar
48import matplotlib.image as mimage
49
50from matplotlib.axes import Axes
51from matplotlib.gridspec import GridSpec, SubplotParams
52from matplotlib.layout_engine import (
53 ConstrainedLayoutEngine, TightLayoutEngine, LayoutEngine,
54 PlaceHolderLayoutEngine
55)
56import matplotlib.legend as mlegend
57from matplotlib.patches import Rectangle
58from matplotlib.text import Text
59from matplotlib.transforms import (Affine2D, Bbox, BboxTransformTo,
60 TransformedBbox)
61
62_log = logging.getLogger(__name__)
63
64
65def _stale_figure_callback(self, val):
66 if self.figure:
67 self.figure.stale = val
68
69
70class _AxesStack:
71 """
72 Helper class to track Axes in a figure.
73
74 Axes are tracked both in the order in which they have been added
75 (``self._axes`` insertion/iteration order) and in the separate "gca" stack
76 (which is the index to which they map in the ``self._axes`` dict).
77 """
78
79 def __init__(self):
80 self._axes = {} # Mapping of Axes to "gca" order.
81 self._counter = itertools.count()
82
83 def as_list(self):
84 """List the Axes that have been added to the figure."""
85 return [*self._axes] # This relies on dict preserving order.
86
87 def remove(self, a):
88 """Remove the Axes from the stack."""
89 self._axes.pop(a)
90
91 def bubble(self, a):
92 """Move an Axes, which must already exist in the stack, to the top."""
93 if a not in self._axes:
94 raise ValueError("Axes has not been added yet")
95 self._axes[a] = next(self._counter)
96
97 def add(self, a):
98 """Add an Axes to the stack, ignoring it if already present."""
99 if a not in self._axes:
100 self._axes[a] = next(self._counter)
101
102 def current(self):
103 """Return the active Axes, or None if the stack is empty."""
104 return max(self._axes, key=self._axes.__getitem__, default=None)
105
106 def __getstate__(self):
107 return {
108 **vars(self),
109 "_counter": max(self._axes.values(), default=0)
110 }
111
112 def __setstate__(self, state):
113 next_counter = state.pop('_counter')
114 vars(self).update(state)
115 self._counter = itertools.count(next_counter)
116
117
118class FigureBase(Artist):
119 """
120 Base class for `.Figure` and `.SubFigure` containing the methods that add
121 artists to the figure or subfigure, create Axes, etc.
122 """
123 def __init__(self, **kwargs):
124 super().__init__()
125 # remove the non-figure artist _axes property
126 # as it makes no sense for a figure to be _in_ an Axes
127 # this is used by the property methods in the artist base class
128 # which are over-ridden in this class
129 del self._axes
130
131 self._suptitle = None
132 self._supxlabel = None
133 self._supylabel = None
134
135 # groupers to keep track of x, y labels and title we want to align.
136 # see self.align_xlabels, self.align_ylabels,
137 # self.align_titles, and axis._get_tick_boxes_siblings
138 self._align_label_groups = {
139 "x": cbook.Grouper(),
140 "y": cbook.Grouper(),
141 "title": cbook.Grouper()
142 }
143
144 self._localaxes = [] # track all Axes
145 self.artists = []
146 self.lines = []
147 self.patches = []
148 self.texts = []
149 self.images = []
150 self.legends = []
151 self.subfigs = []
152 self.stale = True
153 self.suppressComposite = None
154 self.set(**kwargs)
155
156 def _get_draw_artists(self, renderer):
157 """Also runs apply_aspect"""
158 artists = self.get_children()
159
160 artists.remove(self.patch)
161 artists = sorted(
162 (artist for artist in artists if not artist.get_animated()),
163 key=lambda artist: artist.get_zorder())
164 for ax in self._localaxes:
165 locator = ax.get_axes_locator()
166 ax.apply_aspect(locator(ax, renderer) if locator else None)
167
168 for child in ax.get_children():
169 if hasattr(child, 'apply_aspect'):
170 locator = child.get_axes_locator()
171 child.apply_aspect(
172 locator(child, renderer) if locator else None)
173 return artists
174
175 def autofmt_xdate(
176 self, bottom=0.2, rotation=30, ha='right', which='major'):
177 """
178 Date ticklabels often overlap, so it is useful to rotate them
179 and right align them. Also, a common use case is a number of
180 subplots with shared x-axis where the x-axis is date data. The
181 ticklabels are often long, and it helps to rotate them on the
182 bottom subplot and turn them off on other subplots, as well as
183 turn off xlabels.
184
185 Parameters
186 ----------
187 bottom : float, default: 0.2
188 The bottom of the subplots for `subplots_adjust`.
189 rotation : float, default: 30 degrees
190 The rotation angle of the xtick labels in degrees.
191 ha : {'left', 'center', 'right'}, default: 'right'
192 The horizontal alignment of the xticklabels.
193 which : {'major', 'minor', 'both'}, default: 'major'
194 Selects which ticklabels to rotate.
195 """
196 _api.check_in_list(['major', 'minor', 'both'], which=which)
197 allsubplots = all(ax.get_subplotspec() for ax in self.axes)
198 if len(self.axes) == 1:
199 for label in self.axes[0].get_xticklabels(which=which):
200 label.set_ha(ha)
201 label.set_rotation(rotation)
202 else:
203 if allsubplots:
204 for ax in self.get_axes():
205 if ax.get_subplotspec().is_last_row():
206 for label in ax.get_xticklabels(which=which):
207 label.set_ha(ha)
208 label.set_rotation(rotation)
209 else:
210 for label in ax.get_xticklabels(which=which):
211 label.set_visible(False)
212 ax.set_xlabel('')
213
214 if allsubplots:
215 self.subplots_adjust(bottom=bottom)
216 self.stale = True
217
218 def get_children(self):
219 """Get a list of artists contained in the figure."""
220 return [self.patch,
221 *self.artists,
222 *self._localaxes,
223 *self.lines,
224 *self.patches,
225 *self.texts,
226 *self.images,
227 *self.legends,
228 *self.subfigs]
229
230 def contains(self, mouseevent):
231 """
232 Test whether the mouse event occurred on the figure.
233
234 Returns
235 -------
236 bool, {}
237 """
238 if self._different_canvas(mouseevent):
239 return False, {}
240 inside = self.bbox.contains(mouseevent.x, mouseevent.y)
241 return inside, {}
242
243 def get_window_extent(self, renderer=None):
244 # docstring inherited
245 return self.bbox
246
247 def _suplabels(self, t, info, **kwargs):
248 """
249 Add a centered %(name)s to the figure.
250
251 Parameters
252 ----------
253 t : str
254 The %(name)s text.
255 x : float, default: %(x0)s
256 The x location of the text in figure coordinates.
257 y : float, default: %(y0)s
258 The y location of the text in figure coordinates.
259 horizontalalignment, ha : {'center', 'left', 'right'}, default: %(ha)s
260 The horizontal alignment of the text relative to (*x*, *y*).
261 verticalalignment, va : {'top', 'center', 'bottom', 'baseline'}, \
262default: %(va)s
263 The vertical alignment of the text relative to (*x*, *y*).
264 fontsize, size : default: :rc:`figure.%(rc)ssize`
265 The font size of the text. See `.Text.set_size` for possible
266 values.
267 fontweight, weight : default: :rc:`figure.%(rc)sweight`
268 The font weight of the text. See `.Text.set_weight` for possible
269 values.
270
271 Returns
272 -------
273 text
274 The `.Text` instance of the %(name)s.
275
276 Other Parameters
277 ----------------
278 fontproperties : None or dict, optional
279 A dict of font properties. If *fontproperties* is given the
280 default values for font size and weight are taken from the
281 `.FontProperties` defaults. :rc:`figure.%(rc)ssize` and
282 :rc:`figure.%(rc)sweight` are ignored in this case.
283
284 **kwargs
285 Additional kwargs are `matplotlib.text.Text` properties.
286 """
287
288 x = kwargs.pop('x', None)
289 y = kwargs.pop('y', None)
290 if info['name'] in ['_supxlabel', '_suptitle']:
291 autopos = y is None
292 elif info['name'] == '_supylabel':
293 autopos = x is None
294 if x is None:
295 x = info['x0']
296 if y is None:
297 y = info['y0']
298
299 kwargs = cbook.normalize_kwargs(kwargs, Text)
300 kwargs.setdefault('horizontalalignment', info['ha'])
301 kwargs.setdefault('verticalalignment', info['va'])
302 kwargs.setdefault('rotation', info['rotation'])
303
304 if 'fontproperties' not in kwargs:
305 kwargs.setdefault('fontsize', mpl.rcParams[info['size']])
306 kwargs.setdefault('fontweight', mpl.rcParams[info['weight']])
307
308 suplab = getattr(self, info['name'])
309 if suplab is not None:
310 suplab.set_text(t)
311 suplab.set_position((x, y))
312 suplab.set(**kwargs)
313 else:
314 suplab = self.text(x, y, t, **kwargs)
315 setattr(self, info['name'], suplab)
316 suplab._autopos = autopos
317 self.stale = True
318 return suplab
319
320 @_docstring.Substitution(x0=0.5, y0=0.98, name='suptitle', ha='center',
321 va='top', rc='title')
322 @_docstring.copy(_suplabels)
323 def suptitle(self, t, **kwargs):
324 # docstring from _suplabels...
325 info = {'name': '_suptitle', 'x0': 0.5, 'y0': 0.98,
326 'ha': 'center', 'va': 'top', 'rotation': 0,
327 'size': 'figure.titlesize', 'weight': 'figure.titleweight'}
328 return self._suplabels(t, info, **kwargs)
329
330 def get_suptitle(self):
331 """Return the suptitle as string or an empty string if not set."""
332 text_obj = self._suptitle
333 return "" if text_obj is None else text_obj.get_text()
334
335 @_docstring.Substitution(x0=0.5, y0=0.01, name='supxlabel', ha='center',
336 va='bottom', rc='label')
337 @_docstring.copy(_suplabels)
338 def supxlabel(self, t, **kwargs):
339 # docstring from _suplabels...
340 info = {'name': '_supxlabel', 'x0': 0.5, 'y0': 0.01,
341 'ha': 'center', 'va': 'bottom', 'rotation': 0,
342 'size': 'figure.labelsize', 'weight': 'figure.labelweight'}
343 return self._suplabels(t, info, **kwargs)
344
345 def get_supxlabel(self):
346 """Return the supxlabel as string or an empty string if not set."""
347 text_obj = self._supxlabel
348 return "" if text_obj is None else text_obj.get_text()
349
350 @_docstring.Substitution(x0=0.02, y0=0.5, name='supylabel', ha='left',
351 va='center', rc='label')
352 @_docstring.copy(_suplabels)
353 def supylabel(self, t, **kwargs):
354 # docstring from _suplabels...
355 info = {'name': '_supylabel', 'x0': 0.02, 'y0': 0.5,
356 'ha': 'left', 'va': 'center', 'rotation': 'vertical',
357 'rotation_mode': 'anchor', 'size': 'figure.labelsize',
358 'weight': 'figure.labelweight'}
359 return self._suplabels(t, info, **kwargs)
360
361 def get_supylabel(self):
362 """Return the supylabel as string or an empty string if not set."""
363 text_obj = self._supylabel
364 return "" if text_obj is None else text_obj.get_text()
365
366 def get_edgecolor(self):
367 """Get the edge color of the Figure rectangle."""
368 return self.patch.get_edgecolor()
369
370 def get_facecolor(self):
371 """Get the face color of the Figure rectangle."""
372 return self.patch.get_facecolor()
373
374 def get_frameon(self):
375 """
376 Return the figure's background patch visibility, i.e.
377 whether the figure background will be drawn. Equivalent to
378 ``Figure.patch.get_visible()``.
379 """
380 return self.patch.get_visible()
381
382 def set_linewidth(self, linewidth):
383 """
384 Set the line width of the Figure rectangle.
385
386 Parameters
387 ----------
388 linewidth : number
389 """
390 self.patch.set_linewidth(linewidth)
391
392 def get_linewidth(self):
393 """
394 Get the line width of the Figure rectangle.
395 """
396 return self.patch.get_linewidth()
397
398 def set_edgecolor(self, color):
399 """
400 Set the edge color of the Figure rectangle.
401
402 Parameters
403 ----------
404 color : :mpltype:`color`
405 """
406 self.patch.set_edgecolor(color)
407
408 def set_facecolor(self, color):
409 """
410 Set the face color of the Figure rectangle.
411
412 Parameters
413 ----------
414 color : :mpltype:`color`
415 """
416 self.patch.set_facecolor(color)
417
418 def set_frameon(self, b):
419 """
420 Set the figure's background patch visibility, i.e.
421 whether the figure background will be drawn. Equivalent to
422 ``Figure.patch.set_visible()``.
423
424 Parameters
425 ----------
426 b : bool
427 """
428 self.patch.set_visible(b)
429 self.stale = True
430
431 frameon = property(get_frameon, set_frameon)
432
433 def add_artist(self, artist, clip=False):
434 """
435 Add an `.Artist` to the figure.
436
437 Usually artists are added to `~.axes.Axes` objects using
438 `.Axes.add_artist`; this method can be used in the rare cases where
439 one needs to add artists directly to the figure instead.
440
441 Parameters
442 ----------
443 artist : `~matplotlib.artist.Artist`
444 The artist to add to the figure. If the added artist has no
445 transform previously set, its transform will be set to
446 ``figure.transSubfigure``.
447 clip : bool, default: False
448 Whether the added artist should be clipped by the figure patch.
449
450 Returns
451 -------
452 `~matplotlib.artist.Artist`
453 The added artist.
454 """
455 artist.set_figure(self)
456 self.artists.append(artist)
457 artist._remove_method = self.artists.remove
458
459 if not artist.is_transform_set():
460 artist.set_transform(self.transSubfigure)
461
462 if clip and artist.get_clip_path() is None:
463 artist.set_clip_path(self.patch)
464
465 self.stale = True
466 return artist
467
468 @_docstring.dedent_interpd
469 def add_axes(self, *args, **kwargs):
470 """
471 Add an `~.axes.Axes` to the figure.
472
473 Call signatures::
474
475 add_axes(rect, projection=None, polar=False, **kwargs)
476 add_axes(ax)
477
478 Parameters
479 ----------
480 rect : tuple (left, bottom, width, height)
481 The dimensions (left, bottom, width, height) of the new
482 `~.axes.Axes`. All quantities are in fractions of figure width and
483 height.
484
485 projection : {None, 'aitoff', 'hammer', 'lambert', 'mollweide', \
486'polar', 'rectilinear', str}, optional
487 The projection type of the `~.axes.Axes`. *str* is the name of
488 a custom projection, see `~matplotlib.projections`. The default
489 None results in a 'rectilinear' projection.
490
491 polar : bool, default: False
492 If True, equivalent to projection='polar'.
493
494 axes_class : subclass type of `~.axes.Axes`, optional
495 The `.axes.Axes` subclass that is instantiated. This parameter
496 is incompatible with *projection* and *polar*. See
497 :ref:`axisartist_users-guide-index` for examples.
498
499 sharex, sharey : `~matplotlib.axes.Axes`, optional
500 Share the x or y `~matplotlib.axis` with sharex and/or sharey.
501 The axis will have the same limits, ticks, and scale as the axis
502 of the shared Axes.
503
504 label : str
505 A label for the returned Axes.
506
507 Returns
508 -------
509 `~.axes.Axes`, or a subclass of `~.axes.Axes`
510 The returned Axes class depends on the projection used. It is
511 `~.axes.Axes` if rectilinear projection is used and
512 `.projections.polar.PolarAxes` if polar projection is used.
513
514 Other Parameters
515 ----------------
516 **kwargs
517 This method also takes the keyword arguments for
518 the returned Axes class. The keyword arguments for the
519 rectilinear Axes class `~.axes.Axes` can be found in
520 the following table but there might also be other keyword
521 arguments if another projection is used, see the actual Axes
522 class.
523
524 %(Axes:kwdoc)s
525
526 Notes
527 -----
528 In rare circumstances, `.add_axes` may be called with a single
529 argument, an Axes instance already created in the present figure but
530 not in the figure's list of Axes.
531
532 See Also
533 --------
534 .Figure.add_subplot
535 .pyplot.subplot
536 .pyplot.axes
537 .Figure.subplots
538 .pyplot.subplots
539
540 Examples
541 --------
542 Some simple examples::
543
544 rect = l, b, w, h
545 fig = plt.figure()
546 fig.add_axes(rect)
547 fig.add_axes(rect, frameon=False, facecolor='g')
548 fig.add_axes(rect, polar=True)
549 ax = fig.add_axes(rect, projection='polar')
550 fig.delaxes(ax)
551 fig.add_axes(ax)
552 """
553
554 if not len(args) and 'rect' not in kwargs:
555 raise TypeError(
556 "add_axes() missing 1 required positional argument: 'rect'")
557 elif 'rect' in kwargs:
558 if len(args):
559 raise TypeError(
560 "add_axes() got multiple values for argument 'rect'")
561 args = (kwargs.pop('rect'), )
562
563 if isinstance(args[0], Axes):
564 a, *extra_args = args
565 key = a._projection_init
566 if a.get_figure() is not self:
567 raise ValueError(
568 "The Axes must have been created in the present figure")
569 else:
570 rect, *extra_args = args
571 if not np.isfinite(rect).all():
572 raise ValueError(f'all entries in rect must be finite not {rect}')
573 projection_class, pkw = self._process_projection_requirements(**kwargs)
574
575 # create the new Axes using the Axes class given
576 a = projection_class(self, rect, **pkw)
577 key = (projection_class, pkw)
578
579 if extra_args:
580 _api.warn_deprecated(
581 "3.8",
582 name="Passing more than one positional argument to Figure.add_axes",
583 addendum="Any additional positional arguments are currently ignored.")
584 return self._add_axes_internal(a, key)
585
586 @_docstring.dedent_interpd
587 def add_subplot(self, *args, **kwargs):
588 """
589 Add an `~.axes.Axes` to the figure as part of a subplot arrangement.
590
591 Call signatures::
592
593 add_subplot(nrows, ncols, index, **kwargs)
594 add_subplot(pos, **kwargs)
595 add_subplot(ax)
596 add_subplot()
597
598 Parameters
599 ----------
600 *args : int, (int, int, *index*), or `.SubplotSpec`, default: (1, 1, 1)
601 The position of the subplot described by one of
602
603 - Three integers (*nrows*, *ncols*, *index*). The subplot will
604 take the *index* position on a grid with *nrows* rows and
605 *ncols* columns. *index* starts at 1 in the upper left corner
606 and increases to the right. *index* can also be a two-tuple
607 specifying the (*first*, *last*) indices (1-based, and including
608 *last*) of the subplot, e.g., ``fig.add_subplot(3, 1, (1, 2))``
609 makes a subplot that spans the upper 2/3 of the figure.
610 - A 3-digit integer. The digits are interpreted as if given
611 separately as three single-digit integers, i.e.
612 ``fig.add_subplot(235)`` is the same as
613 ``fig.add_subplot(2, 3, 5)``. Note that this can only be used
614 if there are no more than 9 subplots.
615 - A `.SubplotSpec`.
616
617 In rare circumstances, `.add_subplot` may be called with a single
618 argument, a subplot Axes instance already created in the
619 present figure but not in the figure's list of Axes.
620
621 projection : {None, 'aitoff', 'hammer', 'lambert', 'mollweide', \
622'polar', 'rectilinear', str}, optional
623 The projection type of the subplot (`~.axes.Axes`). *str* is the
624 name of a custom projection, see `~matplotlib.projections`. The
625 default None results in a 'rectilinear' projection.
626
627 polar : bool, default: False
628 If True, equivalent to projection='polar'.
629
630 axes_class : subclass type of `~.axes.Axes`, optional
631 The `.axes.Axes` subclass that is instantiated. This parameter
632 is incompatible with *projection* and *polar*. See
633 :ref:`axisartist_users-guide-index` for examples.
634
635 sharex, sharey : `~matplotlib.axes.Axes`, optional
636 Share the x or y `~matplotlib.axis` with sharex and/or sharey.
637 The axis will have the same limits, ticks, and scale as the axis
638 of the shared Axes.
639
640 label : str
641 A label for the returned Axes.
642
643 Returns
644 -------
645 `~.axes.Axes`
646
647 The Axes of the subplot. The returned Axes can actually be an
648 instance of a subclass, such as `.projections.polar.PolarAxes` for
649 polar projections.
650
651 Other Parameters
652 ----------------
653 **kwargs
654 This method also takes the keyword arguments for the returned Axes
655 base class; except for the *figure* argument. The keyword arguments
656 for the rectilinear base class `~.axes.Axes` can be found in
657 the following table but there might also be other keyword
658 arguments if another projection is used.
659
660 %(Axes:kwdoc)s
661
662 See Also
663 --------
664 .Figure.add_axes
665 .pyplot.subplot
666 .pyplot.axes
667 .Figure.subplots
668 .pyplot.subplots
669
670 Examples
671 --------
672 ::
673
674 fig = plt.figure()
675
676 fig.add_subplot(231)
677 ax1 = fig.add_subplot(2, 3, 1) # equivalent but more general
678
679 fig.add_subplot(232, frameon=False) # subplot with no frame
680 fig.add_subplot(233, projection='polar') # polar subplot
681 fig.add_subplot(234, sharex=ax1) # subplot sharing x-axis with ax1
682 fig.add_subplot(235, facecolor="red") # red subplot
683
684 ax1.remove() # delete ax1 from the figure
685 fig.add_subplot(ax1) # add ax1 back to the figure
686 """
687 if 'figure' in kwargs:
688 # Axes itself allows for a 'figure' kwarg, but since we want to
689 # bind the created Axes to self, it is not allowed here.
690 raise _api.kwarg_error("add_subplot", "figure")
691
692 if (len(args) == 1
693 and isinstance(args[0], mpl.axes._base._AxesBase)
694 and args[0].get_subplotspec()):
695 ax = args[0]
696 key = ax._projection_init
697 if ax.get_figure() is not self:
698 raise ValueError("The Axes must have been created in "
699 "the present figure")
700 else:
701 if not args:
702 args = (1, 1, 1)
703 # Normalize correct ijk values to (i, j, k) here so that
704 # add_subplot(211) == add_subplot(2, 1, 1). Invalid values will
705 # trigger errors later (via SubplotSpec._from_subplot_args).
706 if (len(args) == 1 and isinstance(args[0], Integral)
707 and 100 <= args[0] <= 999):
708 args = tuple(map(int, str(args[0])))
709 projection_class, pkw = self._process_projection_requirements(**kwargs)
710 ax = projection_class(self, *args, **pkw)
711 key = (projection_class, pkw)
712 return self._add_axes_internal(ax, key)
713
714 def _add_axes_internal(self, ax, key):
715 """Private helper for `add_axes` and `add_subplot`."""
716 self._axstack.add(ax)
717 if ax not in self._localaxes:
718 self._localaxes.append(ax)
719 self.sca(ax)
720 ax._remove_method = self.delaxes
721 # this is to support plt.subplot's re-selection logic
722 ax._projection_init = key
723 self.stale = True
724 ax.stale_callback = _stale_figure_callback
725 return ax
726
727 def subplots(self, nrows=1, ncols=1, *, sharex=False, sharey=False,
728 squeeze=True, width_ratios=None, height_ratios=None,
729 subplot_kw=None, gridspec_kw=None):
730 """
731 Add a set of subplots to this figure.
732
733 This utility wrapper makes it convenient to create common layouts of
734 subplots in a single call.
735
736 Parameters
737 ----------
738 nrows, ncols : int, default: 1
739 Number of rows/columns of the subplot grid.
740
741 sharex, sharey : bool or {'none', 'all', 'row', 'col'}, default: False
742 Controls sharing of x-axis (*sharex*) or y-axis (*sharey*):
743
744 - True or 'all': x- or y-axis will be shared among all subplots.
745 - False or 'none': each subplot x- or y-axis will be independent.
746 - 'row': each subplot row will share an x- or y-axis.
747 - 'col': each subplot column will share an x- or y-axis.
748
749 When subplots have a shared x-axis along a column, only the x tick
750 labels of the bottom subplot are created. Similarly, when subplots
751 have a shared y-axis along a row, only the y tick labels of the
752 first column subplot are created. To later turn other subplots'
753 ticklabels on, use `~matplotlib.axes.Axes.tick_params`.
754
755 When subplots have a shared axis that has units, calling
756 `.Axis.set_units` will update each axis with the new units.
757
758 Note that it is not possible to unshare axes.
759
760 squeeze : bool, default: True
761 - If True, extra dimensions are squeezed out from the returned
762 array of Axes:
763
764 - if only one subplot is constructed (nrows=ncols=1), the
765 resulting single Axes object is returned as a scalar.
766 - for Nx1 or 1xM subplots, the returned object is a 1D numpy
767 object array of Axes objects.
768 - for NxM, subplots with N>1 and M>1 are returned as a 2D array.
769
770 - If False, no squeezing at all is done: the returned Axes object
771 is always a 2D array containing Axes instances, even if it ends
772 up being 1x1.
773
774 width_ratios : array-like of length *ncols*, optional
775 Defines the relative widths of the columns. Each column gets a
776 relative width of ``width_ratios[i] / sum(width_ratios)``.
777 If not given, all columns will have the same width. Equivalent
778 to ``gridspec_kw={'width_ratios': [...]}``.
779
780 height_ratios : array-like of length *nrows*, optional
781 Defines the relative heights of the rows. Each row gets a
782 relative height of ``height_ratios[i] / sum(height_ratios)``.
783 If not given, all rows will have the same height. Equivalent
784 to ``gridspec_kw={'height_ratios': [...]}``.
785
786 subplot_kw : dict, optional
787 Dict with keywords passed to the `.Figure.add_subplot` call used to
788 create each subplot.
789
790 gridspec_kw : dict, optional
791 Dict with keywords passed to the
792 `~matplotlib.gridspec.GridSpec` constructor used to create
793 the grid the subplots are placed on.
794
795 Returns
796 -------
797 `~.axes.Axes` or array of Axes
798 Either a single `~matplotlib.axes.Axes` object or an array of Axes
799 objects if more than one subplot was created. The dimensions of the
800 resulting array can be controlled with the *squeeze* keyword, see
801 above.
802
803 See Also
804 --------
805 .pyplot.subplots
806 .Figure.add_subplot
807 .pyplot.subplot
808
809 Examples
810 --------
811 ::
812
813 # First create some toy data:
814 x = np.linspace(0, 2*np.pi, 400)
815 y = np.sin(x**2)
816
817 # Create a figure
818 fig = plt.figure()
819
820 # Create a subplot
821 ax = fig.subplots()
822 ax.plot(x, y)
823 ax.set_title('Simple plot')
824
825 # Create two subplots and unpack the output array immediately
826 ax1, ax2 = fig.subplots(1, 2, sharey=True)
827 ax1.plot(x, y)
828 ax1.set_title('Sharing Y axis')
829 ax2.scatter(x, y)
830
831 # Create four polar Axes and access them through the returned array
832 axes = fig.subplots(2, 2, subplot_kw=dict(projection='polar'))
833 axes[0, 0].plot(x, y)
834 axes[1, 1].scatter(x, y)
835
836 # Share an X-axis with each column of subplots
837 fig.subplots(2, 2, sharex='col')
838
839 # Share a Y-axis with each row of subplots
840 fig.subplots(2, 2, sharey='row')
841
842 # Share both X- and Y-axes with all subplots
843 fig.subplots(2, 2, sharex='all', sharey='all')
844
845 # Note that this is the same as
846 fig.subplots(2, 2, sharex=True, sharey=True)
847 """
848 gridspec_kw = dict(gridspec_kw or {})
849 if height_ratios is not None:
850 if 'height_ratios' in gridspec_kw:
851 raise ValueError("'height_ratios' must not be defined both as "
852 "parameter and as key in 'gridspec_kw'")
853 gridspec_kw['height_ratios'] = height_ratios
854 if width_ratios is not None:
855 if 'width_ratios' in gridspec_kw:
856 raise ValueError("'width_ratios' must not be defined both as "
857 "parameter and as key in 'gridspec_kw'")
858 gridspec_kw['width_ratios'] = width_ratios
859
860 gs = self.add_gridspec(nrows, ncols, figure=self, **gridspec_kw)
861 axs = gs.subplots(sharex=sharex, sharey=sharey, squeeze=squeeze,
862 subplot_kw=subplot_kw)
863 return axs
864
865 def delaxes(self, ax):
866 """
867 Remove the `~.axes.Axes` *ax* from the figure; update the current Axes.
868 """
869 self._remove_axes(ax, owners=[self._axstack, self._localaxes])
870
871 def _remove_axes(self, ax, owners):
872 """
873 Common helper for removal of standard Axes (via delaxes) and of child Axes.
874
875 Parameters
876 ----------
877 ax : `~.AxesBase`
878 The Axes to remove.
879 owners
880 List of objects (list or _AxesStack) "owning" the Axes, from which the Axes
881 will be remove()d.
882 """
883 for owner in owners:
884 owner.remove(ax)
885
886 self._axobservers.process("_axes_change_event", self)
887 self.stale = True
888 self.canvas.release_mouse(ax)
889
890 for name in ax._axis_names: # Break link between any shared Axes
891 grouper = ax._shared_axes[name]
892 siblings = [other for other in grouper.get_siblings(ax) if other is not ax]
893 if not siblings: # Axes was not shared along this axis; we're done.
894 continue
895 grouper.remove(ax)
896 # Formatters and locators may previously have been associated with the now
897 # removed axis. Update them to point to an axis still there (we can pick
898 # any of them, and use the first sibling).
899 remaining_axis = siblings[0]._axis_map[name]
900 remaining_axis.get_major_formatter().set_axis(remaining_axis)
901 remaining_axis.get_major_locator().set_axis(remaining_axis)
902 remaining_axis.get_minor_formatter().set_axis(remaining_axis)
903 remaining_axis.get_minor_locator().set_axis(remaining_axis)
904
905 ax._twinned_axes.remove(ax) # Break link between any twinned Axes.
906
907 def clear(self, keep_observers=False):
908 """
909 Clear the figure.
910
911 Parameters
912 ----------
913 keep_observers : bool, default: False
914 Set *keep_observers* to True if, for example,
915 a gui widget is tracking the Axes in the figure.
916 """
917 self.suppressComposite = None
918
919 # first clear the Axes in any subfigures
920 for subfig in self.subfigs:
921 subfig.clear(keep_observers=keep_observers)
922 self.subfigs = []
923
924 for ax in tuple(self.axes): # Iterate over the copy.
925 ax.clear()
926 self.delaxes(ax) # Remove ax from self._axstack.
927
928 self.artists = []
929 self.lines = []
930 self.patches = []
931 self.texts = []
932 self.images = []
933 self.legends = []
934 if not keep_observers:
935 self._axobservers = cbook.CallbackRegistry()
936 self._suptitle = None
937 self._supxlabel = None
938 self._supylabel = None
939
940 self.stale = True
941
942 # synonym for `clear`.
943 def clf(self, keep_observers=False):
944 """
945 [*Discouraged*] Alias for the `clear()` method.
946
947 .. admonition:: Discouraged
948
949 The use of ``clf()`` is discouraged. Use ``clear()`` instead.
950
951 Parameters
952 ----------
953 keep_observers : bool, default: False
954 Set *keep_observers* to True if, for example,
955 a gui widget is tracking the Axes in the figure.
956 """
957 return self.clear(keep_observers=keep_observers)
958
959 # Note: the docstring below is modified with replace for the pyplot
960 # version of this function because the method name differs (plt.figlegend)
961 # the replacements are:
962 # " legend(" -> " figlegend(" for the signatures
963 # "fig.legend(" -> "plt.figlegend" for the code examples
964 # "ax.plot" -> "plt.plot" for consistency in using pyplot when able
965 @_docstring.dedent_interpd
966 def legend(self, *args, **kwargs):
967 """
968 Place a legend on the figure.
969
970 Call signatures::
971
972 legend()
973 legend(handles, labels)
974 legend(handles=handles)
975 legend(labels)
976
977 The call signatures correspond to the following different ways to use
978 this method:
979
980 **1. Automatic detection of elements to be shown in the legend**
981
982 The elements to be added to the legend are automatically determined,
983 when you do not pass in any extra arguments.
984
985 In this case, the labels are taken from the artist. You can specify
986 them either at artist creation or by calling the
987 :meth:`~.Artist.set_label` method on the artist::
988
989 ax.plot([1, 2, 3], label='Inline label')
990 fig.legend()
991
992 or::
993
994 line, = ax.plot([1, 2, 3])
995 line.set_label('Label via method')
996 fig.legend()
997
998 Specific lines can be excluded from the automatic legend element
999 selection by defining a label starting with an underscore.
1000 This is default for all artists, so calling `.Figure.legend` without
1001 any arguments and without setting the labels manually will result in
1002 no legend being drawn.
1003
1004
1005 **2. Explicitly listing the artists and labels in the legend**
1006
1007 For full control of which artists have a legend entry, it is possible
1008 to pass an iterable of legend artists followed by an iterable of
1009 legend labels respectively::
1010
1011 fig.legend([line1, line2, line3], ['label1', 'label2', 'label3'])
1012
1013
1014 **3. Explicitly listing the artists in the legend**
1015
1016 This is similar to 2, but the labels are taken from the artists'
1017 label properties. Example::
1018
1019 line1, = ax1.plot([1, 2, 3], label='label1')
1020 line2, = ax2.plot([1, 2, 3], label='label2')
1021 fig.legend(handles=[line1, line2])
1022
1023
1024 **4. Labeling existing plot elements**
1025
1026 .. admonition:: Discouraged
1027
1028 This call signature is discouraged, because the relation between
1029 plot elements and labels is only implicit by their order and can
1030 easily be mixed up.
1031
1032 To make a legend for all artists on all Axes, call this function with
1033 an iterable of strings, one for each legend item. For example::
1034
1035 fig, (ax1, ax2) = plt.subplots(1, 2)
1036 ax1.plot([1, 3, 5], color='blue')
1037 ax2.plot([2, 4, 6], color='red')
1038 fig.legend(['the blues', 'the reds'])
1039
1040
1041 Parameters
1042 ----------
1043 handles : list of `.Artist`, optional
1044 A list of Artists (lines, patches) to be added to the legend.
1045 Use this together with *labels*, if you need full control on what
1046 is shown in the legend and the automatic mechanism described above
1047 is not sufficient.
1048
1049 The length of handles and labels should be the same in this
1050 case. If they are not, they are truncated to the smaller length.
1051
1052 labels : list of str, optional
1053 A list of labels to show next to the artists.
1054 Use this together with *handles*, if you need full control on what
1055 is shown in the legend and the automatic mechanism described above
1056 is not sufficient.
1057
1058 Returns
1059 -------
1060 `~matplotlib.legend.Legend`
1061
1062 Other Parameters
1063 ----------------
1064 %(_legend_kw_figure)s
1065
1066 See Also
1067 --------
1068 .Axes.legend
1069
1070 Notes
1071 -----
1072 Some artists are not supported by this function. See
1073 :ref:`legend_guide` for details.
1074 """
1075
1076 handles, labels, kwargs = mlegend._parse_legend_args(self.axes, *args, **kwargs)
1077 # explicitly set the bbox transform if the user hasn't.
1078 kwargs.setdefault("bbox_transform", self.transSubfigure)
1079 l = mlegend.Legend(self, handles, labels, **kwargs)
1080 self.legends.append(l)
1081 l._remove_method = self.legends.remove
1082 self.stale = True
1083 return l
1084
1085 @_docstring.dedent_interpd
1086 def text(self, x, y, s, fontdict=None, **kwargs):
1087 """
1088 Add text to figure.
1089
1090 Parameters
1091 ----------
1092 x, y : float
1093 The position to place the text. By default, this is in figure
1094 coordinates, floats in [0, 1]. The coordinate system can be changed
1095 using the *transform* keyword.
1096
1097 s : str
1098 The text string.
1099
1100 fontdict : dict, optional
1101 A dictionary to override the default text properties. If not given,
1102 the defaults are determined by :rc:`font.*`. Properties passed as
1103 *kwargs* override the corresponding ones given in *fontdict*.
1104
1105 Returns
1106 -------
1107 `~.text.Text`
1108
1109 Other Parameters
1110 ----------------
1111 **kwargs : `~matplotlib.text.Text` properties
1112 Other miscellaneous text parameters.
1113
1114 %(Text:kwdoc)s
1115
1116 See Also
1117 --------
1118 .Axes.text
1119 .pyplot.text
1120 """
1121 effective_kwargs = {
1122 'transform': self.transSubfigure,
1123 **(fontdict if fontdict is not None else {}),
1124 **kwargs,
1125 }
1126 text = Text(x=x, y=y, text=s, **effective_kwargs)
1127 text.set_figure(self)
1128 text.stale_callback = _stale_figure_callback
1129
1130 self.texts.append(text)
1131 text._remove_method = self.texts.remove
1132 self.stale = True
1133 return text
1134
1135 @_docstring.dedent_interpd
1136 def colorbar(
1137 self, mappable, cax=None, ax=None, use_gridspec=True, **kwargs):
1138 """
1139 Add a colorbar to a plot.
1140
1141 Parameters
1142 ----------
1143 mappable
1144 The `matplotlib.cm.ScalarMappable` (i.e., `.AxesImage`,
1145 `.ContourSet`, etc.) described by this colorbar. This argument is
1146 mandatory for the `.Figure.colorbar` method but optional for the
1147 `.pyplot.colorbar` function, which sets the default to the current
1148 image.
1149
1150 Note that one can create a `.ScalarMappable` "on-the-fly" to
1151 generate colorbars not attached to a previously drawn artist, e.g.
1152 ::
1153
1154 fig.colorbar(cm.ScalarMappable(norm=norm, cmap=cmap), ax=ax)
1155
1156 cax : `~matplotlib.axes.Axes`, optional
1157 Axes into which the colorbar will be drawn. If `None`, then a new
1158 Axes is created and the space for it will be stolen from the Axes(s)
1159 specified in *ax*.
1160
1161 ax : `~matplotlib.axes.Axes` or iterable or `numpy.ndarray` of Axes, optional
1162 The one or more parent Axes from which space for a new colorbar Axes
1163 will be stolen. This parameter is only used if *cax* is not set.
1164
1165 Defaults to the Axes that contains the mappable used to create the
1166 colorbar.
1167
1168 use_gridspec : bool, optional
1169 If *cax* is ``None``, a new *cax* is created as an instance of
1170 Axes. If *ax* is positioned with a subplotspec and *use_gridspec*
1171 is ``True``, then *cax* is also positioned with a subplotspec.
1172
1173 Returns
1174 -------
1175 colorbar : `~matplotlib.colorbar.Colorbar`
1176
1177 Other Parameters
1178 ----------------
1179 %(_make_axes_kw_doc)s
1180 %(_colormap_kw_doc)s
1181
1182 Notes
1183 -----
1184 If *mappable* is a `~.contour.ContourSet`, its *extend* kwarg is
1185 included automatically.
1186
1187 The *shrink* kwarg provides a simple way to scale the colorbar with
1188 respect to the Axes. Note that if *cax* is specified, it determines the
1189 size of the colorbar, and *shrink* and *aspect* are ignored.
1190
1191 For more precise control, you can manually specify the positions of the
1192 axes objects in which the mappable and the colorbar are drawn. In this
1193 case, do not use any of the Axes properties kwargs.
1194
1195 It is known that some vector graphics viewers (svg and pdf) render
1196 white gaps between segments of the colorbar. This is due to bugs in
1197 the viewers, not Matplotlib. As a workaround, the colorbar can be
1198 rendered with overlapping segments::
1199
1200 cbar = colorbar()
1201 cbar.solids.set_edgecolor("face")
1202 draw()
1203
1204 However, this has negative consequences in other circumstances, e.g.
1205 with semi-transparent images (alpha < 1) and colorbar extensions;
1206 therefore, this workaround is not used by default (see issue #1188).
1207
1208 """
1209
1210 if ax is None:
1211 ax = getattr(mappable, "axes", None)
1212
1213 if cax is None:
1214 if ax is None:
1215 raise ValueError(
1216 'Unable to determine Axes to steal space for Colorbar. '
1217 'Either provide the *cax* argument to use as the Axes for '
1218 'the Colorbar, provide the *ax* argument to steal space '
1219 'from it, or add *mappable* to an Axes.')
1220 fig = ( # Figure of first Axes; logic copied from make_axes.
1221 [*ax.flat] if isinstance(ax, np.ndarray)
1222 else [*ax] if np.iterable(ax)
1223 else [ax])[0].figure
1224 current_ax = fig.gca()
1225 if (fig.get_layout_engine() is not None and
1226 not fig.get_layout_engine().colorbar_gridspec):
1227 use_gridspec = False
1228 if (use_gridspec
1229 and isinstance(ax, mpl.axes._base._AxesBase)
1230 and ax.get_subplotspec()):
1231 cax, kwargs = cbar.make_axes_gridspec(ax, **kwargs)
1232 else:
1233 cax, kwargs = cbar.make_axes(ax, **kwargs)
1234 # make_axes calls add_{axes,subplot} which changes gca; undo that.
1235 fig.sca(current_ax)
1236 cax.grid(visible=False, which='both', axis='both')
1237
1238 if hasattr(mappable, "figure") and mappable.figure is not None:
1239 # Get top level artists
1240 mappable_host_fig = mappable.figure
1241 if isinstance(mappable_host_fig, mpl.figure.SubFigure):
1242 mappable_host_fig = mappable_host_fig.figure
1243 # Warn in case of mismatch
1244 if mappable_host_fig is not self.figure:
1245 _api.warn_external(
1246 f'Adding colorbar to a different Figure '
1247 f'{repr(mappable.figure)} than '
1248 f'{repr(self.figure)} which '
1249 f'fig.colorbar is called on.')
1250
1251 NON_COLORBAR_KEYS = [ # remove kws that cannot be passed to Colorbar
1252 'fraction', 'pad', 'shrink', 'aspect', 'anchor', 'panchor']
1253 cb = cbar.Colorbar(cax, mappable, **{
1254 k: v for k, v in kwargs.items() if k not in NON_COLORBAR_KEYS})
1255 cax.figure.stale = True
1256 return cb
1257
1258 def subplots_adjust(self, left=None, bottom=None, right=None, top=None,
1259 wspace=None, hspace=None):
1260 """
1261 Adjust the subplot layout parameters.
1262
1263 Unset parameters are left unmodified; initial values are given by
1264 :rc:`figure.subplot.[name]`.
1265
1266 Parameters
1267 ----------
1268 left : float, optional
1269 The position of the left edge of the subplots,
1270 as a fraction of the figure width.
1271 right : float, optional
1272 The position of the right edge of the subplots,
1273 as a fraction of the figure width.
1274 bottom : float, optional
1275 The position of the bottom edge of the subplots,
1276 as a fraction of the figure height.
1277 top : float, optional
1278 The position of the top edge of the subplots,
1279 as a fraction of the figure height.
1280 wspace : float, optional
1281 The width of the padding between subplots,
1282 as a fraction of the average Axes width.
1283 hspace : float, optional
1284 The height of the padding between subplots,
1285 as a fraction of the average Axes height.
1286 """
1287 if (self.get_layout_engine() is not None and
1288 not self.get_layout_engine().adjust_compatible):
1289 _api.warn_external(
1290 "This figure was using a layout engine that is "
1291 "incompatible with subplots_adjust and/or tight_layout; "
1292 "not calling subplots_adjust.")
1293 return
1294 self.subplotpars.update(left, bottom, right, top, wspace, hspace)
1295 for ax in self.axes:
1296 if ax.get_subplotspec() is not None:
1297 ax._set_position(ax.get_subplotspec().get_position(self))
1298 self.stale = True
1299
1300 def align_xlabels(self, axs=None):
1301 """
1302 Align the xlabels of subplots in the same subplot row if label
1303 alignment is being done automatically (i.e. the label position is
1304 not manually set).
1305
1306 Alignment persists for draw events after this is called.
1307
1308 If a label is on the bottom, it is aligned with labels on Axes that
1309 also have their label on the bottom and that have the same
1310 bottom-most subplot row. If the label is on the top,
1311 it is aligned with labels on Axes with the same top-most row.
1312
1313 Parameters
1314 ----------
1315 axs : list of `~matplotlib.axes.Axes`
1316 Optional list of (or `~numpy.ndarray`) `~matplotlib.axes.Axes`
1317 to align the xlabels.
1318 Default is to align all Axes on the figure.
1319
1320 See Also
1321 --------
1322 matplotlib.figure.Figure.align_ylabels
1323 matplotlib.figure.Figure.align_titles
1324 matplotlib.figure.Figure.align_labels
1325
1326 Notes
1327 -----
1328 This assumes that ``axs`` are from the same `.GridSpec`, so that
1329 their `.SubplotSpec` positions correspond to figure positions.
1330
1331 Examples
1332 --------
1333 Example with rotated xtick labels::
1334
1335 fig, axs = plt.subplots(1, 2)
1336 for tick in axs[0].get_xticklabels():
1337 tick.set_rotation(55)
1338 axs[0].set_xlabel('XLabel 0')
1339 axs[1].set_xlabel('XLabel 1')
1340 fig.align_xlabels()
1341 """
1342 if axs is None:
1343 axs = self.axes
1344 axs = [ax for ax in np.ravel(axs) if ax.get_subplotspec() is not None]
1345 for ax in axs:
1346 _log.debug(' Working on: %s', ax.get_xlabel())
1347 rowspan = ax.get_subplotspec().rowspan
1348 pos = ax.xaxis.get_label_position() # top or bottom
1349 # Search through other Axes for label positions that are same as
1350 # this one and that share the appropriate row number.
1351 # Add to a grouper associated with each Axes of siblings.
1352 # This list is inspected in `axis.draw` by
1353 # `axis._update_label_position`.
1354 for axc in axs:
1355 if axc.xaxis.get_label_position() == pos:
1356 rowspanc = axc.get_subplotspec().rowspan
1357 if (pos == 'top' and rowspan.start == rowspanc.start or
1358 pos == 'bottom' and rowspan.stop == rowspanc.stop):
1359 # grouper for groups of xlabels to align
1360 self._align_label_groups['x'].join(ax, axc)
1361
1362 def align_ylabels(self, axs=None):
1363 """
1364 Align the ylabels of subplots in the same subplot column if label
1365 alignment is being done automatically (i.e. the label position is
1366 not manually set).
1367
1368 Alignment persists for draw events after this is called.
1369
1370 If a label is on the left, it is aligned with labels on Axes that
1371 also have their label on the left and that have the same
1372 left-most subplot column. If the label is on the right,
1373 it is aligned with labels on Axes with the same right-most column.
1374
1375 Parameters
1376 ----------
1377 axs : list of `~matplotlib.axes.Axes`
1378 Optional list (or `~numpy.ndarray`) of `~matplotlib.axes.Axes`
1379 to align the ylabels.
1380 Default is to align all Axes on the figure.
1381
1382 See Also
1383 --------
1384 matplotlib.figure.Figure.align_xlabels
1385 matplotlib.figure.Figure.align_titles
1386 matplotlib.figure.Figure.align_labels
1387
1388 Notes
1389 -----
1390 This assumes that ``axs`` are from the same `.GridSpec`, so that
1391 their `.SubplotSpec` positions correspond to figure positions.
1392
1393 Examples
1394 --------
1395 Example with large yticks labels::
1396
1397 fig, axs = plt.subplots(2, 1)
1398 axs[0].plot(np.arange(0, 1000, 50))
1399 axs[0].set_ylabel('YLabel 0')
1400 axs[1].set_ylabel('YLabel 1')
1401 fig.align_ylabels()
1402 """
1403 if axs is None:
1404 axs = self.axes
1405 axs = [ax for ax in np.ravel(axs) if ax.get_subplotspec() is not None]
1406 for ax in axs:
1407 _log.debug(' Working on: %s', ax.get_ylabel())
1408 colspan = ax.get_subplotspec().colspan
1409 pos = ax.yaxis.get_label_position() # left or right
1410 # Search through other Axes for label positions that are same as
1411 # this one and that share the appropriate column number.
1412 # Add to a list associated with each Axes of siblings.
1413 # This list is inspected in `axis.draw` by
1414 # `axis._update_label_position`.
1415 for axc in axs:
1416 if axc.yaxis.get_label_position() == pos:
1417 colspanc = axc.get_subplotspec().colspan
1418 if (pos == 'left' and colspan.start == colspanc.start or
1419 pos == 'right' and colspan.stop == colspanc.stop):
1420 # grouper for groups of ylabels to align
1421 self._align_label_groups['y'].join(ax, axc)
1422
1423 def align_titles(self, axs=None):
1424 """
1425 Align the titles of subplots in the same subplot row if title
1426 alignment is being done automatically (i.e. the title position is
1427 not manually set).
1428
1429 Alignment persists for draw events after this is called.
1430
1431 Parameters
1432 ----------
1433 axs : list of `~matplotlib.axes.Axes`
1434 Optional list of (or ndarray) `~matplotlib.axes.Axes`
1435 to align the titles.
1436 Default is to align all Axes on the figure.
1437
1438 See Also
1439 --------
1440 matplotlib.figure.Figure.align_xlabels
1441 matplotlib.figure.Figure.align_ylabels
1442 matplotlib.figure.Figure.align_labels
1443
1444 Notes
1445 -----
1446 This assumes that ``axs`` are from the same `.GridSpec`, so that
1447 their `.SubplotSpec` positions correspond to figure positions.
1448
1449 Examples
1450 --------
1451 Example with titles::
1452
1453 fig, axs = plt.subplots(1, 2)
1454 axs[0].set_aspect('equal')
1455 axs[0].set_title('Title 0')
1456 axs[1].set_title('Title 1')
1457 fig.align_titles()
1458 """
1459 if axs is None:
1460 axs = self.axes
1461 axs = [ax for ax in np.ravel(axs) if ax.get_subplotspec() is not None]
1462 for ax in axs:
1463 _log.debug(' Working on: %s', ax.get_title())
1464 rowspan = ax.get_subplotspec().rowspan
1465 for axc in axs:
1466 rowspanc = axc.get_subplotspec().rowspan
1467 if (rowspan.start == rowspanc.start):
1468 self._align_label_groups['title'].join(ax, axc)
1469
1470 def align_labels(self, axs=None):
1471 """
1472 Align the xlabels and ylabels of subplots with the same subplots
1473 row or column (respectively) if label alignment is being
1474 done automatically (i.e. the label position is not manually set).
1475
1476 Alignment persists for draw events after this is called.
1477
1478 Parameters
1479 ----------
1480 axs : list of `~matplotlib.axes.Axes`
1481 Optional list (or `~numpy.ndarray`) of `~matplotlib.axes.Axes`
1482 to align the labels.
1483 Default is to align all Axes on the figure.
1484
1485 See Also
1486 --------
1487 matplotlib.figure.Figure.align_xlabels
1488 matplotlib.figure.Figure.align_ylabels
1489 matplotlib.figure.Figure.align_titles
1490 """
1491 self.align_xlabels(axs=axs)
1492 self.align_ylabels(axs=axs)
1493
1494 def add_gridspec(self, nrows=1, ncols=1, **kwargs):
1495 """
1496 Low-level API for creating a `.GridSpec` that has this figure as a parent.
1497
1498 This is a low-level API, allowing you to create a gridspec and
1499 subsequently add subplots based on the gridspec. Most users do
1500 not need that freedom and should use the higher-level methods
1501 `~.Figure.subplots` or `~.Figure.subplot_mosaic`.
1502
1503 Parameters
1504 ----------
1505 nrows : int, default: 1
1506 Number of rows in grid.
1507
1508 ncols : int, default: 1
1509 Number of columns in grid.
1510
1511 Returns
1512 -------
1513 `.GridSpec`
1514
1515 Other Parameters
1516 ----------------
1517 **kwargs
1518 Keyword arguments are passed to `.GridSpec`.
1519
1520 See Also
1521 --------
1522 matplotlib.pyplot.subplots
1523
1524 Examples
1525 --------
1526 Adding a subplot that spans two rows::
1527
1528 fig = plt.figure()
1529 gs = fig.add_gridspec(2, 2)
1530 ax1 = fig.add_subplot(gs[0, 0])
1531 ax2 = fig.add_subplot(gs[1, 0])
1532 # spans two rows:
1533 ax3 = fig.add_subplot(gs[:, 1])
1534
1535 """
1536
1537 _ = kwargs.pop('figure', None) # pop in case user has added this...
1538 gs = GridSpec(nrows=nrows, ncols=ncols, figure=self, **kwargs)
1539 return gs
1540
1541 def subfigures(self, nrows=1, ncols=1, squeeze=True,
1542 wspace=None, hspace=None,
1543 width_ratios=None, height_ratios=None,
1544 **kwargs):
1545 """
1546 Add a set of subfigures to this figure or subfigure.
1547
1548 A subfigure has the same artist methods as a figure, and is logically
1549 the same as a figure, but cannot print itself.
1550 See :doc:`/gallery/subplots_axes_and_figures/subfigures`.
1551
1552 .. note::
1553 The *subfigure* concept is new in v3.4, and the API is still provisional.
1554
1555 Parameters
1556 ----------
1557 nrows, ncols : int, default: 1
1558 Number of rows/columns of the subfigure grid.
1559
1560 squeeze : bool, default: True
1561 If True, extra dimensions are squeezed out from the returned
1562 array of subfigures.
1563
1564 wspace, hspace : float, default: None
1565 The amount of width/height reserved for space between subfigures,
1566 expressed as a fraction of the average subfigure width/height.
1567 If not given, the values will be inferred from rcParams if using
1568 constrained layout (see `~.ConstrainedLayoutEngine`), or zero if
1569 not using a layout engine.
1570
1571 width_ratios : array-like of length *ncols*, optional
1572 Defines the relative widths of the columns. Each column gets a
1573 relative width of ``width_ratios[i] / sum(width_ratios)``.
1574 If not given, all columns will have the same width.
1575
1576 height_ratios : array-like of length *nrows*, optional
1577 Defines the relative heights of the rows. Each row gets a
1578 relative height of ``height_ratios[i] / sum(height_ratios)``.
1579 If not given, all rows will have the same height.
1580 """
1581 gs = GridSpec(nrows=nrows, ncols=ncols, figure=self,
1582 wspace=wspace, hspace=hspace,
1583 width_ratios=width_ratios,
1584 height_ratios=height_ratios,
1585 left=0, right=1, bottom=0, top=1)
1586
1587 sfarr = np.empty((nrows, ncols), dtype=object)
1588 for i in range(ncols):
1589 for j in range(nrows):
1590 sfarr[j, i] = self.add_subfigure(gs[j, i], **kwargs)
1591
1592 if self.get_layout_engine() is None and (wspace is not None or
1593 hspace is not None):
1594 # Gridspec wspace and hspace is ignored on subfigure instantiation,
1595 # and no space is left. So need to account for it here if required.
1596 bottoms, tops, lefts, rights = gs.get_grid_positions(self)
1597 for sfrow, bottom, top in zip(sfarr, bottoms, tops):
1598 for sf, left, right in zip(sfrow, lefts, rights):
1599 bbox = Bbox.from_extents(left, bottom, right, top)
1600 sf._redo_transform_rel_fig(bbox=bbox)
1601
1602 if squeeze:
1603 # Discarding unneeded dimensions that equal 1. If we only have one
1604 # subfigure, just return it instead of a 1-element array.
1605 return sfarr.item() if sfarr.size == 1 else sfarr.squeeze()
1606 else:
1607 # Returned axis array will be always 2-d, even if nrows=ncols=1.
1608 return sfarr
1609
1610 def add_subfigure(self, subplotspec, **kwargs):
1611 """
1612 Add a `.SubFigure` to the figure as part of a subplot arrangement.
1613
1614 Parameters
1615 ----------
1616 subplotspec : `.gridspec.SubplotSpec`
1617 Defines the region in a parent gridspec where the subfigure will
1618 be placed.
1619
1620 Returns
1621 -------
1622 `.SubFigure`
1623
1624 Other Parameters
1625 ----------------
1626 **kwargs
1627 Are passed to the `.SubFigure` object.
1628
1629 See Also
1630 --------
1631 .Figure.subfigures
1632 """
1633 sf = SubFigure(self, subplotspec, **kwargs)
1634 self.subfigs += [sf]
1635 sf._remove_method = self.subfigs.remove
1636 sf.stale_callback = _stale_figure_callback
1637 self.stale = True
1638 return sf
1639
1640 def sca(self, a):
1641 """Set the current Axes to be *a* and return *a*."""
1642 self._axstack.bubble(a)
1643 self._axobservers.process("_axes_change_event", self)
1644 return a
1645
1646 def gca(self):
1647 """
1648 Get the current Axes.
1649
1650 If there is currently no Axes on this Figure, a new one is created
1651 using `.Figure.add_subplot`. (To test whether there is currently an
1652 Axes on a Figure, check whether ``figure.axes`` is empty. To test
1653 whether there is currently a Figure on the pyplot figure stack, check
1654 whether `.pyplot.get_fignums()` is empty.)
1655 """
1656 ax = self._axstack.current()
1657 return ax if ax is not None else self.add_subplot()
1658
1659 def _gci(self):
1660 # Helper for `~matplotlib.pyplot.gci`. Do not use elsewhere.
1661 """
1662 Get the current colorable artist.
1663
1664 Specifically, returns the current `.ScalarMappable` instance (`.Image`
1665 created by `imshow` or `figimage`, `.Collection` created by `pcolor` or
1666 `scatter`, etc.), or *None* if no such instance has been defined.
1667
1668 The current image is an attribute of the current Axes, or the nearest
1669 earlier Axes in the current figure that contains an image.
1670
1671 Notes
1672 -----
1673 Historically, the only colorable artists were images; hence the name
1674 ``gci`` (get current image).
1675 """
1676 # Look first for an image in the current Axes.
1677 ax = self._axstack.current()
1678 if ax is None:
1679 return None
1680 im = ax._gci()
1681 if im is not None:
1682 return im
1683 # If there is no image in the current Axes, search for
1684 # one in a previously created Axes. Whether this makes
1685 # sense is debatable, but it is the documented behavior.
1686 for ax in reversed(self.axes):
1687 im = ax._gci()
1688 if im is not None:
1689 return im
1690 return None
1691
1692 def _process_projection_requirements(self, *, axes_class=None, polar=False,
1693 projection=None, **kwargs):
1694 """
1695 Handle the args/kwargs to add_axes/add_subplot/gca, returning::
1696
1697 (axes_proj_class, proj_class_kwargs)
1698
1699 which can be used for new Axes initialization/identification.
1700 """
1701 if axes_class is not None:
1702 if polar or projection is not None:
1703 raise ValueError(
1704 "Cannot combine 'axes_class' and 'projection' or 'polar'")
1705 projection_class = axes_class
1706 else:
1707
1708 if polar:
1709 if projection is not None and projection != 'polar':
1710 raise ValueError(
1711 f"polar={polar}, yet projection={projection!r}. "
1712 "Only one of these arguments should be supplied."
1713 )
1714 projection = 'polar'
1715
1716 if isinstance(projection, str) or projection is None:
1717 projection_class = projections.get_projection_class(projection)
1718 elif hasattr(projection, '_as_mpl_axes'):
1719 projection_class, extra_kwargs = projection._as_mpl_axes()
1720 kwargs.update(**extra_kwargs)
1721 else:
1722 raise TypeError(
1723 f"projection must be a string, None or implement a "
1724 f"_as_mpl_axes method, not {projection!r}")
1725 return projection_class, kwargs
1726
1727 def get_default_bbox_extra_artists(self):
1728 """
1729 Return a list of Artists typically used in `.Figure.get_tightbbox`.
1730 """
1731 bbox_artists = [artist for artist in self.get_children()
1732 if (artist.get_visible() and artist.get_in_layout())]
1733 for ax in self.axes:
1734 if ax.get_visible():
1735 bbox_artists.extend(ax.get_default_bbox_extra_artists())
1736 return bbox_artists
1737
1738 @_api.make_keyword_only("3.8", "bbox_extra_artists")
1739 def get_tightbbox(self, renderer=None, bbox_extra_artists=None):
1740 """
1741 Return a (tight) bounding box of the figure *in inches*.
1742
1743 Note that `.FigureBase` differs from all other artists, which return
1744 their `.Bbox` in pixels.
1745
1746 Artists that have ``artist.set_in_layout(False)`` are not included
1747 in the bbox.
1748
1749 Parameters
1750 ----------
1751 renderer : `.RendererBase` subclass
1752 Renderer that will be used to draw the figures (i.e.
1753 ``fig.canvas.get_renderer()``)
1754
1755 bbox_extra_artists : list of `.Artist` or ``None``
1756 List of artists to include in the tight bounding box. If
1757 ``None`` (default), then all artist children of each Axes are
1758 included in the tight bounding box.
1759
1760 Returns
1761 -------
1762 `.BboxBase`
1763 containing the bounding box (in figure inches).
1764 """
1765
1766 if renderer is None:
1767 renderer = self.figure._get_renderer()
1768
1769 bb = []
1770 if bbox_extra_artists is None:
1771 artists = [artist for artist in self.get_children()
1772 if (artist not in self.axes and artist.get_visible()
1773 and artist.get_in_layout())]
1774 else:
1775 artists = bbox_extra_artists
1776
1777 for a in artists:
1778 bbox = a.get_tightbbox(renderer)
1779 if bbox is not None:
1780 bb.append(bbox)
1781
1782 for ax in self.axes:
1783 if ax.get_visible():
1784 # some Axes don't take the bbox_extra_artists kwarg so we
1785 # need this conditional....
1786 try:
1787 bbox = ax.get_tightbbox(
1788 renderer, bbox_extra_artists=bbox_extra_artists)
1789 except TypeError:
1790 bbox = ax.get_tightbbox(renderer)
1791 bb.append(bbox)
1792 bb = [b for b in bb
1793 if (np.isfinite(b.width) and np.isfinite(b.height)
1794 and (b.width != 0 or b.height != 0))]
1795
1796 isfigure = hasattr(self, 'bbox_inches')
1797 if len(bb) == 0:
1798 if isfigure:
1799 return self.bbox_inches
1800 else:
1801 # subfigures do not have bbox_inches, but do have a bbox
1802 bb = [self.bbox]
1803
1804 _bbox = Bbox.union(bb)
1805
1806 if isfigure:
1807 # transform from pixels to inches...
1808 _bbox = TransformedBbox(_bbox, self.dpi_scale_trans.inverted())
1809
1810 return _bbox
1811
1812 @staticmethod
1813 def _norm_per_subplot_kw(per_subplot_kw):
1814 expanded = {}
1815 for k, v in per_subplot_kw.items():
1816 if isinstance(k, tuple):
1817 for sub_key in k:
1818 if sub_key in expanded:
1819 raise ValueError(f'The key {sub_key!r} appears multiple times.')
1820 expanded[sub_key] = v
1821 else:
1822 if k in expanded:
1823 raise ValueError(f'The key {k!r} appears multiple times.')
1824 expanded[k] = v
1825 return expanded
1826
1827 @staticmethod
1828 def _normalize_grid_string(layout):
1829 if '\n' not in layout:
1830 # single-line string
1831 return [list(ln) for ln in layout.split(';')]
1832 else:
1833 # multi-line string
1834 layout = inspect.cleandoc(layout)
1835 return [list(ln) for ln in layout.strip('\n').split('\n')]
1836
1837 def subplot_mosaic(self, mosaic, *, sharex=False, sharey=False,
1838 width_ratios=None, height_ratios=None,
1839 empty_sentinel='.',
1840 subplot_kw=None, per_subplot_kw=None, gridspec_kw=None):
1841 """
1842 Build a layout of Axes based on ASCII art or nested lists.
1843
1844 This is a helper function to build complex GridSpec layouts visually.
1845
1846 See :ref:`mosaic`
1847 for an example and full API documentation
1848
1849 Parameters
1850 ----------
1851 mosaic : list of list of {hashable or nested} or str
1852
1853 A visual layout of how you want your Axes to be arranged
1854 labeled as strings. For example ::
1855
1856 x = [['A panel', 'A panel', 'edge'],
1857 ['C panel', '.', 'edge']]
1858
1859 produces 4 Axes:
1860
1861 - 'A panel' which is 1 row high and spans the first two columns
1862 - 'edge' which is 2 rows high and is on the right edge
1863 - 'C panel' which in 1 row and 1 column wide in the bottom left
1864 - a blank space 1 row and 1 column wide in the bottom center
1865
1866 Any of the entries in the layout can be a list of lists
1867 of the same form to create nested layouts.
1868
1869 If input is a str, then it can either be a multi-line string of
1870 the form ::
1871
1872 '''
1873 AAE
1874 C.E
1875 '''
1876
1877 where each character is a column and each line is a row. Or it
1878 can be a single-line string where rows are separated by ``;``::
1879
1880 'AB;CC'
1881
1882 The string notation allows only single character Axes labels and
1883 does not support nesting but is very terse.
1884
1885 The Axes identifiers may be `str` or a non-iterable hashable
1886 object (e.g. `tuple` s may not be used).
1887
1888 sharex, sharey : bool, default: False
1889 If True, the x-axis (*sharex*) or y-axis (*sharey*) will be shared
1890 among all subplots. In that case, tick label visibility and axis
1891 units behave as for `subplots`. If False, each subplot's x- or
1892 y-axis will be independent.
1893
1894 width_ratios : array-like of length *ncols*, optional
1895 Defines the relative widths of the columns. Each column gets a
1896 relative width of ``width_ratios[i] / sum(width_ratios)``.
1897 If not given, all columns will have the same width. Equivalent
1898 to ``gridspec_kw={'width_ratios': [...]}``. In the case of nested
1899 layouts, this argument applies only to the outer layout.
1900
1901 height_ratios : array-like of length *nrows*, optional
1902 Defines the relative heights of the rows. Each row gets a
1903 relative height of ``height_ratios[i] / sum(height_ratios)``.
1904 If not given, all rows will have the same height. Equivalent
1905 to ``gridspec_kw={'height_ratios': [...]}``. In the case of nested
1906 layouts, this argument applies only to the outer layout.
1907
1908 subplot_kw : dict, optional
1909 Dictionary with keywords passed to the `.Figure.add_subplot` call
1910 used to create each subplot. These values may be overridden by
1911 values in *per_subplot_kw*.
1912
1913 per_subplot_kw : dict, optional
1914 A dictionary mapping the Axes identifiers or tuples of identifiers
1915 to a dictionary of keyword arguments to be passed to the
1916 `.Figure.add_subplot` call used to create each subplot. The values
1917 in these dictionaries have precedence over the values in
1918 *subplot_kw*.
1919
1920 If *mosaic* is a string, and thus all keys are single characters,
1921 it is possible to use a single string instead of a tuple as keys;
1922 i.e. ``"AB"`` is equivalent to ``("A", "B")``.
1923
1924 .. versionadded:: 3.7
1925
1926 gridspec_kw : dict, optional
1927 Dictionary with keywords passed to the `.GridSpec` constructor used
1928 to create the grid the subplots are placed on. In the case of
1929 nested layouts, this argument applies only to the outer layout.
1930 For more complex layouts, users should use `.Figure.subfigures`
1931 to create the nesting.
1932
1933 empty_sentinel : object, optional
1934 Entry in the layout to mean "leave this space empty". Defaults
1935 to ``'.'``. Note, if *layout* is a string, it is processed via
1936 `inspect.cleandoc` to remove leading white space, which may
1937 interfere with using white-space as the empty sentinel.
1938
1939 Returns
1940 -------
1941 dict[label, Axes]
1942 A dictionary mapping the labels to the Axes objects. The order of
1943 the Axes is left-to-right and top-to-bottom of their position in the
1944 total layout.
1945
1946 """
1947 subplot_kw = subplot_kw or {}
1948 gridspec_kw = dict(gridspec_kw or {})
1949 per_subplot_kw = per_subplot_kw or {}
1950
1951 if height_ratios is not None:
1952 if 'height_ratios' in gridspec_kw:
1953 raise ValueError("'height_ratios' must not be defined both as "
1954 "parameter and as key in 'gridspec_kw'")
1955 gridspec_kw['height_ratios'] = height_ratios
1956 if width_ratios is not None:
1957 if 'width_ratios' in gridspec_kw:
1958 raise ValueError("'width_ratios' must not be defined both as "
1959 "parameter and as key in 'gridspec_kw'")
1960 gridspec_kw['width_ratios'] = width_ratios
1961
1962 # special-case string input
1963 if isinstance(mosaic, str):
1964 mosaic = self._normalize_grid_string(mosaic)
1965 per_subplot_kw = {
1966 tuple(k): v for k, v in per_subplot_kw.items()
1967 }
1968
1969 per_subplot_kw = self._norm_per_subplot_kw(per_subplot_kw)
1970
1971 # Only accept strict bools to allow a possible future API expansion.
1972 _api.check_isinstance(bool, sharex=sharex, sharey=sharey)
1973
1974 def _make_array(inp):
1975 """
1976 Convert input into 2D array
1977
1978 We need to have this internal function rather than
1979 ``np.asarray(..., dtype=object)`` so that a list of lists
1980 of lists does not get converted to an array of dimension > 2.
1981
1982 Returns
1983 -------
1984 2D object array
1985 """
1986 r0, *rest = inp
1987 if isinstance(r0, str):
1988 raise ValueError('List mosaic specification must be 2D')
1989 for j, r in enumerate(rest, start=1):
1990 if isinstance(r, str):
1991 raise ValueError('List mosaic specification must be 2D')
1992 if len(r0) != len(r):
1993 raise ValueError(
1994 "All of the rows must be the same length, however "
1995 f"the first row ({r0!r}) has length {len(r0)} "
1996 f"and row {j} ({r!r}) has length {len(r)}."
1997 )
1998 out = np.zeros((len(inp), len(r0)), dtype=object)
1999 for j, r in enumerate(inp):
2000 for k, v in enumerate(r):
2001 out[j, k] = v
2002 return out
2003
2004 def _identify_keys_and_nested(mosaic):
2005 """
2006 Given a 2D object array, identify unique IDs and nested mosaics
2007
2008 Parameters
2009 ----------
2010 mosaic : 2D object array
2011
2012 Returns
2013 -------
2014 unique_ids : tuple
2015 The unique non-sub mosaic entries in this mosaic
2016 nested : dict[tuple[int, int], 2D object array]
2017 """
2018 # make sure we preserve the user supplied order
2019 unique_ids = cbook._OrderedSet()
2020 nested = {}
2021 for j, row in enumerate(mosaic):
2022 for k, v in enumerate(row):
2023 if v == empty_sentinel:
2024 continue
2025 elif not cbook.is_scalar_or_string(v):
2026 nested[(j, k)] = _make_array(v)
2027 else:
2028 unique_ids.add(v)
2029
2030 return tuple(unique_ids), nested
2031
2032 def _do_layout(gs, mosaic, unique_ids, nested):
2033 """
2034 Recursively do the mosaic.
2035
2036 Parameters
2037 ----------
2038 gs : GridSpec
2039 mosaic : 2D object array
2040 The input converted to a 2D array for this level.
2041 unique_ids : tuple
2042 The identified scalar labels at this level of nesting.
2043 nested : dict[tuple[int, int]], 2D object array
2044 The identified nested mosaics, if any.
2045
2046 Returns
2047 -------
2048 dict[label, Axes]
2049 A flat dict of all of the Axes created.
2050 """
2051 output = dict()
2052
2053 # we need to merge together the Axes at this level and the Axes
2054 # in the (recursively) nested sub-mosaics so that we can add
2055 # them to the figure in the "natural" order if you were to
2056 # ravel in c-order all of the Axes that will be created
2057 #
2058 # This will stash the upper left index of each object (axes or
2059 # nested mosaic) at this level
2060 this_level = dict()
2061
2062 # go through the unique keys,
2063 for name in unique_ids:
2064 # sort out where each axes starts/ends
2065 indx = np.argwhere(mosaic == name)
2066 start_row, start_col = np.min(indx, axis=0)
2067 end_row, end_col = np.max(indx, axis=0) + 1
2068 # and construct the slice object
2069 slc = (slice(start_row, end_row), slice(start_col, end_col))
2070 # some light error checking
2071 if (mosaic[slc] != name).any():
2072 raise ValueError(
2073 f"While trying to layout\n{mosaic!r}\n"
2074 f"we found that the label {name!r} specifies a "
2075 "non-rectangular or non-contiguous area.")
2076 # and stash this slice for later
2077 this_level[(start_row, start_col)] = (name, slc, 'axes')
2078
2079 # do the same thing for the nested mosaics (simpler because these
2080 # cannot be spans yet!)
2081 for (j, k), nested_mosaic in nested.items():
2082 this_level[(j, k)] = (None, nested_mosaic, 'nested')
2083
2084 # now go through the things in this level and add them
2085 # in order left-to-right top-to-bottom
2086 for key in sorted(this_level):
2087 name, arg, method = this_level[key]
2088 # we are doing some hokey function dispatch here based
2089 # on the 'method' string stashed above to sort out if this
2090 # element is an Axes or a nested mosaic.
2091 if method == 'axes':
2092 slc = arg
2093 # add a single Axes
2094 if name in output:
2095 raise ValueError(f"There are duplicate keys {name} "
2096 f"in the layout\n{mosaic!r}")
2097 ax = self.add_subplot(
2098 gs[slc], **{
2099 'label': str(name),
2100 **subplot_kw,
2101 **per_subplot_kw.get(name, {})
2102 }
2103 )
2104 output[name] = ax
2105 elif method == 'nested':
2106 nested_mosaic = arg
2107 j, k = key
2108 # recursively add the nested mosaic
2109 rows, cols = nested_mosaic.shape
2110 nested_output = _do_layout(
2111 gs[j, k].subgridspec(rows, cols),
2112 nested_mosaic,
2113 *_identify_keys_and_nested(nested_mosaic)
2114 )
2115 overlap = set(output) & set(nested_output)
2116 if overlap:
2117 raise ValueError(
2118 f"There are duplicate keys {overlap} "
2119 f"between the outer layout\n{mosaic!r}\n"
2120 f"and the nested layout\n{nested_mosaic}"
2121 )
2122 output.update(nested_output)
2123 else:
2124 raise RuntimeError("This should never happen")
2125 return output
2126
2127 mosaic = _make_array(mosaic)
2128 rows, cols = mosaic.shape
2129 gs = self.add_gridspec(rows, cols, **gridspec_kw)
2130 ret = _do_layout(gs, mosaic, *_identify_keys_and_nested(mosaic))
2131 ax0 = next(iter(ret.values()))
2132 for ax in ret.values():
2133 if sharex:
2134 ax.sharex(ax0)
2135 ax._label_outer_xaxis(skip_non_rectangular_axes=True)
2136 if sharey:
2137 ax.sharey(ax0)
2138 ax._label_outer_yaxis(skip_non_rectangular_axes=True)
2139 if extra := set(per_subplot_kw) - set(ret):
2140 raise ValueError(
2141 f"The keys {extra} are in *per_subplot_kw* "
2142 "but not in the mosaic."
2143 )
2144 return ret
2145
2146 def _set_artist_props(self, a):
2147 if a != self:
2148 a.set_figure(self)
2149 a.stale_callback = _stale_figure_callback
2150 a.set_transform(self.transSubfigure)
2151
2152
2153@_docstring.interpd
2154class SubFigure(FigureBase):
2155 """
2156 Logical figure that can be placed inside a figure.
2157
2158 See :ref:`figure-api-subfigure` for an index of methods on this class.
2159 Typically instantiated using `.Figure.add_subfigure` or
2160 `.SubFigure.add_subfigure`, or `.SubFigure.subfigures`. A subfigure has
2161 the same methods as a figure except for those particularly tied to the size
2162 or dpi of the figure, and is confined to a prescribed region of the figure.
2163 For example the following puts two subfigures side-by-side::
2164
2165 fig = plt.figure()
2166 sfigs = fig.subfigures(1, 2)
2167 axsL = sfigs[0].subplots(1, 2)
2168 axsR = sfigs[1].subplots(2, 1)
2169
2170 See :doc:`/gallery/subplots_axes_and_figures/subfigures`
2171
2172 .. note::
2173 The *subfigure* concept is new in v3.4, and the API is still provisional.
2174 """
2175
2176 def __init__(self, parent, subplotspec, *,
2177 facecolor=None,
2178 edgecolor=None,
2179 linewidth=0.0,
2180 frameon=None,
2181 **kwargs):
2182 """
2183 Parameters
2184 ----------
2185 parent : `.Figure` or `.SubFigure`
2186 Figure or subfigure that contains the SubFigure. SubFigures
2187 can be nested.
2188
2189 subplotspec : `.gridspec.SubplotSpec`
2190 Defines the region in a parent gridspec where the subfigure will
2191 be placed.
2192
2193 facecolor : default: ``"none"``
2194 The figure patch face color; transparent by default.
2195
2196 edgecolor : default: :rc:`figure.edgecolor`
2197 The figure patch edge color.
2198
2199 linewidth : float
2200 The linewidth of the frame (i.e. the edge linewidth of the figure
2201 patch).
2202
2203 frameon : bool, default: :rc:`figure.frameon`
2204 If ``False``, suppress drawing the figure background patch.
2205
2206 Other Parameters
2207 ----------------
2208 **kwargs : `.SubFigure` properties, optional
2209
2210 %(SubFigure:kwdoc)s
2211 """
2212 super().__init__(**kwargs)
2213 if facecolor is None:
2214 facecolor = "none"
2215 if edgecolor is None:
2216 edgecolor = mpl.rcParams['figure.edgecolor']
2217 if frameon is None:
2218 frameon = mpl.rcParams['figure.frameon']
2219
2220 self._subplotspec = subplotspec
2221 self._parent = parent
2222 self.figure = parent.figure
2223
2224 # subfigures use the parent axstack
2225 self._axstack = parent._axstack
2226 self.subplotpars = parent.subplotpars
2227 self.dpi_scale_trans = parent.dpi_scale_trans
2228 self._axobservers = parent._axobservers
2229 self.transFigure = parent.transFigure
2230 self.bbox_relative = Bbox.null()
2231 self._redo_transform_rel_fig()
2232 self.figbbox = self._parent.figbbox
2233 self.bbox = TransformedBbox(self.bbox_relative,
2234 self._parent.transSubfigure)
2235 self.transSubfigure = BboxTransformTo(self.bbox)
2236
2237 self.patch = Rectangle(
2238 xy=(0, 0), width=1, height=1, visible=frameon,
2239 facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth,
2240 # Don't let the figure patch influence bbox calculation.
2241 in_layout=False, transform=self.transSubfigure)
2242 self._set_artist_props(self.patch)
2243 self.patch.set_antialiased(False)
2244
2245 @property
2246 def canvas(self):
2247 return self._parent.canvas
2248
2249 @property
2250 def dpi(self):
2251 return self._parent.dpi
2252
2253 @dpi.setter
2254 def dpi(self, value):
2255 self._parent.dpi = value
2256
2257 def get_dpi(self):
2258 """
2259 Return the resolution of the parent figure in dots-per-inch as a float.
2260 """
2261 return self._parent.dpi
2262
2263 def set_dpi(self, val):
2264 """
2265 Set the resolution of parent figure in dots-per-inch.
2266
2267 Parameters
2268 ----------
2269 val : float
2270 """
2271 self._parent.dpi = val
2272 self.stale = True
2273
2274 def _get_renderer(self):
2275 return self._parent._get_renderer()
2276
2277 def _redo_transform_rel_fig(self, bbox=None):
2278 """
2279 Make the transSubfigure bbox relative to Figure transform.
2280
2281 Parameters
2282 ----------
2283 bbox : bbox or None
2284 If not None, then the bbox is used for relative bounding box.
2285 Otherwise, it is calculated from the subplotspec.
2286 """
2287 if bbox is not None:
2288 self.bbox_relative.p0 = bbox.p0
2289 self.bbox_relative.p1 = bbox.p1
2290 return
2291 # need to figure out *where* this subplotspec is.
2292 gs = self._subplotspec.get_gridspec()
2293 wr = np.asarray(gs.get_width_ratios())
2294 hr = np.asarray(gs.get_height_ratios())
2295 dx = wr[self._subplotspec.colspan].sum() / wr.sum()
2296 dy = hr[self._subplotspec.rowspan].sum() / hr.sum()
2297 x0 = wr[:self._subplotspec.colspan.start].sum() / wr.sum()
2298 y0 = 1 - hr[:self._subplotspec.rowspan.stop].sum() / hr.sum()
2299 self.bbox_relative.p0 = (x0, y0)
2300 self.bbox_relative.p1 = (x0 + dx, y0 + dy)
2301
2302 def get_constrained_layout(self):
2303 """
2304 Return whether constrained layout is being used.
2305
2306 See :ref:`constrainedlayout_guide`.
2307 """
2308 return self._parent.get_constrained_layout()
2309
2310 def get_constrained_layout_pads(self, relative=False):
2311 """
2312 Get padding for ``constrained_layout``.
2313
2314 Returns a list of ``w_pad, h_pad`` in inches and
2315 ``wspace`` and ``hspace`` as fractions of the subplot.
2316
2317 See :ref:`constrainedlayout_guide`.
2318
2319 Parameters
2320 ----------
2321 relative : bool
2322 If `True`, then convert from inches to figure relative.
2323 """
2324 return self._parent.get_constrained_layout_pads(relative=relative)
2325
2326 def get_layout_engine(self):
2327 return self._parent.get_layout_engine()
2328
2329 @property
2330 def axes(self):
2331 """
2332 List of Axes in the SubFigure. You can access and modify the Axes
2333 in the SubFigure through this list.
2334
2335 Modifying this list has no effect. Instead, use `~.SubFigure.add_axes`,
2336 `~.SubFigure.add_subplot` or `~.SubFigure.delaxes` to add or remove an
2337 Axes.
2338
2339 Note: The `.SubFigure.axes` property and `~.SubFigure.get_axes` method
2340 are equivalent.
2341 """
2342 return self._localaxes[:]
2343
2344 get_axes = axes.fget
2345
2346 def draw(self, renderer):
2347 # docstring inherited
2348
2349 # draw the figure bounding box, perhaps none for white figure
2350 if not self.get_visible():
2351 return
2352
2353 artists = self._get_draw_artists(renderer)
2354
2355 try:
2356 renderer.open_group('subfigure', gid=self.get_gid())
2357 self.patch.draw(renderer)
2358 mimage._draw_list_compositing_images(
2359 renderer, self, artists, self.figure.suppressComposite)
2360 renderer.close_group('subfigure')
2361
2362 finally:
2363 self.stale = False
2364
2365
2366@_docstring.interpd
2367class Figure(FigureBase):
2368 """
2369 The top level container for all the plot elements.
2370
2371 See `matplotlib.figure` for an index of class methods.
2372
2373 Attributes
2374 ----------
2375 patch
2376 The `.Rectangle` instance representing the figure background patch.
2377
2378 suppressComposite
2379 For multiple images, the figure will make composite images
2380 depending on the renderer option_image_nocomposite function. If
2381 *suppressComposite* is a boolean, this will override the renderer.
2382 """
2383
2384 # we want to cache the fonts and mathtext at a global level so that when
2385 # multiple figures are created we can reuse them. This helps with a bug on
2386 # windows where the creation of too many figures leads to too many open
2387 # file handles and improves the performance of parsing mathtext. However,
2388 # these global caches are not thread safe. The solution here is to let the
2389 # Figure acquire a shared lock at the start of the draw, and release it when it
2390 # is done. This allows multiple renderers to share the cached fonts and
2391 # parsed text, but only one figure can draw at a time and so the font cache
2392 # and mathtext cache are used by only one renderer at a time.
2393
2394 _render_lock = threading.RLock()
2395
2396 def __str__(self):
2397 return "Figure(%gx%g)" % tuple(self.bbox.size)
2398
2399 def __repr__(self):
2400 return "<{clsname} size {h:g}x{w:g} with {naxes} Axes>".format(
2401 clsname=self.__class__.__name__,
2402 h=self.bbox.size[0], w=self.bbox.size[1],
2403 naxes=len(self.axes),
2404 )
2405
2406 def __init__(self,
2407 figsize=None,
2408 dpi=None,
2409 *,
2410 facecolor=None,
2411 edgecolor=None,
2412 linewidth=0.0,
2413 frameon=None,
2414 subplotpars=None, # rc figure.subplot.*
2415 tight_layout=None, # rc figure.autolayout
2416 constrained_layout=None, # rc figure.constrained_layout.use
2417 layout=None,
2418 **kwargs
2419 ):
2420 """
2421 Parameters
2422 ----------
2423 figsize : 2-tuple of floats, default: :rc:`figure.figsize`
2424 Figure dimension ``(width, height)`` in inches.
2425
2426 dpi : float, default: :rc:`figure.dpi`
2427 Dots per inch.
2428
2429 facecolor : default: :rc:`figure.facecolor`
2430 The figure patch facecolor.
2431
2432 edgecolor : default: :rc:`figure.edgecolor`
2433 The figure patch edge color.
2434
2435 linewidth : float
2436 The linewidth of the frame (i.e. the edge linewidth of the figure
2437 patch).
2438
2439 frameon : bool, default: :rc:`figure.frameon`
2440 If ``False``, suppress drawing the figure background patch.
2441
2442 subplotpars : `~matplotlib.gridspec.SubplotParams`
2443 Subplot parameters. If not given, the default subplot
2444 parameters :rc:`figure.subplot.*` are used.
2445
2446 tight_layout : bool or dict, default: :rc:`figure.autolayout`
2447 Whether to use the tight layout mechanism. See `.set_tight_layout`.
2448
2449 .. admonition:: Discouraged
2450
2451 The use of this parameter is discouraged. Please use
2452 ``layout='tight'`` instead for the common case of
2453 ``tight_layout=True`` and use `.set_tight_layout` otherwise.
2454
2455 constrained_layout : bool, default: :rc:`figure.constrained_layout.use`
2456 This is equal to ``layout='constrained'``.
2457
2458 .. admonition:: Discouraged
2459
2460 The use of this parameter is discouraged. Please use
2461 ``layout='constrained'`` instead.
2462
2463 layout : {'constrained', 'compressed', 'tight', 'none', `.LayoutEngine`, \
2464None}, default: None
2465 The layout mechanism for positioning of plot elements to avoid
2466 overlapping Axes decorations (labels, ticks, etc). Note that
2467 layout managers can have significant performance penalties.
2468
2469 - 'constrained': The constrained layout solver adjusts Axes sizes
2470 to avoid overlapping Axes decorations. Can handle complex plot
2471 layouts and colorbars, and is thus recommended.
2472
2473 See :ref:`constrainedlayout_guide` for examples.
2474
2475 - 'compressed': uses the same algorithm as 'constrained', but
2476 removes extra space between fixed-aspect-ratio Axes. Best for
2477 simple grids of Axes.
2478
2479 - 'tight': Use the tight layout mechanism. This is a relatively
2480 simple algorithm that adjusts the subplot parameters so that
2481 decorations do not overlap.
2482
2483 See :ref:`tight_layout_guide` for examples.
2484
2485 - 'none': Do not use a layout engine.
2486
2487 - A `.LayoutEngine` instance. Builtin layout classes are
2488 `.ConstrainedLayoutEngine` and `.TightLayoutEngine`, more easily
2489 accessible by 'constrained' and 'tight'. Passing an instance
2490 allows third parties to provide their own layout engine.
2491
2492 If not given, fall back to using the parameters *tight_layout* and
2493 *constrained_layout*, including their config defaults
2494 :rc:`figure.autolayout` and :rc:`figure.constrained_layout.use`.
2495
2496 Other Parameters
2497 ----------------
2498 **kwargs : `.Figure` properties, optional
2499
2500 %(Figure:kwdoc)s
2501 """
2502 super().__init__(**kwargs)
2503 self.figure = self
2504 self._layout_engine = None
2505
2506 if layout is not None:
2507 if (tight_layout is not None):
2508 _api.warn_external(
2509 "The Figure parameters 'layout' and 'tight_layout' cannot "
2510 "be used together. Please use 'layout' only.")
2511 if (constrained_layout is not None):
2512 _api.warn_external(
2513 "The Figure parameters 'layout' and 'constrained_layout' "
2514 "cannot be used together. Please use 'layout' only.")
2515 self.set_layout_engine(layout=layout)
2516 elif tight_layout is not None:
2517 if constrained_layout is not None:
2518 _api.warn_external(
2519 "The Figure parameters 'tight_layout' and "
2520 "'constrained_layout' cannot be used together. Please use "
2521 "'layout' parameter")
2522 self.set_layout_engine(layout='tight')
2523 if isinstance(tight_layout, dict):
2524 self.get_layout_engine().set(**tight_layout)
2525 elif constrained_layout is not None:
2526 if isinstance(constrained_layout, dict):
2527 self.set_layout_engine(layout='constrained')
2528 self.get_layout_engine().set(**constrained_layout)
2529 elif constrained_layout:
2530 self.set_layout_engine(layout='constrained')
2531
2532 else:
2533 # everything is None, so use default:
2534 self.set_layout_engine(layout=layout)
2535
2536 # Callbacks traditionally associated with the canvas (and exposed with
2537 # a proxy property), but that actually need to be on the figure for
2538 # pickling.
2539 self._canvas_callbacks = cbook.CallbackRegistry(
2540 signals=FigureCanvasBase.events)
2541 connect = self._canvas_callbacks._connect_picklable
2542 self._mouse_key_ids = [
2543 connect('key_press_event', backend_bases._key_handler),
2544 connect('key_release_event', backend_bases._key_handler),
2545 connect('key_release_event', backend_bases._key_handler),
2546 connect('button_press_event', backend_bases._mouse_handler),
2547 connect('button_release_event', backend_bases._mouse_handler),
2548 connect('scroll_event', backend_bases._mouse_handler),
2549 connect('motion_notify_event', backend_bases._mouse_handler),
2550 ]
2551 self._button_pick_id = connect('button_press_event', self.pick)
2552 self._scroll_pick_id = connect('scroll_event', self.pick)
2553
2554 if figsize is None:
2555 figsize = mpl.rcParams['figure.figsize']
2556 if dpi is None:
2557 dpi = mpl.rcParams['figure.dpi']
2558 if facecolor is None:
2559 facecolor = mpl.rcParams['figure.facecolor']
2560 if edgecolor is None:
2561 edgecolor = mpl.rcParams['figure.edgecolor']
2562 if frameon is None:
2563 frameon = mpl.rcParams['figure.frameon']
2564
2565 if not np.isfinite(figsize).all() or (np.array(figsize) < 0).any():
2566 raise ValueError('figure size must be positive finite not '
2567 f'{figsize}')
2568 self.bbox_inches = Bbox.from_bounds(0, 0, *figsize)
2569
2570 self.dpi_scale_trans = Affine2D().scale(dpi)
2571 # do not use property as it will trigger
2572 self._dpi = dpi
2573 self.bbox = TransformedBbox(self.bbox_inches, self.dpi_scale_trans)
2574 self.figbbox = self.bbox
2575 self.transFigure = BboxTransformTo(self.bbox)
2576 self.transSubfigure = self.transFigure
2577
2578 self.patch = Rectangle(
2579 xy=(0, 0), width=1, height=1, visible=frameon,
2580 facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth,
2581 # Don't let the figure patch influence bbox calculation.
2582 in_layout=False)
2583 self._set_artist_props(self.patch)
2584 self.patch.set_antialiased(False)
2585
2586 FigureCanvasBase(self) # Set self.canvas.
2587
2588 if subplotpars is None:
2589 subplotpars = SubplotParams()
2590
2591 self.subplotpars = subplotpars
2592
2593 self._axstack = _AxesStack() # track all figure Axes and current Axes
2594 self.clear()
2595
2596 def pick(self, mouseevent):
2597 if not self.canvas.widgetlock.locked():
2598 super().pick(mouseevent)
2599
2600 def _check_layout_engines_compat(self, old, new):
2601 """
2602 Helper for set_layout engine
2603
2604 If the figure has used the old engine and added a colorbar then the
2605 value of colorbar_gridspec must be the same on the new engine.
2606 """
2607 if old is None or new is None:
2608 return True
2609 if old.colorbar_gridspec == new.colorbar_gridspec:
2610 return True
2611 # colorbar layout different, so check if any colorbars are on the
2612 # figure...
2613 for ax in self.axes:
2614 if hasattr(ax, '_colorbar'):
2615 # colorbars list themselves as a colorbar.
2616 return False
2617 return True
2618
2619 def set_layout_engine(self, layout=None, **kwargs):
2620 """
2621 Set the layout engine for this figure.
2622
2623 Parameters
2624 ----------
2625 layout : {'constrained', 'compressed', 'tight', 'none', `.LayoutEngine`, None}
2626
2627 - 'constrained' will use `~.ConstrainedLayoutEngine`
2628 - 'compressed' will also use `~.ConstrainedLayoutEngine`, but with
2629 a correction that attempts to make a good layout for fixed-aspect
2630 ratio Axes.
2631 - 'tight' uses `~.TightLayoutEngine`
2632 - 'none' removes layout engine.
2633
2634 If a `.LayoutEngine` instance, that instance will be used.
2635
2636 If `None`, the behavior is controlled by :rc:`figure.autolayout`
2637 (which if `True` behaves as if 'tight' was passed) and
2638 :rc:`figure.constrained_layout.use` (which if `True` behaves as if
2639 'constrained' was passed). If both are `True`,
2640 :rc:`figure.autolayout` takes priority.
2641
2642 Users and libraries can define their own layout engines and pass
2643 the instance directly as well.
2644
2645 **kwargs
2646 The keyword arguments are passed to the layout engine to set things
2647 like padding and margin sizes. Only used if *layout* is a string.
2648
2649 """
2650 if layout is None:
2651 if mpl.rcParams['figure.autolayout']:
2652 layout = 'tight'
2653 elif mpl.rcParams['figure.constrained_layout.use']:
2654 layout = 'constrained'
2655 else:
2656 self._layout_engine = None
2657 return
2658 if layout == 'tight':
2659 new_layout_engine = TightLayoutEngine(**kwargs)
2660 elif layout == 'constrained':
2661 new_layout_engine = ConstrainedLayoutEngine(**kwargs)
2662 elif layout == 'compressed':
2663 new_layout_engine = ConstrainedLayoutEngine(compress=True,
2664 **kwargs)
2665 elif layout == 'none':
2666 if self._layout_engine is not None:
2667 new_layout_engine = PlaceHolderLayoutEngine(
2668 self._layout_engine.adjust_compatible,
2669 self._layout_engine.colorbar_gridspec
2670 )
2671 else:
2672 new_layout_engine = None
2673 elif isinstance(layout, LayoutEngine):
2674 new_layout_engine = layout
2675 else:
2676 raise ValueError(f"Invalid value for 'layout': {layout!r}")
2677
2678 if self._check_layout_engines_compat(self._layout_engine,
2679 new_layout_engine):
2680 self._layout_engine = new_layout_engine
2681 else:
2682 raise RuntimeError('Colorbar layout of new layout engine not '
2683 'compatible with old engine, and a colorbar '
2684 'has been created. Engine not changed.')
2685
2686 def get_layout_engine(self):
2687 return self._layout_engine
2688
2689 # TODO: I'd like to dynamically add the _repr_html_ method
2690 # to the figure in the right context, but then IPython doesn't
2691 # use it, for some reason.
2692
2693 def _repr_html_(self):
2694 # We can't use "isinstance" here, because then we'd end up importing
2695 # webagg unconditionally.
2696 if 'WebAgg' in type(self.canvas).__name__:
2697 from matplotlib.backends import backend_webagg
2698 return backend_webagg.ipython_inline_display(self)
2699
2700 def show(self, warn=True):
2701 """
2702 If using a GUI backend with pyplot, display the figure window.
2703
2704 If the figure was not created using `~.pyplot.figure`, it will lack
2705 a `~.backend_bases.FigureManagerBase`, and this method will raise an
2706 AttributeError.
2707
2708 .. warning::
2709
2710 This does not manage an GUI event loop. Consequently, the figure
2711 may only be shown briefly or not shown at all if you or your
2712 environment are not managing an event loop.
2713
2714 Use cases for `.Figure.show` include running this from a GUI
2715 application (where there is persistently an event loop running) or
2716 from a shell, like IPython, that install an input hook to allow the
2717 interactive shell to accept input while the figure is also being
2718 shown and interactive. Some, but not all, GUI toolkits will
2719 register an input hook on import. See :ref:`cp_integration` for
2720 more details.
2721
2722 If you're in a shell without input hook integration or executing a
2723 python script, you should use `matplotlib.pyplot.show` with
2724 ``block=True`` instead, which takes care of starting and running
2725 the event loop for you.
2726
2727 Parameters
2728 ----------
2729 warn : bool, default: True
2730 If ``True`` and we are not running headless (i.e. on Linux with an
2731 unset DISPLAY), issue warning when called on a non-GUI backend.
2732
2733 """
2734 if self.canvas.manager is None:
2735 raise AttributeError(
2736 "Figure.show works only for figures managed by pyplot, "
2737 "normally created by pyplot.figure()")
2738 try:
2739 self.canvas.manager.show()
2740 except NonGuiException as exc:
2741 if warn:
2742 _api.warn_external(str(exc))
2743
2744 @property
2745 def axes(self):
2746 """
2747 List of Axes in the Figure. You can access and modify the Axes in the
2748 Figure through this list.
2749
2750 Do not modify the list itself. Instead, use `~Figure.add_axes`,
2751 `~.Figure.add_subplot` or `~.Figure.delaxes` to add or remove an Axes.
2752
2753 Note: The `.Figure.axes` property and `~.Figure.get_axes` method are
2754 equivalent.
2755 """
2756 return self._axstack.as_list()
2757
2758 get_axes = axes.fget
2759
2760 def _get_renderer(self):
2761 if hasattr(self.canvas, 'get_renderer'):
2762 return self.canvas.get_renderer()
2763 else:
2764 return _get_renderer(self)
2765
2766 def _get_dpi(self):
2767 return self._dpi
2768
2769 def _set_dpi(self, dpi, forward=True):
2770 """
2771 Parameters
2772 ----------
2773 dpi : float
2774
2775 forward : bool
2776 Passed on to `~.Figure.set_size_inches`
2777 """
2778 if dpi == self._dpi:
2779 # We don't want to cause undue events in backends.
2780 return
2781 self._dpi = dpi
2782 self.dpi_scale_trans.clear().scale(dpi)
2783 w, h = self.get_size_inches()
2784 self.set_size_inches(w, h, forward=forward)
2785
2786 dpi = property(_get_dpi, _set_dpi, doc="The resolution in dots per inch.")
2787
2788 def get_tight_layout(self):
2789 """Return whether `.Figure.tight_layout` is called when drawing."""
2790 return isinstance(self.get_layout_engine(), TightLayoutEngine)
2791
2792 @_api.deprecated("3.6", alternative="set_layout_engine",
2793 pending=True)
2794 def set_tight_layout(self, tight):
2795 """
2796 Set whether and how `.Figure.tight_layout` is called when drawing.
2797
2798 Parameters
2799 ----------
2800 tight : bool or dict with keys "pad", "w_pad", "h_pad", "rect" or None
2801 If a bool, sets whether to call `.Figure.tight_layout` upon drawing.
2802 If ``None``, use :rc:`figure.autolayout` instead.
2803 If a dict, pass it as kwargs to `.Figure.tight_layout`, overriding the
2804 default paddings.
2805 """
2806 if tight is None:
2807 tight = mpl.rcParams['figure.autolayout']
2808 _tight = 'tight' if bool(tight) else 'none'
2809 _tight_parameters = tight if isinstance(tight, dict) else {}
2810 self.set_layout_engine(_tight, **_tight_parameters)
2811 self.stale = True
2812
2813 def get_constrained_layout(self):
2814 """
2815 Return whether constrained layout is being used.
2816
2817 See :ref:`constrainedlayout_guide`.
2818 """
2819 return isinstance(self.get_layout_engine(), ConstrainedLayoutEngine)
2820
2821 @_api.deprecated("3.6", alternative="set_layout_engine('constrained')",
2822 pending=True)
2823 def set_constrained_layout(self, constrained):
2824 """
2825 Set whether ``constrained_layout`` is used upon drawing.
2826
2827 If None, :rc:`figure.constrained_layout.use` value will be used.
2828
2829 When providing a dict containing the keys ``w_pad``, ``h_pad``
2830 the default ``constrained_layout`` paddings will be
2831 overridden. These pads are in inches and default to 3.0/72.0.
2832 ``w_pad`` is the width padding and ``h_pad`` is the height padding.
2833
2834 Parameters
2835 ----------
2836 constrained : bool or dict or None
2837 """
2838 if constrained is None:
2839 constrained = mpl.rcParams['figure.constrained_layout.use']
2840 _constrained = 'constrained' if bool(constrained) else 'none'
2841 _parameters = constrained if isinstance(constrained, dict) else {}
2842 self.set_layout_engine(_constrained, **_parameters)
2843 self.stale = True
2844
2845 @_api.deprecated(
2846 "3.6", alternative="figure.get_layout_engine().set()",
2847 pending=True)
2848 def set_constrained_layout_pads(self, **kwargs):
2849 """
2850 Set padding for ``constrained_layout``.
2851
2852 Tip: The parameters can be passed from a dictionary by using
2853 ``fig.set_constrained_layout(**pad_dict)``.
2854
2855 See :ref:`constrainedlayout_guide`.
2856
2857 Parameters
2858 ----------
2859 w_pad : float, default: :rc:`figure.constrained_layout.w_pad`
2860 Width padding in inches. This is the pad around Axes
2861 and is meant to make sure there is enough room for fonts to
2862 look good. Defaults to 3 pts = 0.04167 inches
2863
2864 h_pad : float, default: :rc:`figure.constrained_layout.h_pad`
2865 Height padding in inches. Defaults to 3 pts.
2866
2867 wspace : float, default: :rc:`figure.constrained_layout.wspace`
2868 Width padding between subplots, expressed as a fraction of the
2869 subplot width. The total padding ends up being w_pad + wspace.
2870
2871 hspace : float, default: :rc:`figure.constrained_layout.hspace`
2872 Height padding between subplots, expressed as a fraction of the
2873 subplot width. The total padding ends up being h_pad + hspace.
2874
2875 """
2876 if isinstance(self.get_layout_engine(), ConstrainedLayoutEngine):
2877 self.get_layout_engine().set(**kwargs)
2878
2879 @_api.deprecated("3.6", alternative="fig.get_layout_engine().get()",
2880 pending=True)
2881 def get_constrained_layout_pads(self, relative=False):
2882 """
2883 Get padding for ``constrained_layout``.
2884
2885 Returns a list of ``w_pad, h_pad`` in inches and
2886 ``wspace`` and ``hspace`` as fractions of the subplot.
2887 All values are None if ``constrained_layout`` is not used.
2888
2889 See :ref:`constrainedlayout_guide`.
2890
2891 Parameters
2892 ----------
2893 relative : bool
2894 If `True`, then convert from inches to figure relative.
2895 """
2896 if not isinstance(self.get_layout_engine(), ConstrainedLayoutEngine):
2897 return None, None, None, None
2898 info = self.get_layout_engine().get()
2899 w_pad = info['w_pad']
2900 h_pad = info['h_pad']
2901 wspace = info['wspace']
2902 hspace = info['hspace']
2903
2904 if relative and (w_pad is not None or h_pad is not None):
2905 renderer = self._get_renderer()
2906 dpi = renderer.dpi
2907 w_pad = w_pad * dpi / renderer.width
2908 h_pad = h_pad * dpi / renderer.height
2909
2910 return w_pad, h_pad, wspace, hspace
2911
2912 def set_canvas(self, canvas):
2913 """
2914 Set the canvas that contains the figure
2915
2916 Parameters
2917 ----------
2918 canvas : FigureCanvas
2919 """
2920 self.canvas = canvas
2921
2922 @_docstring.interpd
2923 def figimage(self, X, xo=0, yo=0, alpha=None, norm=None, cmap=None,
2924 vmin=None, vmax=None, origin=None, resize=False, **kwargs):
2925 """
2926 Add a non-resampled image to the figure.
2927
2928 The image is attached to the lower or upper left corner depending on
2929 *origin*.
2930
2931 Parameters
2932 ----------
2933 X
2934 The image data. This is an array of one of the following shapes:
2935
2936 - (M, N): an image with scalar data. Color-mapping is controlled
2937 by *cmap*, *norm*, *vmin*, and *vmax*.
2938 - (M, N, 3): an image with RGB values (0-1 float or 0-255 int).
2939 - (M, N, 4): an image with RGBA values (0-1 float or 0-255 int),
2940 i.e. including transparency.
2941
2942 xo, yo : int
2943 The *x*/*y* image offset in pixels.
2944
2945 alpha : None or float
2946 The alpha blending value.
2947
2948 %(cmap_doc)s
2949
2950 This parameter is ignored if *X* is RGB(A).
2951
2952 %(norm_doc)s
2953
2954 This parameter is ignored if *X* is RGB(A).
2955
2956 %(vmin_vmax_doc)s
2957
2958 This parameter is ignored if *X* is RGB(A).
2959
2960 origin : {'upper', 'lower'}, default: :rc:`image.origin`
2961 Indicates where the [0, 0] index of the array is in the upper left
2962 or lower left corner of the Axes.
2963
2964 resize : bool
2965 If *True*, resize the figure to match the given image size.
2966
2967 Returns
2968 -------
2969 `matplotlib.image.FigureImage`
2970
2971 Other Parameters
2972 ----------------
2973 **kwargs
2974 Additional kwargs are `.Artist` kwargs passed on to `.FigureImage`.
2975
2976 Notes
2977 -----
2978 figimage complements the Axes image (`~matplotlib.axes.Axes.imshow`)
2979 which will be resampled to fit the current Axes. If you want
2980 a resampled image to fill the entire figure, you can define an
2981 `~matplotlib.axes.Axes` with extent [0, 0, 1, 1].
2982
2983 Examples
2984 --------
2985 ::
2986
2987 f = plt.figure()
2988 nx = int(f.get_figwidth() * f.dpi)
2989 ny = int(f.get_figheight() * f.dpi)
2990 data = np.random.random((ny, nx))
2991 f.figimage(data)
2992 plt.show()
2993 """
2994 if resize:
2995 dpi = self.get_dpi()
2996 figsize = [x / dpi for x in (X.shape[1], X.shape[0])]
2997 self.set_size_inches(figsize, forward=True)
2998
2999 im = mimage.FigureImage(self, cmap=cmap, norm=norm,
3000 offsetx=xo, offsety=yo,
3001 origin=origin, **kwargs)
3002 im.stale_callback = _stale_figure_callback
3003
3004 im.set_array(X)
3005 im.set_alpha(alpha)
3006 if norm is None:
3007 im.set_clim(vmin, vmax)
3008 self.images.append(im)
3009 im._remove_method = self.images.remove
3010 self.stale = True
3011 return im
3012
3013 def set_size_inches(self, w, h=None, forward=True):
3014 """
3015 Set the figure size in inches.
3016
3017 Call signatures::
3018
3019 fig.set_size_inches(w, h) # OR
3020 fig.set_size_inches((w, h))
3021
3022 Parameters
3023 ----------
3024 w : (float, float) or float
3025 Width and height in inches (if height not specified as a separate
3026 argument) or width.
3027 h : float
3028 Height in inches.
3029 forward : bool, default: True
3030 If ``True``, the canvas size is automatically updated, e.g.,
3031 you can resize the figure window from the shell.
3032
3033 See Also
3034 --------
3035 matplotlib.figure.Figure.get_size_inches
3036 matplotlib.figure.Figure.set_figwidth
3037 matplotlib.figure.Figure.set_figheight
3038
3039 Notes
3040 -----
3041 To transform from pixels to inches divide by `Figure.dpi`.
3042 """
3043 if h is None: # Got called with a single pair as argument.
3044 w, h = w
3045 size = np.array([w, h])
3046 if not np.isfinite(size).all() or (size < 0).any():
3047 raise ValueError(f'figure size must be positive finite not {size}')
3048 self.bbox_inches.p1 = size
3049 if forward:
3050 manager = self.canvas.manager
3051 if manager is not None:
3052 manager.resize(*(size * self.dpi).astype(int))
3053 self.stale = True
3054
3055 def get_size_inches(self):
3056 """
3057 Return the current size of the figure in inches.
3058
3059 Returns
3060 -------
3061 ndarray
3062 The size (width, height) of the figure in inches.
3063
3064 See Also
3065 --------
3066 matplotlib.figure.Figure.set_size_inches
3067 matplotlib.figure.Figure.get_figwidth
3068 matplotlib.figure.Figure.get_figheight
3069
3070 Notes
3071 -----
3072 The size in pixels can be obtained by multiplying with `Figure.dpi`.
3073 """
3074 return np.array(self.bbox_inches.p1)
3075
3076 def get_figwidth(self):
3077 """Return the figure width in inches."""
3078 return self.bbox_inches.width
3079
3080 def get_figheight(self):
3081 """Return the figure height in inches."""
3082 return self.bbox_inches.height
3083
3084 def get_dpi(self):
3085 """Return the resolution in dots per inch as a float."""
3086 return self.dpi
3087
3088 def set_dpi(self, val):
3089 """
3090 Set the resolution of the figure in dots-per-inch.
3091
3092 Parameters
3093 ----------
3094 val : float
3095 """
3096 self.dpi = val
3097 self.stale = True
3098
3099 def set_figwidth(self, val, forward=True):
3100 """
3101 Set the width of the figure in inches.
3102
3103 Parameters
3104 ----------
3105 val : float
3106 forward : bool
3107 See `set_size_inches`.
3108
3109 See Also
3110 --------
3111 matplotlib.figure.Figure.set_figheight
3112 matplotlib.figure.Figure.set_size_inches
3113 """
3114 self.set_size_inches(val, self.get_figheight(), forward=forward)
3115
3116 def set_figheight(self, val, forward=True):
3117 """
3118 Set the height of the figure in inches.
3119
3120 Parameters
3121 ----------
3122 val : float
3123 forward : bool
3124 See `set_size_inches`.
3125
3126 See Also
3127 --------
3128 matplotlib.figure.Figure.set_figwidth
3129 matplotlib.figure.Figure.set_size_inches
3130 """
3131 self.set_size_inches(self.get_figwidth(), val, forward=forward)
3132
3133 def clear(self, keep_observers=False):
3134 # docstring inherited
3135 super().clear(keep_observers=keep_observers)
3136 # FigureBase.clear does not clear toolbars, as
3137 # only Figure can have toolbars
3138 toolbar = self.canvas.toolbar
3139 if toolbar is not None:
3140 toolbar.update()
3141
3142 @_finalize_rasterization
3143 @allow_rasterization
3144 def draw(self, renderer):
3145 # docstring inherited
3146 if not self.get_visible():
3147 return
3148
3149 with self._render_lock:
3150
3151 artists = self._get_draw_artists(renderer)
3152 try:
3153 renderer.open_group('figure', gid=self.get_gid())
3154 if self.axes and self.get_layout_engine() is not None:
3155 try:
3156 self.get_layout_engine().execute(self)
3157 except ValueError:
3158 pass
3159 # ValueError can occur when resizing a window.
3160
3161 self.patch.draw(renderer)
3162 mimage._draw_list_compositing_images(
3163 renderer, self, artists, self.suppressComposite)
3164
3165 renderer.close_group('figure')
3166 finally:
3167 self.stale = False
3168
3169 DrawEvent("draw_event", self.canvas, renderer)._process()
3170
3171 def draw_without_rendering(self):
3172 """
3173 Draw the figure with no output. Useful to get the final size of
3174 artists that require a draw before their size is known (e.g. text).
3175 """
3176 renderer = _get_renderer(self)
3177 with renderer._draw_disabled():
3178 self.draw(renderer)
3179
3180 def draw_artist(self, a):
3181 """
3182 Draw `.Artist` *a* only.
3183 """
3184 a.draw(self.canvas.get_renderer())
3185
3186 def __getstate__(self):
3187 state = super().__getstate__()
3188
3189 # The canvas cannot currently be pickled, but this has the benefit
3190 # of meaning that a figure can be detached from one canvas, and
3191 # re-attached to another.
3192 state.pop("canvas")
3193
3194 # discard any changes to the dpi due to pixel ratio changes
3195 state["_dpi"] = state.get('_original_dpi', state['_dpi'])
3196
3197 # add version information to the state
3198 state['__mpl_version__'] = mpl.__version__
3199
3200 # check whether the figure manager (if any) is registered with pyplot
3201 from matplotlib import _pylab_helpers
3202 if self.canvas.manager in _pylab_helpers.Gcf.figs.values():
3203 state['_restore_to_pylab'] = True
3204 return state
3205
3206 def __setstate__(self, state):
3207 version = state.pop('__mpl_version__')
3208 restore_to_pylab = state.pop('_restore_to_pylab', False)
3209
3210 if version != mpl.__version__:
3211 _api.warn_external(
3212 f"This figure was saved with matplotlib version {version} and "
3213 f"loaded with {mpl.__version__} so may not function correctly."
3214 )
3215 self.__dict__ = state
3216
3217 # re-initialise some of the unstored state information
3218 FigureCanvasBase(self) # Set self.canvas.
3219
3220 if restore_to_pylab:
3221 # lazy import to avoid circularity
3222 import matplotlib.pyplot as plt
3223 import matplotlib._pylab_helpers as pylab_helpers
3224 allnums = plt.get_fignums()
3225 num = max(allnums) + 1 if allnums else 1
3226 backend = plt._get_backend_mod()
3227 mgr = backend.new_figure_manager_given_figure(num, self)
3228 pylab_helpers.Gcf._set_new_active_manager(mgr)
3229 plt.draw_if_interactive()
3230
3231 self.stale = True
3232
3233 def add_axobserver(self, func):
3234 """Whenever the Axes state change, ``func(self)`` will be called."""
3235 # Connect a wrapper lambda and not func itself, to avoid it being
3236 # weakref-collected.
3237 self._axobservers.connect("_axes_change_event", lambda arg: func(arg))
3238
3239 def savefig(self, fname, *, transparent=None, **kwargs):
3240 """
3241 Save the current figure as an image or vector graphic to a file.
3242
3243 Call signature::
3244
3245 savefig(fname, *, transparent=None, dpi='figure', format=None,
3246 metadata=None, bbox_inches=None, pad_inches=0.1,
3247 facecolor='auto', edgecolor='auto', backend=None,
3248 **kwargs
3249 )
3250
3251 The available output formats depend on the backend being used.
3252
3253 Parameters
3254 ----------
3255 fname : str or path-like or binary file-like
3256 A path, or a Python file-like object, or
3257 possibly some backend-dependent object such as
3258 `matplotlib.backends.backend_pdf.PdfPages`.
3259
3260 If *format* is set, it determines the output format, and the file
3261 is saved as *fname*. Note that *fname* is used verbatim, and there
3262 is no attempt to make the extension, if any, of *fname* match
3263 *format*, and no extension is appended.
3264
3265 If *format* is not set, then the format is inferred from the
3266 extension of *fname*, if there is one. If *format* is not
3267 set and *fname* has no extension, then the file is saved with
3268 :rc:`savefig.format` and the appropriate extension is appended to
3269 *fname*.
3270
3271 Other Parameters
3272 ----------------
3273 transparent : bool, default: :rc:`savefig.transparent`
3274 If *True*, the Axes patches will all be transparent; the
3275 Figure patch will also be transparent unless *facecolor*
3276 and/or *edgecolor* are specified via kwargs.
3277
3278 If *False* has no effect and the color of the Axes and
3279 Figure patches are unchanged (unless the Figure patch
3280 is specified via the *facecolor* and/or *edgecolor* keyword
3281 arguments in which case those colors are used).
3282
3283 The transparency of these patches will be restored to their
3284 original values upon exit of this function.
3285
3286 This is useful, for example, for displaying
3287 a plot on top of a colored background on a web page.
3288
3289 dpi : float or 'figure', default: :rc:`savefig.dpi`
3290 The resolution in dots per inch. If 'figure', use the figure's
3291 dpi value.
3292
3293 format : str
3294 The file format, e.g. 'png', 'pdf', 'svg', ... The behavior when
3295 this is unset is documented under *fname*.
3296
3297 metadata : dict, optional
3298 Key/value pairs to store in the image metadata. The supported keys
3299 and defaults depend on the image format and backend:
3300
3301 - 'png' with Agg backend: See the parameter ``metadata`` of
3302 `~.FigureCanvasAgg.print_png`.
3303 - 'pdf' with pdf backend: See the parameter ``metadata`` of
3304 `~.backend_pdf.PdfPages`.
3305 - 'svg' with svg backend: See the parameter ``metadata`` of
3306 `~.FigureCanvasSVG.print_svg`.
3307 - 'eps' and 'ps' with PS backend: Only 'Creator' is supported.
3308
3309 Not supported for 'pgf', 'raw', and 'rgba' as those formats do not support
3310 embedding metadata.
3311 Does not currently support 'jpg', 'tiff', or 'webp', but may include
3312 embedding EXIF metadata in the future.
3313
3314 bbox_inches : str or `.Bbox`, default: :rc:`savefig.bbox`
3315 Bounding box in inches: only the given portion of the figure is
3316 saved. If 'tight', try to figure out the tight bbox of the figure.
3317
3318 pad_inches : float or 'layout', default: :rc:`savefig.pad_inches`
3319 Amount of padding in inches around the figure when bbox_inches is
3320 'tight'. If 'layout' use the padding from the constrained or
3321 compressed layout engine; ignored if one of those engines is not in
3322 use.
3323
3324 facecolor : :mpltype:`color` or 'auto', default: :rc:`savefig.facecolor`
3325 The facecolor of the figure. If 'auto', use the current figure
3326 facecolor.
3327
3328 edgecolor : :mpltype:`color` or 'auto', default: :rc:`savefig.edgecolor`
3329 The edgecolor of the figure. If 'auto', use the current figure
3330 edgecolor.
3331
3332 backend : str, optional
3333 Use a non-default backend to render the file, e.g. to render a
3334 png file with the "cairo" backend rather than the default "agg",
3335 or a pdf file with the "pgf" backend rather than the default
3336 "pdf". Note that the default backend is normally sufficient. See
3337 :ref:`the-builtin-backends` for a list of valid backends for each
3338 file format. Custom backends can be referenced as "module://...".
3339
3340 orientation : {'landscape', 'portrait'}
3341 Currently only supported by the postscript backend.
3342
3343 papertype : str
3344 One of 'letter', 'legal', 'executive', 'ledger', 'a0' through
3345 'a10', 'b0' through 'b10'. Only supported for postscript
3346 output.
3347
3348 bbox_extra_artists : list of `~matplotlib.artist.Artist`, optional
3349 A list of extra artists that will be considered when the
3350 tight bbox is calculated.
3351
3352 pil_kwargs : dict, optional
3353 Additional keyword arguments that are passed to
3354 `PIL.Image.Image.save` when saving the figure.
3355
3356 """
3357
3358 kwargs.setdefault('dpi', mpl.rcParams['savefig.dpi'])
3359 if transparent is None:
3360 transparent = mpl.rcParams['savefig.transparent']
3361
3362 with ExitStack() as stack:
3363 if transparent:
3364 def _recursively_make_subfig_transparent(exit_stack, subfig):
3365 exit_stack.enter_context(
3366 subfig.patch._cm_set(
3367 facecolor="none", edgecolor="none"))
3368 for ax in subfig.axes:
3369 exit_stack.enter_context(
3370 ax.patch._cm_set(
3371 facecolor="none", edgecolor="none"))
3372 for sub_subfig in subfig.subfigs:
3373 _recursively_make_subfig_transparent(
3374 exit_stack, sub_subfig)
3375
3376 def _recursively_make_axes_transparent(exit_stack, ax):
3377 exit_stack.enter_context(
3378 ax.patch._cm_set(facecolor="none", edgecolor="none"))
3379 for child_ax in ax.child_axes:
3380 exit_stack.enter_context(
3381 child_ax.patch._cm_set(
3382 facecolor="none", edgecolor="none"))
3383 for child_childax in ax.child_axes:
3384 _recursively_make_axes_transparent(
3385 exit_stack, child_childax)
3386
3387 kwargs.setdefault('facecolor', 'none')
3388 kwargs.setdefault('edgecolor', 'none')
3389 # set subfigure to appear transparent in printed image
3390 for subfig in self.subfigs:
3391 _recursively_make_subfig_transparent(stack, subfig)
3392 # set Axes to be transparent
3393 for ax in self.axes:
3394 _recursively_make_axes_transparent(stack, ax)
3395 self.canvas.print_figure(fname, **kwargs)
3396
3397 def ginput(self, n=1, timeout=30, show_clicks=True,
3398 mouse_add=MouseButton.LEFT,
3399 mouse_pop=MouseButton.RIGHT,
3400 mouse_stop=MouseButton.MIDDLE):
3401 """
3402 Blocking call to interact with a figure.
3403
3404 Wait until the user clicks *n* times on the figure, and return the
3405 coordinates of each click in a list.
3406
3407 There are three possible interactions:
3408
3409 - Add a point.
3410 - Remove the most recently added point.
3411 - Stop the interaction and return the points added so far.
3412
3413 The actions are assigned to mouse buttons via the arguments
3414 *mouse_add*, *mouse_pop* and *mouse_stop*.
3415
3416 Parameters
3417 ----------
3418 n : int, default: 1
3419 Number of mouse clicks to accumulate. If negative, accumulate
3420 clicks until the input is terminated manually.
3421 timeout : float, default: 30 seconds
3422 Number of seconds to wait before timing out. If zero or negative
3423 will never time out.
3424 show_clicks : bool, default: True
3425 If True, show a red cross at the location of each click.
3426 mouse_add : `.MouseButton` or None, default: `.MouseButton.LEFT`
3427 Mouse button used to add points.
3428 mouse_pop : `.MouseButton` or None, default: `.MouseButton.RIGHT`
3429 Mouse button used to remove the most recently added point.
3430 mouse_stop : `.MouseButton` or None, default: `.MouseButton.MIDDLE`
3431 Mouse button used to stop input.
3432
3433 Returns
3434 -------
3435 list of tuples
3436 A list of the clicked (x, y) coordinates.
3437
3438 Notes
3439 -----
3440 The keyboard can also be used to select points in case your mouse
3441 does not have one or more of the buttons. The delete and backspace
3442 keys act like right-clicking (i.e., remove last point), the enter key
3443 terminates input and any other key (not already used by the window
3444 manager) selects a point.
3445 """
3446 clicks = []
3447 marks = []
3448
3449 def handler(event):
3450 is_button = event.name == "button_press_event"
3451 is_key = event.name == "key_press_event"
3452 # Quit (even if not in infinite mode; this is consistent with
3453 # MATLAB and sometimes quite useful, but will require the user to
3454 # test how many points were actually returned before using data).
3455 if (is_button and event.button == mouse_stop
3456 or is_key and event.key in ["escape", "enter"]):
3457 self.canvas.stop_event_loop()
3458 # Pop last click.
3459 elif (is_button and event.button == mouse_pop
3460 or is_key and event.key in ["backspace", "delete"]):
3461 if clicks:
3462 clicks.pop()
3463 if show_clicks:
3464 marks.pop().remove()
3465 self.canvas.draw()
3466 # Add new click.
3467 elif (is_button and event.button == mouse_add
3468 # On macOS/gtk, some keys return None.
3469 or is_key and event.key is not None):
3470 if event.inaxes:
3471 clicks.append((event.xdata, event.ydata))
3472 _log.info("input %i: %f, %f",
3473 len(clicks), event.xdata, event.ydata)
3474 if show_clicks:
3475 line = mpl.lines.Line2D([event.xdata], [event.ydata],
3476 marker="+", color="r")
3477 event.inaxes.add_line(line)
3478 marks.append(line)
3479 self.canvas.draw()
3480 if len(clicks) == n and n > 0:
3481 self.canvas.stop_event_loop()
3482
3483 _blocking_input.blocking_input_loop(
3484 self, ["button_press_event", "key_press_event"], timeout, handler)
3485
3486 # Cleanup.
3487 for mark in marks:
3488 mark.remove()
3489 self.canvas.draw()
3490
3491 return clicks
3492
3493 def waitforbuttonpress(self, timeout=-1):
3494 """
3495 Blocking call to interact with the figure.
3496
3497 Wait for user input and return True if a key was pressed, False if a
3498 mouse button was pressed and None if no input was given within
3499 *timeout* seconds. Negative values deactivate *timeout*.
3500 """
3501 event = None
3502
3503 def handler(ev):
3504 nonlocal event
3505 event = ev
3506 self.canvas.stop_event_loop()
3507
3508 _blocking_input.blocking_input_loop(
3509 self, ["button_press_event", "key_press_event"], timeout, handler)
3510
3511 return None if event is None else event.name == "key_press_event"
3512
3513 def tight_layout(self, *, pad=1.08, h_pad=None, w_pad=None, rect=None):
3514 """
3515 Adjust the padding between and around subplots.
3516
3517 To exclude an artist on the Axes from the bounding box calculation
3518 that determines the subplot parameters (i.e. legend, or annotation),
3519 set ``a.set_in_layout(False)`` for that artist.
3520
3521 Parameters
3522 ----------
3523 pad : float, default: 1.08
3524 Padding between the figure edge and the edges of subplots,
3525 as a fraction of the font size.
3526 h_pad, w_pad : float, default: *pad*
3527 Padding (height/width) between edges of adjacent subplots,
3528 as a fraction of the font size.
3529 rect : tuple (left, bottom, right, top), default: (0, 0, 1, 1)
3530 A rectangle in normalized figure coordinates into which the whole
3531 subplots area (including labels) will fit.
3532
3533 See Also
3534 --------
3535 .Figure.set_layout_engine
3536 .pyplot.tight_layout
3537 """
3538 # note that here we do not permanently set the figures engine to
3539 # tight_layout but rather just perform the layout in place and remove
3540 # any previous engines.
3541 engine = TightLayoutEngine(pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect)
3542 try:
3543 previous_engine = self.get_layout_engine()
3544 self.set_layout_engine(engine)
3545 engine.execute(self)
3546 if previous_engine is not None and not isinstance(
3547 previous_engine, (TightLayoutEngine, PlaceHolderLayoutEngine)
3548 ):
3549 _api.warn_external('The figure layout has changed to tight')
3550 finally:
3551 self.set_layout_engine('none')
3552
3553
3554def figaspect(arg):
3555 """
3556 Calculate the width and height for a figure with a specified aspect ratio.
3557
3558 While the height is taken from :rc:`figure.figsize`, the width is
3559 adjusted to match the desired aspect ratio. Additionally, it is ensured
3560 that the width is in the range [4., 16.] and the height is in the range
3561 [2., 16.]. If necessary, the default height is adjusted to ensure this.
3562
3563 Parameters
3564 ----------
3565 arg : float or 2D array
3566 If a float, this defines the aspect ratio (i.e. the ratio height /
3567 width).
3568 In case of an array the aspect ratio is number of rows / number of
3569 columns, so that the array could be fitted in the figure undistorted.
3570
3571 Returns
3572 -------
3573 width, height : float
3574 The figure size in inches.
3575
3576 Notes
3577 -----
3578 If you want to create an Axes within the figure, that still preserves the
3579 aspect ratio, be sure to create it with equal width and height. See
3580 examples below.
3581
3582 Thanks to Fernando Perez for this function.
3583
3584 Examples
3585 --------
3586 Make a figure twice as tall as it is wide::
3587
3588 w, h = figaspect(2.)
3589 fig = Figure(figsize=(w, h))
3590 ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
3591 ax.imshow(A, **kwargs)
3592
3593 Make a figure with the proper aspect for an array::
3594
3595 A = rand(5, 3)
3596 w, h = figaspect(A)
3597 fig = Figure(figsize=(w, h))
3598 ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
3599 ax.imshow(A, **kwargs)
3600 """
3601
3602 isarray = hasattr(arg, 'shape') and not np.isscalar(arg)
3603
3604 # min/max sizes to respect when autoscaling. If John likes the idea, they
3605 # could become rc parameters, for now they're hardwired.
3606 figsize_min = np.array((4.0, 2.0)) # min length for width/height
3607 figsize_max = np.array((16.0, 16.0)) # max length for width/height
3608
3609 # Extract the aspect ratio of the array
3610 if isarray:
3611 nr, nc = arg.shape[:2]
3612 arr_ratio = nr / nc
3613 else:
3614 arr_ratio = arg
3615
3616 # Height of user figure defaults
3617 fig_height = mpl.rcParams['figure.figsize'][1]
3618
3619 # New size for the figure, keeping the aspect ratio of the caller
3620 newsize = np.array((fig_height / arr_ratio, fig_height))
3621
3622 # Sanity checks, don't drop either dimension below figsize_min
3623 newsize /= min(1.0, *(newsize / figsize_min))
3624
3625 # Avoid humongous windows as well
3626 newsize /= max(1.0, *(newsize / figsize_max))
3627
3628 # Finally, if we have a really funky aspect ratio, break it but respect
3629 # the min/max dimensions (we don't want figures 10 feet tall!)
3630 newsize = np.clip(newsize, figsize_min, figsize_max)
3631 return newsize