figure.py 139 KB

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