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