Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/matplotlib/figure.py: 17%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1029 statements  

1""" 

2`matplotlib.figure` implements the following classes: 

3 

4`Figure` 

5 Top level `~matplotlib.artist.Artist`, which holds all plot elements. 

6 Many methods are implemented in `FigureBase`. 

7 

8`SubFigure` 

9 A logical figure inside a figure, usually added to a figure (or parent 

10 `SubFigure`) with `Figure.add_subfigure` or `Figure.subfigures` methods 

11 (provisional API v3.4). 

12 

13Figures are typically created using pyplot methods `~.pyplot.figure`, 

14`~.pyplot.subplots`, and `~.pyplot.subplot_mosaic`. 

15 

16.. plot:: 

17 :include-source: 

18 

19 fig, ax = plt.subplots(figsize=(2, 2), facecolor='lightskyblue', 

20 layout='constrained') 

21 fig.suptitle('Figure') 

22 ax.set_title('Axes', loc='left', fontstyle='oblique', fontsize='medium') 

23 

24Some situations call for directly instantiating a `~.figure.Figure` class, 

25usually inside an application of some sort (see :ref:`user_interfaces` for a 

26list of examples) . More information about Figures can be found at 

27:ref:`figure-intro`. 

28""" 

29 

30from contextlib import ExitStack 

31import inspect 

32import itertools 

33import logging 

34from numbers import Integral 

35import threading 

36 

37import numpy as np 

38 

39import matplotlib as mpl 

40from matplotlib import _blocking_input, backend_bases, _docstring, projections 

41from matplotlib.artist import ( 

42 Artist, allow_rasterization, _finalize_rasterization) 

43from matplotlib.backend_bases import ( 

44 DrawEvent, FigureCanvasBase, NonGuiException, MouseButton, _get_renderer) 

45import matplotlib._api as _api 

46import matplotlib.cbook as cbook 

47import matplotlib.colorbar as cbar 

48import matplotlib.image as mimage 

49 

50from matplotlib.axes import Axes 

51from matplotlib.gridspec import GridSpec, SubplotParams 

52from matplotlib.layout_engine import ( 

53 ConstrainedLayoutEngine, TightLayoutEngine, LayoutEngine, 

54 PlaceHolderLayoutEngine 

55) 

56import matplotlib.legend as mlegend 

57from matplotlib.patches import Rectangle 

58from matplotlib.text import Text 

59from matplotlib.transforms import (Affine2D, Bbox, BboxTransformTo, 

60 TransformedBbox) 

61 

62_log = logging.getLogger(__name__) 

63 

64 

65def _stale_figure_callback(self, val): 

66 if self.figure: 

67 self.figure.stale = val 

68 

69 

70class _AxesStack: 

71 """ 

72 Helper class to track Axes in a figure. 

73 

74 Axes are tracked both in the order in which they have been added 

75 (``self._axes`` insertion/iteration order) and in the separate "gca" stack 

76 (which is the index to which they map in the ``self._axes`` dict). 

77 """ 

78 

79 def __init__(self): 

80 self._axes = {} # Mapping of Axes to "gca" order. 

81 self._counter = itertools.count() 

82 

83 def as_list(self): 

84 """List the Axes that have been added to the figure.""" 

85 return [*self._axes] # This relies on dict preserving order. 

86 

87 def remove(self, a): 

88 """Remove the Axes from the stack.""" 

89 self._axes.pop(a) 

90 

91 def bubble(self, a): 

92 """Move an Axes, which must already exist in the stack, to the top.""" 

93 if a not in self._axes: 

94 raise ValueError("Axes has not been added yet") 

95 self._axes[a] = next(self._counter) 

96 

97 def add(self, a): 

98 """Add an Axes to the stack, ignoring it if already present.""" 

99 if a not in self._axes: 

100 self._axes[a] = next(self._counter) 

101 

102 def current(self): 

103 """Return the active Axes, or None if the stack is empty.""" 

104 return max(self._axes, key=self._axes.__getitem__, default=None) 

105 

106 def __getstate__(self): 

107 return { 

108 **vars(self), 

109 "_counter": max(self._axes.values(), default=0) 

110 } 

111 

112 def __setstate__(self, state): 

113 next_counter = state.pop('_counter') 

114 vars(self).update(state) 

115 self._counter = itertools.count(next_counter) 

116 

117 

118class FigureBase(Artist): 

119 """ 

120 Base class for `.Figure` and `.SubFigure` containing the methods that add 

121 artists to the figure or subfigure, create Axes, etc. 

122 """ 

123 def __init__(self, **kwargs): 

124 super().__init__() 

125 # remove the non-figure artist _axes property 

126 # as it makes no sense for a figure to be _in_ an Axes 

127 # this is used by the property methods in the artist base class 

128 # which are over-ridden in this class 

129 del self._axes 

130 

131 self._suptitle = None 

132 self._supxlabel = None 

133 self._supylabel = None 

134 

135 # groupers to keep track of x, y labels and title we want to align. 

136 # see self.align_xlabels, self.align_ylabels, 

137 # self.align_titles, and axis._get_tick_boxes_siblings 

138 self._align_label_groups = { 

139 "x": cbook.Grouper(), 

140 "y": cbook.Grouper(), 

141 "title": cbook.Grouper() 

142 } 

143 

144 self._localaxes = [] # track all Axes 

145 self.artists = [] 

146 self.lines = [] 

147 self.patches = [] 

148 self.texts = [] 

149 self.images = [] 

150 self.legends = [] 

151 self.subfigs = [] 

152 self.stale = True 

153 self.suppressComposite = None 

154 self.set(**kwargs) 

155 

156 def _get_draw_artists(self, renderer): 

157 """Also runs apply_aspect""" 

158 artists = self.get_children() 

159 

160 artists.remove(self.patch) 

161 artists = sorted( 

162 (artist for artist in artists if not artist.get_animated()), 

163 key=lambda artist: artist.get_zorder()) 

164 for ax in self._localaxes: 

165 locator = ax.get_axes_locator() 

166 ax.apply_aspect(locator(ax, renderer) if locator else None) 

167 

168 for child in ax.get_children(): 

169 if hasattr(child, 'apply_aspect'): 

170 locator = child.get_axes_locator() 

171 child.apply_aspect( 

172 locator(child, renderer) if locator else None) 

173 return artists 

174 

175 def autofmt_xdate( 

176 self, bottom=0.2, rotation=30, ha='right', which='major'): 

177 """ 

178 Date ticklabels often overlap, so it is useful to rotate them 

179 and right align them. Also, a common use case is a number of 

180 subplots with shared x-axis where the x-axis is date data. The 

181 ticklabels are often long, and it helps to rotate them on the 

182 bottom subplot and turn them off on other subplots, as well as 

183 turn off xlabels. 

184 

185 Parameters 

186 ---------- 

187 bottom : float, default: 0.2 

188 The bottom of the subplots for `subplots_adjust`. 

189 rotation : float, default: 30 degrees 

190 The rotation angle of the xtick labels in degrees. 

191 ha : {'left', 'center', 'right'}, default: 'right' 

192 The horizontal alignment of the xticklabels. 

193 which : {'major', 'minor', 'both'}, default: 'major' 

194 Selects which ticklabels to rotate. 

195 """ 

196 _api.check_in_list(['major', 'minor', 'both'], which=which) 

197 allsubplots = all(ax.get_subplotspec() for ax in self.axes) 

198 if len(self.axes) == 1: 

199 for label in self.axes[0].get_xticklabels(which=which): 

200 label.set_ha(ha) 

201 label.set_rotation(rotation) 

202 else: 

203 if allsubplots: 

204 for ax in self.get_axes(): 

205 if ax.get_subplotspec().is_last_row(): 

206 for label in ax.get_xticklabels(which=which): 

207 label.set_ha(ha) 

208 label.set_rotation(rotation) 

209 else: 

210 for label in ax.get_xticklabels(which=which): 

211 label.set_visible(False) 

212 ax.set_xlabel('') 

213 

214 if allsubplots: 

215 self.subplots_adjust(bottom=bottom) 

216 self.stale = True 

217 

218 def get_children(self): 

219 """Get a list of artists contained in the figure.""" 

220 return [self.patch, 

221 *self.artists, 

222 *self._localaxes, 

223 *self.lines, 

224 *self.patches, 

225 *self.texts, 

226 *self.images, 

227 *self.legends, 

228 *self.subfigs] 

229 

230 def contains(self, mouseevent): 

231 """ 

232 Test whether the mouse event occurred on the figure. 

233 

234 Returns 

235 ------- 

236 bool, {} 

237 """ 

238 if self._different_canvas(mouseevent): 

239 return False, {} 

240 inside = self.bbox.contains(mouseevent.x, mouseevent.y) 

241 return inside, {} 

242 

243 def get_window_extent(self, renderer=None): 

244 # docstring inherited 

245 return self.bbox 

246 

247 def _suplabels(self, t, info, **kwargs): 

248 """ 

249 Add a centered %(name)s to the figure. 

250 

251 Parameters 

252 ---------- 

253 t : str 

254 The %(name)s text. 

255 x : float, default: %(x0)s 

256 The x location of the text in figure coordinates. 

257 y : float, default: %(y0)s 

258 The y location of the text in figure coordinates. 

259 horizontalalignment, ha : {'center', 'left', 'right'}, default: %(ha)s 

260 The horizontal alignment of the text relative to (*x*, *y*). 

261 verticalalignment, va : {'top', 'center', 'bottom', 'baseline'}, \ 

262default: %(va)s 

263 The vertical alignment of the text relative to (*x*, *y*). 

264 fontsize, size : default: :rc:`figure.%(rc)ssize` 

265 The font size of the text. See `.Text.set_size` for possible 

266 values. 

267 fontweight, weight : default: :rc:`figure.%(rc)sweight` 

268 The font weight of the text. See `.Text.set_weight` for possible 

269 values. 

270 

271 Returns 

272 ------- 

273 text 

274 The `.Text` instance of the %(name)s. 

275 

276 Other Parameters 

277 ---------------- 

278 fontproperties : None or dict, optional 

279 A dict of font properties. If *fontproperties* is given the 

280 default values for font size and weight are taken from the 

281 `.FontProperties` defaults. :rc:`figure.%(rc)ssize` and 

282 :rc:`figure.%(rc)sweight` are ignored in this case. 

283 

284 **kwargs 

285 Additional kwargs are `matplotlib.text.Text` properties. 

286 """ 

287 

288 x = kwargs.pop('x', None) 

289 y = kwargs.pop('y', None) 

290 if info['name'] in ['_supxlabel', '_suptitle']: 

291 autopos = y is None 

292 elif info['name'] == '_supylabel': 

293 autopos = x is None 

294 if x is None: 

295 x = info['x0'] 

296 if y is None: 

297 y = info['y0'] 

298 

299 kwargs = cbook.normalize_kwargs(kwargs, Text) 

300 kwargs.setdefault('horizontalalignment', info['ha']) 

301 kwargs.setdefault('verticalalignment', info['va']) 

302 kwargs.setdefault('rotation', info['rotation']) 

303 

304 if 'fontproperties' not in kwargs: 

305 kwargs.setdefault('fontsize', mpl.rcParams[info['size']]) 

306 kwargs.setdefault('fontweight', mpl.rcParams[info['weight']]) 

307 

308 suplab = getattr(self, info['name']) 

309 if suplab is not None: 

310 suplab.set_text(t) 

311 suplab.set_position((x, y)) 

312 suplab.set(**kwargs) 

313 else: 

314 suplab = self.text(x, y, t, **kwargs) 

315 setattr(self, info['name'], suplab) 

316 suplab._autopos = autopos 

317 self.stale = True 

318 return suplab 

319 

320 @_docstring.Substitution(x0=0.5, y0=0.98, name='suptitle', ha='center', 

321 va='top', rc='title') 

322 @_docstring.copy(_suplabels) 

323 def suptitle(self, t, **kwargs): 

324 # docstring from _suplabels... 

325 info = {'name': '_suptitle', 'x0': 0.5, 'y0': 0.98, 

326 'ha': 'center', 'va': 'top', 'rotation': 0, 

327 'size': 'figure.titlesize', 'weight': 'figure.titleweight'} 

328 return self._suplabels(t, info, **kwargs) 

329 

330 def get_suptitle(self): 

331 """Return the suptitle as string or an empty string if not set.""" 

332 text_obj = self._suptitle 

333 return "" if text_obj is None else text_obj.get_text() 

334 

335 @_docstring.Substitution(x0=0.5, y0=0.01, name='supxlabel', ha='center', 

336 va='bottom', rc='label') 

337 @_docstring.copy(_suplabels) 

338 def supxlabel(self, t, **kwargs): 

339 # docstring from _suplabels... 

340 info = {'name': '_supxlabel', 'x0': 0.5, 'y0': 0.01, 

341 'ha': 'center', 'va': 'bottom', 'rotation': 0, 

342 'size': 'figure.labelsize', 'weight': 'figure.labelweight'} 

343 return self._suplabels(t, info, **kwargs) 

344 

345 def get_supxlabel(self): 

346 """Return the supxlabel as string or an empty string if not set.""" 

347 text_obj = self._supxlabel 

348 return "" if text_obj is None else text_obj.get_text() 

349 

350 @_docstring.Substitution(x0=0.02, y0=0.5, name='supylabel', ha='left', 

351 va='center', rc='label') 

352 @_docstring.copy(_suplabels) 

353 def supylabel(self, t, **kwargs): 

354 # docstring from _suplabels... 

355 info = {'name': '_supylabel', 'x0': 0.02, 'y0': 0.5, 

356 'ha': 'left', 'va': 'center', 'rotation': 'vertical', 

357 'rotation_mode': 'anchor', 'size': 'figure.labelsize', 

358 'weight': 'figure.labelweight'} 

359 return self._suplabels(t, info, **kwargs) 

360 

361 def get_supylabel(self): 

362 """Return the supylabel as string or an empty string if not set.""" 

363 text_obj = self._supylabel 

364 return "" if text_obj is None else text_obj.get_text() 

365 

366 def get_edgecolor(self): 

367 """Get the edge color of the Figure rectangle.""" 

368 return self.patch.get_edgecolor() 

369 

370 def get_facecolor(self): 

371 """Get the face color of the Figure rectangle.""" 

372 return self.patch.get_facecolor() 

373 

374 def get_frameon(self): 

375 """ 

376 Return the figure's background patch visibility, i.e. 

377 whether the figure background will be drawn. Equivalent to 

378 ``Figure.patch.get_visible()``. 

379 """ 

380 return self.patch.get_visible() 

381 

382 def set_linewidth(self, linewidth): 

383 """ 

384 Set the line width of the Figure rectangle. 

385 

386 Parameters 

387 ---------- 

388 linewidth : number 

389 """ 

390 self.patch.set_linewidth(linewidth) 

391 

392 def get_linewidth(self): 

393 """ 

394 Get the line width of the Figure rectangle. 

395 """ 

396 return self.patch.get_linewidth() 

397 

398 def set_edgecolor(self, color): 

399 """ 

400 Set the edge color of the Figure rectangle. 

401 

402 Parameters 

403 ---------- 

404 color : :mpltype:`color` 

405 """ 

406 self.patch.set_edgecolor(color) 

407 

408 def set_facecolor(self, color): 

409 """ 

410 Set the face color of the Figure rectangle. 

411 

412 Parameters 

413 ---------- 

414 color : :mpltype:`color` 

415 """ 

416 self.patch.set_facecolor(color) 

417 

418 def set_frameon(self, b): 

419 """ 

420 Set the figure's background patch visibility, i.e. 

421 whether the figure background will be drawn. Equivalent to 

422 ``Figure.patch.set_visible()``. 

423 

424 Parameters 

425 ---------- 

426 b : bool 

427 """ 

428 self.patch.set_visible(b) 

429 self.stale = True 

430 

431 frameon = property(get_frameon, set_frameon) 

432 

433 def add_artist(self, artist, clip=False): 

434 """ 

435 Add an `.Artist` to the figure. 

436 

437 Usually artists are added to `~.axes.Axes` objects using 

438 `.Axes.add_artist`; this method can be used in the rare cases where 

439 one needs to add artists directly to the figure instead. 

440 

441 Parameters 

442 ---------- 

443 artist : `~matplotlib.artist.Artist` 

444 The artist to add to the figure. If the added artist has no 

445 transform previously set, its transform will be set to 

446 ``figure.transSubfigure``. 

447 clip : bool, default: False 

448 Whether the added artist should be clipped by the figure patch. 

449 

450 Returns 

451 ------- 

452 `~matplotlib.artist.Artist` 

453 The added artist. 

454 """ 

455 artist.set_figure(self) 

456 self.artists.append(artist) 

457 artist._remove_method = self.artists.remove 

458 

459 if not artist.is_transform_set(): 

460 artist.set_transform(self.transSubfigure) 

461 

462 if clip and artist.get_clip_path() is None: 

463 artist.set_clip_path(self.patch) 

464 

465 self.stale = True 

466 return artist 

467 

468 @_docstring.dedent_interpd 

469 def add_axes(self, *args, **kwargs): 

470 """ 

471 Add an `~.axes.Axes` to the figure. 

472 

473 Call signatures:: 

474 

475 add_axes(rect, projection=None, polar=False, **kwargs) 

476 add_axes(ax) 

477 

478 Parameters 

479 ---------- 

480 rect : tuple (left, bottom, width, height) 

481 The dimensions (left, bottom, width, height) of the new 

482 `~.axes.Axes`. All quantities are in fractions of figure width and 

483 height. 

484 

485 projection : {None, 'aitoff', 'hammer', 'lambert', 'mollweide', \ 

486'polar', 'rectilinear', str}, optional 

487 The projection type of the `~.axes.Axes`. *str* is the name of 

488 a custom projection, see `~matplotlib.projections`. The default 

489 None results in a 'rectilinear' projection. 

490 

491 polar : bool, default: False 

492 If True, equivalent to projection='polar'. 

493 

494 axes_class : subclass type of `~.axes.Axes`, optional 

495 The `.axes.Axes` subclass that is instantiated. This parameter 

496 is incompatible with *projection* and *polar*. See 

497 :ref:`axisartist_users-guide-index` for examples. 

498 

499 sharex, sharey : `~matplotlib.axes.Axes`, optional 

500 Share the x or y `~matplotlib.axis` with sharex and/or sharey. 

501 The axis will have the same limits, ticks, and scale as the axis 

502 of the shared Axes. 

503 

504 label : str 

505 A label for the returned Axes. 

506 

507 Returns 

508 ------- 

509 `~.axes.Axes`, or a subclass of `~.axes.Axes` 

510 The returned Axes class depends on the projection used. It is 

511 `~.axes.Axes` if rectilinear projection is used and 

512 `.projections.polar.PolarAxes` if polar projection is used. 

513 

514 Other Parameters 

515 ---------------- 

516 **kwargs 

517 This method also takes the keyword arguments for 

518 the returned Axes class. The keyword arguments for the 

519 rectilinear Axes class `~.axes.Axes` can be found in 

520 the following table but there might also be other keyword 

521 arguments if another projection is used, see the actual Axes 

522 class. 

523 

524 %(Axes:kwdoc)s 

525 

526 Notes 

527 ----- 

528 In rare circumstances, `.add_axes` may be called with a single 

529 argument, an Axes instance already created in the present figure but 

530 not in the figure's list of Axes. 

531 

532 See Also 

533 -------- 

534 .Figure.add_subplot 

535 .pyplot.subplot 

536 .pyplot.axes 

537 .Figure.subplots 

538 .pyplot.subplots 

539 

540 Examples 

541 -------- 

542 Some simple examples:: 

543 

544 rect = l, b, w, h 

545 fig = plt.figure() 

546 fig.add_axes(rect) 

547 fig.add_axes(rect, frameon=False, facecolor='g') 

548 fig.add_axes(rect, polar=True) 

549 ax = fig.add_axes(rect, projection='polar') 

550 fig.delaxes(ax) 

551 fig.add_axes(ax) 

552 """ 

553 

554 if not len(args) and 'rect' not in kwargs: 

555 raise TypeError( 

556 "add_axes() missing 1 required positional argument: 'rect'") 

557 elif 'rect' in kwargs: 

558 if len(args): 

559 raise TypeError( 

560 "add_axes() got multiple values for argument 'rect'") 

561 args = (kwargs.pop('rect'), ) 

562 

563 if isinstance(args[0], Axes): 

564 a, *extra_args = args 

565 key = a._projection_init 

566 if a.get_figure() is not self: 

567 raise ValueError( 

568 "The Axes must have been created in the present figure") 

569 else: 

570 rect, *extra_args = args 

571 if not np.isfinite(rect).all(): 

572 raise ValueError(f'all entries in rect must be finite not {rect}') 

573 projection_class, pkw = self._process_projection_requirements(**kwargs) 

574 

575 # create the new Axes using the Axes class given 

576 a = projection_class(self, rect, **pkw) 

577 key = (projection_class, pkw) 

578 

579 if extra_args: 

580 _api.warn_deprecated( 

581 "3.8", 

582 name="Passing more than one positional argument to Figure.add_axes", 

583 addendum="Any additional positional arguments are currently ignored.") 

584 return self._add_axes_internal(a, key) 

585 

586 @_docstring.dedent_interpd 

587 def add_subplot(self, *args, **kwargs): 

588 """ 

589 Add an `~.axes.Axes` to the figure as part of a subplot arrangement. 

590 

591 Call signatures:: 

592 

593 add_subplot(nrows, ncols, index, **kwargs) 

594 add_subplot(pos, **kwargs) 

595 add_subplot(ax) 

596 add_subplot() 

597 

598 Parameters 

599 ---------- 

600 *args : int, (int, int, *index*), or `.SubplotSpec`, default: (1, 1, 1) 

601 The position of the subplot described by one of 

602 

603 - Three integers (*nrows*, *ncols*, *index*). The subplot will 

604 take the *index* position on a grid with *nrows* rows and 

605 *ncols* columns. *index* starts at 1 in the upper left corner 

606 and increases to the right. *index* can also be a two-tuple 

607 specifying the (*first*, *last*) indices (1-based, and including 

608 *last*) of the subplot, e.g., ``fig.add_subplot(3, 1, (1, 2))`` 

609 makes a subplot that spans the upper 2/3 of the figure. 

610 - A 3-digit integer. The digits are interpreted as if given 

611 separately as three single-digit integers, i.e. 

612 ``fig.add_subplot(235)`` is the same as 

613 ``fig.add_subplot(2, 3, 5)``. Note that this can only be used 

614 if there are no more than 9 subplots. 

615 - A `.SubplotSpec`. 

616 

617 In rare circumstances, `.add_subplot` may be called with a single 

618 argument, a subplot Axes instance already created in the 

619 present figure but not in the figure's list of Axes. 

620 

621 projection : {None, 'aitoff', 'hammer', 'lambert', 'mollweide', \ 

622'polar', 'rectilinear', str}, optional 

623 The projection type of the subplot (`~.axes.Axes`). *str* is the 

624 name of a custom projection, see `~matplotlib.projections`. The 

625 default None results in a 'rectilinear' projection. 

626 

627 polar : bool, default: False 

628 If True, equivalent to projection='polar'. 

629 

630 axes_class : subclass type of `~.axes.Axes`, optional 

631 The `.axes.Axes` subclass that is instantiated. This parameter 

632 is incompatible with *projection* and *polar*. See 

633 :ref:`axisartist_users-guide-index` for examples. 

634 

635 sharex, sharey : `~matplotlib.axes.Axes`, optional 

636 Share the x or y `~matplotlib.axis` with sharex and/or sharey. 

637 The axis will have the same limits, ticks, and scale as the axis 

638 of the shared Axes. 

639 

640 label : str 

641 A label for the returned Axes. 

642 

643 Returns 

644 ------- 

645 `~.axes.Axes` 

646 

647 The Axes of the subplot. The returned Axes can actually be an 

648 instance of a subclass, such as `.projections.polar.PolarAxes` for 

649 polar projections. 

650 

651 Other Parameters 

652 ---------------- 

653 **kwargs 

654 This method also takes the keyword arguments for the returned Axes 

655 base class; except for the *figure* argument. The keyword arguments 

656 for the rectilinear base class `~.axes.Axes` can be found in 

657 the following table but there might also be other keyword 

658 arguments if another projection is used. 

659 

660 %(Axes:kwdoc)s 

661 

662 See Also 

663 -------- 

664 .Figure.add_axes 

665 .pyplot.subplot 

666 .pyplot.axes 

667 .Figure.subplots 

668 .pyplot.subplots 

669 

670 Examples 

671 -------- 

672 :: 

673 

674 fig = plt.figure() 

675 

676 fig.add_subplot(231) 

677 ax1 = fig.add_subplot(2, 3, 1) # equivalent but more general 

678 

679 fig.add_subplot(232, frameon=False) # subplot with no frame 

680 fig.add_subplot(233, projection='polar') # polar subplot 

681 fig.add_subplot(234, sharex=ax1) # subplot sharing x-axis with ax1 

682 fig.add_subplot(235, facecolor="red") # red subplot 

683 

684 ax1.remove() # delete ax1 from the figure 

685 fig.add_subplot(ax1) # add ax1 back to the figure 

686 """ 

687 if 'figure' in kwargs: 

688 # Axes itself allows for a 'figure' kwarg, but since we want to 

689 # bind the created Axes to self, it is not allowed here. 

690 raise _api.kwarg_error("add_subplot", "figure") 

691 

692 if (len(args) == 1 

693 and isinstance(args[0], mpl.axes._base._AxesBase) 

694 and args[0].get_subplotspec()): 

695 ax = args[0] 

696 key = ax._projection_init 

697 if ax.get_figure() is not self: 

698 raise ValueError("The Axes must have been created in " 

699 "the present figure") 

700 else: 

701 if not args: 

702 args = (1, 1, 1) 

703 # Normalize correct ijk values to (i, j, k) here so that 

704 # add_subplot(211) == add_subplot(2, 1, 1). Invalid values will 

705 # trigger errors later (via SubplotSpec._from_subplot_args). 

706 if (len(args) == 1 and isinstance(args[0], Integral) 

707 and 100 <= args[0] <= 999): 

708 args = tuple(map(int, str(args[0]))) 

709 projection_class, pkw = self._process_projection_requirements(**kwargs) 

710 ax = projection_class(self, *args, **pkw) 

711 key = (projection_class, pkw) 

712 return self._add_axes_internal(ax, key) 

713 

714 def _add_axes_internal(self, ax, key): 

715 """Private helper for `add_axes` and `add_subplot`.""" 

716 self._axstack.add(ax) 

717 if ax not in self._localaxes: 

718 self._localaxes.append(ax) 

719 self.sca(ax) 

720 ax._remove_method = self.delaxes 

721 # this is to support plt.subplot's re-selection logic 

722 ax._projection_init = key 

723 self.stale = True 

724 ax.stale_callback = _stale_figure_callback 

725 return ax 

726 

727 def subplots(self, nrows=1, ncols=1, *, sharex=False, sharey=False, 

728 squeeze=True, width_ratios=None, height_ratios=None, 

729 subplot_kw=None, gridspec_kw=None): 

730 """ 

731 Add a set of subplots to this figure. 

732 

733 This utility wrapper makes it convenient to create common layouts of 

734 subplots in a single call. 

735 

736 Parameters 

737 ---------- 

738 nrows, ncols : int, default: 1 

739 Number of rows/columns of the subplot grid. 

740 

741 sharex, sharey : bool or {'none', 'all', 'row', 'col'}, default: False 

742 Controls sharing of x-axis (*sharex*) or y-axis (*sharey*): 

743 

744 - True or 'all': x- or y-axis will be shared among all subplots. 

745 - False or 'none': each subplot x- or y-axis will be independent. 

746 - 'row': each subplot row will share an x- or y-axis. 

747 - 'col': each subplot column will share an x- or y-axis. 

748 

749 When subplots have a shared x-axis along a column, only the x tick 

750 labels of the bottom subplot are created. Similarly, when subplots 

751 have a shared y-axis along a row, only the y tick labels of the 

752 first column subplot are created. To later turn other subplots' 

753 ticklabels on, use `~matplotlib.axes.Axes.tick_params`. 

754 

755 When subplots have a shared axis that has units, calling 

756 `.Axis.set_units` will update each axis with the new units. 

757 

758 Note that it is not possible to unshare axes. 

759 

760 squeeze : bool, default: True 

761 - If True, extra dimensions are squeezed out from the returned 

762 array of Axes: 

763 

764 - if only one subplot is constructed (nrows=ncols=1), the 

765 resulting single Axes object is returned as a scalar. 

766 - for Nx1 or 1xM subplots, the returned object is a 1D numpy 

767 object array of Axes objects. 

768 - for NxM, subplots with N>1 and M>1 are returned as a 2D array. 

769 

770 - If False, no squeezing at all is done: the returned Axes object 

771 is always a 2D array containing Axes instances, even if it ends 

772 up being 1x1. 

773 

774 width_ratios : array-like of length *ncols*, optional 

775 Defines the relative widths of the columns. Each column gets a 

776 relative width of ``width_ratios[i] / sum(width_ratios)``. 

777 If not given, all columns will have the same width. Equivalent 

778 to ``gridspec_kw={'width_ratios': [...]}``. 

779 

780 height_ratios : array-like of length *nrows*, optional 

781 Defines the relative heights of the rows. Each row gets a 

782 relative height of ``height_ratios[i] / sum(height_ratios)``. 

783 If not given, all rows will have the same height. Equivalent 

784 to ``gridspec_kw={'height_ratios': [...]}``. 

785 

786 subplot_kw : dict, optional 

787 Dict with keywords passed to the `.Figure.add_subplot` call used to 

788 create each subplot. 

789 

790 gridspec_kw : dict, optional 

791 Dict with keywords passed to the 

792 `~matplotlib.gridspec.GridSpec` constructor used to create 

793 the grid the subplots are placed on. 

794 

795 Returns 

796 ------- 

797 `~.axes.Axes` or array of Axes 

798 Either a single `~matplotlib.axes.Axes` object or an array of Axes 

799 objects if more than one subplot was created. The dimensions of the 

800 resulting array can be controlled with the *squeeze* keyword, see 

801 above. 

802 

803 See Also 

804 -------- 

805 .pyplot.subplots 

806 .Figure.add_subplot 

807 .pyplot.subplot 

808 

809 Examples 

810 -------- 

811 :: 

812 

813 # First create some toy data: 

814 x = np.linspace(0, 2*np.pi, 400) 

815 y = np.sin(x**2) 

816 

817 # Create a figure 

818 fig = plt.figure() 

819 

820 # Create a subplot 

821 ax = fig.subplots() 

822 ax.plot(x, y) 

823 ax.set_title('Simple plot') 

824 

825 # Create two subplots and unpack the output array immediately 

826 ax1, ax2 = fig.subplots(1, 2, sharey=True) 

827 ax1.plot(x, y) 

828 ax1.set_title('Sharing Y axis') 

829 ax2.scatter(x, y) 

830 

831 # Create four polar Axes and access them through the returned array 

832 axes = fig.subplots(2, 2, subplot_kw=dict(projection='polar')) 

833 axes[0, 0].plot(x, y) 

834 axes[1, 1].scatter(x, y) 

835 

836 # Share an X-axis with each column of subplots 

837 fig.subplots(2, 2, sharex='col') 

838 

839 # Share a Y-axis with each row of subplots 

840 fig.subplots(2, 2, sharey='row') 

841 

842 # Share both X- and Y-axes with all subplots 

843 fig.subplots(2, 2, sharex='all', sharey='all') 

844 

845 # Note that this is the same as 

846 fig.subplots(2, 2, sharex=True, sharey=True) 

847 """ 

848 gridspec_kw = dict(gridspec_kw or {}) 

849 if height_ratios is not None: 

850 if 'height_ratios' in gridspec_kw: 

851 raise ValueError("'height_ratios' must not be defined both as " 

852 "parameter and as key in 'gridspec_kw'") 

853 gridspec_kw['height_ratios'] = height_ratios 

854 if width_ratios is not None: 

855 if 'width_ratios' in gridspec_kw: 

856 raise ValueError("'width_ratios' must not be defined both as " 

857 "parameter and as key in 'gridspec_kw'") 

858 gridspec_kw['width_ratios'] = width_ratios 

859 

860 gs = self.add_gridspec(nrows, ncols, figure=self, **gridspec_kw) 

861 axs = gs.subplots(sharex=sharex, sharey=sharey, squeeze=squeeze, 

862 subplot_kw=subplot_kw) 

863 return axs 

864 

865 def delaxes(self, ax): 

866 """ 

867 Remove the `~.axes.Axes` *ax* from the figure; update the current Axes. 

868 """ 

869 self._remove_axes(ax, owners=[self._axstack, self._localaxes]) 

870 

871 def _remove_axes(self, ax, owners): 

872 """ 

873 Common helper for removal of standard Axes (via delaxes) and of child Axes. 

874 

875 Parameters 

876 ---------- 

877 ax : `~.AxesBase` 

878 The Axes to remove. 

879 owners 

880 List of objects (list or _AxesStack) "owning" the Axes, from which the Axes 

881 will be remove()d. 

882 """ 

883 for owner in owners: 

884 owner.remove(ax) 

885 

886 self._axobservers.process("_axes_change_event", self) 

887 self.stale = True 

888 self.canvas.release_mouse(ax) 

889 

890 for name in ax._axis_names: # Break link between any shared Axes 

891 grouper = ax._shared_axes[name] 

892 siblings = [other for other in grouper.get_siblings(ax) if other is not ax] 

893 if not siblings: # Axes was not shared along this axis; we're done. 

894 continue 

895 grouper.remove(ax) 

896 # Formatters and locators may previously have been associated with the now 

897 # removed axis. Update them to point to an axis still there (we can pick 

898 # any of them, and use the first sibling). 

899 remaining_axis = siblings[0]._axis_map[name] 

900 remaining_axis.get_major_formatter().set_axis(remaining_axis) 

901 remaining_axis.get_major_locator().set_axis(remaining_axis) 

902 remaining_axis.get_minor_formatter().set_axis(remaining_axis) 

903 remaining_axis.get_minor_locator().set_axis(remaining_axis) 

904 

905 ax._twinned_axes.remove(ax) # Break link between any twinned Axes. 

906 

907 def clear(self, keep_observers=False): 

908 """ 

909 Clear the figure. 

910 

911 Parameters 

912 ---------- 

913 keep_observers : bool, default: False 

914 Set *keep_observers* to True if, for example, 

915 a gui widget is tracking the Axes in the figure. 

916 """ 

917 self.suppressComposite = None 

918 

919 # first clear the Axes in any subfigures 

920 for subfig in self.subfigs: 

921 subfig.clear(keep_observers=keep_observers) 

922 self.subfigs = [] 

923 

924 for ax in tuple(self.axes): # Iterate over the copy. 

925 ax.clear() 

926 self.delaxes(ax) # Remove ax from self._axstack. 

927 

928 self.artists = [] 

929 self.lines = [] 

930 self.patches = [] 

931 self.texts = [] 

932 self.images = [] 

933 self.legends = [] 

934 if not keep_observers: 

935 self._axobservers = cbook.CallbackRegistry() 

936 self._suptitle = None 

937 self._supxlabel = None 

938 self._supylabel = None 

939 

940 self.stale = True 

941 

942 # synonym for `clear`. 

943 def clf(self, keep_observers=False): 

944 """ 

945 [*Discouraged*] Alias for the `clear()` method. 

946 

947 .. admonition:: Discouraged 

948 

949 The use of ``clf()`` is discouraged. Use ``clear()`` instead. 

950 

951 Parameters 

952 ---------- 

953 keep_observers : bool, default: False 

954 Set *keep_observers* to True if, for example, 

955 a gui widget is tracking the Axes in the figure. 

956 """ 

957 return self.clear(keep_observers=keep_observers) 

958 

959 # Note: the docstring below is modified with replace for the pyplot 

960 # version of this function because the method name differs (plt.figlegend) 

961 # the replacements are: 

962 # " legend(" -> " figlegend(" for the signatures 

963 # "fig.legend(" -> "plt.figlegend" for the code examples 

964 # "ax.plot" -> "plt.plot" for consistency in using pyplot when able 

965 @_docstring.dedent_interpd 

966 def legend(self, *args, **kwargs): 

967 """ 

968 Place a legend on the figure. 

969 

970 Call signatures:: 

971 

972 legend() 

973 legend(handles, labels) 

974 legend(handles=handles) 

975 legend(labels) 

976 

977 The call signatures correspond to the following different ways to use 

978 this method: 

979 

980 **1. Automatic detection of elements to be shown in the legend** 

981 

982 The elements to be added to the legend are automatically determined, 

983 when you do not pass in any extra arguments. 

984 

985 In this case, the labels are taken from the artist. You can specify 

986 them either at artist creation or by calling the 

987 :meth:`~.Artist.set_label` method on the artist:: 

988 

989 ax.plot([1, 2, 3], label='Inline label') 

990 fig.legend() 

991 

992 or:: 

993 

994 line, = ax.plot([1, 2, 3]) 

995 line.set_label('Label via method') 

996 fig.legend() 

997 

998 Specific lines can be excluded from the automatic legend element 

999 selection by defining a label starting with an underscore. 

1000 This is default for all artists, so calling `.Figure.legend` without 

1001 any arguments and without setting the labels manually will result in 

1002 no legend being drawn. 

1003 

1004 

1005 **2. Explicitly listing the artists and labels in the legend** 

1006 

1007 For full control of which artists have a legend entry, it is possible 

1008 to pass an iterable of legend artists followed by an iterable of 

1009 legend labels respectively:: 

1010 

1011 fig.legend([line1, line2, line3], ['label1', 'label2', 'label3']) 

1012 

1013 

1014 **3. Explicitly listing the artists in the legend** 

1015 

1016 This is similar to 2, but the labels are taken from the artists' 

1017 label properties. Example:: 

1018 

1019 line1, = ax1.plot([1, 2, 3], label='label1') 

1020 line2, = ax2.plot([1, 2, 3], label='label2') 

1021 fig.legend(handles=[line1, line2]) 

1022 

1023 

1024 **4. Labeling existing plot elements** 

1025 

1026 .. admonition:: Discouraged 

1027 

1028 This call signature is discouraged, because the relation between 

1029 plot elements and labels is only implicit by their order and can 

1030 easily be mixed up. 

1031 

1032 To make a legend for all artists on all Axes, call this function with 

1033 an iterable of strings, one for each legend item. For example:: 

1034 

1035 fig, (ax1, ax2) = plt.subplots(1, 2) 

1036 ax1.plot([1, 3, 5], color='blue') 

1037 ax2.plot([2, 4, 6], color='red') 

1038 fig.legend(['the blues', 'the reds']) 

1039 

1040 

1041 Parameters 

1042 ---------- 

1043 handles : list of `.Artist`, optional 

1044 A list of Artists (lines, patches) to be added to the legend. 

1045 Use this together with *labels*, if you need full control on what 

1046 is shown in the legend and the automatic mechanism described above 

1047 is not sufficient. 

1048 

1049 The length of handles and labels should be the same in this 

1050 case. If they are not, they are truncated to the smaller length. 

1051 

1052 labels : list of str, optional 

1053 A list of labels to show next to the artists. 

1054 Use this together with *handles*, if you need full control on what 

1055 is shown in the legend and the automatic mechanism described above 

1056 is not sufficient. 

1057 

1058 Returns 

1059 ------- 

1060 `~matplotlib.legend.Legend` 

1061 

1062 Other Parameters 

1063 ---------------- 

1064 %(_legend_kw_figure)s 

1065 

1066 See Also 

1067 -------- 

1068 .Axes.legend 

1069 

1070 Notes 

1071 ----- 

1072 Some artists are not supported by this function. See 

1073 :ref:`legend_guide` for details. 

1074 """ 

1075 

1076 handles, labels, kwargs = mlegend._parse_legend_args(self.axes, *args, **kwargs) 

1077 # explicitly set the bbox transform if the user hasn't. 

1078 kwargs.setdefault("bbox_transform", self.transSubfigure) 

1079 l = mlegend.Legend(self, handles, labels, **kwargs) 

1080 self.legends.append(l) 

1081 l._remove_method = self.legends.remove 

1082 self.stale = True 

1083 return l 

1084 

1085 @_docstring.dedent_interpd 

1086 def text(self, x, y, s, fontdict=None, **kwargs): 

1087 """ 

1088 Add text to figure. 

1089 

1090 Parameters 

1091 ---------- 

1092 x, y : float 

1093 The position to place the text. By default, this is in figure 

1094 coordinates, floats in [0, 1]. The coordinate system can be changed 

1095 using the *transform* keyword. 

1096 

1097 s : str 

1098 The text string. 

1099 

1100 fontdict : dict, optional 

1101 A dictionary to override the default text properties. If not given, 

1102 the defaults are determined by :rc:`font.*`. Properties passed as 

1103 *kwargs* override the corresponding ones given in *fontdict*. 

1104 

1105 Returns 

1106 ------- 

1107 `~.text.Text` 

1108 

1109 Other Parameters 

1110 ---------------- 

1111 **kwargs : `~matplotlib.text.Text` properties 

1112 Other miscellaneous text parameters. 

1113 

1114 %(Text:kwdoc)s 

1115 

1116 See Also 

1117 -------- 

1118 .Axes.text 

1119 .pyplot.text 

1120 """ 

1121 effective_kwargs = { 

1122 'transform': self.transSubfigure, 

1123 **(fontdict if fontdict is not None else {}), 

1124 **kwargs, 

1125 } 

1126 text = Text(x=x, y=y, text=s, **effective_kwargs) 

1127 text.set_figure(self) 

1128 text.stale_callback = _stale_figure_callback 

1129 

1130 self.texts.append(text) 

1131 text._remove_method = self.texts.remove 

1132 self.stale = True 

1133 return text 

1134 

1135 @_docstring.dedent_interpd 

1136 def colorbar( 

1137 self, mappable, cax=None, ax=None, use_gridspec=True, **kwargs): 

1138 """ 

1139 Add a colorbar to a plot. 

1140 

1141 Parameters 

1142 ---------- 

1143 mappable 

1144 The `matplotlib.cm.ScalarMappable` (i.e., `.AxesImage`, 

1145 `.ContourSet`, etc.) described by this colorbar. This argument is 

1146 mandatory for the `.Figure.colorbar` method but optional for the 

1147 `.pyplot.colorbar` function, which sets the default to the current 

1148 image. 

1149 

1150 Note that one can create a `.ScalarMappable` "on-the-fly" to 

1151 generate colorbars not attached to a previously drawn artist, e.g. 

1152 :: 

1153 

1154 fig.colorbar(cm.ScalarMappable(norm=norm, cmap=cmap), ax=ax) 

1155 

1156 cax : `~matplotlib.axes.Axes`, optional 

1157 Axes into which the colorbar will be drawn. If `None`, then a new 

1158 Axes is created and the space for it will be stolen from the Axes(s) 

1159 specified in *ax*. 

1160 

1161 ax : `~matplotlib.axes.Axes` or iterable or `numpy.ndarray` of Axes, optional 

1162 The one or more parent Axes from which space for a new colorbar Axes 

1163 will be stolen. This parameter is only used if *cax* is not set. 

1164 

1165 Defaults to the Axes that contains the mappable used to create the 

1166 colorbar. 

1167 

1168 use_gridspec : bool, optional 

1169 If *cax* is ``None``, a new *cax* is created as an instance of 

1170 Axes. If *ax* is positioned with a subplotspec and *use_gridspec* 

1171 is ``True``, then *cax* is also positioned with a subplotspec. 

1172 

1173 Returns 

1174 ------- 

1175 colorbar : `~matplotlib.colorbar.Colorbar` 

1176 

1177 Other Parameters 

1178 ---------------- 

1179 %(_make_axes_kw_doc)s 

1180 %(_colormap_kw_doc)s 

1181 

1182 Notes 

1183 ----- 

1184 If *mappable* is a `~.contour.ContourSet`, its *extend* kwarg is 

1185 included automatically. 

1186 

1187 The *shrink* kwarg provides a simple way to scale the colorbar with 

1188 respect to the Axes. Note that if *cax* is specified, it determines the 

1189 size of the colorbar, and *shrink* and *aspect* are ignored. 

1190 

1191 For more precise control, you can manually specify the positions of the 

1192 axes objects in which the mappable and the colorbar are drawn. In this 

1193 case, do not use any of the Axes properties kwargs. 

1194 

1195 It is known that some vector graphics viewers (svg and pdf) render 

1196 white gaps between segments of the colorbar. This is due to bugs in 

1197 the viewers, not Matplotlib. As a workaround, the colorbar can be 

1198 rendered with overlapping segments:: 

1199 

1200 cbar = colorbar() 

1201 cbar.solids.set_edgecolor("face") 

1202 draw() 

1203 

1204 However, this has negative consequences in other circumstances, e.g. 

1205 with semi-transparent images (alpha < 1) and colorbar extensions; 

1206 therefore, this workaround is not used by default (see issue #1188). 

1207 

1208 """ 

1209 

1210 if ax is None: 

1211 ax = getattr(mappable, "axes", None) 

1212 

1213 if cax is None: 

1214 if ax is None: 

1215 raise ValueError( 

1216 'Unable to determine Axes to steal space for Colorbar. ' 

1217 'Either provide the *cax* argument to use as the Axes for ' 

1218 'the Colorbar, provide the *ax* argument to steal space ' 

1219 'from it, or add *mappable* to an Axes.') 

1220 fig = ( # Figure of first Axes; logic copied from make_axes. 

1221 [*ax.flat] if isinstance(ax, np.ndarray) 

1222 else [*ax] if np.iterable(ax) 

1223 else [ax])[0].figure 

1224 current_ax = fig.gca() 

1225 if (fig.get_layout_engine() is not None and 

1226 not fig.get_layout_engine().colorbar_gridspec): 

1227 use_gridspec = False 

1228 if (use_gridspec 

1229 and isinstance(ax, mpl.axes._base._AxesBase) 

1230 and ax.get_subplotspec()): 

1231 cax, kwargs = cbar.make_axes_gridspec(ax, **kwargs) 

1232 else: 

1233 cax, kwargs = cbar.make_axes(ax, **kwargs) 

1234 # make_axes calls add_{axes,subplot} which changes gca; undo that. 

1235 fig.sca(current_ax) 

1236 cax.grid(visible=False, which='both', axis='both') 

1237 

1238 if hasattr(mappable, "figure") and mappable.figure is not None: 

1239 # Get top level artists 

1240 mappable_host_fig = mappable.figure 

1241 if isinstance(mappable_host_fig, mpl.figure.SubFigure): 

1242 mappable_host_fig = mappable_host_fig.figure 

1243 # Warn in case of mismatch 

1244 if mappable_host_fig is not self.figure: 

1245 _api.warn_external( 

1246 f'Adding colorbar to a different Figure ' 

1247 f'{repr(mappable.figure)} than ' 

1248 f'{repr(self.figure)} which ' 

1249 f'fig.colorbar is called on.') 

1250 

1251 NON_COLORBAR_KEYS = [ # remove kws that cannot be passed to Colorbar 

1252 'fraction', 'pad', 'shrink', 'aspect', 'anchor', 'panchor'] 

1253 cb = cbar.Colorbar(cax, mappable, **{ 

1254 k: v for k, v in kwargs.items() if k not in NON_COLORBAR_KEYS}) 

1255 cax.figure.stale = True 

1256 return cb 

1257 

1258 def subplots_adjust(self, left=None, bottom=None, right=None, top=None, 

1259 wspace=None, hspace=None): 

1260 """ 

1261 Adjust the subplot layout parameters. 

1262 

1263 Unset parameters are left unmodified; initial values are given by 

1264 :rc:`figure.subplot.[name]`. 

1265 

1266 Parameters 

1267 ---------- 

1268 left : float, optional 

1269 The position of the left edge of the subplots, 

1270 as a fraction of the figure width. 

1271 right : float, optional 

1272 The position of the right edge of the subplots, 

1273 as a fraction of the figure width. 

1274 bottom : float, optional 

1275 The position of the bottom edge of the subplots, 

1276 as a fraction of the figure height. 

1277 top : float, optional 

1278 The position of the top edge of the subplots, 

1279 as a fraction of the figure height. 

1280 wspace : float, optional 

1281 The width of the padding between subplots, 

1282 as a fraction of the average Axes width. 

1283 hspace : float, optional 

1284 The height of the padding between subplots, 

1285 as a fraction of the average Axes height. 

1286 """ 

1287 if (self.get_layout_engine() is not None and 

1288 not self.get_layout_engine().adjust_compatible): 

1289 _api.warn_external( 

1290 "This figure was using a layout engine that is " 

1291 "incompatible with subplots_adjust and/or tight_layout; " 

1292 "not calling subplots_adjust.") 

1293 return 

1294 self.subplotpars.update(left, bottom, right, top, wspace, hspace) 

1295 for ax in self.axes: 

1296 if ax.get_subplotspec() is not None: 

1297 ax._set_position(ax.get_subplotspec().get_position(self)) 

1298 self.stale = True 

1299 

1300 def align_xlabels(self, axs=None): 

1301 """ 

1302 Align the xlabels of subplots in the same subplot row if label 

1303 alignment is being done automatically (i.e. the label position is 

1304 not manually set). 

1305 

1306 Alignment persists for draw events after this is called. 

1307 

1308 If a label is on the bottom, it is aligned with labels on Axes that 

1309 also have their label on the bottom and that have the same 

1310 bottom-most subplot row. If the label is on the top, 

1311 it is aligned with labels on Axes with the same top-most row. 

1312 

1313 Parameters 

1314 ---------- 

1315 axs : list of `~matplotlib.axes.Axes` 

1316 Optional list of (or `~numpy.ndarray`) `~matplotlib.axes.Axes` 

1317 to align the xlabels. 

1318 Default is to align all Axes on the figure. 

1319 

1320 See Also 

1321 -------- 

1322 matplotlib.figure.Figure.align_ylabels 

1323 matplotlib.figure.Figure.align_titles 

1324 matplotlib.figure.Figure.align_labels 

1325 

1326 Notes 

1327 ----- 

1328 This assumes that ``axs`` are from the same `.GridSpec`, so that 

1329 their `.SubplotSpec` positions correspond to figure positions. 

1330 

1331 Examples 

1332 -------- 

1333 Example with rotated xtick labels:: 

1334 

1335 fig, axs = plt.subplots(1, 2) 

1336 for tick in axs[0].get_xticklabels(): 

1337 tick.set_rotation(55) 

1338 axs[0].set_xlabel('XLabel 0') 

1339 axs[1].set_xlabel('XLabel 1') 

1340 fig.align_xlabels() 

1341 """ 

1342 if axs is None: 

1343 axs = self.axes 

1344 axs = [ax for ax in np.ravel(axs) if ax.get_subplotspec() is not None] 

1345 for ax in axs: 

1346 _log.debug(' Working on: %s', ax.get_xlabel()) 

1347 rowspan = ax.get_subplotspec().rowspan 

1348 pos = ax.xaxis.get_label_position() # top or bottom 

1349 # Search through other Axes for label positions that are same as 

1350 # this one and that share the appropriate row number. 

1351 # Add to a grouper associated with each Axes of siblings. 

1352 # This list is inspected in `axis.draw` by 

1353 # `axis._update_label_position`. 

1354 for axc in axs: 

1355 if axc.xaxis.get_label_position() == pos: 

1356 rowspanc = axc.get_subplotspec().rowspan 

1357 if (pos == 'top' and rowspan.start == rowspanc.start or 

1358 pos == 'bottom' and rowspan.stop == rowspanc.stop): 

1359 # grouper for groups of xlabels to align 

1360 self._align_label_groups['x'].join(ax, axc) 

1361 

1362 def align_ylabels(self, axs=None): 

1363 """ 

1364 Align the ylabels of subplots in the same subplot column if label 

1365 alignment is being done automatically (i.e. the label position is 

1366 not manually set). 

1367 

1368 Alignment persists for draw events after this is called. 

1369 

1370 If a label is on the left, it is aligned with labels on Axes that 

1371 also have their label on the left and that have the same 

1372 left-most subplot column. If the label is on the right, 

1373 it is aligned with labels on Axes with the same right-most column. 

1374 

1375 Parameters 

1376 ---------- 

1377 axs : list of `~matplotlib.axes.Axes` 

1378 Optional list (or `~numpy.ndarray`) of `~matplotlib.axes.Axes` 

1379 to align the ylabels. 

1380 Default is to align all Axes on the figure. 

1381 

1382 See Also 

1383 -------- 

1384 matplotlib.figure.Figure.align_xlabels 

1385 matplotlib.figure.Figure.align_titles 

1386 matplotlib.figure.Figure.align_labels 

1387 

1388 Notes 

1389 ----- 

1390 This assumes that ``axs`` are from the same `.GridSpec`, so that 

1391 their `.SubplotSpec` positions correspond to figure positions. 

1392 

1393 Examples 

1394 -------- 

1395 Example with large yticks labels:: 

1396 

1397 fig, axs = plt.subplots(2, 1) 

1398 axs[0].plot(np.arange(0, 1000, 50)) 

1399 axs[0].set_ylabel('YLabel 0') 

1400 axs[1].set_ylabel('YLabel 1') 

1401 fig.align_ylabels() 

1402 """ 

1403 if axs is None: 

1404 axs = self.axes 

1405 axs = [ax for ax in np.ravel(axs) if ax.get_subplotspec() is not None] 

1406 for ax in axs: 

1407 _log.debug(' Working on: %s', ax.get_ylabel()) 

1408 colspan = ax.get_subplotspec().colspan 

1409 pos = ax.yaxis.get_label_position() # left or right 

1410 # Search through other Axes for label positions that are same as 

1411 # this one and that share the appropriate column number. 

1412 # Add to a list associated with each Axes of siblings. 

1413 # This list is inspected in `axis.draw` by 

1414 # `axis._update_label_position`. 

1415 for axc in axs: 

1416 if axc.yaxis.get_label_position() == pos: 

1417 colspanc = axc.get_subplotspec().colspan 

1418 if (pos == 'left' and colspan.start == colspanc.start or 

1419 pos == 'right' and colspan.stop == colspanc.stop): 

1420 # grouper for groups of ylabels to align 

1421 self._align_label_groups['y'].join(ax, axc) 

1422 

1423 def align_titles(self, axs=None): 

1424 """ 

1425 Align the titles of subplots in the same subplot row if title 

1426 alignment is being done automatically (i.e. the title position is 

1427 not manually set). 

1428 

1429 Alignment persists for draw events after this is called. 

1430 

1431 Parameters 

1432 ---------- 

1433 axs : list of `~matplotlib.axes.Axes` 

1434 Optional list of (or ndarray) `~matplotlib.axes.Axes` 

1435 to align the titles. 

1436 Default is to align all Axes on the figure. 

1437 

1438 See Also 

1439 -------- 

1440 matplotlib.figure.Figure.align_xlabels 

1441 matplotlib.figure.Figure.align_ylabels 

1442 matplotlib.figure.Figure.align_labels 

1443 

1444 Notes 

1445 ----- 

1446 This assumes that ``axs`` are from the same `.GridSpec`, so that 

1447 their `.SubplotSpec` positions correspond to figure positions. 

1448 

1449 Examples 

1450 -------- 

1451 Example with titles:: 

1452 

1453 fig, axs = plt.subplots(1, 2) 

1454 axs[0].set_aspect('equal') 

1455 axs[0].set_title('Title 0') 

1456 axs[1].set_title('Title 1') 

1457 fig.align_titles() 

1458 """ 

1459 if axs is None: 

1460 axs = self.axes 

1461 axs = [ax for ax in np.ravel(axs) if ax.get_subplotspec() is not None] 

1462 for ax in axs: 

1463 _log.debug(' Working on: %s', ax.get_title()) 

1464 rowspan = ax.get_subplotspec().rowspan 

1465 for axc in axs: 

1466 rowspanc = axc.get_subplotspec().rowspan 

1467 if (rowspan.start == rowspanc.start): 

1468 self._align_label_groups['title'].join(ax, axc) 

1469 

1470 def align_labels(self, axs=None): 

1471 """ 

1472 Align the xlabels and ylabels of subplots with the same subplots 

1473 row or column (respectively) if label alignment is being 

1474 done automatically (i.e. the label position is not manually set). 

1475 

1476 Alignment persists for draw events after this is called. 

1477 

1478 Parameters 

1479 ---------- 

1480 axs : list of `~matplotlib.axes.Axes` 

1481 Optional list (or `~numpy.ndarray`) of `~matplotlib.axes.Axes` 

1482 to align the labels. 

1483 Default is to align all Axes on the figure. 

1484 

1485 See Also 

1486 -------- 

1487 matplotlib.figure.Figure.align_xlabels 

1488 matplotlib.figure.Figure.align_ylabels 

1489 matplotlib.figure.Figure.align_titles 

1490 """ 

1491 self.align_xlabels(axs=axs) 

1492 self.align_ylabels(axs=axs) 

1493 

1494 def add_gridspec(self, nrows=1, ncols=1, **kwargs): 

1495 """ 

1496 Low-level API for creating a `.GridSpec` that has this figure as a parent. 

1497 

1498 This is a low-level API, allowing you to create a gridspec and 

1499 subsequently add subplots based on the gridspec. Most users do 

1500 not need that freedom and should use the higher-level methods 

1501 `~.Figure.subplots` or `~.Figure.subplot_mosaic`. 

1502 

1503 Parameters 

1504 ---------- 

1505 nrows : int, default: 1 

1506 Number of rows in grid. 

1507 

1508 ncols : int, default: 1 

1509 Number of columns in grid. 

1510 

1511 Returns 

1512 ------- 

1513 `.GridSpec` 

1514 

1515 Other Parameters 

1516 ---------------- 

1517 **kwargs 

1518 Keyword arguments are passed to `.GridSpec`. 

1519 

1520 See Also 

1521 -------- 

1522 matplotlib.pyplot.subplots 

1523 

1524 Examples 

1525 -------- 

1526 Adding a subplot that spans two rows:: 

1527 

1528 fig = plt.figure() 

1529 gs = fig.add_gridspec(2, 2) 

1530 ax1 = fig.add_subplot(gs[0, 0]) 

1531 ax2 = fig.add_subplot(gs[1, 0]) 

1532 # spans two rows: 

1533 ax3 = fig.add_subplot(gs[:, 1]) 

1534 

1535 """ 

1536 

1537 _ = kwargs.pop('figure', None) # pop in case user has added this... 

1538 gs = GridSpec(nrows=nrows, ncols=ncols, figure=self, **kwargs) 

1539 return gs 

1540 

1541 def subfigures(self, nrows=1, ncols=1, squeeze=True, 

1542 wspace=None, hspace=None, 

1543 width_ratios=None, height_ratios=None, 

1544 **kwargs): 

1545 """ 

1546 Add a set of subfigures to this figure or subfigure. 

1547 

1548 A subfigure has the same artist methods as a figure, and is logically 

1549 the same as a figure, but cannot print itself. 

1550 See :doc:`/gallery/subplots_axes_and_figures/subfigures`. 

1551 

1552 .. note:: 

1553 The *subfigure* concept is new in v3.4, and the API is still provisional. 

1554 

1555 Parameters 

1556 ---------- 

1557 nrows, ncols : int, default: 1 

1558 Number of rows/columns of the subfigure grid. 

1559 

1560 squeeze : bool, default: True 

1561 If True, extra dimensions are squeezed out from the returned 

1562 array of subfigures. 

1563 

1564 wspace, hspace : float, default: None 

1565 The amount of width/height reserved for space between subfigures, 

1566 expressed as a fraction of the average subfigure width/height. 

1567 If not given, the values will be inferred from rcParams if using 

1568 constrained layout (see `~.ConstrainedLayoutEngine`), or zero if 

1569 not using a layout engine. 

1570 

1571 width_ratios : array-like of length *ncols*, optional 

1572 Defines the relative widths of the columns. Each column gets a 

1573 relative width of ``width_ratios[i] / sum(width_ratios)``. 

1574 If not given, all columns will have the same width. 

1575 

1576 height_ratios : array-like of length *nrows*, optional 

1577 Defines the relative heights of the rows. Each row gets a 

1578 relative height of ``height_ratios[i] / sum(height_ratios)``. 

1579 If not given, all rows will have the same height. 

1580 """ 

1581 gs = GridSpec(nrows=nrows, ncols=ncols, figure=self, 

1582 wspace=wspace, hspace=hspace, 

1583 width_ratios=width_ratios, 

1584 height_ratios=height_ratios, 

1585 left=0, right=1, bottom=0, top=1) 

1586 

1587 sfarr = np.empty((nrows, ncols), dtype=object) 

1588 for i in range(ncols): 

1589 for j in range(nrows): 

1590 sfarr[j, i] = self.add_subfigure(gs[j, i], **kwargs) 

1591 

1592 if self.get_layout_engine() is None and (wspace is not None or 

1593 hspace is not None): 

1594 # Gridspec wspace and hspace is ignored on subfigure instantiation, 

1595 # and no space is left. So need to account for it here if required. 

1596 bottoms, tops, lefts, rights = gs.get_grid_positions(self) 

1597 for sfrow, bottom, top in zip(sfarr, bottoms, tops): 

1598 for sf, left, right in zip(sfrow, lefts, rights): 

1599 bbox = Bbox.from_extents(left, bottom, right, top) 

1600 sf._redo_transform_rel_fig(bbox=bbox) 

1601 

1602 if squeeze: 

1603 # Discarding unneeded dimensions that equal 1. If we only have one 

1604 # subfigure, just return it instead of a 1-element array. 

1605 return sfarr.item() if sfarr.size == 1 else sfarr.squeeze() 

1606 else: 

1607 # Returned axis array will be always 2-d, even if nrows=ncols=1. 

1608 return sfarr 

1609 

1610 def add_subfigure(self, subplotspec, **kwargs): 

1611 """ 

1612 Add a `.SubFigure` to the figure as part of a subplot arrangement. 

1613 

1614 Parameters 

1615 ---------- 

1616 subplotspec : `.gridspec.SubplotSpec` 

1617 Defines the region in a parent gridspec where the subfigure will 

1618 be placed. 

1619 

1620 Returns 

1621 ------- 

1622 `.SubFigure` 

1623 

1624 Other Parameters 

1625 ---------------- 

1626 **kwargs 

1627 Are passed to the `.SubFigure` object. 

1628 

1629 See Also 

1630 -------- 

1631 .Figure.subfigures 

1632 """ 

1633 sf = SubFigure(self, subplotspec, **kwargs) 

1634 self.subfigs += [sf] 

1635 sf._remove_method = self.subfigs.remove 

1636 sf.stale_callback = _stale_figure_callback 

1637 self.stale = True 

1638 return sf 

1639 

1640 def sca(self, a): 

1641 """Set the current Axes to be *a* and return *a*.""" 

1642 self._axstack.bubble(a) 

1643 self._axobservers.process("_axes_change_event", self) 

1644 return a 

1645 

1646 def gca(self): 

1647 """ 

1648 Get the current Axes. 

1649 

1650 If there is currently no Axes on this Figure, a new one is created 

1651 using `.Figure.add_subplot`. (To test whether there is currently an 

1652 Axes on a Figure, check whether ``figure.axes`` is empty. To test 

1653 whether there is currently a Figure on the pyplot figure stack, check 

1654 whether `.pyplot.get_fignums()` is empty.) 

1655 """ 

1656 ax = self._axstack.current() 

1657 return ax if ax is not None else self.add_subplot() 

1658 

1659 def _gci(self): 

1660 # Helper for `~matplotlib.pyplot.gci`. Do not use elsewhere. 

1661 """ 

1662 Get the current colorable artist. 

1663 

1664 Specifically, returns the current `.ScalarMappable` instance (`.Image` 

1665 created by `imshow` or `figimage`, `.Collection` created by `pcolor` or 

1666 `scatter`, etc.), or *None* if no such instance has been defined. 

1667 

1668 The current image is an attribute of the current Axes, or the nearest 

1669 earlier Axes in the current figure that contains an image. 

1670 

1671 Notes 

1672 ----- 

1673 Historically, the only colorable artists were images; hence the name 

1674 ``gci`` (get current image). 

1675 """ 

1676 # Look first for an image in the current Axes. 

1677 ax = self._axstack.current() 

1678 if ax is None: 

1679 return None 

1680 im = ax._gci() 

1681 if im is not None: 

1682 return im 

1683 # If there is no image in the current Axes, search for 

1684 # one in a previously created Axes. Whether this makes 

1685 # sense is debatable, but it is the documented behavior. 

1686 for ax in reversed(self.axes): 

1687 im = ax._gci() 

1688 if im is not None: 

1689 return im 

1690 return None 

1691 

1692 def _process_projection_requirements(self, *, axes_class=None, polar=False, 

1693 projection=None, **kwargs): 

1694 """ 

1695 Handle the args/kwargs to add_axes/add_subplot/gca, returning:: 

1696 

1697 (axes_proj_class, proj_class_kwargs) 

1698 

1699 which can be used for new Axes initialization/identification. 

1700 """ 

1701 if axes_class is not None: 

1702 if polar or projection is not None: 

1703 raise ValueError( 

1704 "Cannot combine 'axes_class' and 'projection' or 'polar'") 

1705 projection_class = axes_class 

1706 else: 

1707 

1708 if polar: 

1709 if projection is not None and projection != 'polar': 

1710 raise ValueError( 

1711 f"polar={polar}, yet projection={projection!r}. " 

1712 "Only one of these arguments should be supplied." 

1713 ) 

1714 projection = 'polar' 

1715 

1716 if isinstance(projection, str) or projection is None: 

1717 projection_class = projections.get_projection_class(projection) 

1718 elif hasattr(projection, '_as_mpl_axes'): 

1719 projection_class, extra_kwargs = projection._as_mpl_axes() 

1720 kwargs.update(**extra_kwargs) 

1721 else: 

1722 raise TypeError( 

1723 f"projection must be a string, None or implement a " 

1724 f"_as_mpl_axes method, not {projection!r}") 

1725 return projection_class, kwargs 

1726 

1727 def get_default_bbox_extra_artists(self): 

1728 """ 

1729 Return a list of Artists typically used in `.Figure.get_tightbbox`. 

1730 """ 

1731 bbox_artists = [artist for artist in self.get_children() 

1732 if (artist.get_visible() and artist.get_in_layout())] 

1733 for ax in self.axes: 

1734 if ax.get_visible(): 

1735 bbox_artists.extend(ax.get_default_bbox_extra_artists()) 

1736 return bbox_artists 

1737 

1738 @_api.make_keyword_only("3.8", "bbox_extra_artists") 

1739 def get_tightbbox(self, renderer=None, bbox_extra_artists=None): 

1740 """ 

1741 Return a (tight) bounding box of the figure *in inches*. 

1742 

1743 Note that `.FigureBase` differs from all other artists, which return 

1744 their `.Bbox` in pixels. 

1745 

1746 Artists that have ``artist.set_in_layout(False)`` are not included 

1747 in the bbox. 

1748 

1749 Parameters 

1750 ---------- 

1751 renderer : `.RendererBase` subclass 

1752 Renderer that will be used to draw the figures (i.e. 

1753 ``fig.canvas.get_renderer()``) 

1754 

1755 bbox_extra_artists : list of `.Artist` or ``None`` 

1756 List of artists to include in the tight bounding box. If 

1757 ``None`` (default), then all artist children of each Axes are 

1758 included in the tight bounding box. 

1759 

1760 Returns 

1761 ------- 

1762 `.BboxBase` 

1763 containing the bounding box (in figure inches). 

1764 """ 

1765 

1766 if renderer is None: 

1767 renderer = self.figure._get_renderer() 

1768 

1769 bb = [] 

1770 if bbox_extra_artists is None: 

1771 artists = [artist for artist in self.get_children() 

1772 if (artist not in self.axes and artist.get_visible() 

1773 and artist.get_in_layout())] 

1774 else: 

1775 artists = bbox_extra_artists 

1776 

1777 for a in artists: 

1778 bbox = a.get_tightbbox(renderer) 

1779 if bbox is not None: 

1780 bb.append(bbox) 

1781 

1782 for ax in self.axes: 

1783 if ax.get_visible(): 

1784 # some Axes don't take the bbox_extra_artists kwarg so we 

1785 # need this conditional.... 

1786 try: 

1787 bbox = ax.get_tightbbox( 

1788 renderer, bbox_extra_artists=bbox_extra_artists) 

1789 except TypeError: 

1790 bbox = ax.get_tightbbox(renderer) 

1791 bb.append(bbox) 

1792 bb = [b for b in bb 

1793 if (np.isfinite(b.width) and np.isfinite(b.height) 

1794 and (b.width != 0 or b.height != 0))] 

1795 

1796 isfigure = hasattr(self, 'bbox_inches') 

1797 if len(bb) == 0: 

1798 if isfigure: 

1799 return self.bbox_inches 

1800 else: 

1801 # subfigures do not have bbox_inches, but do have a bbox 

1802 bb = [self.bbox] 

1803 

1804 _bbox = Bbox.union(bb) 

1805 

1806 if isfigure: 

1807 # transform from pixels to inches... 

1808 _bbox = TransformedBbox(_bbox, self.dpi_scale_trans.inverted()) 

1809 

1810 return _bbox 

1811 

1812 @staticmethod 

1813 def _norm_per_subplot_kw(per_subplot_kw): 

1814 expanded = {} 

1815 for k, v in per_subplot_kw.items(): 

1816 if isinstance(k, tuple): 

1817 for sub_key in k: 

1818 if sub_key in expanded: 

1819 raise ValueError(f'The key {sub_key!r} appears multiple times.') 

1820 expanded[sub_key] = v 

1821 else: 

1822 if k in expanded: 

1823 raise ValueError(f'The key {k!r} appears multiple times.') 

1824 expanded[k] = v 

1825 return expanded 

1826 

1827 @staticmethod 

1828 def _normalize_grid_string(layout): 

1829 if '\n' not in layout: 

1830 # single-line string 

1831 return [list(ln) for ln in layout.split(';')] 

1832 else: 

1833 # multi-line string 

1834 layout = inspect.cleandoc(layout) 

1835 return [list(ln) for ln in layout.strip('\n').split('\n')] 

1836 

1837 def subplot_mosaic(self, mosaic, *, sharex=False, sharey=False, 

1838 width_ratios=None, height_ratios=None, 

1839 empty_sentinel='.', 

1840 subplot_kw=None, per_subplot_kw=None, gridspec_kw=None): 

1841 """ 

1842 Build a layout of Axes based on ASCII art or nested lists. 

1843 

1844 This is a helper function to build complex GridSpec layouts visually. 

1845 

1846 See :ref:`mosaic` 

1847 for an example and full API documentation 

1848 

1849 Parameters 

1850 ---------- 

1851 mosaic : list of list of {hashable or nested} or str 

1852 

1853 A visual layout of how you want your Axes to be arranged 

1854 labeled as strings. For example :: 

1855 

1856 x = [['A panel', 'A panel', 'edge'], 

1857 ['C panel', '.', 'edge']] 

1858 

1859 produces 4 Axes: 

1860 

1861 - 'A panel' which is 1 row high and spans the first two columns 

1862 - 'edge' which is 2 rows high and is on the right edge 

1863 - 'C panel' which in 1 row and 1 column wide in the bottom left 

1864 - a blank space 1 row and 1 column wide in the bottom center 

1865 

1866 Any of the entries in the layout can be a list of lists 

1867 of the same form to create nested layouts. 

1868 

1869 If input is a str, then it can either be a multi-line string of 

1870 the form :: 

1871 

1872 ''' 

1873 AAE 

1874 C.E 

1875 ''' 

1876 

1877 where each character is a column and each line is a row. Or it 

1878 can be a single-line string where rows are separated by ``;``:: 

1879 

1880 'AB;CC' 

1881 

1882 The string notation allows only single character Axes labels and 

1883 does not support nesting but is very terse. 

1884 

1885 The Axes identifiers may be `str` or a non-iterable hashable 

1886 object (e.g. `tuple` s may not be used). 

1887 

1888 sharex, sharey : bool, default: False 

1889 If True, the x-axis (*sharex*) or y-axis (*sharey*) will be shared 

1890 among all subplots. In that case, tick label visibility and axis 

1891 units behave as for `subplots`. If False, each subplot's x- or 

1892 y-axis will be independent. 

1893 

1894 width_ratios : array-like of length *ncols*, optional 

1895 Defines the relative widths of the columns. Each column gets a 

1896 relative width of ``width_ratios[i] / sum(width_ratios)``. 

1897 If not given, all columns will have the same width. Equivalent 

1898 to ``gridspec_kw={'width_ratios': [...]}``. In the case of nested 

1899 layouts, this argument applies only to the outer layout. 

1900 

1901 height_ratios : array-like of length *nrows*, optional 

1902 Defines the relative heights of the rows. Each row gets a 

1903 relative height of ``height_ratios[i] / sum(height_ratios)``. 

1904 If not given, all rows will have the same height. Equivalent 

1905 to ``gridspec_kw={'height_ratios': [...]}``. In the case of nested 

1906 layouts, this argument applies only to the outer layout. 

1907 

1908 subplot_kw : dict, optional 

1909 Dictionary with keywords passed to the `.Figure.add_subplot` call 

1910 used to create each subplot. These values may be overridden by 

1911 values in *per_subplot_kw*. 

1912 

1913 per_subplot_kw : dict, optional 

1914 A dictionary mapping the Axes identifiers or tuples of identifiers 

1915 to a dictionary of keyword arguments to be passed to the 

1916 `.Figure.add_subplot` call used to create each subplot. The values 

1917 in these dictionaries have precedence over the values in 

1918 *subplot_kw*. 

1919 

1920 If *mosaic* is a string, and thus all keys are single characters, 

1921 it is possible to use a single string instead of a tuple as keys; 

1922 i.e. ``"AB"`` is equivalent to ``("A", "B")``. 

1923 

1924 .. versionadded:: 3.7 

1925 

1926 gridspec_kw : dict, optional 

1927 Dictionary with keywords passed to the `.GridSpec` constructor used 

1928 to create the grid the subplots are placed on. In the case of 

1929 nested layouts, this argument applies only to the outer layout. 

1930 For more complex layouts, users should use `.Figure.subfigures` 

1931 to create the nesting. 

1932 

1933 empty_sentinel : object, optional 

1934 Entry in the layout to mean "leave this space empty". Defaults 

1935 to ``'.'``. Note, if *layout* is a string, it is processed via 

1936 `inspect.cleandoc` to remove leading white space, which may 

1937 interfere with using white-space as the empty sentinel. 

1938 

1939 Returns 

1940 ------- 

1941 dict[label, Axes] 

1942 A dictionary mapping the labels to the Axes objects. The order of 

1943 the Axes is left-to-right and top-to-bottom of their position in the 

1944 total layout. 

1945 

1946 """ 

1947 subplot_kw = subplot_kw or {} 

1948 gridspec_kw = dict(gridspec_kw or {}) 

1949 per_subplot_kw = per_subplot_kw or {} 

1950 

1951 if height_ratios is not None: 

1952 if 'height_ratios' in gridspec_kw: 

1953 raise ValueError("'height_ratios' must not be defined both as " 

1954 "parameter and as key in 'gridspec_kw'") 

1955 gridspec_kw['height_ratios'] = height_ratios 

1956 if width_ratios is not None: 

1957 if 'width_ratios' in gridspec_kw: 

1958 raise ValueError("'width_ratios' must not be defined both as " 

1959 "parameter and as key in 'gridspec_kw'") 

1960 gridspec_kw['width_ratios'] = width_ratios 

1961 

1962 # special-case string input 

1963 if isinstance(mosaic, str): 

1964 mosaic = self._normalize_grid_string(mosaic) 

1965 per_subplot_kw = { 

1966 tuple(k): v for k, v in per_subplot_kw.items() 

1967 } 

1968 

1969 per_subplot_kw = self._norm_per_subplot_kw(per_subplot_kw) 

1970 

1971 # Only accept strict bools to allow a possible future API expansion. 

1972 _api.check_isinstance(bool, sharex=sharex, sharey=sharey) 

1973 

1974 def _make_array(inp): 

1975 """ 

1976 Convert input into 2D array 

1977 

1978 We need to have this internal function rather than 

1979 ``np.asarray(..., dtype=object)`` so that a list of lists 

1980 of lists does not get converted to an array of dimension > 2. 

1981 

1982 Returns 

1983 ------- 

1984 2D object array 

1985 """ 

1986 r0, *rest = inp 

1987 if isinstance(r0, str): 

1988 raise ValueError('List mosaic specification must be 2D') 

1989 for j, r in enumerate(rest, start=1): 

1990 if isinstance(r, str): 

1991 raise ValueError('List mosaic specification must be 2D') 

1992 if len(r0) != len(r): 

1993 raise ValueError( 

1994 "All of the rows must be the same length, however " 

1995 f"the first row ({r0!r}) has length {len(r0)} " 

1996 f"and row {j} ({r!r}) has length {len(r)}." 

1997 ) 

1998 out = np.zeros((len(inp), len(r0)), dtype=object) 

1999 for j, r in enumerate(inp): 

2000 for k, v in enumerate(r): 

2001 out[j, k] = v 

2002 return out 

2003 

2004 def _identify_keys_and_nested(mosaic): 

2005 """ 

2006 Given a 2D object array, identify unique IDs and nested mosaics 

2007 

2008 Parameters 

2009 ---------- 

2010 mosaic : 2D object array 

2011 

2012 Returns 

2013 ------- 

2014 unique_ids : tuple 

2015 The unique non-sub mosaic entries in this mosaic 

2016 nested : dict[tuple[int, int], 2D object array] 

2017 """ 

2018 # make sure we preserve the user supplied order 

2019 unique_ids = cbook._OrderedSet() 

2020 nested = {} 

2021 for j, row in enumerate(mosaic): 

2022 for k, v in enumerate(row): 

2023 if v == empty_sentinel: 

2024 continue 

2025 elif not cbook.is_scalar_or_string(v): 

2026 nested[(j, k)] = _make_array(v) 

2027 else: 

2028 unique_ids.add(v) 

2029 

2030 return tuple(unique_ids), nested 

2031 

2032 def _do_layout(gs, mosaic, unique_ids, nested): 

2033 """ 

2034 Recursively do the mosaic. 

2035 

2036 Parameters 

2037 ---------- 

2038 gs : GridSpec 

2039 mosaic : 2D object array 

2040 The input converted to a 2D array for this level. 

2041 unique_ids : tuple 

2042 The identified scalar labels at this level of nesting. 

2043 nested : dict[tuple[int, int]], 2D object array 

2044 The identified nested mosaics, if any. 

2045 

2046 Returns 

2047 ------- 

2048 dict[label, Axes] 

2049 A flat dict of all of the Axes created. 

2050 """ 

2051 output = dict() 

2052 

2053 # we need to merge together the Axes at this level and the Axes 

2054 # in the (recursively) nested sub-mosaics so that we can add 

2055 # them to the figure in the "natural" order if you were to 

2056 # ravel in c-order all of the Axes that will be created 

2057 # 

2058 # This will stash the upper left index of each object (axes or 

2059 # nested mosaic) at this level 

2060 this_level = dict() 

2061 

2062 # go through the unique keys, 

2063 for name in unique_ids: 

2064 # sort out where each axes starts/ends 

2065 indx = np.argwhere(mosaic == name) 

2066 start_row, start_col = np.min(indx, axis=0) 

2067 end_row, end_col = np.max(indx, axis=0) + 1 

2068 # and construct the slice object 

2069 slc = (slice(start_row, end_row), slice(start_col, end_col)) 

2070 # some light error checking 

2071 if (mosaic[slc] != name).any(): 

2072 raise ValueError( 

2073 f"While trying to layout\n{mosaic!r}\n" 

2074 f"we found that the label {name!r} specifies a " 

2075 "non-rectangular or non-contiguous area.") 

2076 # and stash this slice for later 

2077 this_level[(start_row, start_col)] = (name, slc, 'axes') 

2078 

2079 # do the same thing for the nested mosaics (simpler because these 

2080 # cannot be spans yet!) 

2081 for (j, k), nested_mosaic in nested.items(): 

2082 this_level[(j, k)] = (None, nested_mosaic, 'nested') 

2083 

2084 # now go through the things in this level and add them 

2085 # in order left-to-right top-to-bottom 

2086 for key in sorted(this_level): 

2087 name, arg, method = this_level[key] 

2088 # we are doing some hokey function dispatch here based 

2089 # on the 'method' string stashed above to sort out if this 

2090 # element is an Axes or a nested mosaic. 

2091 if method == 'axes': 

2092 slc = arg 

2093 # add a single Axes 

2094 if name in output: 

2095 raise ValueError(f"There are duplicate keys {name} " 

2096 f"in the layout\n{mosaic!r}") 

2097 ax = self.add_subplot( 

2098 gs[slc], **{ 

2099 'label': str(name), 

2100 **subplot_kw, 

2101 **per_subplot_kw.get(name, {}) 

2102 } 

2103 ) 

2104 output[name] = ax 

2105 elif method == 'nested': 

2106 nested_mosaic = arg 

2107 j, k = key 

2108 # recursively add the nested mosaic 

2109 rows, cols = nested_mosaic.shape 

2110 nested_output = _do_layout( 

2111 gs[j, k].subgridspec(rows, cols), 

2112 nested_mosaic, 

2113 *_identify_keys_and_nested(nested_mosaic) 

2114 ) 

2115 overlap = set(output) & set(nested_output) 

2116 if overlap: 

2117 raise ValueError( 

2118 f"There are duplicate keys {overlap} " 

2119 f"between the outer layout\n{mosaic!r}\n" 

2120 f"and the nested layout\n{nested_mosaic}" 

2121 ) 

2122 output.update(nested_output) 

2123 else: 

2124 raise RuntimeError("This should never happen") 

2125 return output 

2126 

2127 mosaic = _make_array(mosaic) 

2128 rows, cols = mosaic.shape 

2129 gs = self.add_gridspec(rows, cols, **gridspec_kw) 

2130 ret = _do_layout(gs, mosaic, *_identify_keys_and_nested(mosaic)) 

2131 ax0 = next(iter(ret.values())) 

2132 for ax in ret.values(): 

2133 if sharex: 

2134 ax.sharex(ax0) 

2135 ax._label_outer_xaxis(skip_non_rectangular_axes=True) 

2136 if sharey: 

2137 ax.sharey(ax0) 

2138 ax._label_outer_yaxis(skip_non_rectangular_axes=True) 

2139 if extra := set(per_subplot_kw) - set(ret): 

2140 raise ValueError( 

2141 f"The keys {extra} are in *per_subplot_kw* " 

2142 "but not in the mosaic." 

2143 ) 

2144 return ret 

2145 

2146 def _set_artist_props(self, a): 

2147 if a != self: 

2148 a.set_figure(self) 

2149 a.stale_callback = _stale_figure_callback 

2150 a.set_transform(self.transSubfigure) 

2151 

2152 

2153@_docstring.interpd 

2154class SubFigure(FigureBase): 

2155 """ 

2156 Logical figure that can be placed inside a figure. 

2157 

2158 See :ref:`figure-api-subfigure` for an index of methods on this class. 

2159 Typically instantiated using `.Figure.add_subfigure` or 

2160 `.SubFigure.add_subfigure`, or `.SubFigure.subfigures`. A subfigure has 

2161 the same methods as a figure except for those particularly tied to the size 

2162 or dpi of the figure, and is confined to a prescribed region of the figure. 

2163 For example the following puts two subfigures side-by-side:: 

2164 

2165 fig = plt.figure() 

2166 sfigs = fig.subfigures(1, 2) 

2167 axsL = sfigs[0].subplots(1, 2) 

2168 axsR = sfigs[1].subplots(2, 1) 

2169 

2170 See :doc:`/gallery/subplots_axes_and_figures/subfigures` 

2171 

2172 .. note:: 

2173 The *subfigure* concept is new in v3.4, and the API is still provisional. 

2174 """ 

2175 

2176 def __init__(self, parent, subplotspec, *, 

2177 facecolor=None, 

2178 edgecolor=None, 

2179 linewidth=0.0, 

2180 frameon=None, 

2181 **kwargs): 

2182 """ 

2183 Parameters 

2184 ---------- 

2185 parent : `.Figure` or `.SubFigure` 

2186 Figure or subfigure that contains the SubFigure. SubFigures 

2187 can be nested. 

2188 

2189 subplotspec : `.gridspec.SubplotSpec` 

2190 Defines the region in a parent gridspec where the subfigure will 

2191 be placed. 

2192 

2193 facecolor : default: ``"none"`` 

2194 The figure patch face color; transparent by default. 

2195 

2196 edgecolor : default: :rc:`figure.edgecolor` 

2197 The figure patch edge color. 

2198 

2199 linewidth : float 

2200 The linewidth of the frame (i.e. the edge linewidth of the figure 

2201 patch). 

2202 

2203 frameon : bool, default: :rc:`figure.frameon` 

2204 If ``False``, suppress drawing the figure background patch. 

2205 

2206 Other Parameters 

2207 ---------------- 

2208 **kwargs : `.SubFigure` properties, optional 

2209 

2210 %(SubFigure:kwdoc)s 

2211 """ 

2212 super().__init__(**kwargs) 

2213 if facecolor is None: 

2214 facecolor = "none" 

2215 if edgecolor is None: 

2216 edgecolor = mpl.rcParams['figure.edgecolor'] 

2217 if frameon is None: 

2218 frameon = mpl.rcParams['figure.frameon'] 

2219 

2220 self._subplotspec = subplotspec 

2221 self._parent = parent 

2222 self.figure = parent.figure 

2223 

2224 # subfigures use the parent axstack 

2225 self._axstack = parent._axstack 

2226 self.subplotpars = parent.subplotpars 

2227 self.dpi_scale_trans = parent.dpi_scale_trans 

2228 self._axobservers = parent._axobservers 

2229 self.transFigure = parent.transFigure 

2230 self.bbox_relative = Bbox.null() 

2231 self._redo_transform_rel_fig() 

2232 self.figbbox = self._parent.figbbox 

2233 self.bbox = TransformedBbox(self.bbox_relative, 

2234 self._parent.transSubfigure) 

2235 self.transSubfigure = BboxTransformTo(self.bbox) 

2236 

2237 self.patch = Rectangle( 

2238 xy=(0, 0), width=1, height=1, visible=frameon, 

2239 facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth, 

2240 # Don't let the figure patch influence bbox calculation. 

2241 in_layout=False, transform=self.transSubfigure) 

2242 self._set_artist_props(self.patch) 

2243 self.patch.set_antialiased(False) 

2244 

2245 @property 

2246 def canvas(self): 

2247 return self._parent.canvas 

2248 

2249 @property 

2250 def dpi(self): 

2251 return self._parent.dpi 

2252 

2253 @dpi.setter 

2254 def dpi(self, value): 

2255 self._parent.dpi = value 

2256 

2257 def get_dpi(self): 

2258 """ 

2259 Return the resolution of the parent figure in dots-per-inch as a float. 

2260 """ 

2261 return self._parent.dpi 

2262 

2263 def set_dpi(self, val): 

2264 """ 

2265 Set the resolution of parent figure in dots-per-inch. 

2266 

2267 Parameters 

2268 ---------- 

2269 val : float 

2270 """ 

2271 self._parent.dpi = val 

2272 self.stale = True 

2273 

2274 def _get_renderer(self): 

2275 return self._parent._get_renderer() 

2276 

2277 def _redo_transform_rel_fig(self, bbox=None): 

2278 """ 

2279 Make the transSubfigure bbox relative to Figure transform. 

2280 

2281 Parameters 

2282 ---------- 

2283 bbox : bbox or None 

2284 If not None, then the bbox is used for relative bounding box. 

2285 Otherwise, it is calculated from the subplotspec. 

2286 """ 

2287 if bbox is not None: 

2288 self.bbox_relative.p0 = bbox.p0 

2289 self.bbox_relative.p1 = bbox.p1 

2290 return 

2291 # need to figure out *where* this subplotspec is. 

2292 gs = self._subplotspec.get_gridspec() 

2293 wr = np.asarray(gs.get_width_ratios()) 

2294 hr = np.asarray(gs.get_height_ratios()) 

2295 dx = wr[self._subplotspec.colspan].sum() / wr.sum() 

2296 dy = hr[self._subplotspec.rowspan].sum() / hr.sum() 

2297 x0 = wr[:self._subplotspec.colspan.start].sum() / wr.sum() 

2298 y0 = 1 - hr[:self._subplotspec.rowspan.stop].sum() / hr.sum() 

2299 self.bbox_relative.p0 = (x0, y0) 

2300 self.bbox_relative.p1 = (x0 + dx, y0 + dy) 

2301 

2302 def get_constrained_layout(self): 

2303 """ 

2304 Return whether constrained layout is being used. 

2305 

2306 See :ref:`constrainedlayout_guide`. 

2307 """ 

2308 return self._parent.get_constrained_layout() 

2309 

2310 def get_constrained_layout_pads(self, relative=False): 

2311 """ 

2312 Get padding for ``constrained_layout``. 

2313 

2314 Returns a list of ``w_pad, h_pad`` in inches and 

2315 ``wspace`` and ``hspace`` as fractions of the subplot. 

2316 

2317 See :ref:`constrainedlayout_guide`. 

2318 

2319 Parameters 

2320 ---------- 

2321 relative : bool 

2322 If `True`, then convert from inches to figure relative. 

2323 """ 

2324 return self._parent.get_constrained_layout_pads(relative=relative) 

2325 

2326 def get_layout_engine(self): 

2327 return self._parent.get_layout_engine() 

2328 

2329 @property 

2330 def axes(self): 

2331 """ 

2332 List of Axes in the SubFigure. You can access and modify the Axes 

2333 in the SubFigure through this list. 

2334 

2335 Modifying this list has no effect. Instead, use `~.SubFigure.add_axes`, 

2336 `~.SubFigure.add_subplot` or `~.SubFigure.delaxes` to add or remove an 

2337 Axes. 

2338 

2339 Note: The `.SubFigure.axes` property and `~.SubFigure.get_axes` method 

2340 are equivalent. 

2341 """ 

2342 return self._localaxes[:] 

2343 

2344 get_axes = axes.fget 

2345 

2346 def draw(self, renderer): 

2347 # docstring inherited 

2348 

2349 # draw the figure bounding box, perhaps none for white figure 

2350 if not self.get_visible(): 

2351 return 

2352 

2353 artists = self._get_draw_artists(renderer) 

2354 

2355 try: 

2356 renderer.open_group('subfigure', gid=self.get_gid()) 

2357 self.patch.draw(renderer) 

2358 mimage._draw_list_compositing_images( 

2359 renderer, self, artists, self.figure.suppressComposite) 

2360 renderer.close_group('subfigure') 

2361 

2362 finally: 

2363 self.stale = False 

2364 

2365 

2366@_docstring.interpd 

2367class Figure(FigureBase): 

2368 """ 

2369 The top level container for all the plot elements. 

2370 

2371 See `matplotlib.figure` for an index of class methods. 

2372 

2373 Attributes 

2374 ---------- 

2375 patch 

2376 The `.Rectangle` instance representing the figure background patch. 

2377 

2378 suppressComposite 

2379 For multiple images, the figure will make composite images 

2380 depending on the renderer option_image_nocomposite function. If 

2381 *suppressComposite* is a boolean, this will override the renderer. 

2382 """ 

2383 

2384 # we want to cache the fonts and mathtext at a global level so that when 

2385 # multiple figures are created we can reuse them. This helps with a bug on 

2386 # windows where the creation of too many figures leads to too many open 

2387 # file handles and improves the performance of parsing mathtext. However, 

2388 # these global caches are not thread safe. The solution here is to let the 

2389 # Figure acquire a shared lock at the start of the draw, and release it when it 

2390 # is done. This allows multiple renderers to share the cached fonts and 

2391 # parsed text, but only one figure can draw at a time and so the font cache 

2392 # and mathtext cache are used by only one renderer at a time. 

2393 

2394 _render_lock = threading.RLock() 

2395 

2396 def __str__(self): 

2397 return "Figure(%gx%g)" % tuple(self.bbox.size) 

2398 

2399 def __repr__(self): 

2400 return "<{clsname} size {h:g}x{w:g} with {naxes} Axes>".format( 

2401 clsname=self.__class__.__name__, 

2402 h=self.bbox.size[0], w=self.bbox.size[1], 

2403 naxes=len(self.axes), 

2404 ) 

2405 

2406 def __init__(self, 

2407 figsize=None, 

2408 dpi=None, 

2409 *, 

2410 facecolor=None, 

2411 edgecolor=None, 

2412 linewidth=0.0, 

2413 frameon=None, 

2414 subplotpars=None, # rc figure.subplot.* 

2415 tight_layout=None, # rc figure.autolayout 

2416 constrained_layout=None, # rc figure.constrained_layout.use 

2417 layout=None, 

2418 **kwargs 

2419 ): 

2420 """ 

2421 Parameters 

2422 ---------- 

2423 figsize : 2-tuple of floats, default: :rc:`figure.figsize` 

2424 Figure dimension ``(width, height)`` in inches. 

2425 

2426 dpi : float, default: :rc:`figure.dpi` 

2427 Dots per inch. 

2428 

2429 facecolor : default: :rc:`figure.facecolor` 

2430 The figure patch facecolor. 

2431 

2432 edgecolor : default: :rc:`figure.edgecolor` 

2433 The figure patch edge color. 

2434 

2435 linewidth : float 

2436 The linewidth of the frame (i.e. the edge linewidth of the figure 

2437 patch). 

2438 

2439 frameon : bool, default: :rc:`figure.frameon` 

2440 If ``False``, suppress drawing the figure background patch. 

2441 

2442 subplotpars : `~matplotlib.gridspec.SubplotParams` 

2443 Subplot parameters. If not given, the default subplot 

2444 parameters :rc:`figure.subplot.*` are used. 

2445 

2446 tight_layout : bool or dict, default: :rc:`figure.autolayout` 

2447 Whether to use the tight layout mechanism. See `.set_tight_layout`. 

2448 

2449 .. admonition:: Discouraged 

2450 

2451 The use of this parameter is discouraged. Please use 

2452 ``layout='tight'`` instead for the common case of 

2453 ``tight_layout=True`` and use `.set_tight_layout` otherwise. 

2454 

2455 constrained_layout : bool, default: :rc:`figure.constrained_layout.use` 

2456 This is equal to ``layout='constrained'``. 

2457 

2458 .. admonition:: Discouraged 

2459 

2460 The use of this parameter is discouraged. Please use 

2461 ``layout='constrained'`` instead. 

2462 

2463 layout : {'constrained', 'compressed', 'tight', 'none', `.LayoutEngine`, \ 

2464None}, default: None 

2465 The layout mechanism for positioning of plot elements to avoid 

2466 overlapping Axes decorations (labels, ticks, etc). Note that 

2467 layout managers can have significant performance penalties. 

2468 

2469 - 'constrained': The constrained layout solver adjusts Axes sizes 

2470 to avoid overlapping Axes decorations. Can handle complex plot 

2471 layouts and colorbars, and is thus recommended. 

2472 

2473 See :ref:`constrainedlayout_guide` for examples. 

2474 

2475 - 'compressed': uses the same algorithm as 'constrained', but 

2476 removes extra space between fixed-aspect-ratio Axes. Best for 

2477 simple grids of Axes. 

2478 

2479 - 'tight': Use the tight layout mechanism. This is a relatively 

2480 simple algorithm that adjusts the subplot parameters so that 

2481 decorations do not overlap. 

2482 

2483 See :ref:`tight_layout_guide` for examples. 

2484 

2485 - 'none': Do not use a layout engine. 

2486 

2487 - A `.LayoutEngine` instance. Builtin layout classes are 

2488 `.ConstrainedLayoutEngine` and `.TightLayoutEngine`, more easily 

2489 accessible by 'constrained' and 'tight'. Passing an instance 

2490 allows third parties to provide their own layout engine. 

2491 

2492 If not given, fall back to using the parameters *tight_layout* and 

2493 *constrained_layout*, including their config defaults 

2494 :rc:`figure.autolayout` and :rc:`figure.constrained_layout.use`. 

2495 

2496 Other Parameters 

2497 ---------------- 

2498 **kwargs : `.Figure` properties, optional 

2499 

2500 %(Figure:kwdoc)s 

2501 """ 

2502 super().__init__(**kwargs) 

2503 self.figure = self 

2504 self._layout_engine = None 

2505 

2506 if layout is not None: 

2507 if (tight_layout is not None): 

2508 _api.warn_external( 

2509 "The Figure parameters 'layout' and 'tight_layout' cannot " 

2510 "be used together. Please use 'layout' only.") 

2511 if (constrained_layout is not None): 

2512 _api.warn_external( 

2513 "The Figure parameters 'layout' and 'constrained_layout' " 

2514 "cannot be used together. Please use 'layout' only.") 

2515 self.set_layout_engine(layout=layout) 

2516 elif tight_layout is not None: 

2517 if constrained_layout is not None: 

2518 _api.warn_external( 

2519 "The Figure parameters 'tight_layout' and " 

2520 "'constrained_layout' cannot be used together. Please use " 

2521 "'layout' parameter") 

2522 self.set_layout_engine(layout='tight') 

2523 if isinstance(tight_layout, dict): 

2524 self.get_layout_engine().set(**tight_layout) 

2525 elif constrained_layout is not None: 

2526 if isinstance(constrained_layout, dict): 

2527 self.set_layout_engine(layout='constrained') 

2528 self.get_layout_engine().set(**constrained_layout) 

2529 elif constrained_layout: 

2530 self.set_layout_engine(layout='constrained') 

2531 

2532 else: 

2533 # everything is None, so use default: 

2534 self.set_layout_engine(layout=layout) 

2535 

2536 # Callbacks traditionally associated with the canvas (and exposed with 

2537 # a proxy property), but that actually need to be on the figure for 

2538 # pickling. 

2539 self._canvas_callbacks = cbook.CallbackRegistry( 

2540 signals=FigureCanvasBase.events) 

2541 connect = self._canvas_callbacks._connect_picklable 

2542 self._mouse_key_ids = [ 

2543 connect('key_press_event', backend_bases._key_handler), 

2544 connect('key_release_event', backend_bases._key_handler), 

2545 connect('key_release_event', backend_bases._key_handler), 

2546 connect('button_press_event', backend_bases._mouse_handler), 

2547 connect('button_release_event', backend_bases._mouse_handler), 

2548 connect('scroll_event', backend_bases._mouse_handler), 

2549 connect('motion_notify_event', backend_bases._mouse_handler), 

2550 ] 

2551 self._button_pick_id = connect('button_press_event', self.pick) 

2552 self._scroll_pick_id = connect('scroll_event', self.pick) 

2553 

2554 if figsize is None: 

2555 figsize = mpl.rcParams['figure.figsize'] 

2556 if dpi is None: 

2557 dpi = mpl.rcParams['figure.dpi'] 

2558 if facecolor is None: 

2559 facecolor = mpl.rcParams['figure.facecolor'] 

2560 if edgecolor is None: 

2561 edgecolor = mpl.rcParams['figure.edgecolor'] 

2562 if frameon is None: 

2563 frameon = mpl.rcParams['figure.frameon'] 

2564 

2565 if not np.isfinite(figsize).all() or (np.array(figsize) < 0).any(): 

2566 raise ValueError('figure size must be positive finite not ' 

2567 f'{figsize}') 

2568 self.bbox_inches = Bbox.from_bounds(0, 0, *figsize) 

2569 

2570 self.dpi_scale_trans = Affine2D().scale(dpi) 

2571 # do not use property as it will trigger 

2572 self._dpi = dpi 

2573 self.bbox = TransformedBbox(self.bbox_inches, self.dpi_scale_trans) 

2574 self.figbbox = self.bbox 

2575 self.transFigure = BboxTransformTo(self.bbox) 

2576 self.transSubfigure = self.transFigure 

2577 

2578 self.patch = Rectangle( 

2579 xy=(0, 0), width=1, height=1, visible=frameon, 

2580 facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth, 

2581 # Don't let the figure patch influence bbox calculation. 

2582 in_layout=False) 

2583 self._set_artist_props(self.patch) 

2584 self.patch.set_antialiased(False) 

2585 

2586 FigureCanvasBase(self) # Set self.canvas. 

2587 

2588 if subplotpars is None: 

2589 subplotpars = SubplotParams() 

2590 

2591 self.subplotpars = subplotpars 

2592 

2593 self._axstack = _AxesStack() # track all figure Axes and current Axes 

2594 self.clear() 

2595 

2596 def pick(self, mouseevent): 

2597 if not self.canvas.widgetlock.locked(): 

2598 super().pick(mouseevent) 

2599 

2600 def _check_layout_engines_compat(self, old, new): 

2601 """ 

2602 Helper for set_layout engine 

2603 

2604 If the figure has used the old engine and added a colorbar then the 

2605 value of colorbar_gridspec must be the same on the new engine. 

2606 """ 

2607 if old is None or new is None: 

2608 return True 

2609 if old.colorbar_gridspec == new.colorbar_gridspec: 

2610 return True 

2611 # colorbar layout different, so check if any colorbars are on the 

2612 # figure... 

2613 for ax in self.axes: 

2614 if hasattr(ax, '_colorbar'): 

2615 # colorbars list themselves as a colorbar. 

2616 return False 

2617 return True 

2618 

2619 def set_layout_engine(self, layout=None, **kwargs): 

2620 """ 

2621 Set the layout engine for this figure. 

2622 

2623 Parameters 

2624 ---------- 

2625 layout : {'constrained', 'compressed', 'tight', 'none', `.LayoutEngine`, None} 

2626 

2627 - 'constrained' will use `~.ConstrainedLayoutEngine` 

2628 - 'compressed' will also use `~.ConstrainedLayoutEngine`, but with 

2629 a correction that attempts to make a good layout for fixed-aspect 

2630 ratio Axes. 

2631 - 'tight' uses `~.TightLayoutEngine` 

2632 - 'none' removes layout engine. 

2633 

2634 If a `.LayoutEngine` instance, that instance will be used. 

2635 

2636 If `None`, the behavior is controlled by :rc:`figure.autolayout` 

2637 (which if `True` behaves as if 'tight' was passed) and 

2638 :rc:`figure.constrained_layout.use` (which if `True` behaves as if 

2639 'constrained' was passed). If both are `True`, 

2640 :rc:`figure.autolayout` takes priority. 

2641 

2642 Users and libraries can define their own layout engines and pass 

2643 the instance directly as well. 

2644 

2645 **kwargs 

2646 The keyword arguments are passed to the layout engine to set things 

2647 like padding and margin sizes. Only used if *layout* is a string. 

2648 

2649 """ 

2650 if layout is None: 

2651 if mpl.rcParams['figure.autolayout']: 

2652 layout = 'tight' 

2653 elif mpl.rcParams['figure.constrained_layout.use']: 

2654 layout = 'constrained' 

2655 else: 

2656 self._layout_engine = None 

2657 return 

2658 if layout == 'tight': 

2659 new_layout_engine = TightLayoutEngine(**kwargs) 

2660 elif layout == 'constrained': 

2661 new_layout_engine = ConstrainedLayoutEngine(**kwargs) 

2662 elif layout == 'compressed': 

2663 new_layout_engine = ConstrainedLayoutEngine(compress=True, 

2664 **kwargs) 

2665 elif layout == 'none': 

2666 if self._layout_engine is not None: 

2667 new_layout_engine = PlaceHolderLayoutEngine( 

2668 self._layout_engine.adjust_compatible, 

2669 self._layout_engine.colorbar_gridspec 

2670 ) 

2671 else: 

2672 new_layout_engine = None 

2673 elif isinstance(layout, LayoutEngine): 

2674 new_layout_engine = layout 

2675 else: 

2676 raise ValueError(f"Invalid value for 'layout': {layout!r}") 

2677 

2678 if self._check_layout_engines_compat(self._layout_engine, 

2679 new_layout_engine): 

2680 self._layout_engine = new_layout_engine 

2681 else: 

2682 raise RuntimeError('Colorbar layout of new layout engine not ' 

2683 'compatible with old engine, and a colorbar ' 

2684 'has been created. Engine not changed.') 

2685 

2686 def get_layout_engine(self): 

2687 return self._layout_engine 

2688 

2689 # TODO: I'd like to dynamically add the _repr_html_ method 

2690 # to the figure in the right context, but then IPython doesn't 

2691 # use it, for some reason. 

2692 

2693 def _repr_html_(self): 

2694 # We can't use "isinstance" here, because then we'd end up importing 

2695 # webagg unconditionally. 

2696 if 'WebAgg' in type(self.canvas).__name__: 

2697 from matplotlib.backends import backend_webagg 

2698 return backend_webagg.ipython_inline_display(self) 

2699 

2700 def show(self, warn=True): 

2701 """ 

2702 If using a GUI backend with pyplot, display the figure window. 

2703 

2704 If the figure was not created using `~.pyplot.figure`, it will lack 

2705 a `~.backend_bases.FigureManagerBase`, and this method will raise an 

2706 AttributeError. 

2707 

2708 .. warning:: 

2709 

2710 This does not manage an GUI event loop. Consequently, the figure 

2711 may only be shown briefly or not shown at all if you or your 

2712 environment are not managing an event loop. 

2713 

2714 Use cases for `.Figure.show` include running this from a GUI 

2715 application (where there is persistently an event loop running) or 

2716 from a shell, like IPython, that install an input hook to allow the 

2717 interactive shell to accept input while the figure is also being 

2718 shown and interactive. Some, but not all, GUI toolkits will 

2719 register an input hook on import. See :ref:`cp_integration` for 

2720 more details. 

2721 

2722 If you're in a shell without input hook integration or executing a 

2723 python script, you should use `matplotlib.pyplot.show` with 

2724 ``block=True`` instead, which takes care of starting and running 

2725 the event loop for you. 

2726 

2727 Parameters 

2728 ---------- 

2729 warn : bool, default: True 

2730 If ``True`` and we are not running headless (i.e. on Linux with an 

2731 unset DISPLAY), issue warning when called on a non-GUI backend. 

2732 

2733 """ 

2734 if self.canvas.manager is None: 

2735 raise AttributeError( 

2736 "Figure.show works only for figures managed by pyplot, " 

2737 "normally created by pyplot.figure()") 

2738 try: 

2739 self.canvas.manager.show() 

2740 except NonGuiException as exc: 

2741 if warn: 

2742 _api.warn_external(str(exc)) 

2743 

2744 @property 

2745 def axes(self): 

2746 """ 

2747 List of Axes in the Figure. You can access and modify the Axes in the 

2748 Figure through this list. 

2749 

2750 Do not modify the list itself. Instead, use `~Figure.add_axes`, 

2751 `~.Figure.add_subplot` or `~.Figure.delaxes` to add or remove an Axes. 

2752 

2753 Note: The `.Figure.axes` property and `~.Figure.get_axes` method are 

2754 equivalent. 

2755 """ 

2756 return self._axstack.as_list() 

2757 

2758 get_axes = axes.fget 

2759 

2760 def _get_renderer(self): 

2761 if hasattr(self.canvas, 'get_renderer'): 

2762 return self.canvas.get_renderer() 

2763 else: 

2764 return _get_renderer(self) 

2765 

2766 def _get_dpi(self): 

2767 return self._dpi 

2768 

2769 def _set_dpi(self, dpi, forward=True): 

2770 """ 

2771 Parameters 

2772 ---------- 

2773 dpi : float 

2774 

2775 forward : bool 

2776 Passed on to `~.Figure.set_size_inches` 

2777 """ 

2778 if dpi == self._dpi: 

2779 # We don't want to cause undue events in backends. 

2780 return 

2781 self._dpi = dpi 

2782 self.dpi_scale_trans.clear().scale(dpi) 

2783 w, h = self.get_size_inches() 

2784 self.set_size_inches(w, h, forward=forward) 

2785 

2786 dpi = property(_get_dpi, _set_dpi, doc="The resolution in dots per inch.") 

2787 

2788 def get_tight_layout(self): 

2789 """Return whether `.Figure.tight_layout` is called when drawing.""" 

2790 return isinstance(self.get_layout_engine(), TightLayoutEngine) 

2791 

2792 @_api.deprecated("3.6", alternative="set_layout_engine", 

2793 pending=True) 

2794 def set_tight_layout(self, tight): 

2795 """ 

2796 Set whether and how `.Figure.tight_layout` is called when drawing. 

2797 

2798 Parameters 

2799 ---------- 

2800 tight : bool or dict with keys "pad", "w_pad", "h_pad", "rect" or None 

2801 If a bool, sets whether to call `.Figure.tight_layout` upon drawing. 

2802 If ``None``, use :rc:`figure.autolayout` instead. 

2803 If a dict, pass it as kwargs to `.Figure.tight_layout`, overriding the 

2804 default paddings. 

2805 """ 

2806 if tight is None: 

2807 tight = mpl.rcParams['figure.autolayout'] 

2808 _tight = 'tight' if bool(tight) else 'none' 

2809 _tight_parameters = tight if isinstance(tight, dict) else {} 

2810 self.set_layout_engine(_tight, **_tight_parameters) 

2811 self.stale = True 

2812 

2813 def get_constrained_layout(self): 

2814 """ 

2815 Return whether constrained layout is being used. 

2816 

2817 See :ref:`constrainedlayout_guide`. 

2818 """ 

2819 return isinstance(self.get_layout_engine(), ConstrainedLayoutEngine) 

2820 

2821 @_api.deprecated("3.6", alternative="set_layout_engine('constrained')", 

2822 pending=True) 

2823 def set_constrained_layout(self, constrained): 

2824 """ 

2825 Set whether ``constrained_layout`` is used upon drawing. 

2826 

2827 If None, :rc:`figure.constrained_layout.use` value will be used. 

2828 

2829 When providing a dict containing the keys ``w_pad``, ``h_pad`` 

2830 the default ``constrained_layout`` paddings will be 

2831 overridden. These pads are in inches and default to 3.0/72.0. 

2832 ``w_pad`` is the width padding and ``h_pad`` is the height padding. 

2833 

2834 Parameters 

2835 ---------- 

2836 constrained : bool or dict or None 

2837 """ 

2838 if constrained is None: 

2839 constrained = mpl.rcParams['figure.constrained_layout.use'] 

2840 _constrained = 'constrained' if bool(constrained) else 'none' 

2841 _parameters = constrained if isinstance(constrained, dict) else {} 

2842 self.set_layout_engine(_constrained, **_parameters) 

2843 self.stale = True 

2844 

2845 @_api.deprecated( 

2846 "3.6", alternative="figure.get_layout_engine().set()", 

2847 pending=True) 

2848 def set_constrained_layout_pads(self, **kwargs): 

2849 """ 

2850 Set padding for ``constrained_layout``. 

2851 

2852 Tip: The parameters can be passed from a dictionary by using 

2853 ``fig.set_constrained_layout(**pad_dict)``. 

2854 

2855 See :ref:`constrainedlayout_guide`. 

2856 

2857 Parameters 

2858 ---------- 

2859 w_pad : float, default: :rc:`figure.constrained_layout.w_pad` 

2860 Width padding in inches. This is the pad around Axes 

2861 and is meant to make sure there is enough room for fonts to 

2862 look good. Defaults to 3 pts = 0.04167 inches 

2863 

2864 h_pad : float, default: :rc:`figure.constrained_layout.h_pad` 

2865 Height padding in inches. Defaults to 3 pts. 

2866 

2867 wspace : float, default: :rc:`figure.constrained_layout.wspace` 

2868 Width padding between subplots, expressed as a fraction of the 

2869 subplot width. The total padding ends up being w_pad + wspace. 

2870 

2871 hspace : float, default: :rc:`figure.constrained_layout.hspace` 

2872 Height padding between subplots, expressed as a fraction of the 

2873 subplot width. The total padding ends up being h_pad + hspace. 

2874 

2875 """ 

2876 if isinstance(self.get_layout_engine(), ConstrainedLayoutEngine): 

2877 self.get_layout_engine().set(**kwargs) 

2878 

2879 @_api.deprecated("3.6", alternative="fig.get_layout_engine().get()", 

2880 pending=True) 

2881 def get_constrained_layout_pads(self, relative=False): 

2882 """ 

2883 Get padding for ``constrained_layout``. 

2884 

2885 Returns a list of ``w_pad, h_pad`` in inches and 

2886 ``wspace`` and ``hspace`` as fractions of the subplot. 

2887 All values are None if ``constrained_layout`` is not used. 

2888 

2889 See :ref:`constrainedlayout_guide`. 

2890 

2891 Parameters 

2892 ---------- 

2893 relative : bool 

2894 If `True`, then convert from inches to figure relative. 

2895 """ 

2896 if not isinstance(self.get_layout_engine(), ConstrainedLayoutEngine): 

2897 return None, None, None, None 

2898 info = self.get_layout_engine().get() 

2899 w_pad = info['w_pad'] 

2900 h_pad = info['h_pad'] 

2901 wspace = info['wspace'] 

2902 hspace = info['hspace'] 

2903 

2904 if relative and (w_pad is not None or h_pad is not None): 

2905 renderer = self._get_renderer() 

2906 dpi = renderer.dpi 

2907 w_pad = w_pad * dpi / renderer.width 

2908 h_pad = h_pad * dpi / renderer.height 

2909 

2910 return w_pad, h_pad, wspace, hspace 

2911 

2912 def set_canvas(self, canvas): 

2913 """ 

2914 Set the canvas that contains the figure 

2915 

2916 Parameters 

2917 ---------- 

2918 canvas : FigureCanvas 

2919 """ 

2920 self.canvas = canvas 

2921 

2922 @_docstring.interpd 

2923 def figimage(self, X, xo=0, yo=0, alpha=None, norm=None, cmap=None, 

2924 vmin=None, vmax=None, origin=None, resize=False, **kwargs): 

2925 """ 

2926 Add a non-resampled image to the figure. 

2927 

2928 The image is attached to the lower or upper left corner depending on 

2929 *origin*. 

2930 

2931 Parameters 

2932 ---------- 

2933 X 

2934 The image data. This is an array of one of the following shapes: 

2935 

2936 - (M, N): an image with scalar data. Color-mapping is controlled 

2937 by *cmap*, *norm*, *vmin*, and *vmax*. 

2938 - (M, N, 3): an image with RGB values (0-1 float or 0-255 int). 

2939 - (M, N, 4): an image with RGBA values (0-1 float or 0-255 int), 

2940 i.e. including transparency. 

2941 

2942 xo, yo : int 

2943 The *x*/*y* image offset in pixels. 

2944 

2945 alpha : None or float 

2946 The alpha blending value. 

2947 

2948 %(cmap_doc)s 

2949 

2950 This parameter is ignored if *X* is RGB(A). 

2951 

2952 %(norm_doc)s 

2953 

2954 This parameter is ignored if *X* is RGB(A). 

2955 

2956 %(vmin_vmax_doc)s 

2957 

2958 This parameter is ignored if *X* is RGB(A). 

2959 

2960 origin : {'upper', 'lower'}, default: :rc:`image.origin` 

2961 Indicates where the [0, 0] index of the array is in the upper left 

2962 or lower left corner of the Axes. 

2963 

2964 resize : bool 

2965 If *True*, resize the figure to match the given image size. 

2966 

2967 Returns 

2968 ------- 

2969 `matplotlib.image.FigureImage` 

2970 

2971 Other Parameters 

2972 ---------------- 

2973 **kwargs 

2974 Additional kwargs are `.Artist` kwargs passed on to `.FigureImage`. 

2975 

2976 Notes 

2977 ----- 

2978 figimage complements the Axes image (`~matplotlib.axes.Axes.imshow`) 

2979 which will be resampled to fit the current Axes. If you want 

2980 a resampled image to fill the entire figure, you can define an 

2981 `~matplotlib.axes.Axes` with extent [0, 0, 1, 1]. 

2982 

2983 Examples 

2984 -------- 

2985 :: 

2986 

2987 f = plt.figure() 

2988 nx = int(f.get_figwidth() * f.dpi) 

2989 ny = int(f.get_figheight() * f.dpi) 

2990 data = np.random.random((ny, nx)) 

2991 f.figimage(data) 

2992 plt.show() 

2993 """ 

2994 if resize: 

2995 dpi = self.get_dpi() 

2996 figsize = [x / dpi for x in (X.shape[1], X.shape[0])] 

2997 self.set_size_inches(figsize, forward=True) 

2998 

2999 im = mimage.FigureImage(self, cmap=cmap, norm=norm, 

3000 offsetx=xo, offsety=yo, 

3001 origin=origin, **kwargs) 

3002 im.stale_callback = _stale_figure_callback 

3003 

3004 im.set_array(X) 

3005 im.set_alpha(alpha) 

3006 if norm is None: 

3007 im.set_clim(vmin, vmax) 

3008 self.images.append(im) 

3009 im._remove_method = self.images.remove 

3010 self.stale = True 

3011 return im 

3012 

3013 def set_size_inches(self, w, h=None, forward=True): 

3014 """ 

3015 Set the figure size in inches. 

3016 

3017 Call signatures:: 

3018 

3019 fig.set_size_inches(w, h) # OR 

3020 fig.set_size_inches((w, h)) 

3021 

3022 Parameters 

3023 ---------- 

3024 w : (float, float) or float 

3025 Width and height in inches (if height not specified as a separate 

3026 argument) or width. 

3027 h : float 

3028 Height in inches. 

3029 forward : bool, default: True 

3030 If ``True``, the canvas size is automatically updated, e.g., 

3031 you can resize the figure window from the shell. 

3032 

3033 See Also 

3034 -------- 

3035 matplotlib.figure.Figure.get_size_inches 

3036 matplotlib.figure.Figure.set_figwidth 

3037 matplotlib.figure.Figure.set_figheight 

3038 

3039 Notes 

3040 ----- 

3041 To transform from pixels to inches divide by `Figure.dpi`. 

3042 """ 

3043 if h is None: # Got called with a single pair as argument. 

3044 w, h = w 

3045 size = np.array([w, h]) 

3046 if not np.isfinite(size).all() or (size < 0).any(): 

3047 raise ValueError(f'figure size must be positive finite not {size}') 

3048 self.bbox_inches.p1 = size 

3049 if forward: 

3050 manager = self.canvas.manager 

3051 if manager is not None: 

3052 manager.resize(*(size * self.dpi).astype(int)) 

3053 self.stale = True 

3054 

3055 def get_size_inches(self): 

3056 """ 

3057 Return the current size of the figure in inches. 

3058 

3059 Returns 

3060 ------- 

3061 ndarray 

3062 The size (width, height) of the figure in inches. 

3063 

3064 See Also 

3065 -------- 

3066 matplotlib.figure.Figure.set_size_inches 

3067 matplotlib.figure.Figure.get_figwidth 

3068 matplotlib.figure.Figure.get_figheight 

3069 

3070 Notes 

3071 ----- 

3072 The size in pixels can be obtained by multiplying with `Figure.dpi`. 

3073 """ 

3074 return np.array(self.bbox_inches.p1) 

3075 

3076 def get_figwidth(self): 

3077 """Return the figure width in inches.""" 

3078 return self.bbox_inches.width 

3079 

3080 def get_figheight(self): 

3081 """Return the figure height in inches.""" 

3082 return self.bbox_inches.height 

3083 

3084 def get_dpi(self): 

3085 """Return the resolution in dots per inch as a float.""" 

3086 return self.dpi 

3087 

3088 def set_dpi(self, val): 

3089 """ 

3090 Set the resolution of the figure in dots-per-inch. 

3091 

3092 Parameters 

3093 ---------- 

3094 val : float 

3095 """ 

3096 self.dpi = val 

3097 self.stale = True 

3098 

3099 def set_figwidth(self, val, forward=True): 

3100 """ 

3101 Set the width of the figure in inches. 

3102 

3103 Parameters 

3104 ---------- 

3105 val : float 

3106 forward : bool 

3107 See `set_size_inches`. 

3108 

3109 See Also 

3110 -------- 

3111 matplotlib.figure.Figure.set_figheight 

3112 matplotlib.figure.Figure.set_size_inches 

3113 """ 

3114 self.set_size_inches(val, self.get_figheight(), forward=forward) 

3115 

3116 def set_figheight(self, val, forward=True): 

3117 """ 

3118 Set the height of the figure in inches. 

3119 

3120 Parameters 

3121 ---------- 

3122 val : float 

3123 forward : bool 

3124 See `set_size_inches`. 

3125 

3126 See Also 

3127 -------- 

3128 matplotlib.figure.Figure.set_figwidth 

3129 matplotlib.figure.Figure.set_size_inches 

3130 """ 

3131 self.set_size_inches(self.get_figwidth(), val, forward=forward) 

3132 

3133 def clear(self, keep_observers=False): 

3134 # docstring inherited 

3135 super().clear(keep_observers=keep_observers) 

3136 # FigureBase.clear does not clear toolbars, as 

3137 # only Figure can have toolbars 

3138 toolbar = self.canvas.toolbar 

3139 if toolbar is not None: 

3140 toolbar.update() 

3141 

3142 @_finalize_rasterization 

3143 @allow_rasterization 

3144 def draw(self, renderer): 

3145 # docstring inherited 

3146 if not self.get_visible(): 

3147 return 

3148 

3149 with self._render_lock: 

3150 

3151 artists = self._get_draw_artists(renderer) 

3152 try: 

3153 renderer.open_group('figure', gid=self.get_gid()) 

3154 if self.axes and self.get_layout_engine() is not None: 

3155 try: 

3156 self.get_layout_engine().execute(self) 

3157 except ValueError: 

3158 pass 

3159 # ValueError can occur when resizing a window. 

3160 

3161 self.patch.draw(renderer) 

3162 mimage._draw_list_compositing_images( 

3163 renderer, self, artists, self.suppressComposite) 

3164 

3165 renderer.close_group('figure') 

3166 finally: 

3167 self.stale = False 

3168 

3169 DrawEvent("draw_event", self.canvas, renderer)._process() 

3170 

3171 def draw_without_rendering(self): 

3172 """ 

3173 Draw the figure with no output. Useful to get the final size of 

3174 artists that require a draw before their size is known (e.g. text). 

3175 """ 

3176 renderer = _get_renderer(self) 

3177 with renderer._draw_disabled(): 

3178 self.draw(renderer) 

3179 

3180 def draw_artist(self, a): 

3181 """ 

3182 Draw `.Artist` *a* only. 

3183 """ 

3184 a.draw(self.canvas.get_renderer()) 

3185 

3186 def __getstate__(self): 

3187 state = super().__getstate__() 

3188 

3189 # The canvas cannot currently be pickled, but this has the benefit 

3190 # of meaning that a figure can be detached from one canvas, and 

3191 # re-attached to another. 

3192 state.pop("canvas") 

3193 

3194 # discard any changes to the dpi due to pixel ratio changes 

3195 state["_dpi"] = state.get('_original_dpi', state['_dpi']) 

3196 

3197 # add version information to the state 

3198 state['__mpl_version__'] = mpl.__version__ 

3199 

3200 # check whether the figure manager (if any) is registered with pyplot 

3201 from matplotlib import _pylab_helpers 

3202 if self.canvas.manager in _pylab_helpers.Gcf.figs.values(): 

3203 state['_restore_to_pylab'] = True 

3204 return state 

3205 

3206 def __setstate__(self, state): 

3207 version = state.pop('__mpl_version__') 

3208 restore_to_pylab = state.pop('_restore_to_pylab', False) 

3209 

3210 if version != mpl.__version__: 

3211 _api.warn_external( 

3212 f"This figure was saved with matplotlib version {version} and " 

3213 f"loaded with {mpl.__version__} so may not function correctly." 

3214 ) 

3215 self.__dict__ = state 

3216 

3217 # re-initialise some of the unstored state information 

3218 FigureCanvasBase(self) # Set self.canvas. 

3219 

3220 if restore_to_pylab: 

3221 # lazy import to avoid circularity 

3222 import matplotlib.pyplot as plt 

3223 import matplotlib._pylab_helpers as pylab_helpers 

3224 allnums = plt.get_fignums() 

3225 num = max(allnums) + 1 if allnums else 1 

3226 backend = plt._get_backend_mod() 

3227 mgr = backend.new_figure_manager_given_figure(num, self) 

3228 pylab_helpers.Gcf._set_new_active_manager(mgr) 

3229 plt.draw_if_interactive() 

3230 

3231 self.stale = True 

3232 

3233 def add_axobserver(self, func): 

3234 """Whenever the Axes state change, ``func(self)`` will be called.""" 

3235 # Connect a wrapper lambda and not func itself, to avoid it being 

3236 # weakref-collected. 

3237 self._axobservers.connect("_axes_change_event", lambda arg: func(arg)) 

3238 

3239 def savefig(self, fname, *, transparent=None, **kwargs): 

3240 """ 

3241 Save the current figure as an image or vector graphic to a file. 

3242 

3243 Call signature:: 

3244 

3245 savefig(fname, *, transparent=None, dpi='figure', format=None, 

3246 metadata=None, bbox_inches=None, pad_inches=0.1, 

3247 facecolor='auto', edgecolor='auto', backend=None, 

3248 **kwargs 

3249 ) 

3250 

3251 The available output formats depend on the backend being used. 

3252 

3253 Parameters 

3254 ---------- 

3255 fname : str or path-like or binary file-like 

3256 A path, or a Python file-like object, or 

3257 possibly some backend-dependent object such as 

3258 `matplotlib.backends.backend_pdf.PdfPages`. 

3259 

3260 If *format* is set, it determines the output format, and the file 

3261 is saved as *fname*. Note that *fname* is used verbatim, and there 

3262 is no attempt to make the extension, if any, of *fname* match 

3263 *format*, and no extension is appended. 

3264 

3265 If *format* is not set, then the format is inferred from the 

3266 extension of *fname*, if there is one. If *format* is not 

3267 set and *fname* has no extension, then the file is saved with 

3268 :rc:`savefig.format` and the appropriate extension is appended to 

3269 *fname*. 

3270 

3271 Other Parameters 

3272 ---------------- 

3273 transparent : bool, default: :rc:`savefig.transparent` 

3274 If *True*, the Axes patches will all be transparent; the 

3275 Figure patch will also be transparent unless *facecolor* 

3276 and/or *edgecolor* are specified via kwargs. 

3277 

3278 If *False* has no effect and the color of the Axes and 

3279 Figure patches are unchanged (unless the Figure patch 

3280 is specified via the *facecolor* and/or *edgecolor* keyword 

3281 arguments in which case those colors are used). 

3282 

3283 The transparency of these patches will be restored to their 

3284 original values upon exit of this function. 

3285 

3286 This is useful, for example, for displaying 

3287 a plot on top of a colored background on a web page. 

3288 

3289 dpi : float or 'figure', default: :rc:`savefig.dpi` 

3290 The resolution in dots per inch. If 'figure', use the figure's 

3291 dpi value. 

3292 

3293 format : str 

3294 The file format, e.g. 'png', 'pdf', 'svg', ... The behavior when 

3295 this is unset is documented under *fname*. 

3296 

3297 metadata : dict, optional 

3298 Key/value pairs to store in the image metadata. The supported keys 

3299 and defaults depend on the image format and backend: 

3300 

3301 - 'png' with Agg backend: See the parameter ``metadata`` of 

3302 `~.FigureCanvasAgg.print_png`. 

3303 - 'pdf' with pdf backend: See the parameter ``metadata`` of 

3304 `~.backend_pdf.PdfPages`. 

3305 - 'svg' with svg backend: See the parameter ``metadata`` of 

3306 `~.FigureCanvasSVG.print_svg`. 

3307 - 'eps' and 'ps' with PS backend: Only 'Creator' is supported. 

3308 

3309 Not supported for 'pgf', 'raw', and 'rgba' as those formats do not support 

3310 embedding metadata. 

3311 Does not currently support 'jpg', 'tiff', or 'webp', but may include 

3312 embedding EXIF metadata in the future. 

3313 

3314 bbox_inches : str or `.Bbox`, default: :rc:`savefig.bbox` 

3315 Bounding box in inches: only the given portion of the figure is 

3316 saved. If 'tight', try to figure out the tight bbox of the figure. 

3317 

3318 pad_inches : float or 'layout', default: :rc:`savefig.pad_inches` 

3319 Amount of padding in inches around the figure when bbox_inches is 

3320 'tight'. If 'layout' use the padding from the constrained or 

3321 compressed layout engine; ignored if one of those engines is not in 

3322 use. 

3323 

3324 facecolor : :mpltype:`color` or 'auto', default: :rc:`savefig.facecolor` 

3325 The facecolor of the figure. If 'auto', use the current figure 

3326 facecolor. 

3327 

3328 edgecolor : :mpltype:`color` or 'auto', default: :rc:`savefig.edgecolor` 

3329 The edgecolor of the figure. If 'auto', use the current figure 

3330 edgecolor. 

3331 

3332 backend : str, optional 

3333 Use a non-default backend to render the file, e.g. to render a 

3334 png file with the "cairo" backend rather than the default "agg", 

3335 or a pdf file with the "pgf" backend rather than the default 

3336 "pdf". Note that the default backend is normally sufficient. See 

3337 :ref:`the-builtin-backends` for a list of valid backends for each 

3338 file format. Custom backends can be referenced as "module://...". 

3339 

3340 orientation : {'landscape', 'portrait'} 

3341 Currently only supported by the postscript backend. 

3342 

3343 papertype : str 

3344 One of 'letter', 'legal', 'executive', 'ledger', 'a0' through 

3345 'a10', 'b0' through 'b10'. Only supported for postscript 

3346 output. 

3347 

3348 bbox_extra_artists : list of `~matplotlib.artist.Artist`, optional 

3349 A list of extra artists that will be considered when the 

3350 tight bbox is calculated. 

3351 

3352 pil_kwargs : dict, optional 

3353 Additional keyword arguments that are passed to 

3354 `PIL.Image.Image.save` when saving the figure. 

3355 

3356 """ 

3357 

3358 kwargs.setdefault('dpi', mpl.rcParams['savefig.dpi']) 

3359 if transparent is None: 

3360 transparent = mpl.rcParams['savefig.transparent'] 

3361 

3362 with ExitStack() as stack: 

3363 if transparent: 

3364 def _recursively_make_subfig_transparent(exit_stack, subfig): 

3365 exit_stack.enter_context( 

3366 subfig.patch._cm_set( 

3367 facecolor="none", edgecolor="none")) 

3368 for ax in subfig.axes: 

3369 exit_stack.enter_context( 

3370 ax.patch._cm_set( 

3371 facecolor="none", edgecolor="none")) 

3372 for sub_subfig in subfig.subfigs: 

3373 _recursively_make_subfig_transparent( 

3374 exit_stack, sub_subfig) 

3375 

3376 def _recursively_make_axes_transparent(exit_stack, ax): 

3377 exit_stack.enter_context( 

3378 ax.patch._cm_set(facecolor="none", edgecolor="none")) 

3379 for child_ax in ax.child_axes: 

3380 exit_stack.enter_context( 

3381 child_ax.patch._cm_set( 

3382 facecolor="none", edgecolor="none")) 

3383 for child_childax in ax.child_axes: 

3384 _recursively_make_axes_transparent( 

3385 exit_stack, child_childax) 

3386 

3387 kwargs.setdefault('facecolor', 'none') 

3388 kwargs.setdefault('edgecolor', 'none') 

3389 # set subfigure to appear transparent in printed image 

3390 for subfig in self.subfigs: 

3391 _recursively_make_subfig_transparent(stack, subfig) 

3392 # set Axes to be transparent 

3393 for ax in self.axes: 

3394 _recursively_make_axes_transparent(stack, ax) 

3395 self.canvas.print_figure(fname, **kwargs) 

3396 

3397 def ginput(self, n=1, timeout=30, show_clicks=True, 

3398 mouse_add=MouseButton.LEFT, 

3399 mouse_pop=MouseButton.RIGHT, 

3400 mouse_stop=MouseButton.MIDDLE): 

3401 """ 

3402 Blocking call to interact with a figure. 

3403 

3404 Wait until the user clicks *n* times on the figure, and return the 

3405 coordinates of each click in a list. 

3406 

3407 There are three possible interactions: 

3408 

3409 - Add a point. 

3410 - Remove the most recently added point. 

3411 - Stop the interaction and return the points added so far. 

3412 

3413 The actions are assigned to mouse buttons via the arguments 

3414 *mouse_add*, *mouse_pop* and *mouse_stop*. 

3415 

3416 Parameters 

3417 ---------- 

3418 n : int, default: 1 

3419 Number of mouse clicks to accumulate. If negative, accumulate 

3420 clicks until the input is terminated manually. 

3421 timeout : float, default: 30 seconds 

3422 Number of seconds to wait before timing out. If zero or negative 

3423 will never time out. 

3424 show_clicks : bool, default: True 

3425 If True, show a red cross at the location of each click. 

3426 mouse_add : `.MouseButton` or None, default: `.MouseButton.LEFT` 

3427 Mouse button used to add points. 

3428 mouse_pop : `.MouseButton` or None, default: `.MouseButton.RIGHT` 

3429 Mouse button used to remove the most recently added point. 

3430 mouse_stop : `.MouseButton` or None, default: `.MouseButton.MIDDLE` 

3431 Mouse button used to stop input. 

3432 

3433 Returns 

3434 ------- 

3435 list of tuples 

3436 A list of the clicked (x, y) coordinates. 

3437 

3438 Notes 

3439 ----- 

3440 The keyboard can also be used to select points in case your mouse 

3441 does not have one or more of the buttons. The delete and backspace 

3442 keys act like right-clicking (i.e., remove last point), the enter key 

3443 terminates input and any other key (not already used by the window 

3444 manager) selects a point. 

3445 """ 

3446 clicks = [] 

3447 marks = [] 

3448 

3449 def handler(event): 

3450 is_button = event.name == "button_press_event" 

3451 is_key = event.name == "key_press_event" 

3452 # Quit (even if not in infinite mode; this is consistent with 

3453 # MATLAB and sometimes quite useful, but will require the user to 

3454 # test how many points were actually returned before using data). 

3455 if (is_button and event.button == mouse_stop 

3456 or is_key and event.key in ["escape", "enter"]): 

3457 self.canvas.stop_event_loop() 

3458 # Pop last click. 

3459 elif (is_button and event.button == mouse_pop 

3460 or is_key and event.key in ["backspace", "delete"]): 

3461 if clicks: 

3462 clicks.pop() 

3463 if show_clicks: 

3464 marks.pop().remove() 

3465 self.canvas.draw() 

3466 # Add new click. 

3467 elif (is_button and event.button == mouse_add 

3468 # On macOS/gtk, some keys return None. 

3469 or is_key and event.key is not None): 

3470 if event.inaxes: 

3471 clicks.append((event.xdata, event.ydata)) 

3472 _log.info("input %i: %f, %f", 

3473 len(clicks), event.xdata, event.ydata) 

3474 if show_clicks: 

3475 line = mpl.lines.Line2D([event.xdata], [event.ydata], 

3476 marker="+", color="r") 

3477 event.inaxes.add_line(line) 

3478 marks.append(line) 

3479 self.canvas.draw() 

3480 if len(clicks) == n and n > 0: 

3481 self.canvas.stop_event_loop() 

3482 

3483 _blocking_input.blocking_input_loop( 

3484 self, ["button_press_event", "key_press_event"], timeout, handler) 

3485 

3486 # Cleanup. 

3487 for mark in marks: 

3488 mark.remove() 

3489 self.canvas.draw() 

3490 

3491 return clicks 

3492 

3493 def waitforbuttonpress(self, timeout=-1): 

3494 """ 

3495 Blocking call to interact with the figure. 

3496 

3497 Wait for user input and return True if a key was pressed, False if a 

3498 mouse button was pressed and None if no input was given within 

3499 *timeout* seconds. Negative values deactivate *timeout*. 

3500 """ 

3501 event = None 

3502 

3503 def handler(ev): 

3504 nonlocal event 

3505 event = ev 

3506 self.canvas.stop_event_loop() 

3507 

3508 _blocking_input.blocking_input_loop( 

3509 self, ["button_press_event", "key_press_event"], timeout, handler) 

3510 

3511 return None if event is None else event.name == "key_press_event" 

3512 

3513 def tight_layout(self, *, pad=1.08, h_pad=None, w_pad=None, rect=None): 

3514 """ 

3515 Adjust the padding between and around subplots. 

3516 

3517 To exclude an artist on the Axes from the bounding box calculation 

3518 that determines the subplot parameters (i.e. legend, or annotation), 

3519 set ``a.set_in_layout(False)`` for that artist. 

3520 

3521 Parameters 

3522 ---------- 

3523 pad : float, default: 1.08 

3524 Padding between the figure edge and the edges of subplots, 

3525 as a fraction of the font size. 

3526 h_pad, w_pad : float, default: *pad* 

3527 Padding (height/width) between edges of adjacent subplots, 

3528 as a fraction of the font size. 

3529 rect : tuple (left, bottom, right, top), default: (0, 0, 1, 1) 

3530 A rectangle in normalized figure coordinates into which the whole 

3531 subplots area (including labels) will fit. 

3532 

3533 See Also 

3534 -------- 

3535 .Figure.set_layout_engine 

3536 .pyplot.tight_layout 

3537 """ 

3538 # note that here we do not permanently set the figures engine to 

3539 # tight_layout but rather just perform the layout in place and remove 

3540 # any previous engines. 

3541 engine = TightLayoutEngine(pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) 

3542 try: 

3543 previous_engine = self.get_layout_engine() 

3544 self.set_layout_engine(engine) 

3545 engine.execute(self) 

3546 if previous_engine is not None and not isinstance( 

3547 previous_engine, (TightLayoutEngine, PlaceHolderLayoutEngine) 

3548 ): 

3549 _api.warn_external('The figure layout has changed to tight') 

3550 finally: 

3551 self.set_layout_engine('none') 

3552 

3553 

3554def figaspect(arg): 

3555 """ 

3556 Calculate the width and height for a figure with a specified aspect ratio. 

3557 

3558 While the height is taken from :rc:`figure.figsize`, the width is 

3559 adjusted to match the desired aspect ratio. Additionally, it is ensured 

3560 that the width is in the range [4., 16.] and the height is in the range 

3561 [2., 16.]. If necessary, the default height is adjusted to ensure this. 

3562 

3563 Parameters 

3564 ---------- 

3565 arg : float or 2D array 

3566 If a float, this defines the aspect ratio (i.e. the ratio height / 

3567 width). 

3568 In case of an array the aspect ratio is number of rows / number of 

3569 columns, so that the array could be fitted in the figure undistorted. 

3570 

3571 Returns 

3572 ------- 

3573 width, height : float 

3574 The figure size in inches. 

3575 

3576 Notes 

3577 ----- 

3578 If you want to create an Axes within the figure, that still preserves the 

3579 aspect ratio, be sure to create it with equal width and height. See 

3580 examples below. 

3581 

3582 Thanks to Fernando Perez for this function. 

3583 

3584 Examples 

3585 -------- 

3586 Make a figure twice as tall as it is wide:: 

3587 

3588 w, h = figaspect(2.) 

3589 fig = Figure(figsize=(w, h)) 

3590 ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]) 

3591 ax.imshow(A, **kwargs) 

3592 

3593 Make a figure with the proper aspect for an array:: 

3594 

3595 A = rand(5, 3) 

3596 w, h = figaspect(A) 

3597 fig = Figure(figsize=(w, h)) 

3598 ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]) 

3599 ax.imshow(A, **kwargs) 

3600 """ 

3601 

3602 isarray = hasattr(arg, 'shape') and not np.isscalar(arg) 

3603 

3604 # min/max sizes to respect when autoscaling. If John likes the idea, they 

3605 # could become rc parameters, for now they're hardwired. 

3606 figsize_min = np.array((4.0, 2.0)) # min length for width/height 

3607 figsize_max = np.array((16.0, 16.0)) # max length for width/height 

3608 

3609 # Extract the aspect ratio of the array 

3610 if isarray: 

3611 nr, nc = arg.shape[:2] 

3612 arr_ratio = nr / nc 

3613 else: 

3614 arr_ratio = arg 

3615 

3616 # Height of user figure defaults 

3617 fig_height = mpl.rcParams['figure.figsize'][1] 

3618 

3619 # New size for the figure, keeping the aspect ratio of the caller 

3620 newsize = np.array((fig_height / arr_ratio, fig_height)) 

3621 

3622 # Sanity checks, don't drop either dimension below figsize_min 

3623 newsize /= min(1.0, *(newsize / figsize_min)) 

3624 

3625 # Avoid humongous windows as well 

3626 newsize /= max(1.0, *(newsize / figsize_max)) 

3627 

3628 # Finally, if we have a really funky aspect ratio, break it but respect 

3629 # the min/max dimensions (we don't want figures 10 feet tall!) 

3630 newsize = np.clip(newsize, figsize_min, figsize_max) 

3631 return newsize