axis.py 90 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537
  1. """
  2. Classes for the ticks and x and y axis.
  3. """
  4. import datetime
  5. import functools
  6. import logging
  7. import numpy as np
  8. import matplotlib as mpl
  9. import matplotlib.artist as martist
  10. import matplotlib.cbook as cbook
  11. import matplotlib.lines as mlines
  12. import matplotlib.scale as mscale
  13. import matplotlib.text as mtext
  14. import matplotlib.ticker as mticker
  15. import matplotlib.transforms as mtransforms
  16. import matplotlib.units as munits
  17. _log = logging.getLogger(__name__)
  18. GRIDLINE_INTERPOLATION_STEPS = 180
  19. # This list is being used for compatibility with Axes.grid, which
  20. # allows all Line2D kwargs.
  21. _line_inspector = martist.ArtistInspector(mlines.Line2D)
  22. _line_param_names = _line_inspector.get_setters()
  23. _line_param_aliases = [list(d)[0] for d in _line_inspector.aliasd.values()]
  24. _gridline_param_names = ['grid_' + name
  25. for name in _line_param_names + _line_param_aliases]
  26. class Tick(martist.Artist):
  27. """
  28. Abstract base class for the axis ticks, grid lines and labels.
  29. Ticks mark a position on an Axis. They contain two lines as markers and
  30. two labels; one each for the bottom and top positions (in case of an
  31. `.XAxis`) or for the left and right positions (in case of a `.YAxis`).
  32. Attributes
  33. ----------
  34. tick1line : `.Line2D`
  35. The left/bottom tick marker.
  36. tick2line : `.Line2D`
  37. The right/top tick marker.
  38. gridline : `.Line2D`
  39. The grid line associated with the label position.
  40. label1 : `.Text`
  41. The left/bottom tick label.
  42. label2 : `.Text`
  43. The right/top tick label.
  44. """
  45. @cbook._delete_parameter("3.3", "label")
  46. def __init__(self, axes, loc, label=None,
  47. size=None, # points
  48. width=None,
  49. color=None,
  50. tickdir=None,
  51. pad=None,
  52. labelsize=None,
  53. labelcolor=None,
  54. zorder=None,
  55. gridOn=None, # defaults to axes.grid depending on
  56. # axes.grid.which
  57. tick1On=True,
  58. tick2On=True,
  59. label1On=True,
  60. label2On=False,
  61. major=True,
  62. labelrotation=0,
  63. grid_color=None,
  64. grid_linestyle=None,
  65. grid_linewidth=None,
  66. grid_alpha=None,
  67. **kw # Other Line2D kwargs applied to gridlines.
  68. ):
  69. """
  70. bbox is the Bound2D bounding box in display coords of the Axes
  71. loc is the tick location in data coords
  72. size is the tick size in points
  73. """
  74. martist.Artist.__init__(self)
  75. if gridOn is None:
  76. if major and (mpl.rcParams['axes.grid.which']
  77. in ('both', 'major')):
  78. gridOn = mpl.rcParams['axes.grid']
  79. elif (not major) and (mpl.rcParams['axes.grid.which']
  80. in ('both', 'minor')):
  81. gridOn = mpl.rcParams['axes.grid']
  82. else:
  83. gridOn = False
  84. self.set_figure(axes.figure)
  85. self.axes = axes
  86. name = self.__name__.lower()
  87. self._loc = loc
  88. self._major = major
  89. major_minor = "major" if major else "minor"
  90. if size is None:
  91. size = mpl.rcParams[f"{name}.{major_minor}.size"]
  92. self._size = size
  93. if width is None:
  94. width = mpl.rcParams[f"{name}.{major_minor}.width"]
  95. self._width = width
  96. if color is None:
  97. color = mpl.rcParams[f"{name}.color"]
  98. if pad is None:
  99. pad = mpl.rcParams[f"{name}.{major_minor}.pad"]
  100. self._base_pad = pad
  101. if labelcolor is None:
  102. labelcolor = mpl.rcParams[f"{name}.color"]
  103. if labelsize is None:
  104. labelsize = mpl.rcParams[f"{name}.labelsize"]
  105. self._set_labelrotation(labelrotation)
  106. if zorder is None:
  107. if major:
  108. zorder = mlines.Line2D.zorder + 0.01
  109. else:
  110. zorder = mlines.Line2D.zorder
  111. self._zorder = zorder
  112. if grid_color is None:
  113. grid_color = mpl.rcParams["grid.color"]
  114. if grid_linestyle is None:
  115. grid_linestyle = mpl.rcParams["grid.linestyle"]
  116. if grid_linewidth is None:
  117. grid_linewidth = mpl.rcParams["grid.linewidth"]
  118. if grid_alpha is None:
  119. grid_alpha = mpl.rcParams["grid.alpha"]
  120. grid_kw = {k[5:]: v for k, v in kw.items()}
  121. self.apply_tickdir(tickdir)
  122. self.tick1line = mlines.Line2D(
  123. [], [],
  124. color=color, linestyle="none", zorder=zorder, visible=tick1On,
  125. markeredgecolor=color, markersize=size, markeredgewidth=width,
  126. )
  127. self.tick2line = mlines.Line2D(
  128. [], [],
  129. color=color, linestyle="none", zorder=zorder, visible=tick2On,
  130. markeredgecolor=color, markersize=size, markeredgewidth=width,
  131. )
  132. self.gridline = mlines.Line2D(
  133. [], [],
  134. color=grid_color, alpha=grid_alpha, visible=gridOn,
  135. linestyle=grid_linestyle, linewidth=grid_linewidth, marker="",
  136. **grid_kw,
  137. )
  138. self.gridline.get_path()._interpolation_steps = \
  139. GRIDLINE_INTERPOLATION_STEPS
  140. self.label1 = mtext.Text(
  141. np.nan, np.nan,
  142. fontsize=labelsize, color=labelcolor, visible=label1On)
  143. self.label2 = mtext.Text(
  144. np.nan, np.nan,
  145. fontsize=labelsize, color=labelcolor, visible=label2On)
  146. for meth, attr in [("_get_tick1line", "tick1line"),
  147. ("_get_tick2line", "tick2line"),
  148. ("_get_gridline", "gridline"),
  149. ("_get_text1", "label1"),
  150. ("_get_text2", "label2")]:
  151. overridden_method = cbook._deprecate_method_override(
  152. getattr(__class__, meth), self, since="3.3", message="Relying "
  153. f"on {meth} to initialize Tick.{attr} is deprecated since "
  154. f"%(since)s and will not work %(removal)s; please directly "
  155. f"set the attribute in the subclass' __init__ instead.")
  156. if overridden_method:
  157. setattr(self, attr, overridden_method())
  158. for artist in [self.tick1line, self.tick2line, self.gridline,
  159. self.label1, self.label2]:
  160. self._set_artist_props(artist)
  161. self.update_position(loc)
  162. @property
  163. @cbook.deprecated("3.1", alternative="Tick.label1", pending=True)
  164. def label(self):
  165. return self.label1
  166. def _set_labelrotation(self, labelrotation):
  167. if isinstance(labelrotation, str):
  168. mode = labelrotation
  169. angle = 0
  170. elif isinstance(labelrotation, (tuple, list)):
  171. mode, angle = labelrotation
  172. else:
  173. mode = 'default'
  174. angle = labelrotation
  175. cbook._check_in_list(['auto', 'default'], labelrotation=mode)
  176. self._labelrotation = (mode, angle)
  177. def apply_tickdir(self, tickdir):
  178. """Calculate ``self._pad`` and ``self._tickmarkers``."""
  179. def get_tickdir(self):
  180. return self._tickdir
  181. def get_tick_padding(self):
  182. """Get the length of the tick outside of the axes."""
  183. padding = {
  184. 'in': 0.0,
  185. 'inout': 0.5,
  186. 'out': 1.0
  187. }
  188. return self._size * padding[self._tickdir]
  189. def get_children(self):
  190. children = [self.tick1line, self.tick2line,
  191. self.gridline, self.label1, self.label2]
  192. return children
  193. def set_clip_path(self, clippath, transform=None):
  194. # docstring inherited
  195. martist.Artist.set_clip_path(self, clippath, transform)
  196. self.gridline.set_clip_path(clippath, transform)
  197. self.stale = True
  198. def get_pad_pixels(self):
  199. return self.figure.dpi * self._base_pad / 72
  200. def contains(self, mouseevent):
  201. """
  202. Test whether the mouse event occurred in the Tick marks.
  203. This function always returns false. It is more useful to test if the
  204. axis as a whole contains the mouse rather than the set of tick marks.
  205. """
  206. inside, info = self._default_contains(mouseevent)
  207. if inside is not None:
  208. return inside, info
  209. return False, {}
  210. def set_pad(self, val):
  211. """
  212. Set the tick label pad in points
  213. Parameters
  214. ----------
  215. val : float
  216. """
  217. self._apply_params(pad=val)
  218. self.stale = True
  219. def get_pad(self):
  220. """Get the value of the tick label pad in points."""
  221. return self._base_pad
  222. def _get_text1(self):
  223. """Get the default Text 1 instance."""
  224. def _get_text2(self):
  225. """Get the default Text 2 instance."""
  226. def _get_tick1line(self):
  227. """Get the default line2D instance for tick1."""
  228. def _get_tick2line(self):
  229. """Get the default line2D instance for tick2."""
  230. def _get_gridline(self):
  231. """Get the default grid Line2d instance for this tick."""
  232. def get_loc(self):
  233. """Return the tick location (data coords) as a scalar."""
  234. return self._loc
  235. @martist.allow_rasterization
  236. def draw(self, renderer):
  237. if not self.get_visible():
  238. self.stale = False
  239. return
  240. renderer.open_group(self.__name__, gid=self.get_gid())
  241. for artist in [self.gridline, self.tick1line, self.tick2line,
  242. self.label1, self.label2]:
  243. artist.draw(renderer)
  244. renderer.close_group(self.__name__)
  245. self.stale = False
  246. def set_label1(self, s):
  247. """
  248. Set the label1 text.
  249. Parameters
  250. ----------
  251. s : str
  252. """
  253. self.label1.set_text(s)
  254. self.stale = True
  255. set_label = set_label1
  256. def set_label2(self, s):
  257. """
  258. Set the label2 text.
  259. Parameters
  260. ----------
  261. s : str
  262. """
  263. self.label2.set_text(s)
  264. self.stale = True
  265. def set_url(self, url):
  266. """
  267. Set the url of label1 and label2.
  268. Parameters
  269. ----------
  270. url : str
  271. """
  272. super().set_url(url)
  273. self.label1.set_url(url)
  274. self.label2.set_url(url)
  275. self.stale = True
  276. def _set_artist_props(self, a):
  277. a.set_figure(self.figure)
  278. def get_view_interval(self):
  279. """
  280. Return the view limits ``(min, max)`` of the axis the tick belongs to.
  281. """
  282. raise NotImplementedError('Derived must override')
  283. def _apply_params(self, **kw):
  284. for name, target in [("gridOn", self.gridline),
  285. ("tick1On", self.tick1line),
  286. ("tick2On", self.tick2line),
  287. ("label1On", self.label1),
  288. ("label2On", self.label2)]:
  289. if name in kw:
  290. target.set_visible(kw.pop(name))
  291. if any(k in kw for k in ['size', 'width', 'pad', 'tickdir']):
  292. self._size = kw.pop('size', self._size)
  293. # Width could be handled outside this block, but it is
  294. # convenient to leave it here.
  295. self._width = kw.pop('width', self._width)
  296. self._base_pad = kw.pop('pad', self._base_pad)
  297. # apply_tickdir uses _size and _base_pad to make _pad,
  298. # and also makes _tickmarkers.
  299. self.apply_tickdir(kw.pop('tickdir', self._tickdir))
  300. self.tick1line.set_marker(self._tickmarkers[0])
  301. self.tick2line.set_marker(self._tickmarkers[1])
  302. for line in (self.tick1line, self.tick2line):
  303. line.set_markersize(self._size)
  304. line.set_markeredgewidth(self._width)
  305. # _get_text1_transform uses _pad from apply_tickdir.
  306. trans = self._get_text1_transform()[0]
  307. self.label1.set_transform(trans)
  308. trans = self._get_text2_transform()[0]
  309. self.label2.set_transform(trans)
  310. tick_kw = {k: v for k, v in kw.items() if k in ['color', 'zorder']}
  311. if 'color' in kw:
  312. tick_kw['markeredgecolor'] = kw['color']
  313. self.tick1line.set(**tick_kw)
  314. self.tick2line.set(**tick_kw)
  315. for k, v in tick_kw.items():
  316. setattr(self, '_' + k, v)
  317. if 'labelrotation' in kw:
  318. self._set_labelrotation(kw.pop('labelrotation'))
  319. self.label1.set(rotation=self._labelrotation[1])
  320. self.label2.set(rotation=self._labelrotation[1])
  321. label_kw = {k[5:]: v for k, v in kw.items()
  322. if k in ['labelsize', 'labelcolor']}
  323. self.label1.set(**label_kw)
  324. self.label2.set(**label_kw)
  325. for k, v in label_kw.items():
  326. # for labelsize the text objects covert str ('small')
  327. # -> points. grab the integer from the `Text` object
  328. # instead of saving the string representation
  329. v = getattr(self.label1, 'get_' + k)()
  330. setattr(self, '_label' + k, v)
  331. grid_kw = {k[5:]: v for k, v in kw.items()
  332. if k in _gridline_param_names}
  333. self.gridline.set(**grid_kw)
  334. for k, v in grid_kw.items():
  335. setattr(self, '_grid_' + k, v)
  336. def update_position(self, loc):
  337. """Set the location of tick in data coords with scalar *loc*."""
  338. raise NotImplementedError('Derived must override')
  339. def _get_text1_transform(self):
  340. raise NotImplementedError('Derived must override')
  341. def _get_text2_transform(self):
  342. raise NotImplementedError('Derived must override')
  343. class XTick(Tick):
  344. """
  345. Contains all the Artists needed to make an x tick - the tick line,
  346. the label text and the grid line
  347. """
  348. __name__ = 'xtick'
  349. def __init__(self, *args, **kwargs):
  350. super().__init__(*args, **kwargs)
  351. # x in data coords, y in axes coords
  352. self.tick1line.set(
  353. xdata=[0], ydata=[0],
  354. transform=self.axes.get_xaxis_transform(which="tick1"),
  355. marker=self._tickmarkers[0],
  356. )
  357. self.tick2line.set(
  358. xdata=[0], ydata=[1],
  359. transform=self.axes.get_xaxis_transform(which="tick2"),
  360. marker=self._tickmarkers[1],
  361. )
  362. self.gridline.set(
  363. xdata=[0, 0], ydata=[0, 1],
  364. transform=self.axes.get_xaxis_transform(which="grid"),
  365. )
  366. # the y loc is 3 points below the min of y axis
  367. trans, va, ha = self._get_text1_transform()
  368. self.label1.set(
  369. x=0, y=0,
  370. verticalalignment=va, horizontalalignment=ha, transform=trans,
  371. )
  372. trans, va, ha = self._get_text2_transform()
  373. self.label2.set(
  374. x=0, y=1,
  375. verticalalignment=va, horizontalalignment=ha, transform=trans,
  376. )
  377. def _get_text1_transform(self):
  378. return self.axes.get_xaxis_text1_transform(self._pad)
  379. def _get_text2_transform(self):
  380. return self.axes.get_xaxis_text2_transform(self._pad)
  381. def apply_tickdir(self, tickdir):
  382. """Set tick direction. Valid values are 'in', 'out', 'inout'."""
  383. if tickdir is None:
  384. tickdir = mpl.rcParams['%s.direction' % self.__name__.lower()]
  385. cbook._check_in_list(['in', 'out', 'inout'], tickdir=tickdir)
  386. self._tickdir = tickdir
  387. if self._tickdir == 'in':
  388. self._tickmarkers = (mlines.TICKUP, mlines.TICKDOWN)
  389. elif self._tickdir == 'inout':
  390. self._tickmarkers = ('|', '|')
  391. else:
  392. self._tickmarkers = (mlines.TICKDOWN, mlines.TICKUP)
  393. self._pad = self._base_pad + self.get_tick_padding()
  394. self.stale = True
  395. def update_position(self, loc):
  396. """Set the location of tick in data coords with scalar *loc*."""
  397. self.tick1line.set_xdata((loc,))
  398. self.tick2line.set_xdata((loc,))
  399. self.gridline.set_xdata((loc,))
  400. self.label1.set_x(loc)
  401. self.label2.set_x(loc)
  402. self._loc = loc
  403. self.stale = True
  404. def get_view_interval(self):
  405. # docstring inherited
  406. return self.axes.viewLim.intervalx
  407. class YTick(Tick):
  408. """
  409. Contains all the Artists needed to make a Y tick - the tick line,
  410. the label text and the grid line
  411. """
  412. __name__ = 'ytick'
  413. def __init__(self, *args, **kwargs):
  414. super().__init__(*args, **kwargs)
  415. # x in axes coords, y in data coords
  416. self.tick1line.set(
  417. xdata=[0], ydata=[0],
  418. transform=self.axes.get_yaxis_transform(which="tick1"),
  419. marker=self._tickmarkers[0],
  420. )
  421. self.tick2line.set(
  422. xdata=[1], ydata=[0],
  423. transform=self.axes.get_yaxis_transform(which="tick2"),
  424. marker=self._tickmarkers[1],
  425. )
  426. self.gridline.set(
  427. xdata=[0, 1], ydata=[0, 0],
  428. transform=self.axes.get_yaxis_transform(which="grid"),
  429. )
  430. # the y loc is 3 points below the min of y axis
  431. trans, va, ha = self._get_text1_transform()
  432. self.label1.set(
  433. x=0, y=0,
  434. verticalalignment=va, horizontalalignment=ha, transform=trans,
  435. )
  436. trans, va, ha = self._get_text2_transform()
  437. self.label2.set(
  438. x=1, y=0,
  439. verticalalignment=va, horizontalalignment=ha, transform=trans,
  440. )
  441. def _get_text1_transform(self):
  442. return self.axes.get_yaxis_text1_transform(self._pad)
  443. def _get_text2_transform(self):
  444. return self.axes.get_yaxis_text2_transform(self._pad)
  445. def apply_tickdir(self, tickdir):
  446. if tickdir is None:
  447. tickdir = mpl.rcParams['%s.direction' % self.__name__.lower()]
  448. self._tickdir = tickdir
  449. if self._tickdir == 'in':
  450. self._tickmarkers = (mlines.TICKRIGHT, mlines.TICKLEFT)
  451. elif self._tickdir == 'inout':
  452. self._tickmarkers = ('_', '_')
  453. else:
  454. self._tickmarkers = (mlines.TICKLEFT, mlines.TICKRIGHT)
  455. self._pad = self._base_pad + self.get_tick_padding()
  456. self.stale = True
  457. def update_position(self, loc):
  458. """Set the location of tick in data coords with scalar *loc*."""
  459. self.tick1line.set_ydata((loc,))
  460. self.tick2line.set_ydata((loc,))
  461. self.gridline.set_ydata((loc,))
  462. self.label1.set_y(loc)
  463. self.label2.set_y(loc)
  464. self._loc = loc
  465. self.stale = True
  466. def get_view_interval(self):
  467. # docstring inherited
  468. return self.axes.viewLim.intervaly
  469. class Ticker:
  470. """
  471. A container for the objects defining tick position and format.
  472. Attributes
  473. ----------
  474. locator : `matplotlib.ticker.Locator` subclass
  475. Determines the positions of the ticks.
  476. formatter : `matplotlib.ticker.Formatter` subclass
  477. Determines the format of the tick labels.
  478. """
  479. def __init__(self):
  480. self._locator = None
  481. self._formatter = None
  482. @property
  483. def locator(self):
  484. return self._locator
  485. @locator.setter
  486. def locator(self, locator):
  487. if not isinstance(locator, mticker.Locator):
  488. cbook.warn_deprecated(
  489. "3.2", message="Support for locators that do not subclass "
  490. "matplotlib.ticker.Locator is deprecated since %(since)s and "
  491. "support for them will be removed %(removal)s.")
  492. self._locator = locator
  493. @property
  494. def formatter(self):
  495. return self._formatter
  496. @formatter.setter
  497. def formatter(self, formatter):
  498. if not isinstance(formatter, mticker.Formatter):
  499. cbook.warn_deprecated(
  500. "3.2", message="Support for formatters that do not subclass "
  501. "matplotlib.ticker.Formatter is deprecated since %(since)s "
  502. "and support for them will be removed %(removal)s.")
  503. self._formatter = formatter
  504. class _LazyTickList:
  505. """
  506. A descriptor for lazy instantiation of tick lists.
  507. See comment above definition of the ``majorTicks`` and ``minorTicks``
  508. attributes.
  509. """
  510. def __init__(self, major):
  511. self._major = major
  512. def __get__(self, instance, cls):
  513. if instance is None:
  514. return self
  515. else:
  516. # instance._get_tick() can itself try to access the majorTicks
  517. # attribute (e.g. in certain projection classes which override
  518. # e.g. get_xaxis_text1_transform). In order to avoid infinite
  519. # recursion, first set the majorTicks on the instance to an empty
  520. # list, then create the tick and append it.
  521. if self._major:
  522. instance.majorTicks = []
  523. tick = instance._get_tick(major=True)
  524. instance.majorTicks.append(tick)
  525. return instance.majorTicks
  526. else:
  527. instance.minorTicks = []
  528. tick = instance._get_tick(major=False)
  529. instance.minorTicks.append(tick)
  530. return instance.minorTicks
  531. class Axis(martist.Artist):
  532. """
  533. Base class for `.XAxis` and `.YAxis`.
  534. Attributes
  535. ----------
  536. isDefault_label : bool
  537. axes : `matplotlib.axes.Axes`
  538. The `~.axes.Axes` to which the Axis belongs.
  539. major : `matplotlib.axis.Ticker`
  540. Determines the major tick positions and their label format.
  541. minor : `matplotlib.axis.Ticker`
  542. Determines the minor tick positions and their label format.
  543. callbacks : `matplotlib.cbook.CallbackRegistry`
  544. label : `.Text`
  545. The axis label.
  546. labelpad : float
  547. The distance between the axis label and the tick labels.
  548. Defaults to :rc:`axes.labelpad` = 4.
  549. offsetText : `.Text`
  550. A `.Text` object containing the data offset of the ticks (if any).
  551. pickradius : float
  552. The acceptance radius for containment tests. See also `.Axis.contains`.
  553. majorTicks : list of `.Tick`
  554. The major ticks.
  555. minorTicks : list of `.Tick`
  556. The minor ticks.
  557. """
  558. OFFSETTEXTPAD = 3
  559. def __str__(self):
  560. return "{}({},{})".format(
  561. type(self).__name__, *self.axes.transAxes.transform((0, 0)))
  562. def __init__(self, axes, pickradius=15):
  563. """
  564. Parameters
  565. ----------
  566. axes : `matplotlib.axes.Axes`
  567. The `~.axes.Axes` to which the created Axis belongs.
  568. pickradius : float
  569. The acceptance radius for containment tests. See also
  570. `.Axis.contains`.
  571. """
  572. martist.Artist.__init__(self)
  573. self._remove_overlapping_locs = True
  574. self.set_figure(axes.figure)
  575. self.isDefault_label = True
  576. self.axes = axes
  577. self.major = Ticker()
  578. self.minor = Ticker()
  579. self.callbacks = cbook.CallbackRegistry()
  580. self._autolabelpos = True
  581. self._smart_bounds = False # Deprecated in 3.2
  582. self.label = mtext.Text(
  583. np.nan, np.nan,
  584. fontsize=mpl.rcParams['axes.labelsize'],
  585. fontweight=mpl.rcParams['axes.labelweight'],
  586. color=mpl.rcParams['axes.labelcolor'],
  587. )
  588. self._set_artist_props(self.label)
  589. self.offsetText = mtext.Text(np.nan, np.nan)
  590. self._set_artist_props(self.offsetText)
  591. self.labelpad = mpl.rcParams['axes.labelpad']
  592. self.pickradius = pickradius
  593. # Initialize here for testing; later add API
  594. self._major_tick_kw = dict()
  595. self._minor_tick_kw = dict()
  596. self.cla()
  597. self._set_scale('linear')
  598. # During initialization, Axis objects often create ticks that are later
  599. # unused; this turns out to be a very slow step. Instead, use a custom
  600. # descriptor to make the tick lists lazy and instantiate them as needed.
  601. majorTicks = _LazyTickList(major=True)
  602. minorTicks = _LazyTickList(major=False)
  603. def get_remove_overlapping_locs(self):
  604. return self._remove_overlapping_locs
  605. def set_remove_overlapping_locs(self, val):
  606. self._remove_overlapping_locs = bool(val)
  607. remove_overlapping_locs = property(
  608. get_remove_overlapping_locs, set_remove_overlapping_locs,
  609. doc=('If minor ticker locations that overlap with major '
  610. 'ticker locations should be trimmed.'))
  611. def set_label_coords(self, x, y, transform=None):
  612. """
  613. Set the coordinates of the label.
  614. By default, the x coordinate of the y label and the y coordinate of the
  615. x label are determined by the tick label bounding boxes, but this can
  616. lead to poor alignment of multiple labels if there are multiple axes.
  617. You can also specify the coordinate system of the label with the
  618. transform. If None, the default coordinate system will be the axes
  619. coordinate system: (0, 0) is bottom left, (0.5, 0.5) is center, etc.
  620. """
  621. self._autolabelpos = False
  622. if transform is None:
  623. transform = self.axes.transAxes
  624. self.label.set_transform(transform)
  625. self.label.set_position((x, y))
  626. self.stale = True
  627. def get_transform(self):
  628. return self._scale.get_transform()
  629. def get_scale(self):
  630. """Return this Axis' scale (as a str)."""
  631. return self._scale.name
  632. def _set_scale(self, value, **kwargs):
  633. self._scale = mscale.scale_factory(value, self, **kwargs)
  634. self._scale.set_default_locators_and_formatters(self)
  635. self.isDefault_majloc = True
  636. self.isDefault_minloc = True
  637. self.isDefault_majfmt = True
  638. self.isDefault_minfmt = True
  639. def limit_range_for_scale(self, vmin, vmax):
  640. return self._scale.limit_range_for_scale(vmin, vmax, self.get_minpos())
  641. def get_children(self):
  642. return [self.label, self.offsetText,
  643. *self.get_major_ticks(), *self.get_minor_ticks()]
  644. def cla(self):
  645. """Clear this axis."""
  646. self.label.set_text('') # self.set_label_text would change isDefault_
  647. self._set_scale('linear')
  648. # Clear the callback registry for this axis, or it may "leak"
  649. self.callbacks = cbook.CallbackRegistry()
  650. # whether the grids are on
  651. self._major_tick_kw['gridOn'] = (
  652. mpl.rcParams['axes.grid'] and
  653. mpl.rcParams['axes.grid.which'] in ('both', 'major'))
  654. self._minor_tick_kw['gridOn'] = (
  655. mpl.rcParams['axes.grid'] and
  656. mpl.rcParams['axes.grid.which'] in ('both', 'minor'))
  657. self.reset_ticks()
  658. self.converter = None
  659. self.units = None
  660. self.set_units(None)
  661. self.stale = True
  662. def reset_ticks(self):
  663. """
  664. Re-initialize the major and minor Tick lists.
  665. Each list starts with a single fresh Tick.
  666. """
  667. # Restore the lazy tick lists.
  668. try:
  669. del self.majorTicks
  670. except AttributeError:
  671. pass
  672. try:
  673. del self.minorTicks
  674. except AttributeError:
  675. pass
  676. try:
  677. self.set_clip_path(self.axes.patch)
  678. except AttributeError:
  679. pass
  680. def set_tick_params(self, which='major', reset=False, **kw):
  681. """
  682. Set appearance parameters for ticks, ticklabels, and gridlines.
  683. For documentation of keyword arguments, see
  684. :meth:`matplotlib.axes.Axes.tick_params`.
  685. """
  686. cbook._check_in_list(['major', 'minor', 'both'], which=which)
  687. kwtrans = self._translate_tick_kw(kw)
  688. # the kwargs are stored in self._major/minor_tick_kw so that any
  689. # future new ticks will automatically get them
  690. if reset:
  691. if which in ['major', 'both']:
  692. self._major_tick_kw.clear()
  693. self._major_tick_kw.update(kwtrans)
  694. if which in ['minor', 'both']:
  695. self._minor_tick_kw.clear()
  696. self._minor_tick_kw.update(kwtrans)
  697. self.reset_ticks()
  698. else:
  699. if which in ['major', 'both']:
  700. self._major_tick_kw.update(kwtrans)
  701. for tick in self.majorTicks:
  702. tick._apply_params(**kwtrans)
  703. if which in ['minor', 'both']:
  704. self._minor_tick_kw.update(kwtrans)
  705. for tick in self.minorTicks:
  706. tick._apply_params(**kwtrans)
  707. # labelOn and labelcolor also apply to the offset text.
  708. if 'label1On' in kwtrans or 'label2On' in kwtrans:
  709. self.offsetText.set_visible(
  710. self._major_tick_kw.get('label1On', False)
  711. or self._major_tick_kw.get('label2On', False))
  712. if 'labelcolor' in kwtrans:
  713. self.offsetText.set_color(kwtrans['labelcolor'])
  714. self.stale = True
  715. @staticmethod
  716. def _translate_tick_kw(kw):
  717. # The following lists may be moved to a more accessible location.
  718. kwkeys = ['size', 'width', 'color', 'tickdir', 'pad',
  719. 'labelsize', 'labelcolor', 'zorder', 'gridOn',
  720. 'tick1On', 'tick2On', 'label1On', 'label2On',
  721. 'length', 'direction', 'left', 'bottom', 'right', 'top',
  722. 'labelleft', 'labelbottom', 'labelright', 'labeltop',
  723. 'labelrotation'] + _gridline_param_names
  724. kwtrans = {}
  725. if 'length' in kw:
  726. kwtrans['size'] = kw.pop('length')
  727. if 'direction' in kw:
  728. kwtrans['tickdir'] = kw.pop('direction')
  729. if 'rotation' in kw:
  730. kwtrans['labelrotation'] = kw.pop('rotation')
  731. if 'left' in kw:
  732. kwtrans['tick1On'] = kw.pop('left')
  733. if 'bottom' in kw:
  734. kwtrans['tick1On'] = kw.pop('bottom')
  735. if 'right' in kw:
  736. kwtrans['tick2On'] = kw.pop('right')
  737. if 'top' in kw:
  738. kwtrans['tick2On'] = kw.pop('top')
  739. if 'labelleft' in kw:
  740. kwtrans['label1On'] = kw.pop('labelleft')
  741. if 'labelbottom' in kw:
  742. kwtrans['label1On'] = kw.pop('labelbottom')
  743. if 'labelright' in kw:
  744. kwtrans['label2On'] = kw.pop('labelright')
  745. if 'labeltop' in kw:
  746. kwtrans['label2On'] = kw.pop('labeltop')
  747. if 'colors' in kw:
  748. c = kw.pop('colors')
  749. kwtrans['color'] = c
  750. kwtrans['labelcolor'] = c
  751. # Maybe move the checking up to the caller of this method.
  752. for key in kw:
  753. if key not in kwkeys:
  754. raise ValueError(
  755. "keyword %s is not recognized; valid keywords are %s"
  756. % (key, kwkeys))
  757. kwtrans.update(kw)
  758. return kwtrans
  759. def set_clip_path(self, clippath, transform=None):
  760. martist.Artist.set_clip_path(self, clippath, transform)
  761. for child in self.majorTicks + self.minorTicks:
  762. child.set_clip_path(clippath, transform)
  763. self.stale = True
  764. def get_view_interval(self):
  765. """Return the view limits ``(min, max)`` of this axis."""
  766. raise NotImplementedError('Derived must override')
  767. def set_view_interval(self, vmin, vmax, ignore=False):
  768. """
  769. Set the axis view limits. This method is for internal use; Matplotlib
  770. users should typically use e.g. `~.Axes.set_xlim` or `~.Axes.set_ylim`.
  771. If *ignore* is False (the default), this method will never reduce the
  772. preexisting view limits, only expand them if *vmin* or *vmax* are not
  773. within them. Moreover, the order of *vmin* and *vmax* does not matter;
  774. the orientation of the axis will not change.
  775. If *ignore* is True, the view limits will be set exactly to ``(vmin,
  776. vmax)`` in that order.
  777. """
  778. raise NotImplementedError('Derived must override')
  779. def get_data_interval(self):
  780. """Return the Interval instance for this axis data limits."""
  781. raise NotImplementedError('Derived must override')
  782. def set_data_interval(self, vmin, vmax, ignore=False):
  783. """
  784. Set the axis data limits. This method is for internal use.
  785. If *ignore* is False (the default), this method will never reduce the
  786. preexisting data limits, only expand them if *vmin* or *vmax* are not
  787. within them. Moreover, the order of *vmin* and *vmax* does not matter;
  788. the orientation of the axis will not change.
  789. If *ignore* is True, the data limits will be set exactly to ``(vmin,
  790. vmax)`` in that order.
  791. """
  792. raise NotImplementedError('Derived must override')
  793. def get_inverted(self):
  794. """
  795. Return whether this Axis is oriented in the "inverse" direction.
  796. The "normal" direction is increasing to the right for the x-axis and to
  797. the top for the y-axis; the "inverse" direction is increasing to the
  798. left for the x-axis and to the bottom for the y-axis.
  799. """
  800. low, high = self.get_view_interval()
  801. return high < low
  802. def set_inverted(self, inverted):
  803. """
  804. Set whether this Axis is oriented in the "inverse" direction.
  805. The "normal" direction is increasing to the right for the x-axis and to
  806. the top for the y-axis; the "inverse" direction is increasing to the
  807. left for the x-axis and to the bottom for the y-axis.
  808. """
  809. # Currently, must be implemented in subclasses using set_xlim/set_ylim
  810. # rather than generically using set_view_interval, so that shared
  811. # axes get updated as well.
  812. raise NotImplementedError('Derived must override')
  813. def set_default_intervals(self):
  814. """
  815. Set the default limits for the axis data and view interval if they
  816. have not been not mutated yet.
  817. """
  818. # this is mainly in support of custom object plotting. For
  819. # example, if someone passes in a datetime object, we do not
  820. # know automagically how to set the default min/max of the
  821. # data and view limits. The unit conversion AxisInfo
  822. # interface provides a hook for custom types to register
  823. # default limits through the AxisInfo.default_limits
  824. # attribute, and the derived code below will check for that
  825. # and use it if is available (else just use 0..1)
  826. def _set_artist_props(self, a):
  827. if a is None:
  828. return
  829. a.set_figure(self.figure)
  830. def get_ticklabel_extents(self, renderer):
  831. """
  832. Get the extents of the tick labels on either side
  833. of the axes.
  834. """
  835. ticks_to_draw = self._update_ticks()
  836. ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw,
  837. renderer)
  838. if len(ticklabelBoxes):
  839. bbox = mtransforms.Bbox.union(ticklabelBoxes)
  840. else:
  841. bbox = mtransforms.Bbox.from_extents(0, 0, 0, 0)
  842. if len(ticklabelBoxes2):
  843. bbox2 = mtransforms.Bbox.union(ticklabelBoxes2)
  844. else:
  845. bbox2 = mtransforms.Bbox.from_extents(0, 0, 0, 0)
  846. return bbox, bbox2
  847. @cbook.deprecated("3.2")
  848. def set_smart_bounds(self, value):
  849. """Set the axis to have smart bounds."""
  850. self._smart_bounds = value
  851. self.stale = True
  852. @cbook.deprecated("3.2")
  853. def get_smart_bounds(self):
  854. """Return whether the axis has smart bounds."""
  855. return self._smart_bounds
  856. def _update_ticks(self):
  857. """
  858. Update ticks (position and labels) using the current data interval of
  859. the axes. Return the list of ticks that will be drawn.
  860. """
  861. major_locs = self.get_majorticklocs()
  862. major_labels = self.major.formatter.format_ticks(major_locs)
  863. major_ticks = self.get_major_ticks(len(major_locs))
  864. self.major.formatter.set_locs(major_locs)
  865. for tick, loc, label in zip(major_ticks, major_locs, major_labels):
  866. tick.update_position(loc)
  867. tick.set_label1(label)
  868. tick.set_label2(label)
  869. minor_locs = self.get_minorticklocs()
  870. minor_labels = self.minor.formatter.format_ticks(minor_locs)
  871. minor_ticks = self.get_minor_ticks(len(minor_locs))
  872. self.minor.formatter.set_locs(minor_locs)
  873. for tick, loc, label in zip(minor_ticks, minor_locs, minor_labels):
  874. tick.update_position(loc)
  875. tick.set_label1(label)
  876. tick.set_label2(label)
  877. ticks = [*major_ticks, *minor_ticks]
  878. view_low, view_high = self.get_view_interval()
  879. if view_low > view_high:
  880. view_low, view_high = view_high, view_low
  881. if self._smart_bounds and ticks: # _smart_bounds is deprecated in 3.2
  882. # handle inverted limits
  883. data_low, data_high = sorted(self.get_data_interval())
  884. locs = np.sort([tick.get_loc() for tick in ticks])
  885. if data_low <= view_low:
  886. # data extends beyond view, take view as limit
  887. ilow = view_low
  888. else:
  889. # data stops within view, take best tick
  890. good_locs = locs[locs <= data_low]
  891. if len(good_locs):
  892. # last tick prior or equal to first data point
  893. ilow = good_locs[-1]
  894. else:
  895. # No ticks (why not?), take first tick
  896. ilow = locs[0]
  897. if data_high >= view_high:
  898. # data extends beyond view, take view as limit
  899. ihigh = view_high
  900. else:
  901. # data stops within view, take best tick
  902. good_locs = locs[locs >= data_high]
  903. if len(good_locs):
  904. # first tick after or equal to last data point
  905. ihigh = good_locs[0]
  906. else:
  907. # No ticks (why not?), take last tick
  908. ihigh = locs[-1]
  909. ticks = [tick for tick in ticks if ilow <= tick.get_loc() <= ihigh]
  910. interval_t = self.get_transform().transform([view_low, view_high])
  911. ticks_to_draw = []
  912. for tick in ticks:
  913. try:
  914. loc_t = self.get_transform().transform(tick.get_loc())
  915. except AssertionError:
  916. # transforms.transform doesn't allow masked values but
  917. # some scales might make them, so we need this try/except.
  918. pass
  919. else:
  920. if mtransforms._interval_contains_close(interval_t, loc_t):
  921. ticks_to_draw.append(tick)
  922. return ticks_to_draw
  923. def _get_tick_bboxes(self, ticks, renderer):
  924. """Return lists of bboxes for ticks' label1's and label2's."""
  925. return ([tick.label1.get_window_extent(renderer)
  926. for tick in ticks if tick.label1.get_visible()],
  927. [tick.label2.get_window_extent(renderer)
  928. for tick in ticks if tick.label2.get_visible()])
  929. def get_tightbbox(self, renderer, *, for_layout_only=False):
  930. """
  931. Return a bounding box that encloses the axis. It only accounts
  932. tick labels, axis label, and offsetText.
  933. If *for_layout_only* is True, then the width of the label (if this
  934. is an x-axis) or the height of the label (if this is a y-axis) is
  935. collapsed to near zero. This allows tight/constrained_layout to ignore
  936. too-long labels when doing their layout.
  937. """
  938. if not self.get_visible():
  939. return
  940. ticks_to_draw = self._update_ticks()
  941. self._update_label_position(renderer)
  942. # go back to just this axis's tick labels
  943. ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(
  944. ticks_to_draw, renderer)
  945. self._update_offset_text_position(ticklabelBoxes, ticklabelBoxes2)
  946. self.offsetText.set_text(self.major.formatter.get_offset())
  947. bboxes = [
  948. *(a.get_window_extent(renderer)
  949. for a in [self.offsetText]
  950. if a.get_visible()),
  951. *ticklabelBoxes,
  952. *ticklabelBoxes2,
  953. ]
  954. # take care of label
  955. if self.label.get_visible():
  956. bb = self.label.get_window_extent(renderer)
  957. # for constrained/tight_layout, we want to ignore the label's
  958. # width/height because the adjustments they make can't be improved.
  959. # this code collapses the relevant direction
  960. if for_layout_only:
  961. if self.axis_name == "x" and bb.width > 0:
  962. bb.x0 = (bb.x0 + bb.x1) / 2 - 0.5
  963. bb.x1 = bb.x0 + 1.0
  964. if self.axis_name == "y" and bb.height > 0:
  965. bb.y0 = (bb.y0 + bb.y1) / 2 - 0.5
  966. bb.y1 = bb.y0 + 1.0
  967. bboxes.append(bb)
  968. bboxes = [b for b in bboxes
  969. if 0 < b.width < np.inf and 0 < b.height < np.inf]
  970. if bboxes:
  971. return mtransforms.Bbox.union(bboxes)
  972. else:
  973. return None
  974. def get_tick_padding(self):
  975. values = []
  976. if len(self.majorTicks):
  977. values.append(self.majorTicks[0].get_tick_padding())
  978. if len(self.minorTicks):
  979. values.append(self.minorTicks[0].get_tick_padding())
  980. return max(values, default=0)
  981. @martist.allow_rasterization
  982. def draw(self, renderer, *args, **kwargs):
  983. # docstring inherited
  984. if not self.get_visible():
  985. return
  986. renderer.open_group(__name__, gid=self.get_gid())
  987. ticks_to_draw = self._update_ticks()
  988. ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw,
  989. renderer)
  990. for tick in ticks_to_draw:
  991. tick.draw(renderer)
  992. # scale up the axis label box to also find the neighbors, not
  993. # just the tick labels that actually overlap note we need a
  994. # *copy* of the axis label box because we don't want to scale
  995. # the actual bbox
  996. self._update_label_position(renderer)
  997. self.label.draw(renderer)
  998. self._update_offset_text_position(ticklabelBoxes, ticklabelBoxes2)
  999. self.offsetText.set_text(self.major.formatter.get_offset())
  1000. self.offsetText.draw(renderer)
  1001. renderer.close_group(__name__)
  1002. self.stale = False
  1003. def get_gridlines(self):
  1004. r"""Return this Axis' grid lines as a list of `.Line2D`\s."""
  1005. ticks = self.get_major_ticks()
  1006. return cbook.silent_list('Line2D gridline',
  1007. [tick.gridline for tick in ticks])
  1008. def get_label(self):
  1009. """Return the axis label as a Text instance."""
  1010. return self.label
  1011. def get_offset_text(self):
  1012. """Return the axis offsetText as a Text instance."""
  1013. return self.offsetText
  1014. def get_pickradius(self):
  1015. """Return the depth of the axis used by the picker."""
  1016. return self.pickradius
  1017. def get_majorticklabels(self):
  1018. """Return this Axis' major tick labels, as a list of `~.text.Text`."""
  1019. ticks = self.get_major_ticks()
  1020. labels1 = [tick.label1 for tick in ticks if tick.label1.get_visible()]
  1021. labels2 = [tick.label2 for tick in ticks if tick.label2.get_visible()]
  1022. return labels1 + labels2
  1023. def get_minorticklabels(self):
  1024. """Return this Axis' minor tick labels, as a list of `~.text.Text`."""
  1025. ticks = self.get_minor_ticks()
  1026. labels1 = [tick.label1 for tick in ticks if tick.label1.get_visible()]
  1027. labels2 = [tick.label2 for tick in ticks if tick.label2.get_visible()]
  1028. return labels1 + labels2
  1029. def get_ticklabels(self, minor=False, which=None):
  1030. """
  1031. Get this Axis' tick labels.
  1032. Parameters
  1033. ----------
  1034. minor : bool
  1035. Whether to return the minor or the major ticklabels.
  1036. which : None, ('minor', 'major', 'both')
  1037. Overrides *minor*.
  1038. Selects which ticklabels to return
  1039. Returns
  1040. -------
  1041. list of `~matplotlib.text.Text`
  1042. Notes
  1043. -----
  1044. The tick label strings are not populated until a ``draw`` method has
  1045. been called.
  1046. See also: `~.pyplot.draw` and `~.FigureCanvasBase.draw`.
  1047. """
  1048. if which is not None:
  1049. if which == 'minor':
  1050. return self.get_minorticklabels()
  1051. elif which == 'major':
  1052. return self.get_majorticklabels()
  1053. elif which == 'both':
  1054. return self.get_majorticklabels() + self.get_minorticklabels()
  1055. else:
  1056. cbook._check_in_list(['major', 'minor', 'both'], which=which)
  1057. if minor:
  1058. return self.get_minorticklabels()
  1059. return self.get_majorticklabels()
  1060. def get_majorticklines(self):
  1061. r"""Return this Axis' major tick lines as a list of `.Line2D`\s."""
  1062. lines = []
  1063. ticks = self.get_major_ticks()
  1064. for tick in ticks:
  1065. lines.append(tick.tick1line)
  1066. lines.append(tick.tick2line)
  1067. return cbook.silent_list('Line2D ticklines', lines)
  1068. def get_minorticklines(self):
  1069. r"""Return this Axis' minor tick lines as a list of `.Line2D`\s."""
  1070. lines = []
  1071. ticks = self.get_minor_ticks()
  1072. for tick in ticks:
  1073. lines.append(tick.tick1line)
  1074. lines.append(tick.tick2line)
  1075. return cbook.silent_list('Line2D ticklines', lines)
  1076. def get_ticklines(self, minor=False):
  1077. r"""Return this Axis' tick lines as a list of `.Line2D`\s."""
  1078. if minor:
  1079. return self.get_minorticklines()
  1080. return self.get_majorticklines()
  1081. def get_majorticklocs(self):
  1082. """Return this Axis' major tick locations in data coordinates."""
  1083. return self.major.locator()
  1084. def get_minorticklocs(self):
  1085. """Return this Axis' minor tick locations in data coordinates."""
  1086. # Remove minor ticks duplicating major ticks.
  1087. major_locs = self.major.locator()
  1088. minor_locs = self.minor.locator()
  1089. transform = self._scale.get_transform()
  1090. tr_minor_locs = transform.transform(minor_locs)
  1091. tr_major_locs = transform.transform(major_locs)
  1092. lo, hi = sorted(transform.transform(self.get_view_interval()))
  1093. # Use the transformed view limits as scale. 1e-5 is the default rtol
  1094. # for np.isclose.
  1095. tol = (hi - lo) * 1e-5
  1096. if self.remove_overlapping_locs:
  1097. minor_locs = [
  1098. loc for loc, tr_loc in zip(minor_locs, tr_minor_locs)
  1099. if ~np.isclose(tr_loc, tr_major_locs, atol=tol, rtol=0).any()]
  1100. return minor_locs
  1101. @cbook._make_keyword_only("3.3", "minor")
  1102. def get_ticklocs(self, minor=False):
  1103. """Return this Axis' tick locations in data coordinates."""
  1104. return self.get_minorticklocs() if minor else self.get_majorticklocs()
  1105. def get_ticks_direction(self, minor=False):
  1106. """
  1107. Get the tick directions as a numpy array
  1108. Parameters
  1109. ----------
  1110. minor : bool, default: False
  1111. True to return the minor tick directions,
  1112. False to return the major tick directions.
  1113. Returns
  1114. -------
  1115. numpy array of tick directions
  1116. """
  1117. if minor:
  1118. return np.array(
  1119. [tick._tickdir for tick in self.get_minor_ticks()])
  1120. else:
  1121. return np.array(
  1122. [tick._tickdir for tick in self.get_major_ticks()])
  1123. def _get_tick(self, major):
  1124. """Return the default tick instance."""
  1125. raise NotImplementedError('derived must override')
  1126. def _get_tick_label_size(self, axis_name):
  1127. """
  1128. Return the text size of tick labels for this Axis.
  1129. This is a convenience function to avoid having to create a `Tick` in
  1130. `.get_tick_space`, since it is expensive.
  1131. """
  1132. tick_kw = self._major_tick_kw
  1133. size = tick_kw.get('labelsize',
  1134. mpl.rcParams[f'{axis_name}tick.labelsize'])
  1135. return mtext.FontProperties(size=size).get_size_in_points()
  1136. def _copy_tick_props(self, src, dest):
  1137. """Copy the properties from *src* tick to *dest* tick."""
  1138. if src is None or dest is None:
  1139. return
  1140. dest.label1.update_from(src.label1)
  1141. dest.label2.update_from(src.label2)
  1142. dest.tick1line.update_from(src.tick1line)
  1143. dest.tick2line.update_from(src.tick2line)
  1144. dest.gridline.update_from(src.gridline)
  1145. def get_label_text(self):
  1146. """Get the text of the label."""
  1147. return self.label.get_text()
  1148. def get_major_locator(self):
  1149. """Get the locator of the major ticker."""
  1150. return self.major.locator
  1151. def get_minor_locator(self):
  1152. """Get the locator of the minor ticker."""
  1153. return self.minor.locator
  1154. def get_major_formatter(self):
  1155. """Get the formatter of the major ticker."""
  1156. return self.major.formatter
  1157. def get_minor_formatter(self):
  1158. """Get the formatter of the minor ticker."""
  1159. return self.minor.formatter
  1160. def get_major_ticks(self, numticks=None):
  1161. r"""Return the list of major `.Tick`\s."""
  1162. if numticks is None:
  1163. numticks = len(self.get_majorticklocs())
  1164. while len(self.majorTicks) < numticks:
  1165. # Update the new tick label properties from the old.
  1166. tick = self._get_tick(major=True)
  1167. self.majorTicks.append(tick)
  1168. self._copy_tick_props(self.majorTicks[0], tick)
  1169. return self.majorTicks[:numticks]
  1170. def get_minor_ticks(self, numticks=None):
  1171. r"""Return the list of minor `.Tick`\s."""
  1172. if numticks is None:
  1173. numticks = len(self.get_minorticklocs())
  1174. while len(self.minorTicks) < numticks:
  1175. # Update the new tick label properties from the old.
  1176. tick = self._get_tick(major=False)
  1177. self.minorTicks.append(tick)
  1178. self._copy_tick_props(self.minorTicks[0], tick)
  1179. return self.minorTicks[:numticks]
  1180. def grid(self, b=None, which='major', **kwargs):
  1181. """
  1182. Configure the grid lines.
  1183. Parameters
  1184. ----------
  1185. b : bool or None
  1186. Whether to show the grid lines. If any *kwargs* are supplied,
  1187. it is assumed you want the grid on and *b* will be set to True.
  1188. If *b* is *None* and there are no *kwargs*, this toggles the
  1189. visibility of the lines.
  1190. which : {'major', 'minor', 'both'}
  1191. The grid lines to apply the changes on.
  1192. **kwargs : `.Line2D` properties
  1193. Define the line properties of the grid, e.g.::
  1194. grid(color='r', linestyle='-', linewidth=2)
  1195. """
  1196. if b is not None:
  1197. if 'visible' in kwargs and bool(b) != bool(kwargs['visible']):
  1198. raise ValueError(
  1199. "'b' and 'visible' specify inconsistent grid visibilities")
  1200. if kwargs and not b: # something false-like but not None
  1201. cbook._warn_external('First parameter to grid() is false, '
  1202. 'but line properties are supplied. The '
  1203. 'grid will be enabled.')
  1204. b = True
  1205. which = which.lower()
  1206. cbook._check_in_list(['major', 'minor', 'both'], which=which)
  1207. gridkw = {'grid_' + item[0]: item[1] for item in kwargs.items()}
  1208. if 'grid_visible' in gridkw:
  1209. forced_visibility = True
  1210. gridkw['gridOn'] = gridkw.pop('grid_visible')
  1211. else:
  1212. forced_visibility = False
  1213. if which in ['minor', 'both']:
  1214. if b is None and not forced_visibility:
  1215. gridkw['gridOn'] = not self._minor_tick_kw['gridOn']
  1216. elif b is not None:
  1217. gridkw['gridOn'] = b
  1218. self.set_tick_params(which='minor', **gridkw)
  1219. if which in ['major', 'both']:
  1220. if b is None and not forced_visibility:
  1221. gridkw['gridOn'] = not self._major_tick_kw['gridOn']
  1222. elif b is not None:
  1223. gridkw['gridOn'] = b
  1224. self.set_tick_params(which='major', **gridkw)
  1225. self.stale = True
  1226. def update_units(self, data):
  1227. """
  1228. Introspect *data* for units converter and update the
  1229. axis.converter instance if necessary. Return *True*
  1230. if *data* is registered for unit conversion.
  1231. """
  1232. converter = munits.registry.get_converter(data)
  1233. if converter is None:
  1234. return False
  1235. neednew = self.converter != converter
  1236. self.converter = converter
  1237. default = self.converter.default_units(data, self)
  1238. if default is not None and self.units is None:
  1239. self.set_units(default)
  1240. if neednew:
  1241. self._update_axisinfo()
  1242. self.stale = True
  1243. return True
  1244. def _update_axisinfo(self):
  1245. """
  1246. Check the axis converter for the stored units to see if the
  1247. axis info needs to be updated.
  1248. """
  1249. if self.converter is None:
  1250. return
  1251. info = self.converter.axisinfo(self.units, self)
  1252. if info is None:
  1253. return
  1254. if info.majloc is not None and \
  1255. self.major.locator != info.majloc and self.isDefault_majloc:
  1256. self.set_major_locator(info.majloc)
  1257. self.isDefault_majloc = True
  1258. if info.minloc is not None and \
  1259. self.minor.locator != info.minloc and self.isDefault_minloc:
  1260. self.set_minor_locator(info.minloc)
  1261. self.isDefault_minloc = True
  1262. if info.majfmt is not None and \
  1263. self.major.formatter != info.majfmt and self.isDefault_majfmt:
  1264. self.set_major_formatter(info.majfmt)
  1265. self.isDefault_majfmt = True
  1266. if info.minfmt is not None and \
  1267. self.minor.formatter != info.minfmt and self.isDefault_minfmt:
  1268. self.set_minor_formatter(info.minfmt)
  1269. self.isDefault_minfmt = True
  1270. if info.label is not None and self.isDefault_label:
  1271. self.set_label_text(info.label)
  1272. self.isDefault_label = True
  1273. self.set_default_intervals()
  1274. def have_units(self):
  1275. return self.converter is not None or self.units is not None
  1276. def convert_units(self, x):
  1277. # If x is natively supported by Matplotlib, doesn't need converting
  1278. if munits._is_natively_supported(x):
  1279. return x
  1280. if self.converter is None:
  1281. self.converter = munits.registry.get_converter(x)
  1282. if self.converter is None:
  1283. return x
  1284. try:
  1285. ret = self.converter.convert(x, self.units, self)
  1286. except Exception as e:
  1287. raise munits.ConversionError('Failed to convert value(s) to axis '
  1288. f'units: {x!r}') from e
  1289. return ret
  1290. def set_units(self, u):
  1291. """
  1292. Set the units for axis.
  1293. Parameters
  1294. ----------
  1295. u : units tag
  1296. """
  1297. if u == self.units:
  1298. return
  1299. self.units = u
  1300. self._update_axisinfo()
  1301. self.callbacks.process('units')
  1302. self.callbacks.process('units finalize')
  1303. self.stale = True
  1304. def get_units(self):
  1305. """Return the units for axis."""
  1306. return self.units
  1307. def set_label_text(self, label, fontdict=None, **kwargs):
  1308. """
  1309. Set the text value of the axis label.
  1310. Parameters
  1311. ----------
  1312. label : str
  1313. Text string.
  1314. fontdict : dict
  1315. Text properties.
  1316. **kwargs
  1317. Merged into fontdict.
  1318. """
  1319. self.isDefault_label = False
  1320. self.label.set_text(label)
  1321. if fontdict is not None:
  1322. self.label.update(fontdict)
  1323. self.label.update(kwargs)
  1324. self.stale = True
  1325. return self.label
  1326. def set_major_formatter(self, formatter):
  1327. """
  1328. Set the formatter of the major ticker.
  1329. In addition to a `~matplotlib.ticker.Formatter` instance,
  1330. this also accepts a ``str`` or function.
  1331. For a ``str`` a `~matplotlib.ticker.StrMethodFormatter` is used.
  1332. The field used for the value must be labeled ``'x'`` and the field used
  1333. for the position must be labeled ``'pos'``.
  1334. See the `~matplotlib.ticker.StrMethodFormatter` documentation for
  1335. more information.
  1336. For a function, a `~matplotlib.ticker.FuncFormatter` is used.
  1337. The function must take two inputs (a tick value ``x`` and a
  1338. position ``pos``), and return a string containing the corresponding
  1339. tick label.
  1340. See the `~matplotlib.ticker.FuncFormatter` documentation for
  1341. more information.
  1342. Parameters
  1343. ----------
  1344. formatter : `~matplotlib.ticker.Formatter`, ``str``, or function
  1345. """
  1346. self._set_formatter(formatter, self.major)
  1347. def set_minor_formatter(self, formatter):
  1348. """
  1349. Set the formatter of the minor ticker.
  1350. In addition to a `~matplotlib.ticker.Formatter` instance,
  1351. this also accepts a ``str`` or function.
  1352. See `.Axis.set_major_formatter` for more information.
  1353. Parameters
  1354. ----------
  1355. formatter : `~matplotlib.ticker.Formatter`, ``str``, or function
  1356. """
  1357. self._set_formatter(formatter, self.minor)
  1358. def _set_formatter(self, formatter, level):
  1359. if isinstance(formatter, str):
  1360. formatter = mticker.StrMethodFormatter(formatter)
  1361. # Don't allow any other TickHelper to avoid easy-to-make errors,
  1362. # like using a Locator instead of a Formatter.
  1363. elif (callable(formatter) and
  1364. not isinstance(formatter, mticker.TickHelper)):
  1365. formatter = mticker.FuncFormatter(formatter)
  1366. else:
  1367. cbook._check_isinstance(mticker.Formatter, formatter=formatter)
  1368. if (isinstance(formatter, mticker.FixedFormatter)
  1369. and len(formatter.seq) > 0
  1370. and not isinstance(level.locator, mticker.FixedLocator)):
  1371. cbook._warn_external('FixedFormatter should only be used together '
  1372. 'with FixedLocator')
  1373. if level == self.major:
  1374. self.isDefault_majfmt = False
  1375. else:
  1376. self.isDefault_minfmt = False
  1377. level.formatter = formatter
  1378. formatter.set_axis(self)
  1379. self.stale = True
  1380. def set_major_locator(self, locator):
  1381. """
  1382. Set the locator of the major ticker.
  1383. Parameters
  1384. ----------
  1385. locator : `~matplotlib.ticker.Locator`
  1386. """
  1387. cbook._check_isinstance(mticker.Locator, locator=locator)
  1388. self.isDefault_majloc = False
  1389. self.major.locator = locator
  1390. if self.major.formatter:
  1391. self.major.formatter._set_locator(locator)
  1392. locator.set_axis(self)
  1393. self.stale = True
  1394. def set_minor_locator(self, locator):
  1395. """
  1396. Set the locator of the minor ticker.
  1397. Parameters
  1398. ----------
  1399. locator : `~matplotlib.ticker.Locator`
  1400. """
  1401. cbook._check_isinstance(mticker.Locator, locator=locator)
  1402. self.isDefault_minloc = False
  1403. self.minor.locator = locator
  1404. if self.minor.formatter:
  1405. self.minor.formatter._set_locator(locator)
  1406. locator.set_axis(self)
  1407. self.stale = True
  1408. def set_pickradius(self, pickradius):
  1409. """
  1410. Set the depth of the axis used by the picker.
  1411. Parameters
  1412. ----------
  1413. pickradius : float
  1414. """
  1415. self.pickradius = pickradius
  1416. # Helper for set_ticklabels. Defining it here makes it pickleable.
  1417. @staticmethod
  1418. def _format_with_dict(tickd, x, pos):
  1419. return tickd.get(x, "")
  1420. def set_ticklabels(self, ticklabels, *, minor=False, **kwargs):
  1421. r"""
  1422. Set the text values of the tick labels.
  1423. .. warning::
  1424. This method should only be used after fixing the tick positions
  1425. using `.Axis.set_ticks`. Otherwise, the labels may end up in
  1426. unexpected positions.
  1427. Parameters
  1428. ----------
  1429. ticklabels : sequence of str or of `.Text`\s
  1430. Texts for labeling each tick location in the sequence set by
  1431. `.Axis.set_ticks`; the number of labels must match the number of
  1432. locations.
  1433. minor : bool
  1434. If True, set minor ticks instead of major ticks.
  1435. **kwargs
  1436. Text properties.
  1437. Returns
  1438. -------
  1439. list of `.Text`\s
  1440. For each tick, includes ``tick.label1`` if it is visible, then
  1441. ``tick.label2`` if it is visible, in that order.
  1442. """
  1443. ticklabels = [t.get_text() if hasattr(t, 'get_text') else t
  1444. for t in ticklabels]
  1445. locator = (self.get_minor_locator() if minor
  1446. else self.get_major_locator())
  1447. if isinstance(locator, mticker.FixedLocator):
  1448. # Passing [] as a list of ticklabels is often used as a way to
  1449. # remove all tick labels, so only error for > 0 ticklabels
  1450. if len(locator.locs) != len(ticklabels) and len(ticklabels) != 0:
  1451. raise ValueError(
  1452. "The number of FixedLocator locations"
  1453. f" ({len(locator.locs)}), usually from a call to"
  1454. " set_ticks, does not match"
  1455. f" the number of ticklabels ({len(ticklabels)}).")
  1456. tickd = {loc: lab for loc, lab in zip(locator.locs, ticklabels)}
  1457. func = functools.partial(self._format_with_dict, tickd)
  1458. formatter = mticker.FuncFormatter(func)
  1459. else:
  1460. formatter = mticker.FixedFormatter(ticklabels)
  1461. if minor:
  1462. self.set_minor_formatter(formatter)
  1463. locs = self.get_minorticklocs()
  1464. ticks = self.get_minor_ticks(len(locs))
  1465. else:
  1466. self.set_major_formatter(formatter)
  1467. locs = self.get_majorticklocs()
  1468. ticks = self.get_major_ticks(len(locs))
  1469. ret = []
  1470. for pos, (loc, tick) in enumerate(zip(locs, ticks)):
  1471. tick.update_position(loc)
  1472. tick_label = formatter(loc, pos)
  1473. # deal with label1
  1474. tick.label1.set_text(tick_label)
  1475. tick.label1.update(kwargs)
  1476. # deal with label2
  1477. tick.label2.set_text(tick_label)
  1478. tick.label2.update(kwargs)
  1479. # only return visible tick labels
  1480. if tick.label1.get_visible():
  1481. ret.append(tick.label1)
  1482. if tick.label2.get_visible():
  1483. ret.append(tick.label2)
  1484. self.stale = True
  1485. return ret
  1486. # Wrapper around set_ticklabels used to generate Axes.set_x/ytickabels; can
  1487. # go away once the API of Axes.set_x/yticklabels becomes consistent.
  1488. @cbook._make_keyword_only("3.3", "fontdict")
  1489. def _set_ticklabels(self, labels, fontdict=None, minor=False, **kwargs):
  1490. """
  1491. Set this Axis' labels with list of string labels.
  1492. .. warning::
  1493. This method should only be used after fixing the tick positions
  1494. using `.Axis.set_ticks`. Otherwise, the labels may end up in
  1495. unexpected positions.
  1496. Parameters
  1497. ----------
  1498. labels : list of str
  1499. The label texts.
  1500. fontdict : dict, optional
  1501. A dictionary controlling the appearance of the ticklabels.
  1502. The default *fontdict* is::
  1503. {'fontsize': rcParams['axes.titlesize'],
  1504. 'fontweight': rcParams['axes.titleweight'],
  1505. 'verticalalignment': 'baseline',
  1506. 'horizontalalignment': loc}
  1507. minor : bool, default: False
  1508. Whether to set the minor ticklabels rather than the major ones.
  1509. Returns
  1510. -------
  1511. list of `~.Text`
  1512. The labels.
  1513. Other Parameters
  1514. ----------------
  1515. **kwargs : `~.text.Text` properties.
  1516. """
  1517. if fontdict is not None:
  1518. kwargs.update(fontdict)
  1519. return self.set_ticklabels(labels, minor=minor, **kwargs)
  1520. @cbook._make_keyword_only("3.2", "minor")
  1521. def set_ticks(self, ticks, minor=False):
  1522. """
  1523. Set this Axis' tick locations.
  1524. Parameters
  1525. ----------
  1526. ticks : list of floats
  1527. List of tick locations.
  1528. minor : bool, default: False
  1529. If ``False``, set the major ticks; if ``True``, the minor ticks.
  1530. """
  1531. # XXX if the user changes units, the information will be lost here
  1532. ticks = self.convert_units(ticks)
  1533. if len(ticks) > 1:
  1534. xleft, xright = self.get_view_interval()
  1535. if xright > xleft:
  1536. self.set_view_interval(min(ticks), max(ticks))
  1537. else:
  1538. self.set_view_interval(max(ticks), min(ticks))
  1539. self.axes.stale = True
  1540. if minor:
  1541. self.set_minor_locator(mticker.FixedLocator(ticks))
  1542. return self.get_minor_ticks(len(ticks))
  1543. else:
  1544. self.set_major_locator(mticker.FixedLocator(ticks))
  1545. return self.get_major_ticks(len(ticks))
  1546. def _get_tick_boxes_siblings(self, xdir, renderer):
  1547. """
  1548. Get the bounding boxes for this `.axis` and its siblings
  1549. as set by `.Figure.align_xlabels` or `.Figure.align_ylablels`.
  1550. By default it just gets bboxes for self.
  1551. """
  1552. raise NotImplementedError('Derived must override')
  1553. def _update_label_position(self, renderer):
  1554. """
  1555. Update the label position based on the bounding box enclosing
  1556. all the ticklabels and axis spine.
  1557. """
  1558. raise NotImplementedError('Derived must override')
  1559. def _update_offset_text_position(self, bboxes, bboxes2):
  1560. """
  1561. Update the offset text position based on the sequence of bounding
  1562. boxes of all the ticklabels.
  1563. """
  1564. raise NotImplementedError('Derived must override')
  1565. @cbook.deprecated("3.3")
  1566. def pan(self, numsteps):
  1567. """Pan by *numsteps* (can be positive or negative)."""
  1568. self.major.locator.pan(numsteps)
  1569. @cbook.deprecated("3.3")
  1570. def zoom(self, direction):
  1571. """Zoom in/out on axis; if *direction* is >0 zoom in, else zoom out."""
  1572. self.major.locator.zoom(direction)
  1573. def axis_date(self, tz=None):
  1574. """
  1575. Sets up axis ticks and labels to treat data along this Axis as dates.
  1576. Parameters
  1577. ----------
  1578. tz : str or `datetime.tzinfo`, default: :rc:`timezone`
  1579. The timezone used to create date labels.
  1580. """
  1581. # By providing a sample datetime instance with the desired timezone,
  1582. # the registered converter can be selected, and the "units" attribute,
  1583. # which is the timezone, can be set.
  1584. if isinstance(tz, str):
  1585. import dateutil.tz
  1586. tz = dateutil.tz.gettz(tz)
  1587. self.update_units(datetime.datetime(2009, 1, 1, 0, 0, 0, 0, tz))
  1588. def get_tick_space(self):
  1589. """Return the estimated number of ticks that can fit on the axis."""
  1590. # Must be overridden in the subclass
  1591. raise NotImplementedError()
  1592. def _get_ticks_position(self):
  1593. """
  1594. Helper for `XAxis.get_ticks_position` and `YAxis.get_ticks_position`.
  1595. Check the visibility of tick1line, label1, tick2line, and label2 on
  1596. the first major and the first minor ticks, and return
  1597. - 1 if only tick1line and label1 are visible (which corresponds to
  1598. "bottom" for the x-axis and "left" for the y-axis);
  1599. - 2 if only tick2line and label2 are visible (which corresponds to
  1600. "top" for the x-axis and "right" for the y-axis);
  1601. - "default" if only tick1line, tick2line and label1 are visible;
  1602. - "unknown" otherwise.
  1603. """
  1604. major = self.majorTicks[0]
  1605. minor = self.minorTicks[0]
  1606. if all(tick.tick1line.get_visible()
  1607. and not tick.tick2line.get_visible()
  1608. and tick.label1.get_visible()
  1609. and not tick.label2.get_visible()
  1610. for tick in [major, minor]):
  1611. return 1
  1612. elif all(tick.tick2line.get_visible()
  1613. and not tick.tick1line.get_visible()
  1614. and tick.label2.get_visible()
  1615. and not tick.label1.get_visible()
  1616. for tick in [major, minor]):
  1617. return 2
  1618. elif all(tick.tick1line.get_visible()
  1619. and tick.tick2line.get_visible()
  1620. and tick.label1.get_visible()
  1621. and not tick.label2.get_visible()
  1622. for tick in [major, minor]):
  1623. return "default"
  1624. else:
  1625. return "unknown"
  1626. def get_label_position(self):
  1627. """
  1628. Return the label position (top or bottom)
  1629. """
  1630. return self.label_position
  1631. def set_label_position(self, position):
  1632. """
  1633. Set the label position (top or bottom)
  1634. Parameters
  1635. ----------
  1636. position : {'top', 'bottom'}
  1637. """
  1638. raise NotImplementedError()
  1639. def get_minpos(self):
  1640. raise NotImplementedError()
  1641. def _make_getset_interval(method_name, lim_name, attr_name):
  1642. """
  1643. Helper to generate ``get_{data,view}_interval`` and
  1644. ``set_{data,view}_interval`` implementations.
  1645. """
  1646. def getter(self):
  1647. # docstring inherited.
  1648. return getattr(getattr(self.axes, lim_name), attr_name)
  1649. def setter(self, vmin, vmax, ignore=False):
  1650. # docstring inherited.
  1651. if ignore:
  1652. setattr(getattr(self.axes, lim_name), attr_name, (vmin, vmax))
  1653. else:
  1654. oldmin, oldmax = getter(self)
  1655. if oldmin < oldmax:
  1656. setter(self, min(vmin, vmax, oldmin), max(vmin, vmax, oldmax),
  1657. ignore=True)
  1658. else:
  1659. setter(self, max(vmin, vmax, oldmin), min(vmin, vmax, oldmax),
  1660. ignore=True)
  1661. self.stale = True
  1662. getter.__name__ = f"get_{method_name}_interval"
  1663. setter.__name__ = f"set_{method_name}_interval"
  1664. return getter, setter
  1665. class XAxis(Axis):
  1666. __name__ = 'xaxis'
  1667. axis_name = 'x' #: Read-only name identifying the axis.
  1668. def __init__(self, *args, **kwargs):
  1669. super().__init__(*args, **kwargs)
  1670. # x in axes coords, y in display coords (to be updated at draw time by
  1671. # _update_label_positions and _update_offset_text_position).
  1672. self.label.set(
  1673. x=0.5, y=0,
  1674. verticalalignment='top', horizontalalignment='center',
  1675. transform=mtransforms.blended_transform_factory(
  1676. self.axes.transAxes, mtransforms.IdentityTransform()),
  1677. )
  1678. self.label_position = 'bottom'
  1679. self.offsetText.set(
  1680. x=1, y=0,
  1681. verticalalignment='top', horizontalalignment='right',
  1682. transform=mtransforms.blended_transform_factory(
  1683. self.axes.transAxes, mtransforms.IdentityTransform()),
  1684. fontsize=mpl.rcParams['xtick.labelsize'],
  1685. color=mpl.rcParams['xtick.color'],
  1686. )
  1687. self.offset_text_position = 'bottom'
  1688. def contains(self, mouseevent):
  1689. """Test whether the mouse event occurred in the x axis."""
  1690. inside, info = self._default_contains(mouseevent)
  1691. if inside is not None:
  1692. return inside, info
  1693. x, y = mouseevent.x, mouseevent.y
  1694. try:
  1695. trans = self.axes.transAxes.inverted()
  1696. xaxes, yaxes = trans.transform((x, y))
  1697. except ValueError:
  1698. return False, {}
  1699. (l, b), (r, t) = self.axes.transAxes.transform([(0, 0), (1, 1)])
  1700. inaxis = 0 <= xaxes <= 1 and (
  1701. b - self.pickradius < y < b or
  1702. t < y < t + self.pickradius)
  1703. return inaxis, {}
  1704. def _get_tick(self, major):
  1705. if major:
  1706. tick_kw = self._major_tick_kw
  1707. else:
  1708. tick_kw = self._minor_tick_kw
  1709. return XTick(self.axes, 0, major=major, **tick_kw)
  1710. def set_label_position(self, position):
  1711. """
  1712. Set the label position (top or bottom)
  1713. Parameters
  1714. ----------
  1715. position : {'top', 'bottom'}
  1716. """
  1717. self.label.set_verticalalignment(cbook._check_getitem({
  1718. 'top': 'baseline', 'bottom': 'top',
  1719. }, position=position))
  1720. self.label_position = position
  1721. self.stale = True
  1722. def _get_tick_boxes_siblings(self, renderer):
  1723. """
  1724. Get the bounding boxes for this `.axis` and its siblings
  1725. as set by `.Figure.align_xlabels` or `.Figure.align_ylablels`.
  1726. By default it just gets bboxes for self.
  1727. """
  1728. bboxes = []
  1729. bboxes2 = []
  1730. # get the Grouper that keeps track of x-label groups for this figure
  1731. grp = self.figure._align_xlabel_grp
  1732. # if we want to align labels from other axes:
  1733. for nn, axx in enumerate(grp.get_siblings(self.axes)):
  1734. ticks_to_draw = axx.xaxis._update_ticks()
  1735. tlb, tlb2 = axx.xaxis._get_tick_bboxes(ticks_to_draw, renderer)
  1736. bboxes.extend(tlb)
  1737. bboxes2.extend(tlb2)
  1738. return bboxes, bboxes2
  1739. def _update_label_position(self, renderer):
  1740. """
  1741. Update the label position based on the bounding box enclosing
  1742. all the ticklabels and axis spine
  1743. """
  1744. if not self._autolabelpos:
  1745. return
  1746. # get bounding boxes for this axis and any siblings
  1747. # that have been set by `fig.align_xlabels()`
  1748. bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer)
  1749. x, y = self.label.get_position()
  1750. if self.label_position == 'bottom':
  1751. try:
  1752. spine = self.axes.spines['bottom']
  1753. spinebbox = spine.get_transform().transform_path(
  1754. spine.get_path()).get_extents()
  1755. except KeyError:
  1756. # use axes if spine doesn't exist
  1757. spinebbox = self.axes.bbox
  1758. bbox = mtransforms.Bbox.union(bboxes + [spinebbox])
  1759. bottom = bbox.y0
  1760. self.label.set_position(
  1761. (x, bottom - self.labelpad * self.figure.dpi / 72)
  1762. )
  1763. else:
  1764. try:
  1765. spine = self.axes.spines['top']
  1766. spinebbox = spine.get_transform().transform_path(
  1767. spine.get_path()).get_extents()
  1768. except KeyError:
  1769. # use axes if spine doesn't exist
  1770. spinebbox = self.axes.bbox
  1771. bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox])
  1772. top = bbox.y1
  1773. self.label.set_position(
  1774. (x, top + self.labelpad * self.figure.dpi / 72)
  1775. )
  1776. def _update_offset_text_position(self, bboxes, bboxes2):
  1777. """
  1778. Update the offset_text position based on the sequence of bounding
  1779. boxes of all the ticklabels
  1780. """
  1781. x, y = self.offsetText.get_position()
  1782. if not hasattr(self, '_tick_position'):
  1783. self._tick_position = 'bottom'
  1784. if self._tick_position == 'bottom':
  1785. if not len(bboxes):
  1786. bottom = self.axes.bbox.ymin
  1787. else:
  1788. bbox = mtransforms.Bbox.union(bboxes)
  1789. bottom = bbox.y0
  1790. y = bottom - self.OFFSETTEXTPAD * self.figure.dpi / 72
  1791. else:
  1792. if not len(bboxes2):
  1793. top = self.axes.bbox.ymax
  1794. else:
  1795. bbox = mtransforms.Bbox.union(bboxes2)
  1796. top = bbox.y1
  1797. y = top + self.OFFSETTEXTPAD * self.figure.dpi / 72
  1798. self.offsetText.set_position((x, y))
  1799. def get_text_heights(self, renderer):
  1800. """
  1801. Return how much space should be reserved for text above and below the
  1802. axes, as a pair of floats.
  1803. """
  1804. bbox, bbox2 = self.get_ticklabel_extents(renderer)
  1805. # MGDTODO: Need a better way to get the pad
  1806. padPixels = self.majorTicks[0].get_pad_pixels()
  1807. above = 0.0
  1808. if bbox2.height:
  1809. above += bbox2.height + padPixels
  1810. below = 0.0
  1811. if bbox.height:
  1812. below += bbox.height + padPixels
  1813. if self.get_label_position() == 'top':
  1814. above += self.label.get_window_extent(renderer).height + padPixels
  1815. else:
  1816. below += self.label.get_window_extent(renderer).height + padPixels
  1817. return above, below
  1818. def set_ticks_position(self, position):
  1819. """
  1820. Set the ticks position.
  1821. Parameters
  1822. ----------
  1823. position : {'top', 'bottom', 'both', 'default', 'none'}
  1824. 'both' sets the ticks to appear on both positions, but does not
  1825. change the tick labels. 'default' resets the tick positions to
  1826. the default: ticks on both positions, labels at bottom. 'none'
  1827. can be used if you don't want any ticks. 'none' and 'both'
  1828. affect only the ticks, not the labels.
  1829. """
  1830. cbook._check_in_list(['top', 'bottom', 'both', 'default', 'none'],
  1831. position=position)
  1832. if position == 'top':
  1833. self.set_tick_params(which='both', top=True, labeltop=True,
  1834. bottom=False, labelbottom=False)
  1835. self._tick_position = 'top'
  1836. self.offsetText.set_verticalalignment('bottom')
  1837. elif position == 'bottom':
  1838. self.set_tick_params(which='both', top=False, labeltop=False,
  1839. bottom=True, labelbottom=True)
  1840. self._tick_position = 'bottom'
  1841. self.offsetText.set_verticalalignment('top')
  1842. elif position == 'both':
  1843. self.set_tick_params(which='both', top=True,
  1844. bottom=True)
  1845. elif position == 'none':
  1846. self.set_tick_params(which='both', top=False,
  1847. bottom=False)
  1848. elif position == 'default':
  1849. self.set_tick_params(which='both', top=True, labeltop=False,
  1850. bottom=True, labelbottom=True)
  1851. self._tick_position = 'bottom'
  1852. self.offsetText.set_verticalalignment('top')
  1853. else:
  1854. assert False, "unhandled parameter not caught by _check_in_list"
  1855. self.stale = True
  1856. def tick_top(self):
  1857. """
  1858. Move ticks and ticklabels (if present) to the top of the axes.
  1859. """
  1860. label = True
  1861. if 'label1On' in self._major_tick_kw:
  1862. label = (self._major_tick_kw['label1On']
  1863. or self._major_tick_kw['label2On'])
  1864. self.set_ticks_position('top')
  1865. # If labels were turned off before this was called, leave them off.
  1866. self.set_tick_params(which='both', labeltop=label)
  1867. def tick_bottom(self):
  1868. """
  1869. Move ticks and ticklabels (if present) to the bottom of the axes.
  1870. """
  1871. label = True
  1872. if 'label1On' in self._major_tick_kw:
  1873. label = (self._major_tick_kw['label1On']
  1874. or self._major_tick_kw['label2On'])
  1875. self.set_ticks_position('bottom')
  1876. # If labels were turned off before this was called, leave them off.
  1877. self.set_tick_params(which='both', labelbottom=label)
  1878. def get_ticks_position(self):
  1879. """
  1880. Return the ticks position ("top", "bottom", "default", or "unknown").
  1881. """
  1882. return {1: "bottom", 2: "top",
  1883. "default": "default", "unknown": "unknown"}[
  1884. self._get_ticks_position()]
  1885. get_view_interval, set_view_interval = _make_getset_interval(
  1886. "view", "viewLim", "intervalx")
  1887. get_data_interval, set_data_interval = _make_getset_interval(
  1888. "data", "dataLim", "intervalx")
  1889. def get_minpos(self):
  1890. return self.axes.dataLim.minposx
  1891. def set_inverted(self, inverted):
  1892. # docstring inherited
  1893. a, b = self.get_view_interval()
  1894. # cast to bool to avoid bad interaction between python 3.8 and np.bool_
  1895. self.axes.set_xlim(sorted((a, b), reverse=bool(inverted)), auto=None)
  1896. def set_default_intervals(self):
  1897. # docstring inherited
  1898. xmin, xmax = 0., 1.
  1899. dataMutated = self.axes.dataLim.mutatedx()
  1900. viewMutated = self.axes.viewLim.mutatedx()
  1901. if not dataMutated or not viewMutated:
  1902. if self.converter is not None:
  1903. info = self.converter.axisinfo(self.units, self)
  1904. if info.default_limits is not None:
  1905. valmin, valmax = info.default_limits
  1906. xmin = self.converter.convert(valmin, self.units, self)
  1907. xmax = self.converter.convert(valmax, self.units, self)
  1908. if not dataMutated:
  1909. self.axes.dataLim.intervalx = xmin, xmax
  1910. if not viewMutated:
  1911. self.axes.viewLim.intervalx = xmin, xmax
  1912. self.stale = True
  1913. def get_tick_space(self):
  1914. ends = self.axes.transAxes.transform([[0, 0], [1, 0]])
  1915. length = ((ends[1][0] - ends[0][0]) / self.axes.figure.dpi) * 72
  1916. # There is a heuristic here that the aspect ratio of tick text
  1917. # is no more than 3:1
  1918. size = self._get_tick_label_size('x') * 3
  1919. if size > 0:
  1920. return int(np.floor(length / size))
  1921. else:
  1922. return 2**31 - 1
  1923. class YAxis(Axis):
  1924. __name__ = 'yaxis'
  1925. axis_name = 'y' #: Read-only name identifying the axis.
  1926. def __init__(self, *args, **kwargs):
  1927. super().__init__(*args, **kwargs)
  1928. # x in display coords, y in axes coords (to be updated at draw time by
  1929. # _update_label_positions and _update_offset_text_position).
  1930. self.label.set(
  1931. x=0, y=0.5,
  1932. verticalalignment='bottom', horizontalalignment='center',
  1933. rotation='vertical', rotation_mode='anchor',
  1934. transform=mtransforms.blended_transform_factory(
  1935. mtransforms.IdentityTransform(), self.axes.transAxes),
  1936. )
  1937. self.label_position = 'left'
  1938. # x in axes coords, y in display coords(!).
  1939. self.offsetText.set(
  1940. x=0, y=0.5,
  1941. verticalalignment='baseline', horizontalalignment='left',
  1942. transform=mtransforms.blended_transform_factory(
  1943. self.axes.transAxes, mtransforms.IdentityTransform()),
  1944. fontsize=mpl.rcParams['ytick.labelsize'],
  1945. color=mpl.rcParams['ytick.color'],
  1946. )
  1947. self.offset_text_position = 'left'
  1948. def contains(self, mouseevent):
  1949. # docstring inherited
  1950. inside, info = self._default_contains(mouseevent)
  1951. if inside is not None:
  1952. return inside, info
  1953. x, y = mouseevent.x, mouseevent.y
  1954. try:
  1955. trans = self.axes.transAxes.inverted()
  1956. xaxes, yaxes = trans.transform((x, y))
  1957. except ValueError:
  1958. return False, {}
  1959. (l, b), (r, t) = self.axes.transAxes.transform([(0, 0), (1, 1)])
  1960. inaxis = 0 <= yaxes <= 1 and (
  1961. l - self.pickradius < x < l or
  1962. r < x < r + self.pickradius)
  1963. return inaxis, {}
  1964. def _get_tick(self, major):
  1965. if major:
  1966. tick_kw = self._major_tick_kw
  1967. else:
  1968. tick_kw = self._minor_tick_kw
  1969. return YTick(self.axes, 0, major=major, **tick_kw)
  1970. def set_label_position(self, position):
  1971. """
  1972. Set the label position (left or right)
  1973. Parameters
  1974. ----------
  1975. position : {'left', 'right'}
  1976. """
  1977. self.label.set_rotation_mode('anchor')
  1978. self.label.set_horizontalalignment('center')
  1979. self.label.set_verticalalignment(cbook._check_getitem({
  1980. 'left': 'bottom', 'right': 'top',
  1981. }, position=position))
  1982. self.label_position = position
  1983. self.stale = True
  1984. def _get_tick_boxes_siblings(self, renderer):
  1985. """
  1986. Get the bounding boxes for this `.axis` and its siblings
  1987. as set by `.Figure.align_xlabels` or `.Figure.align_ylablels`.
  1988. By default it just gets bboxes for self.
  1989. """
  1990. bboxes = []
  1991. bboxes2 = []
  1992. # get the Grouper that keeps track of y-label groups for this figure
  1993. grp = self.figure._align_ylabel_grp
  1994. # if we want to align labels from other axes:
  1995. for axx in grp.get_siblings(self.axes):
  1996. ticks_to_draw = axx.yaxis._update_ticks()
  1997. tlb, tlb2 = axx.yaxis._get_tick_bboxes(ticks_to_draw, renderer)
  1998. bboxes.extend(tlb)
  1999. bboxes2.extend(tlb2)
  2000. return bboxes, bboxes2
  2001. def _update_label_position(self, renderer):
  2002. """
  2003. Update the label position based on the bounding box enclosing
  2004. all the ticklabels and axis spine
  2005. """
  2006. if not self._autolabelpos:
  2007. return
  2008. # get bounding boxes for this axis and any siblings
  2009. # that have been set by `fig.align_ylabels()`
  2010. bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer)
  2011. x, y = self.label.get_position()
  2012. if self.label_position == 'left':
  2013. try:
  2014. spine = self.axes.spines['left']
  2015. spinebbox = spine.get_transform().transform_path(
  2016. spine.get_path()).get_extents()
  2017. except KeyError:
  2018. # use axes if spine doesn't exist
  2019. spinebbox = self.axes.bbox
  2020. bbox = mtransforms.Bbox.union(bboxes + [spinebbox])
  2021. left = bbox.x0
  2022. self.label.set_position(
  2023. (left - self.labelpad * self.figure.dpi / 72, y)
  2024. )
  2025. else:
  2026. try:
  2027. spine = self.axes.spines['right']
  2028. spinebbox = spine.get_transform().transform_path(
  2029. spine.get_path()).get_extents()
  2030. except KeyError:
  2031. # use axes if spine doesn't exist
  2032. spinebbox = self.axes.bbox
  2033. bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox])
  2034. right = bbox.x1
  2035. self.label.set_position(
  2036. (right + self.labelpad * self.figure.dpi / 72, y)
  2037. )
  2038. def _update_offset_text_position(self, bboxes, bboxes2):
  2039. """
  2040. Update the offset_text position based on the sequence of bounding
  2041. boxes of all the ticklabels
  2042. """
  2043. x, y = self.offsetText.get_position()
  2044. top = self.axes.bbox.ymax
  2045. self.offsetText.set_position(
  2046. (x, top + self.OFFSETTEXTPAD * self.figure.dpi / 72)
  2047. )
  2048. def set_offset_position(self, position):
  2049. """
  2050. Parameters
  2051. ----------
  2052. position : {'left', 'right'}
  2053. """
  2054. x, y = self.offsetText.get_position()
  2055. x = cbook._check_getitem({'left': 0, 'right': 1}, position=position)
  2056. self.offsetText.set_ha(position)
  2057. self.offsetText.set_position((x, y))
  2058. self.stale = True
  2059. def get_text_widths(self, renderer):
  2060. bbox, bbox2 = self.get_ticklabel_extents(renderer)
  2061. # MGDTODO: Need a better way to get the pad
  2062. padPixels = self.majorTicks[0].get_pad_pixels()
  2063. left = 0.0
  2064. if bbox.width:
  2065. left += bbox.width + padPixels
  2066. right = 0.0
  2067. if bbox2.width:
  2068. right += bbox2.width + padPixels
  2069. if self.get_label_position() == 'left':
  2070. left += self.label.get_window_extent(renderer).width + padPixels
  2071. else:
  2072. right += self.label.get_window_extent(renderer).width + padPixels
  2073. return left, right
  2074. def set_ticks_position(self, position):
  2075. """
  2076. Set the ticks position.
  2077. Parameters
  2078. ----------
  2079. position : {'left', 'right', 'both', 'default', 'none'}
  2080. 'both' sets the ticks to appear on both positions, but does not
  2081. change the tick labels. 'default' resets the tick positions to
  2082. the default: ticks on both positions, labels at left. 'none'
  2083. can be used if you don't want any ticks. 'none' and 'both'
  2084. affect only the ticks, not the labels.
  2085. """
  2086. cbook._check_in_list(['left', 'right', 'both', 'default', 'none'],
  2087. position=position)
  2088. if position == 'right':
  2089. self.set_tick_params(which='both', right=True, labelright=True,
  2090. left=False, labelleft=False)
  2091. self.set_offset_position(position)
  2092. elif position == 'left':
  2093. self.set_tick_params(which='both', right=False, labelright=False,
  2094. left=True, labelleft=True)
  2095. self.set_offset_position(position)
  2096. elif position == 'both':
  2097. self.set_tick_params(which='both', right=True,
  2098. left=True)
  2099. elif position == 'none':
  2100. self.set_tick_params(which='both', right=False,
  2101. left=False)
  2102. elif position == 'default':
  2103. self.set_tick_params(which='both', right=True, labelright=False,
  2104. left=True, labelleft=True)
  2105. else:
  2106. assert False, "unhandled parameter not caught by _check_in_list"
  2107. self.stale = True
  2108. def tick_right(self):
  2109. """
  2110. Move ticks and ticklabels (if present) to the right of the axes.
  2111. """
  2112. label = True
  2113. if 'label1On' in self._major_tick_kw:
  2114. label = (self._major_tick_kw['label1On']
  2115. or self._major_tick_kw['label2On'])
  2116. self.set_ticks_position('right')
  2117. # if labels were turned off before this was called
  2118. # leave them off
  2119. self.set_tick_params(which='both', labelright=label)
  2120. def tick_left(self):
  2121. """
  2122. Move ticks and ticklabels (if present) to the left of the axes.
  2123. """
  2124. label = True
  2125. if 'label1On' in self._major_tick_kw:
  2126. label = (self._major_tick_kw['label1On']
  2127. or self._major_tick_kw['label2On'])
  2128. self.set_ticks_position('left')
  2129. # if labels were turned off before this was called
  2130. # leave them off
  2131. self.set_tick_params(which='both', labelleft=label)
  2132. def get_ticks_position(self):
  2133. """
  2134. Return the ticks position ("left", "right", "default", or "unknown").
  2135. """
  2136. return {1: "left", 2: "right",
  2137. "default": "default", "unknown": "unknown"}[
  2138. self._get_ticks_position()]
  2139. get_view_interval, set_view_interval = _make_getset_interval(
  2140. "view", "viewLim", "intervaly")
  2141. get_data_interval, set_data_interval = _make_getset_interval(
  2142. "data", "dataLim", "intervaly")
  2143. def get_minpos(self):
  2144. return self.axes.dataLim.minposy
  2145. def set_inverted(self, inverted):
  2146. # docstring inherited
  2147. a, b = self.get_view_interval()
  2148. # cast to bool to avoid bad interaction between python 3.8 and np.bool_
  2149. self.axes.set_ylim(sorted((a, b), reverse=bool(inverted)), auto=None)
  2150. def set_default_intervals(self):
  2151. # docstring inherited
  2152. ymin, ymax = 0., 1.
  2153. dataMutated = self.axes.dataLim.mutatedy()
  2154. viewMutated = self.axes.viewLim.mutatedy()
  2155. if not dataMutated or not viewMutated:
  2156. if self.converter is not None:
  2157. info = self.converter.axisinfo(self.units, self)
  2158. if info.default_limits is not None:
  2159. valmin, valmax = info.default_limits
  2160. ymin = self.converter.convert(valmin, self.units, self)
  2161. ymax = self.converter.convert(valmax, self.units, self)
  2162. if not dataMutated:
  2163. self.axes.dataLim.intervaly = ymin, ymax
  2164. if not viewMutated:
  2165. self.axes.viewLim.intervaly = ymin, ymax
  2166. self.stale = True
  2167. def get_tick_space(self):
  2168. ends = self.axes.transAxes.transform([[0, 0], [0, 1]])
  2169. length = ((ends[1][1] - ends[0][1]) / self.axes.figure.dpi) * 72
  2170. # Having a spacing of at least 2 just looks good.
  2171. size = self._get_tick_label_size('y') * 2
  2172. if size > 0:
  2173. return int(np.floor(length / size))
  2174. else:
  2175. return 2**31 - 1