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