Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/matplotlib/axis.py : 19%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Classes for the ticks and x and y axis.
3"""
5import datetime
6import logging
8import numpy as np
10from matplotlib import rcParams
11import matplotlib.artist as martist
12import matplotlib.cbook as cbook
13import matplotlib.font_manager as font_manager
14import matplotlib.lines as mlines
15import matplotlib.scale as mscale
16import matplotlib.text as mtext
17import matplotlib.ticker as mticker
18import matplotlib.transforms as mtransforms
19import matplotlib.units as munits
21_log = logging.getLogger(__name__)
23GRIDLINE_INTERPOLATION_STEPS = 180
25# This list is being used for compatibility with Axes.grid, which
26# allows all Line2D kwargs.
27_line_AI = martist.ArtistInspector(mlines.Line2D)
28_line_param_names = _line_AI.get_setters()
29_line_param_aliases = [list(d)[0] for d in _line_AI.aliasd.values()]
30_gridline_param_names = ['grid_' + name
31 for name in _line_param_names + _line_param_aliases]
34class Tick(martist.Artist):
35 """
36 Abstract base class for the axis ticks, grid lines and labels.
38 Ticks mark a position on an Axis. They contain two lines as markers and
39 two labels; one each for the bottom and top positions (in case of an
40 `.XAxis`) or for the left and right positions (in case of a `.YAxis`).
42 Attributes
43 ----------
44 tick1line : `.Line2D`
45 The left/bottom tick marker.
46 tick2line : `.Line2D`
47 The right/top tick marker.
48 gridline : `.Line2D`
49 The grid line associated with the label position.
50 label1 : `.Text`
51 The left/bottom tick label.
52 label2 : `.Text`
53 The right/top tick label.
55 """
56 def __init__(self, axes, loc, label,
57 size=None, # points
58 width=None,
59 color=None,
60 tickdir=None,
61 pad=None,
62 labelsize=None,
63 labelcolor=None,
64 zorder=None,
65 gridOn=None, # defaults to axes.grid depending on
66 # axes.grid.which
67 tick1On=True,
68 tick2On=True,
69 label1On=True,
70 label2On=False,
71 major=True,
72 labelrotation=0,
73 grid_color=None,
74 grid_linestyle=None,
75 grid_linewidth=None,
76 grid_alpha=None,
77 **kw # Other Line2D kwargs applied to gridlines.
78 ):
79 """
80 bbox is the Bound2D bounding box in display coords of the Axes
81 loc is the tick location in data coords
82 size is the tick size in points
83 """
84 martist.Artist.__init__(self)
86 if gridOn is None:
87 if major and (rcParams['axes.grid.which'] in ('both', 'major')):
88 gridOn = rcParams['axes.grid']
89 elif (not major) and (rcParams['axes.grid.which']
90 in ('both', 'minor')):
91 gridOn = rcParams['axes.grid']
92 else:
93 gridOn = False
95 self.set_figure(axes.figure)
96 self.axes = axes
98 name = self.__name__.lower()
100 self._loc = loc
102 if size is None:
103 if major:
104 size = rcParams['%s.major.size' % name]
105 else:
106 size = rcParams['%s.minor.size' % name]
107 self._size = size
109 if width is None:
110 if major:
111 width = rcParams['%s.major.width' % name]
112 else:
113 width = rcParams['%s.minor.width' % name]
114 self._width = width
116 if color is None:
117 color = rcParams['%s.color' % name]
118 self._color = color
120 if pad is None:
121 if major:
122 pad = rcParams['%s.major.pad' % name]
123 else:
124 pad = rcParams['%s.minor.pad' % name]
125 self._base_pad = pad
127 if labelcolor is None:
128 labelcolor = rcParams['%s.color' % name]
129 self._labelcolor = labelcolor
131 if labelsize is None:
132 labelsize = rcParams['%s.labelsize' % name]
133 self._labelsize = labelsize
135 self._set_labelrotation(labelrotation)
137 if zorder is None:
138 if major:
139 zorder = mlines.Line2D.zorder + 0.01
140 else:
141 zorder = mlines.Line2D.zorder
142 self._zorder = zorder
144 self._grid_color = (rcParams['grid.color']
145 if grid_color is None else grid_color)
146 self._grid_linestyle = (rcParams['grid.linestyle']
147 if grid_linestyle is None else grid_linestyle)
148 self._grid_linewidth = (rcParams['grid.linewidth']
149 if grid_linewidth is None else grid_linewidth)
150 self._grid_alpha = (rcParams['grid.alpha']
151 if grid_alpha is None else grid_alpha)
153 self._grid_kw = {k[5:]: v for k, v in kw.items()}
155 self.apply_tickdir(tickdir)
157 self.tick1line = self._get_tick1line()
158 self.tick2line = self._get_tick2line()
159 self.gridline = self._get_gridline()
160 self.label1 = self._get_text1()
161 self.label2 = self._get_text2()
163 self.gridline.set_visible(gridOn)
164 self.tick1line.set_visible(tick1On)
165 self.tick2line.set_visible(tick2On)
166 self.label1.set_visible(label1On)
167 self.label2.set_visible(label2On)
169 self.update_position(loc)
171 for _old_name, _new_name in [
172 ("gridOn", "gridline"),
173 ("tick1On", "tick1line"),
174 ("tick2On", "tick2line"),
175 ("label1On", "label1"),
176 ("label2On", "label2")]:
177 locals()[_old_name] = property(
178 cbook.deprecated(
179 "3.1",
180 name=_old_name,
181 alternative="Tick.{}.get_visible".format(_new_name))(
182 lambda self, _new_name=_new_name:
183 getattr(self, _new_name).get_visible()),
184 cbook.deprecated(
185 "3.1",
186 name=_old_name,
187 alternative="Tick.{}.set_visible".format(_new_name))(
188 lambda self, value, _new_name=_new_name:
189 getattr(self, _new_name).set_visible(value)))
190 del _old_name, _new_name
192 @property
193 @cbook.deprecated("3.1", alternative="Tick.label1", pending=True)
194 def label(self):
195 return self.label1
197 def _set_labelrotation(self, labelrotation):
198 if isinstance(labelrotation, str):
199 mode = labelrotation
200 angle = 0
201 elif isinstance(labelrotation, (tuple, list)):
202 mode, angle = labelrotation
203 else:
204 mode = 'default'
205 angle = labelrotation
206 cbook._check_in_list(['auto', 'default'], labelrotation=mode)
207 self._labelrotation = (mode, angle)
209 def apply_tickdir(self, tickdir):
210 """Calculate self._pad and self._tickmarkers."""
212 def get_tickdir(self):
213 return self._tickdir
215 def get_tick_padding(self):
216 """Get the length of the tick outside of the axes."""
217 padding = {
218 'in': 0.0,
219 'inout': 0.5,
220 'out': 1.0
221 }
222 return self._size * padding[self._tickdir]
224 def get_children(self):
225 children = [self.tick1line, self.tick2line,
226 self.gridline, self.label1, self.label2]
227 return children
229 def set_clip_path(self, clippath, transform=None):
230 # docstring inherited
231 martist.Artist.set_clip_path(self, clippath, transform)
232 self.gridline.set_clip_path(clippath, transform)
233 self.stale = True
235 def get_pad_pixels(self):
236 return self.figure.dpi * self._base_pad / 72
238 def contains(self, mouseevent):
239 """
240 Test whether the mouse event occurred in the Tick marks.
242 This function always returns false. It is more useful to test if the
243 axis as a whole contains the mouse rather than the set of tick marks.
244 """
245 inside, info = self._default_contains(mouseevent)
246 if inside is not None:
247 return inside, info
248 return False, {}
250 def set_pad(self, val):
251 """
252 Set the tick label pad in points
254 Parameters
255 ----------
256 val : float
257 """
258 self._apply_params(pad=val)
259 self.stale = True
261 def get_pad(self):
262 'Get the value of the tick label pad in points'
263 return self._base_pad
265 def _get_text1(self):
266 'Get the default Text 1 instance'
267 pass
269 def _get_text2(self):
270 'Get the default Text 2 instance'
271 pass
273 def _get_tick1line(self):
274 'Get the default line2D instance for tick1'
275 pass
277 def _get_tick2line(self):
278 'Get the default line2D instance for tick2'
279 pass
281 def _get_gridline(self):
282 'Get the default grid Line2d instance for this tick'
283 pass
285 def get_loc(self):
286 'Return the tick location (data coords) as a scalar'
287 return self._loc
289 @martist.allow_rasterization
290 def draw(self, renderer):
291 if not self.get_visible():
292 self.stale = False
293 return
294 renderer.open_group(self.__name__, gid=self.get_gid())
295 for artist in [self.gridline, self.tick1line, self.tick2line,
296 self.label1, self.label2]:
297 artist.draw(renderer)
298 renderer.close_group(self.__name__)
299 self.stale = False
301 def set_label1(self, s):
302 """
303 Set the label1 text.
305 Parameters
306 ----------
307 s : str
308 """
309 self.label1.set_text(s)
310 self.stale = True
312 set_label = set_label1
314 def set_label2(self, s):
315 """
316 Set the label2 text.
318 Parameters
319 ----------
320 s : str
321 """
322 self.label2.set_text(s)
323 self.stale = True
325 def _set_artist_props(self, a):
326 a.set_figure(self.figure)
328 def get_view_interval(self):
329 """
330 Return the view limits ``(min, max)`` of the axis the tick belongs to.
331 """
332 raise NotImplementedError('Derived must override')
334 def _apply_params(self, **kw):
335 for name, target in [("gridOn", self.gridline),
336 ("tick1On", self.tick1line),
337 ("tick2On", self.tick2line),
338 ("label1On", self.label1),
339 ("label2On", self.label2)]:
340 if name in kw:
341 target.set_visible(kw.pop(name))
342 if any(k in kw for k in ['size', 'width', 'pad', 'tickdir']):
343 self._size = kw.pop('size', self._size)
344 # Width could be handled outside this block, but it is
345 # convenient to leave it here.
346 self._width = kw.pop('width', self._width)
347 self._base_pad = kw.pop('pad', self._base_pad)
348 # apply_tickdir uses _size and _base_pad to make _pad,
349 # and also makes _tickmarkers.
350 self.apply_tickdir(kw.pop('tickdir', self._tickdir))
351 self.tick1line.set_marker(self._tickmarkers[0])
352 self.tick2line.set_marker(self._tickmarkers[1])
353 for line in (self.tick1line, self.tick2line):
354 line.set_markersize(self._size)
355 line.set_markeredgewidth(self._width)
356 # _get_text1_transform uses _pad from apply_tickdir.
357 trans = self._get_text1_transform()[0]
358 self.label1.set_transform(trans)
359 trans = self._get_text2_transform()[0]
360 self.label2.set_transform(trans)
361 tick_kw = {k: v for k, v in kw.items() if k in ['color', 'zorder']}
362 self.tick1line.set(**tick_kw)
363 self.tick2line.set(**tick_kw)
364 for k, v in tick_kw.items():
365 setattr(self, '_' + k, v)
367 if 'labelrotation' in kw:
368 self._set_labelrotation(kw.pop('labelrotation'))
369 self.label1.set(rotation=self._labelrotation[1])
370 self.label2.set(rotation=self._labelrotation[1])
372 label_kw = {k[5:]: v for k, v in kw.items()
373 if k in ['labelsize', 'labelcolor']}
374 self.label1.set(**label_kw)
375 self.label2.set(**label_kw)
376 for k, v in label_kw.items():
377 # for labelsize the text objects covert str ('small')
378 # -> points. grab the integer from the `Text` object
379 # instead of saving the string representation
380 v = getattr(self.label1, 'get_' + k)()
381 setattr(self, '_label' + k, v)
383 grid_kw = {k[5:]: v for k, v in kw.items()
384 if k in _gridline_param_names}
385 self.gridline.set(**grid_kw)
386 for k, v in grid_kw.items():
387 setattr(self, '_grid_' + k, v)
389 def update_position(self, loc):
390 'Set the location of tick in data coords with scalar *loc*'
391 raise NotImplementedError('Derived must override')
393 def _get_text1_transform(self):
394 raise NotImplementedError('Derived must override')
396 def _get_text2_transform(self):
397 raise NotImplementedError('Derived must override')
400class XTick(Tick):
401 """
402 Contains all the Artists needed to make an x tick - the tick line,
403 the label text and the grid line
404 """
405 __name__ = 'xtick'
407 def _get_text1_transform(self):
408 return self.axes.get_xaxis_text1_transform(self._pad)
410 def _get_text2_transform(self):
411 return self.axes.get_xaxis_text2_transform(self._pad)
413 def apply_tickdir(self, tickdir):
414 if tickdir is None:
415 tickdir = rcParams['%s.direction' % self.__name__.lower()]
416 self._tickdir = tickdir
418 if self._tickdir == 'in':
419 self._tickmarkers = (mlines.TICKUP, mlines.TICKDOWN)
420 elif self._tickdir == 'inout':
421 self._tickmarkers = ('|', '|')
422 else:
423 self._tickmarkers = (mlines.TICKDOWN, mlines.TICKUP)
424 self._pad = self._base_pad + self.get_tick_padding()
425 self.stale = True
427 def _get_text1(self):
428 'Get the default Text instance'
429 # the y loc is 3 points below the min of y axis
430 # get the affine as an a, b, c, d, tx, ty list
431 # x in data coords, y in axes coords
432 trans, vert, horiz = self._get_text1_transform()
433 t = mtext.Text(
434 x=0, y=0,
435 fontproperties=font_manager.FontProperties(size=self._labelsize),
436 color=self._labelcolor,
437 verticalalignment=vert,
438 horizontalalignment=horiz,
439 )
440 t.set_transform(trans)
441 self._set_artist_props(t)
442 return t
444 def _get_text2(self):
445 'Get the default Text 2 instance'
446 # x in data coords, y in axes coords
447 trans, vert, horiz = self._get_text2_transform()
448 t = mtext.Text(
449 x=0, y=1,
450 fontproperties=font_manager.FontProperties(size=self._labelsize),
451 color=self._labelcolor,
452 verticalalignment=vert,
453 horizontalalignment=horiz,
454 )
455 t.set_transform(trans)
456 self._set_artist_props(t)
457 return t
459 def _get_tick1line(self):
460 'Get the default line2D instance'
461 # x in data coords, y in axes coords
462 l = mlines.Line2D(xdata=(0,), ydata=(0,), color=self._color,
463 linestyle='None', marker=self._tickmarkers[0],
464 markersize=self._size,
465 markeredgewidth=self._width, zorder=self._zorder)
466 l.set_transform(self.axes.get_xaxis_transform(which='tick1'))
467 self._set_artist_props(l)
468 return l
470 def _get_tick2line(self):
471 'Get the default line2D instance'
472 # x in data coords, y in axes coords
473 l = mlines.Line2D(xdata=(0,), ydata=(1,),
474 color=self._color,
475 linestyle='None',
476 marker=self._tickmarkers[1],
477 markersize=self._size,
478 markeredgewidth=self._width,
479 zorder=self._zorder)
481 l.set_transform(self.axes.get_xaxis_transform(which='tick2'))
482 self._set_artist_props(l)
483 return l
485 def _get_gridline(self):
486 'Get the default line2D instance'
487 # x in data coords, y in axes coords
488 l = mlines.Line2D(xdata=(0.0, 0.0), ydata=(0, 1.0),
489 color=self._grid_color,
490 linestyle=self._grid_linestyle,
491 linewidth=self._grid_linewidth,
492 alpha=self._grid_alpha,
493 markersize=0,
494 **self._grid_kw)
495 l.set_transform(self.axes.get_xaxis_transform(which='grid'))
496 l.get_path()._interpolation_steps = GRIDLINE_INTERPOLATION_STEPS
497 self._set_artist_props(l)
499 return l
501 def update_position(self, loc):
502 """Set the location of tick in data coords with scalar *loc*."""
503 self.tick1line.set_xdata((loc,))
504 self.tick2line.set_xdata((loc,))
505 self.gridline.set_xdata((loc,))
506 self.label1.set_x(loc)
507 self.label2.set_x(loc)
508 self._loc = loc
509 self.stale = True
511 def get_view_interval(self):
512 # docstring inherited
513 return self.axes.viewLim.intervalx
516class YTick(Tick):
517 """
518 Contains all the Artists needed to make a Y tick - the tick line,
519 the label text and the grid line
520 """
521 __name__ = 'ytick'
523 def _get_text1_transform(self):
524 return self.axes.get_yaxis_text1_transform(self._pad)
526 def _get_text2_transform(self):
527 return self.axes.get_yaxis_text2_transform(self._pad)
529 def apply_tickdir(self, tickdir):
530 if tickdir is None:
531 tickdir = rcParams['%s.direction' % self.__name__.lower()]
532 self._tickdir = tickdir
534 if self._tickdir == 'in':
535 self._tickmarkers = (mlines.TICKRIGHT, mlines.TICKLEFT)
536 elif self._tickdir == 'inout':
537 self._tickmarkers = ('_', '_')
538 else:
539 self._tickmarkers = (mlines.TICKLEFT, mlines.TICKRIGHT)
540 self._pad = self._base_pad + self.get_tick_padding()
541 self.stale = True
543 # how far from the y axis line the right of the ticklabel are
544 def _get_text1(self):
545 'Get the default Text instance'
546 # x in axes coords, y in data coords
547 trans, vert, horiz = self._get_text1_transform()
548 t = mtext.Text(
549 x=0, y=0,
550 fontproperties=font_manager.FontProperties(size=self._labelsize),
551 color=self._labelcolor,
552 verticalalignment=vert,
553 horizontalalignment=horiz,
554 )
555 t.set_transform(trans)
556 self._set_artist_props(t)
557 return t
559 def _get_text2(self):
560 'Get the default Text instance'
561 # x in axes coords, y in data coords
562 trans, vert, horiz = self._get_text2_transform()
563 t = mtext.Text(
564 x=1, y=0,
565 fontproperties=font_manager.FontProperties(size=self._labelsize),
566 color=self._labelcolor,
567 verticalalignment=vert,
568 horizontalalignment=horiz,
569 )
570 t.set_transform(trans)
571 self._set_artist_props(t)
572 return t
574 def _get_tick1line(self):
575 'Get the default line2D instance'
576 # x in axes coords, y in data coords
578 l = mlines.Line2D((0,), (0,),
579 color=self._color,
580 marker=self._tickmarkers[0],
581 linestyle='None',
582 markersize=self._size,
583 markeredgewidth=self._width,
584 zorder=self._zorder)
585 l.set_transform(self.axes.get_yaxis_transform(which='tick1'))
586 self._set_artist_props(l)
587 return l
589 def _get_tick2line(self):
590 'Get the default line2D instance'
591 # x in axes coords, y in data coords
592 l = mlines.Line2D((1,), (0,),
593 color=self._color,
594 marker=self._tickmarkers[1],
595 linestyle='None',
596 markersize=self._size,
597 markeredgewidth=self._width,
598 zorder=self._zorder)
599 l.set_transform(self.axes.get_yaxis_transform(which='tick2'))
600 self._set_artist_props(l)
601 return l
603 def _get_gridline(self):
604 'Get the default line2D instance'
605 # x in axes coords, y in data coords
606 l = mlines.Line2D(xdata=(0, 1), ydata=(0, 0),
607 color=self._grid_color,
608 linestyle=self._grid_linestyle,
609 linewidth=self._grid_linewidth,
610 alpha=self._grid_alpha,
611 markersize=0,
612 **self._grid_kw)
613 l.set_transform(self.axes.get_yaxis_transform(which='grid'))
614 l.get_path()._interpolation_steps = GRIDLINE_INTERPOLATION_STEPS
615 self._set_artist_props(l)
616 return l
618 def update_position(self, loc):
619 """Set the location of tick in data coords with scalar *loc*."""
620 self.tick1line.set_ydata((loc,))
621 self.tick2line.set_ydata((loc,))
622 self.gridline.set_ydata((loc,))
623 self.label1.set_y(loc)
624 self.label2.set_y(loc)
625 self._loc = loc
626 self.stale = True
628 def get_view_interval(self):
629 # docstring inherited
630 return self.axes.viewLim.intervaly
633class Ticker:
634 """
635 A container for the objects defining tick position and format.
637 Attributes
638 ----------
639 locator : `matplotlib.ticker.Locator` subclass
640 Determines the positions of the ticks.
641 formatter : `matplotlib.ticker.Formatter` subclass
642 Determines the format of the tick labels.
643 """
645 def __init__(self):
646 self._locator = None
647 self._formatter = None
649 @property
650 def locator(self):
651 return self._locator
653 @locator.setter
654 def locator(self, locator):
655 if not isinstance(locator, mticker.Locator):
656 cbook.warn_deprecated(
657 "3.2", message="Support for locators that do not subclass "
658 "matplotlib.ticker.Locator is deprecated since %(since)s and "
659 "support for them will be removed %(removal)s.")
660 self._locator = locator
662 @property
663 def formatter(self):
664 return self._formatter
666 @formatter.setter
667 def formatter(self, formatter):
668 if not isinstance(formatter, mticker.Formatter):
669 cbook.warn_deprecated(
670 "3.2", message="Support for formatters that do not subclass "
671 "matplotlib.ticker.Formatter is deprecated since %(since)s "
672 "and support for them will be removed %(removal)s.")
673 self._formatter = formatter
676class _LazyTickList:
677 """
678 A descriptor for lazy instantiation of tick lists.
680 See comment above definition of the ``majorTicks`` and ``minorTicks``
681 attributes.
682 """
684 def __init__(self, major):
685 self._major = major
687 def __get__(self, instance, cls):
688 if instance is None:
689 return self
690 else:
691 # instance._get_tick() can itself try to access the majorTicks
692 # attribute (e.g. in certain projection classes which override
693 # e.g. get_xaxis_text1_transform). In order to avoid infinite
694 # recursion, first set the majorTicks on the instance to an empty
695 # list, then create the tick and append it.
696 if self._major:
697 instance.majorTicks = []
698 tick = instance._get_tick(major=True)
699 instance.majorTicks.append(tick)
700 return instance.majorTicks
701 else:
702 instance.minorTicks = []
703 tick = instance._get_tick(major=False)
704 instance.minorTicks.append(tick)
705 return instance.minorTicks
708class Axis(martist.Artist):
709 """
710 Base class for `.XAxis` and `.YAxis`.
712 Attributes
713 ----------
714 isDefault_label : bool
716 axes : `matplotlib.axes.Axes`
717 The `~.axes.Axes` to which the Axis belongs.
718 major : `matplotlib.axis.Ticker`
719 Determines the major tick positions and their label format.
720 minor : `matplotlib.axis.Ticker`
721 Determines the minor tick positions and their label format.
722 callbacks : `matplotlib.cbook.CallbackRegistry`
724 label : `.Text`
725 The axis label.
726 labelpad : float
727 The distance between the axis label and the tick labels.
728 Defaults to :rc:`axes.labelpad` = 4.
729 offsetText : `.Text`
730 A `.Text` object containing the data offset of the ticks (if any).
731 pickradius : float
732 The acceptance radius for containment tests. See also `.Axis.contains`.
733 majorTicks : list of `.Tick`
734 The major ticks.
735 minorTicks : list of `.Tick`
736 The minor ticks.
737 """
738 OFFSETTEXTPAD = 3
740 def __str__(self):
741 return "{}({},{})".format(
742 type(self).__name__, *self.axes.transAxes.transform((0, 0)))
744 def __init__(self, axes, pickradius=15):
745 """
746 Parameters
747 ----------
748 axes : `matplotlib.axes.Axes`
749 The `~.axes.Axes` to which the created Axis belongs.
750 pickradius : float
751 The acceptance radius for containment tests. See also
752 `.Axis.contains`.
753 """
754 martist.Artist.__init__(self)
755 self._remove_overlapping_locs = True
757 self.set_figure(axes.figure)
759 self.isDefault_label = True
761 self.axes = axes
762 self.major = Ticker()
763 self.minor = Ticker()
764 self.callbacks = cbook.CallbackRegistry()
766 self._autolabelpos = True
767 self._smart_bounds = False # Deprecated in 3.2
769 self.label = self._get_label()
770 self.labelpad = rcParams['axes.labelpad']
771 self.offsetText = self._get_offset_text()
773 self.pickradius = pickradius
775 # Initialize here for testing; later add API
776 self._major_tick_kw = dict()
777 self._minor_tick_kw = dict()
779 self.cla()
780 self._set_scale('linear')
782 # During initialization, Axis objects often create ticks that are later
783 # unused; this turns out to be a very slow step. Instead, use a custom
784 # descriptor to make the tick lists lazy and instantiate them as needed.
785 majorTicks = _LazyTickList(major=True)
786 minorTicks = _LazyTickList(major=False)
788 def get_remove_overlapping_locs(self):
789 return self._remove_overlapping_locs
791 def set_remove_overlapping_locs(self, val):
792 self._remove_overlapping_locs = bool(val)
794 remove_overlapping_locs = property(
795 get_remove_overlapping_locs, set_remove_overlapping_locs,
796 doc=('If minor ticker locations that overlap with major '
797 'ticker locations should be trimmed.'))
799 def set_label_coords(self, x, y, transform=None):
800 """
801 Set the coordinates of the label.
803 By default, the x coordinate of the y label and the y coordinate of the
804 x label are determined by the tick label bounding boxes, but this can
805 lead to poor alignment of multiple labels if there are multiple axes.
807 You can also specify the coordinate system of the label with the
808 transform. If None, the default coordinate system will be the axes
809 coordinate system: (0, 0) is bottom left, (0.5, 0.5) is center, etc.
810 """
811 self._autolabelpos = False
812 if transform is None:
813 transform = self.axes.transAxes
815 self.label.set_transform(transform)
816 self.label.set_position((x, y))
817 self.stale = True
819 def get_transform(self):
820 return self._scale.get_transform()
822 def get_scale(self):
823 return self._scale.name
825 def _set_scale(self, value, **kwargs):
826 self._scale = mscale.scale_factory(value, self, **kwargs)
827 self._scale.set_default_locators_and_formatters(self)
829 self.isDefault_majloc = True
830 self.isDefault_minloc = True
831 self.isDefault_majfmt = True
832 self.isDefault_minfmt = True
834 def limit_range_for_scale(self, vmin, vmax):
835 return self._scale.limit_range_for_scale(vmin, vmax, self.get_minpos())
837 def get_children(self):
838 return [self.label, self.offsetText,
839 *self.get_major_ticks(), *self.get_minor_ticks()]
841 def cla(self):
842 """Clear this axis."""
844 self.label.set_text('') # self.set_label_text would change isDefault_
846 self._set_scale('linear')
848 # Clear the callback registry for this axis, or it may "leak"
849 self.callbacks = cbook.CallbackRegistry()
851 # whether the grids are on
852 self._gridOnMajor = (rcParams['axes.grid'] and
853 rcParams['axes.grid.which'] in ('both', 'major'))
854 self._gridOnMinor = (rcParams['axes.grid'] and
855 rcParams['axes.grid.which'] in ('both', 'minor'))
857 self.reset_ticks()
859 self.converter = None
860 self.units = None
861 self.set_units(None)
862 self.stale = True
864 def reset_ticks(self):
865 """
866 Re-initialize the major and minor Tick lists.
868 Each list starts with a single fresh Tick.
869 """
870 # Restore the lazy tick lists.
871 try:
872 del self.majorTicks
873 except AttributeError:
874 pass
875 try:
876 del self.minorTicks
877 except AttributeError:
878 pass
879 try:
880 self.set_clip_path(self.axes.patch)
881 except AttributeError:
882 pass
884 def set_tick_params(self, which='major', reset=False, **kw):
885 """
886 Set appearance parameters for ticks, ticklabels, and gridlines.
888 For documentation of keyword arguments, see
889 :meth:`matplotlib.axes.Axes.tick_params`.
890 """
891 dicts = []
892 if which == 'major' or which == 'both':
893 dicts.append(self._major_tick_kw)
894 if which == 'minor' or which == 'both':
895 dicts.append(self._minor_tick_kw)
896 kwtrans = self._translate_tick_kw(kw)
898 # this stashes the parameter changes so any new ticks will
899 # automatically get them
900 for d in dicts:
901 if reset:
902 d.clear()
903 d.update(kwtrans)
905 if reset:
906 self.reset_ticks()
907 else:
908 # apply the new kwargs to the existing ticks
909 if which == 'major' or which == 'both':
910 for tick in self.majorTicks:
911 tick._apply_params(**kwtrans)
912 if which == 'minor' or which == 'both':
913 for tick in self.minorTicks:
914 tick._apply_params(**kwtrans)
915 # special-case label color to also apply to the offset
916 # text
917 if 'labelcolor' in kwtrans:
918 self.offsetText.set_color(kwtrans['labelcolor'])
920 self.stale = True
922 @staticmethod
923 def _translate_tick_kw(kw):
924 # The following lists may be moved to a more accessible location.
925 kwkeys = ['size', 'width', 'color', 'tickdir', 'pad',
926 'labelsize', 'labelcolor', 'zorder', 'gridOn',
927 'tick1On', 'tick2On', 'label1On', 'label2On',
928 'length', 'direction', 'left', 'bottom', 'right', 'top',
929 'labelleft', 'labelbottom', 'labelright', 'labeltop',
930 'labelrotation'] + _gridline_param_names
931 kwtrans = {}
932 if 'length' in kw:
933 kwtrans['size'] = kw.pop('length')
934 if 'direction' in kw:
935 kwtrans['tickdir'] = kw.pop('direction')
936 if 'rotation' in kw:
937 kwtrans['labelrotation'] = kw.pop('rotation')
938 if 'left' in kw:
939 kwtrans['tick1On'] = kw.pop('left')
940 if 'bottom' in kw:
941 kwtrans['tick1On'] = kw.pop('bottom')
942 if 'right' in kw:
943 kwtrans['tick2On'] = kw.pop('right')
944 if 'top' in kw:
945 kwtrans['tick2On'] = kw.pop('top')
946 if 'labelleft' in kw:
947 kwtrans['label1On'] = kw.pop('labelleft')
948 if 'labelbottom' in kw:
949 kwtrans['label1On'] = kw.pop('labelbottom')
950 if 'labelright' in kw:
951 kwtrans['label2On'] = kw.pop('labelright')
952 if 'labeltop' in kw:
953 kwtrans['label2On'] = kw.pop('labeltop')
954 if 'colors' in kw:
955 c = kw.pop('colors')
956 kwtrans['color'] = c
957 kwtrans['labelcolor'] = c
958 # Maybe move the checking up to the caller of this method.
959 for key in kw:
960 if key not in kwkeys:
961 raise ValueError(
962 "keyword %s is not recognized; valid keywords are %s"
963 % (key, kwkeys))
964 kwtrans.update(kw)
965 return kwtrans
967 def set_clip_path(self, clippath, transform=None):
968 martist.Artist.set_clip_path(self, clippath, transform)
969 for child in self.majorTicks + self.minorTicks:
970 child.set_clip_path(clippath, transform)
971 self.stale = True
973 def get_view_interval(self):
974 """Return the view limits ``(min, max)`` of this axis."""
975 raise NotImplementedError('Derived must override')
977 def set_view_interval(self, vmin, vmax, ignore=False):
978 """
979 Set the axis view limits. This method is for internal use; Matplotlib
980 users should typically use e.g. `~Axes.set_xlim` and `~Axes.set_ylim`.
982 If *ignore* is False (the default), this method will never reduce the
983 preexisting view limits, only expand them if *vmin* or *vmax* are not
984 within them. Moreover, the order of *vmin* and *vmax* does not matter;
985 the orientation of the axis will not change.
987 If *ignore* is True, the view limits will be set exactly to ``(vmin,
988 vmax)`` in that order.
989 """
990 raise NotImplementedError('Derived must override')
992 def get_data_interval(self):
993 """Return the Interval instance for this axis data limits."""
994 raise NotImplementedError('Derived must override')
996 def set_data_interval(self, vmin, vmax, ignore=False):
997 """
998 Set the axis data limits. This method is for internal use.
1000 If *ignore* is False (the default), this method will never reduce the
1001 preexisting data limits, only expand them if *vmin* or *vmax* are not
1002 within them. Moreover, the order of *vmin* and *vmax* does not matter;
1003 the orientation of the axis will not change.
1005 If *ignore* is True, the data limits will be set exactly to ``(vmin,
1006 vmax)`` in that order.
1007 """
1008 raise NotImplementedError('Derived must override')
1010 def get_inverted(self):
1011 """
1012 Return whether the axis is oriented in the "inverse" direction.
1014 The "normal" direction is increasing to the right for the x-axis and to
1015 the top for the y-axis; the "inverse" direction is increasing to the
1016 left for the x-axis and to the bottom for the y-axis.
1017 """
1018 low, high = self.get_view_interval()
1019 return high < low
1021 def set_inverted(self, inverted):
1022 """
1023 Set whether the axis is oriented in the "inverse" direction.
1025 The "normal" direction is increasing to the right for the x-axis and to
1026 the top for the y-axis; the "inverse" direction is increasing to the
1027 left for the x-axis and to the bottom for the y-axis.
1028 """
1029 # Currently, must be implemented in subclasses using set_xlim/set_ylim
1030 # rather than generically using set_view_interval, so that shared
1031 # axes get updated as well.
1032 raise NotImplementedError('Derived must override')
1034 def set_default_intervals(self):
1035 """
1036 Set the default limits for the axis data and view interval if they
1037 have not been not mutated yet.
1038 """
1039 # this is mainly in support of custom object plotting. For
1040 # example, if someone passes in a datetime object, we do not
1041 # know automagically how to set the default min/max of the
1042 # data and view limits. The unit conversion AxisInfo
1043 # interface provides a hook for custom types to register
1044 # default limits through the AxisInfo.default_limits
1045 # attribute, and the derived code below will check for that
1046 # and use it if is available (else just use 0..1)
1048 def _set_artist_props(self, a):
1049 if a is None:
1050 return
1051 a.set_figure(self.figure)
1053 @cbook.deprecated("3.1")
1054 def iter_ticks(self):
1055 """
1056 Yield ``(Tick, location, label)`` tuples for major and minor ticks.
1057 """
1058 major_locs = self.get_majorticklocs()
1059 major_labels = self.major.formatter.format_ticks(major_locs)
1060 major_ticks = self.get_major_ticks(len(major_locs))
1061 yield from zip(major_ticks, major_locs, major_labels)
1062 minor_locs = self.get_minorticklocs()
1063 minor_labels = self.minor.formatter.format_ticks(minor_locs)
1064 minor_ticks = self.get_minor_ticks(len(minor_locs))
1065 yield from zip(minor_ticks, minor_locs, minor_labels)
1067 def get_ticklabel_extents(self, renderer):
1068 """
1069 Get the extents of the tick labels on either side
1070 of the axes.
1071 """
1073 ticks_to_draw = self._update_ticks()
1074 ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw,
1075 renderer)
1077 if len(ticklabelBoxes):
1078 bbox = mtransforms.Bbox.union(ticklabelBoxes)
1079 else:
1080 bbox = mtransforms.Bbox.from_extents(0, 0, 0, 0)
1081 if len(ticklabelBoxes2):
1082 bbox2 = mtransforms.Bbox.union(ticklabelBoxes2)
1083 else:
1084 bbox2 = mtransforms.Bbox.from_extents(0, 0, 0, 0)
1085 return bbox, bbox2
1087 @cbook.deprecated("3.2")
1088 def set_smart_bounds(self, value):
1089 """Set the axis to have smart bounds."""
1090 self._smart_bounds = value
1091 self.stale = True
1093 @cbook.deprecated("3.2")
1094 def get_smart_bounds(self):
1095 """Return whether the axis has smart bounds."""
1096 return self._smart_bounds
1098 def _update_ticks(self):
1099 """
1100 Update ticks (position and labels) using the current data interval of
1101 the axes. Return the list of ticks that will be drawn.
1102 """
1103 major_locs = self.get_majorticklocs()
1104 major_labels = self.major.formatter.format_ticks(major_locs)
1105 major_ticks = self.get_major_ticks(len(major_locs))
1106 self.major.formatter.set_locs(major_locs)
1107 for tick, loc, label in zip(major_ticks, major_locs, major_labels):
1108 tick.update_position(loc)
1109 tick.set_label1(label)
1110 tick.set_label2(label)
1111 minor_locs = self.get_minorticklocs()
1112 minor_labels = self.minor.formatter.format_ticks(minor_locs)
1113 minor_ticks = self.get_minor_ticks(len(minor_locs))
1114 self.minor.formatter.set_locs(minor_locs)
1115 for tick, loc, label in zip(minor_ticks, minor_locs, minor_labels):
1116 tick.update_position(loc)
1117 tick.set_label1(label)
1118 tick.set_label2(label)
1119 ticks = [*major_ticks, *minor_ticks]
1121 view_low, view_high = self.get_view_interval()
1122 if view_low > view_high:
1123 view_low, view_high = view_high, view_low
1125 if self._smart_bounds and ticks: # _smart_bounds is deprecated in 3.2
1126 # handle inverted limits
1127 data_low, data_high = sorted(self.get_data_interval())
1128 locs = np.sort([tick.get_loc() for tick in ticks])
1129 if data_low <= view_low:
1130 # data extends beyond view, take view as limit
1131 ilow = view_low
1132 else:
1133 # data stops within view, take best tick
1134 good_locs = locs[locs <= data_low]
1135 if len(good_locs):
1136 # last tick prior or equal to first data point
1137 ilow = good_locs[-1]
1138 else:
1139 # No ticks (why not?), take first tick
1140 ilow = locs[0]
1141 if data_high >= view_high:
1142 # data extends beyond view, take view as limit
1143 ihigh = view_high
1144 else:
1145 # data stops within view, take best tick
1146 good_locs = locs[locs >= data_high]
1147 if len(good_locs):
1148 # first tick after or equal to last data point
1149 ihigh = good_locs[0]
1150 else:
1151 # No ticks (why not?), take last tick
1152 ihigh = locs[-1]
1153 ticks = [tick for tick in ticks if ilow <= tick.get_loc() <= ihigh]
1155 interval_t = self.get_transform().transform([view_low, view_high])
1157 ticks_to_draw = []
1158 for tick in ticks:
1159 try:
1160 loc_t = self.get_transform().transform(tick.get_loc())
1161 except AssertionError:
1162 # transforms.transform doesn't allow masked values but
1163 # some scales might make them, so we need this try/except.
1164 pass
1165 else:
1166 if mtransforms._interval_contains_close(interval_t, loc_t):
1167 ticks_to_draw.append(tick)
1169 return ticks_to_draw
1171 def _get_tick_bboxes(self, ticks, renderer):
1172 """Return lists of bboxes for ticks' label1's and label2's."""
1173 return ([tick.label1.get_window_extent(renderer)
1174 for tick in ticks if tick.label1.get_visible()],
1175 [tick.label2.get_window_extent(renderer)
1176 for tick in ticks if tick.label2.get_visible()])
1178 def get_tightbbox(self, renderer):
1179 """
1180 Return a bounding box that encloses the axis. It only accounts
1181 tick labels, axis label, and offsetText.
1182 """
1183 if not self.get_visible():
1184 return
1186 ticks_to_draw = self._update_ticks()
1188 self._update_label_position(renderer)
1190 # go back to just this axis's tick labels
1191 ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(
1192 ticks_to_draw, renderer)
1194 self._update_offset_text_position(ticklabelBoxes, ticklabelBoxes2)
1195 self.offsetText.set_text(self.major.formatter.get_offset())
1197 bboxes = [
1198 *(a.get_window_extent(renderer)
1199 for a in [self.label, self.offsetText]
1200 if a.get_visible()),
1201 *ticklabelBoxes,
1202 *ticklabelBoxes2,
1203 ]
1204 bboxes = [b for b in bboxes
1205 if 0 < b.width < np.inf and 0 < b.height < np.inf]
1206 if bboxes:
1207 return mtransforms.Bbox.union(bboxes)
1208 else:
1209 return None
1211 def get_tick_padding(self):
1212 values = []
1213 if len(self.majorTicks):
1214 values.append(self.majorTicks[0].get_tick_padding())
1215 if len(self.minorTicks):
1216 values.append(self.minorTicks[0].get_tick_padding())
1217 return max(values, default=0)
1219 @martist.allow_rasterization
1220 def draw(self, renderer, *args, **kwargs):
1221 'Draw the axis lines, grid lines, tick lines and labels'
1223 if not self.get_visible():
1224 return
1225 renderer.open_group(__name__, gid=self.get_gid())
1227 ticks_to_draw = self._update_ticks()
1228 ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw,
1229 renderer)
1231 for tick in ticks_to_draw:
1232 tick.draw(renderer)
1234 # scale up the axis label box to also find the neighbors, not
1235 # just the tick labels that actually overlap note we need a
1236 # *copy* of the axis label box because we don't wan't to scale
1237 # the actual bbox
1239 self._update_label_position(renderer)
1241 self.label.draw(renderer)
1243 self._update_offset_text_position(ticklabelBoxes, ticklabelBoxes2)
1244 self.offsetText.set_text(self.major.formatter.get_offset())
1245 self.offsetText.draw(renderer)
1247 renderer.close_group(__name__)
1248 self.stale = False
1250 def _get_label(self):
1251 raise NotImplementedError('Derived must override')
1253 def _get_offset_text(self):
1254 raise NotImplementedError('Derived must override')
1256 def get_gridlines(self):
1257 'Return the grid lines as a list of Line2D instance'
1258 ticks = self.get_major_ticks()
1259 return cbook.silent_list('Line2D gridline',
1260 [tick.gridline for tick in ticks])
1262 def get_label(self):
1263 'Return the axis label as a Text instance'
1264 return self.label
1266 def get_offset_text(self):
1267 'Return the axis offsetText as a Text instance'
1268 return self.offsetText
1270 def get_pickradius(self):
1271 'Return the depth of the axis used by the picker'
1272 return self.pickradius
1274 def get_majorticklabels(self):
1275 'Return a list of Text instances for the major ticklabels.'
1276 ticks = self.get_major_ticks()
1277 labels1 = [tick.label1 for tick in ticks if tick.label1.get_visible()]
1278 labels2 = [tick.label2 for tick in ticks if tick.label2.get_visible()]
1279 return cbook.silent_list('Text major ticklabel', labels1 + labels2)
1281 def get_minorticklabels(self):
1282 'Return a list of Text instances for the minor ticklabels.'
1283 ticks = self.get_minor_ticks()
1284 labels1 = [tick.label1 for tick in ticks if tick.label1.get_visible()]
1285 labels2 = [tick.label2 for tick in ticks if tick.label2.get_visible()]
1286 return cbook.silent_list('Text minor ticklabel', labels1 + labels2)
1288 def get_ticklabels(self, minor=False, which=None):
1289 """
1290 Get the tick labels as a list of `~matplotlib.text.Text` instances.
1292 Parameters
1293 ----------
1294 minor : bool
1295 If True return the minor ticklabels,
1296 else return the major ticklabels
1298 which : None, ('minor', 'major', 'both')
1299 Overrides *minor*.
1301 Selects which ticklabels to return
1303 Returns
1304 -------
1305 ret : list
1306 List of `~matplotlib.text.Text` instances.
1307 """
1309 if which is not None:
1310 if which == 'minor':
1311 return self.get_minorticklabels()
1312 elif which == 'major':
1313 return self.get_majorticklabels()
1314 elif which == 'both':
1315 return self.get_majorticklabels() + self.get_minorticklabels()
1316 else:
1317 cbook._check_in_list(['major', 'minor', 'both'], which=which)
1318 if minor:
1319 return self.get_minorticklabels()
1320 return self.get_majorticklabels()
1322 def get_majorticklines(self):
1323 'Return the major tick lines as a list of Line2D instances'
1324 lines = []
1325 ticks = self.get_major_ticks()
1326 for tick in ticks:
1327 lines.append(tick.tick1line)
1328 lines.append(tick.tick2line)
1329 return cbook.silent_list('Line2D ticklines', lines)
1331 def get_minorticklines(self):
1332 'Return the minor tick lines as a list of Line2D instances'
1333 lines = []
1334 ticks = self.get_minor_ticks()
1335 for tick in ticks:
1336 lines.append(tick.tick1line)
1337 lines.append(tick.tick2line)
1338 return cbook.silent_list('Line2D ticklines', lines)
1340 def get_ticklines(self, minor=False):
1341 'Return the tick lines as a list of Line2D instances'
1342 if minor:
1343 return self.get_minorticklines()
1344 return self.get_majorticklines()
1346 def get_majorticklocs(self):
1347 """Get the array of major tick locations in data coordinates."""
1348 return self.major.locator()
1350 def get_minorticklocs(self):
1351 """Get the array of minor tick locations in data coordinates."""
1352 # Remove minor ticks duplicating major ticks.
1353 major_locs = self.major.locator()
1354 minor_locs = self.minor.locator()
1355 transform = self._scale.get_transform()
1356 tr_minor_locs = transform.transform(minor_locs)
1357 tr_major_locs = transform.transform(major_locs)
1358 lo, hi = sorted(transform.transform(self.get_view_interval()))
1359 # Use the transformed view limits as scale. 1e-5 is the default rtol
1360 # for np.isclose.
1361 tol = (hi - lo) * 1e-5
1362 if self.remove_overlapping_locs:
1363 minor_locs = [
1364 loc for loc, tr_loc in zip(minor_locs, tr_minor_locs)
1365 if ~np.isclose(tr_loc, tr_major_locs, atol=tol, rtol=0).any()]
1366 return minor_locs
1368 def get_ticklocs(self, minor=False):
1369 """Get the array of tick locations in data coordinates."""
1370 return self.get_minorticklocs() if minor else self.get_majorticklocs()
1372 def get_ticks_direction(self, minor=False):
1373 """
1374 Get the tick directions as a numpy array
1376 Parameters
1377 ----------
1378 minor : boolean
1379 True to return the minor tick directions,
1380 False to return the major tick directions,
1381 Default is False
1383 Returns
1384 -------
1385 numpy array of tick directions
1386 """
1387 if minor:
1388 return np.array(
1389 [tick._tickdir for tick in self.get_minor_ticks()])
1390 else:
1391 return np.array(
1392 [tick._tickdir for tick in self.get_major_ticks()])
1394 def _get_tick(self, major):
1395 """Return the default tick instance."""
1396 raise NotImplementedError('derived must override')
1398 def _copy_tick_props(self, src, dest):
1399 """Copy the properties from *src* tick to *dest* tick."""
1400 if src is None or dest is None:
1401 return
1402 dest.label1.update_from(src.label1)
1403 dest.label2.update_from(src.label2)
1404 dest.tick1line.update_from(src.tick1line)
1405 dest.tick2line.update_from(src.tick2line)
1406 dest.gridline.update_from(src.gridline)
1408 def get_label_text(self):
1409 'Get the text of the label'
1410 return self.label.get_text()
1412 def get_major_locator(self):
1413 'Get the locator of the major ticker'
1414 return self.major.locator
1416 def get_minor_locator(self):
1417 'Get the locator of the minor ticker'
1418 return self.minor.locator
1420 def get_major_formatter(self):
1421 'Get the formatter of the major ticker'
1422 return self.major.formatter
1424 def get_minor_formatter(self):
1425 'Get the formatter of the minor ticker'
1426 return self.minor.formatter
1428 def get_major_ticks(self, numticks=None):
1429 'Get the tick instances; grow as necessary.'
1430 if numticks is None:
1431 numticks = len(self.get_majorticklocs())
1433 while len(self.majorTicks) < numticks:
1434 # Update the new tick label properties from the old.
1435 tick = self._get_tick(major=True)
1436 self.majorTicks.append(tick)
1437 tick.gridline.set_visible(self._gridOnMajor)
1438 self._copy_tick_props(self.majorTicks[0], tick)
1440 return self.majorTicks[:numticks]
1442 def get_minor_ticks(self, numticks=None):
1443 'Get the minor tick instances; grow as necessary.'
1444 if numticks is None:
1445 numticks = len(self.get_minorticklocs())
1447 while len(self.minorTicks) < numticks:
1448 # Update the new tick label properties from the old.
1449 tick = self._get_tick(major=False)
1450 self.minorTicks.append(tick)
1451 tick.gridline.set_visible(self._gridOnMinor)
1452 self._copy_tick_props(self.minorTicks[0], tick)
1454 return self.minorTicks[:numticks]
1456 def grid(self, b=None, which='major', **kwargs):
1457 """
1458 Configure the grid lines.
1460 Parameters
1461 ----------
1462 b : bool or None
1463 Whether to show the grid lines. If any *kwargs* are supplied,
1464 it is assumed you want the grid on and *b* will be set to True.
1466 If *b* is *None* and there are no *kwargs*, this toggles the
1467 visibility of the lines.
1469 which : {'major', 'minor', 'both'}
1470 The grid lines to apply the changes on.
1472 **kwargs : `.Line2D` properties
1473 Define the line properties of the grid, e.g.::
1475 grid(color='r', linestyle='-', linewidth=2)
1477 """
1478 if len(kwargs):
1479 if not b and b is not None: # something false-like but not None
1480 cbook._warn_external('First parameter to grid() is false, '
1481 'but line properties are supplied. The '
1482 'grid will be enabled.')
1483 b = True
1484 which = which.lower()
1485 cbook._check_in_list(['major', 'minor', 'both'], which=which)
1486 gridkw = {'grid_' + item[0]: item[1] for item in kwargs.items()}
1488 if which in ['minor', 'both']:
1489 if b is None:
1490 self._gridOnMinor = not self._gridOnMinor
1491 else:
1492 self._gridOnMinor = b
1493 self.set_tick_params(which='minor', gridOn=self._gridOnMinor,
1494 **gridkw)
1495 if which in ['major', 'both']:
1496 if b is None:
1497 self._gridOnMajor = not self._gridOnMajor
1498 else:
1499 self._gridOnMajor = b
1500 self.set_tick_params(which='major', gridOn=self._gridOnMajor,
1501 **gridkw)
1502 self.stale = True
1504 def update_units(self, data):
1505 """
1506 Introspect *data* for units converter and update the
1507 axis.converter instance if necessary. Return *True*
1508 if *data* is registered for unit conversion.
1509 """
1510 converter = munits.registry.get_converter(data)
1511 if converter is None:
1512 return False
1514 neednew = self.converter != converter
1515 self.converter = converter
1516 default = self.converter.default_units(data, self)
1517 if default is not None and self.units is None:
1518 self.set_units(default)
1520 if neednew:
1521 self._update_axisinfo()
1522 self.stale = True
1523 return True
1525 def _update_axisinfo(self):
1526 """
1527 Check the axis converter for the stored units to see if the
1528 axis info needs to be updated.
1529 """
1530 if self.converter is None:
1531 return
1533 info = self.converter.axisinfo(self.units, self)
1535 if info is None:
1536 return
1537 if info.majloc is not None and \
1538 self.major.locator != info.majloc and self.isDefault_majloc:
1539 self.set_major_locator(info.majloc)
1540 self.isDefault_majloc = True
1541 if info.minloc is not None and \
1542 self.minor.locator != info.minloc and self.isDefault_minloc:
1543 self.set_minor_locator(info.minloc)
1544 self.isDefault_minloc = True
1545 if info.majfmt is not None and \
1546 self.major.formatter != info.majfmt and self.isDefault_majfmt:
1547 self.set_major_formatter(info.majfmt)
1548 self.isDefault_majfmt = True
1549 if info.minfmt is not None and \
1550 self.minor.formatter != info.minfmt and self.isDefault_minfmt:
1551 self.set_minor_formatter(info.minfmt)
1552 self.isDefault_minfmt = True
1553 if info.label is not None and self.isDefault_label:
1554 self.set_label_text(info.label)
1555 self.isDefault_label = True
1557 self.set_default_intervals()
1559 def have_units(self):
1560 return self.converter is not None or self.units is not None
1562 def convert_units(self, x):
1563 # If x is natively supported by Matplotlib, doesn't need converting
1564 if munits._is_natively_supported(x):
1565 return x
1567 if self.converter is None:
1568 self.converter = munits.registry.get_converter(x)
1570 if self.converter is None:
1571 return x
1572 try:
1573 ret = self.converter.convert(x, self.units, self)
1574 except Exception as e:
1575 raise munits.ConversionError('Failed to convert value(s) to axis '
1576 f'units: {x!r}') from e
1577 return ret
1579 def set_units(self, u):
1580 """
1581 Set the units for axis.
1583 Parameters
1584 ----------
1585 u : units tag
1586 """
1587 if u == self.units:
1588 return
1589 self.units = u
1590 self._update_axisinfo()
1591 self.callbacks.process('units')
1592 self.callbacks.process('units finalize')
1593 self.stale = True
1595 def get_units(self):
1596 """Return the units for axis."""
1597 return self.units
1599 def set_label_text(self, label, fontdict=None, **kwargs):
1600 """
1601 Set the text value of the axis label.
1603 Parameters
1604 ----------
1605 label : str
1606 Text string.
1607 fontdict : dict
1608 Text properties.
1609 **kwargs
1610 Merged into fontdict.
1611 """
1612 self.isDefault_label = False
1613 self.label.set_text(label)
1614 if fontdict is not None:
1615 self.label.update(fontdict)
1616 self.label.update(kwargs)
1617 self.stale = True
1618 return self.label
1620 def set_major_formatter(self, formatter):
1621 """
1622 Set the formatter of the major ticker.
1624 Parameters
1625 ----------
1626 formatter : `~matplotlib.ticker.Formatter`
1627 """
1628 cbook._check_isinstance(mticker.Formatter, formatter=formatter)
1629 self.isDefault_majfmt = False
1630 self.major.formatter = formatter
1631 formatter.set_axis(self)
1632 self.stale = True
1634 def set_minor_formatter(self, formatter):
1635 """
1636 Set the formatter of the minor ticker.
1638 Parameters
1639 ----------
1640 formatter : `~matplotlib.ticker.Formatter`
1641 """
1642 cbook._check_isinstance(mticker.Formatter, formatter=formatter)
1643 self.isDefault_minfmt = False
1644 self.minor.formatter = formatter
1645 formatter.set_axis(self)
1646 self.stale = True
1648 def set_major_locator(self, locator):
1649 """
1650 Set the locator of the major ticker.
1652 Parameters
1653 ----------
1654 locator : `~matplotlib.ticker.Locator`
1655 """
1656 cbook._check_isinstance(mticker.Locator, locator=locator)
1657 self.isDefault_majloc = False
1658 self.major.locator = locator
1659 if self.major.formatter:
1660 self.major.formatter._set_locator(locator)
1661 locator.set_axis(self)
1662 self.stale = True
1664 def set_minor_locator(self, locator):
1665 """
1666 Set the locator of the minor ticker.
1668 Parameters
1669 ----------
1670 locator : `~matplotlib.ticker.Locator`
1671 """
1672 cbook._check_isinstance(mticker.Locator, locator=locator)
1673 self.isDefault_minloc = False
1674 self.minor.locator = locator
1675 if self.minor.formatter:
1676 self.minor.formatter._set_locator(locator)
1677 locator.set_axis(self)
1678 self.stale = True
1680 def set_pickradius(self, pickradius):
1681 """
1682 Set the depth of the axis used by the picker.
1684 Parameters
1685 ----------
1686 pickradius : float
1687 """
1688 self.pickradius = pickradius
1690 def set_ticklabels(self, ticklabels, *args, minor=False, **kwargs):
1691 r"""
1692 Set the text values of the tick labels.
1694 Parameters
1695 ----------
1696 ticklabels : sequence of str or of `Text`\s
1697 List of texts for tick labels; must include values for non-visible
1698 labels.
1699 minor : bool
1700 If True, set minor ticks instead of major ticks.
1701 **kwargs
1702 Text properties.
1704 Returns
1705 -------
1706 labels : list of `Text`\s
1707 For each tick, includes ``tick.label1`` if it is visible, then
1708 ``tick.label2`` if it is visible, in that order.
1709 """
1710 if args:
1711 cbook.warn_deprecated(
1712 "3.1", message="Additional positional arguments to "
1713 "set_ticklabels are ignored, and deprecated since Matplotlib "
1714 "3.1; passing them will raise a TypeError in Matplotlib 3.3.")
1715 get_labels = []
1716 for t in ticklabels:
1717 # try calling get_text() to check whether it is Text object
1718 # if it is Text, get label content
1719 try:
1720 get_labels.append(t.get_text())
1721 # otherwise add the label to the list directly
1722 except AttributeError:
1723 get_labels.append(t)
1724 # replace the ticklabels list with the processed one
1725 ticklabels = get_labels
1727 if minor:
1728 self.set_minor_formatter(mticker.FixedFormatter(ticklabels))
1729 ticks = self.get_minor_ticks()
1730 else:
1731 self.set_major_formatter(mticker.FixedFormatter(ticklabels))
1732 ticks = self.get_major_ticks()
1733 ret = []
1734 for tick_label, tick in zip(ticklabels, ticks):
1735 # deal with label1
1736 tick.label1.set_text(tick_label)
1737 tick.label1.update(kwargs)
1738 # deal with label2
1739 tick.label2.set_text(tick_label)
1740 tick.label2.update(kwargs)
1741 # only return visible tick labels
1742 if tick.label1.get_visible():
1743 ret.append(tick.label1)
1744 if tick.label2.get_visible():
1745 ret.append(tick.label2)
1747 self.stale = True
1748 return ret
1750 @cbook._make_keyword_only("3.2", "minor")
1751 def set_ticks(self, ticks, minor=False):
1752 """
1753 Set the locations of the tick marks from sequence ticks
1755 Parameters
1756 ----------
1757 ticks : sequence of floats
1758 minor : bool
1759 """
1760 # XXX if the user changes units, the information will be lost here
1761 ticks = self.convert_units(ticks)
1762 if len(ticks) > 1:
1763 xleft, xright = self.get_view_interval()
1764 if xright > xleft:
1765 self.set_view_interval(min(ticks), max(ticks))
1766 else:
1767 self.set_view_interval(max(ticks), min(ticks))
1768 if minor:
1769 self.set_minor_locator(mticker.FixedLocator(ticks))
1770 return self.get_minor_ticks(len(ticks))
1771 else:
1772 self.set_major_locator(mticker.FixedLocator(ticks))
1773 return self.get_major_ticks(len(ticks))
1775 def _get_tick_boxes_siblings(self, xdir, renderer):
1776 """
1777 Get the bounding boxes for this `.axis` and its siblings
1778 as set by `.Figure.align_xlabels` or `.Figure.align_ylablels`.
1780 By default it just gets bboxes for self.
1781 """
1782 raise NotImplementedError('Derived must override')
1784 def _update_label_position(self, renderer):
1785 """
1786 Update the label position based on the bounding box enclosing
1787 all the ticklabels and axis spine.
1788 """
1789 raise NotImplementedError('Derived must override')
1791 def _update_offset_text_position(self, bboxes, bboxes2):
1792 """
1793 Update the offset text position based on the sequence of bounding
1794 boxes of all the ticklabels.
1795 """
1796 raise NotImplementedError('Derived must override')
1798 def pan(self, numsteps):
1799 """Pan by *numsteps* (can be positive or negative)."""
1800 self.major.locator.pan(numsteps)
1802 def zoom(self, direction):
1803 """Zoom in/out on axis; if *direction* is >0 zoom in, else zoom out."""
1804 self.major.locator.zoom(direction)
1806 def axis_date(self, tz=None):
1807 """
1808 Sets up axis ticks and labels treating data along this axis as dates.
1810 Parameters
1811 ----------
1812 tz : tzinfo or str or None
1813 The timezone used to create date labels.
1814 """
1815 # By providing a sample datetime instance with the desired timezone,
1816 # the registered converter can be selected, and the "units" attribute,
1817 # which is the timezone, can be set.
1818 if isinstance(tz, str):
1819 import dateutil.tz
1820 tz = dateutil.tz.gettz(tz)
1821 self.update_units(datetime.datetime(2009, 1, 1, 0, 0, 0, 0, tz))
1823 def get_tick_space(self):
1824 """Return the estimated number of ticks that can fit on the axis."""
1825 # Must be overridden in the subclass
1826 raise NotImplementedError()
1828 def _get_ticks_position(self):
1829 """
1830 Helper for `XAxis.get_ticks_position` and `YAxis.get_ticks_position`.
1832 Check the visibility of tick1line, label1, tick2line, and label2 on
1833 the first major and the first minor ticks, and return
1835 - 1 if only tick1line and label1 are visible (which corresponds to
1836 "bottom" for the x-axis and "left" for the y-axis);
1837 - 2 if only tick2line and label2 are visible (which corresponds to
1838 "top" for the x-axis and "right" for the y-axis);
1839 - "default" if only tick1line, tick2line and label1 are visible;
1840 - "unknown" otherwise.
1841 """
1842 major = self.majorTicks[0]
1843 minor = self.minorTicks[0]
1844 if all(tick.tick1line.get_visible()
1845 and not tick.tick2line.get_visible()
1846 and tick.label1.get_visible()
1847 and not tick.label2.get_visible()
1848 for tick in [major, minor]):
1849 return 1
1850 elif all(tick.tick2line.get_visible()
1851 and not tick.tick1line.get_visible()
1852 and tick.label2.get_visible()
1853 and not tick.label1.get_visible()
1854 for tick in [major, minor]):
1855 return 2
1856 elif all(tick.tick1line.get_visible()
1857 and tick.tick2line.get_visible()
1858 and tick.label1.get_visible()
1859 and not tick.label2.get_visible()
1860 for tick in [major, minor]):
1861 return "default"
1862 else:
1863 return "unknown"
1865 def get_label_position(self):
1866 """
1867 Return the label position (top or bottom)
1868 """
1869 return self.label_position
1871 def set_label_position(self, position):
1872 """
1873 Set the label position (top or bottom)
1875 Parameters
1876 ----------
1877 position : {'top', 'bottom'}
1878 """
1879 raise NotImplementedError()
1881 def get_minpos(self):
1882 raise NotImplementedError()
1885def _make_getset_interval(method_name, lim_name, attr_name):
1886 """
1887 Helper to generate ``get_{data,view}_interval`` and
1888 ``set_{data,view}_interval`` implementations.
1889 """
1891 def getter(self):
1892 # docstring inherited.
1893 return getattr(getattr(self.axes, lim_name), attr_name)
1895 def setter(self, vmin, vmax, ignore=False):
1896 # docstring inherited.
1897 if ignore:
1898 setattr(getattr(self.axes, lim_name), attr_name, (vmin, vmax))
1899 else:
1900 oldmin, oldmax = getter(self)
1901 if oldmin < oldmax:
1902 setter(self, min(vmin, vmax, oldmin), max(vmin, vmax, oldmax),
1903 ignore=True)
1904 else:
1905 setter(self, max(vmin, vmax, oldmin), min(vmin, vmax, oldmax),
1906 ignore=True)
1907 self.stale = True
1909 getter.__name__ = f"get_{method_name}_interval"
1910 setter.__name__ = f"set_{method_name}_interval"
1912 return getter, setter
1915class XAxis(Axis):
1916 __name__ = 'xaxis'
1917 axis_name = 'x' #: Read-only name identifying the axis.
1919 def contains(self, mouseevent):
1920 """Test whether the mouse event occurred in the x axis.
1921 """
1922 inside, info = self._default_contains(mouseevent)
1923 if inside is not None:
1924 return inside, info
1926 x, y = mouseevent.x, mouseevent.y
1927 try:
1928 trans = self.axes.transAxes.inverted()
1929 xaxes, yaxes = trans.transform((x, y))
1930 except ValueError:
1931 return False, {}
1932 (l, b), (r, t) = self.axes.transAxes.transform([(0, 0), (1, 1)])
1933 inaxis = 0 <= xaxes <= 1 and (
1934 b - self.pickradius < y < b or
1935 t < y < t + self.pickradius)
1936 return inaxis, {}
1938 def _get_tick(self, major):
1939 if major:
1940 tick_kw = self._major_tick_kw
1941 else:
1942 tick_kw = self._minor_tick_kw
1943 return XTick(self.axes, 0, '', major=major, **tick_kw)
1945 def _get_label(self):
1946 # x in axes coords, y in display coords (to be updated at draw
1947 # time by _update_label_positions)
1948 label = mtext.Text(x=0.5, y=0,
1949 fontproperties=font_manager.FontProperties(
1950 size=rcParams['axes.labelsize'],
1951 weight=rcParams['axes.labelweight']),
1952 color=rcParams['axes.labelcolor'],
1953 verticalalignment='top',
1954 horizontalalignment='center')
1956 label.set_transform(mtransforms.blended_transform_factory(
1957 self.axes.transAxes, mtransforms.IdentityTransform()))
1959 self._set_artist_props(label)
1960 self.label_position = 'bottom'
1961 return label
1963 def _get_offset_text(self):
1964 # x in axes coords, y in display coords (to be updated at draw time)
1965 offsetText = mtext.Text(x=1, y=0,
1966 fontproperties=font_manager.FontProperties(
1967 size=rcParams['xtick.labelsize']),
1968 color=rcParams['xtick.color'],
1969 verticalalignment='top',
1970 horizontalalignment='right')
1971 offsetText.set_transform(mtransforms.blended_transform_factory(
1972 self.axes.transAxes, mtransforms.IdentityTransform())
1973 )
1974 self._set_artist_props(offsetText)
1975 self.offset_text_position = 'bottom'
1976 return offsetText
1978 def set_label_position(self, position):
1979 """
1980 Set the label position (top or bottom)
1982 Parameters
1983 ----------
1984 position : {'top', 'bottom'}
1985 """
1986 self.label.set_verticalalignment(cbook._check_getitem({
1987 'top': 'baseline', 'bottom': 'top',
1988 }, position=position))
1989 self.label_position = position
1990 self.stale = True
1992 def _get_tick_boxes_siblings(self, renderer):
1993 """
1994 Get the bounding boxes for this `.axis` and its siblings
1995 as set by `.Figure.align_xlabels` or `.Figure.align_ylablels`.
1997 By default it just gets bboxes for self.
1998 """
1999 bboxes = []
2000 bboxes2 = []
2001 # get the Grouper that keeps track of x-label groups for this figure
2002 grp = self.figure._align_xlabel_grp
2003 # if we want to align labels from other axes:
2004 for nn, axx in enumerate(grp.get_siblings(self.axes)):
2005 ticks_to_draw = axx.xaxis._update_ticks()
2006 tlb, tlb2 = axx.xaxis._get_tick_bboxes(ticks_to_draw, renderer)
2007 bboxes.extend(tlb)
2008 bboxes2.extend(tlb2)
2009 return bboxes, bboxes2
2011 def _update_label_position(self, renderer):
2012 """
2013 Update the label position based on the bounding box enclosing
2014 all the ticklabels and axis spine
2015 """
2016 if not self._autolabelpos:
2017 return
2019 # get bounding boxes for this axis and any siblings
2020 # that have been set by `fig.align_xlabels()`
2021 bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer)
2023 x, y = self.label.get_position()
2024 if self.label_position == 'bottom':
2025 try:
2026 spine = self.axes.spines['bottom']
2027 spinebbox = spine.get_transform().transform_path(
2028 spine.get_path()).get_extents()
2029 except KeyError:
2030 # use axes if spine doesn't exist
2031 spinebbox = self.axes.bbox
2032 bbox = mtransforms.Bbox.union(bboxes + [spinebbox])
2033 bottom = bbox.y0
2035 self.label.set_position(
2036 (x, bottom - self.labelpad * self.figure.dpi / 72)
2037 )
2039 else:
2040 try:
2041 spine = self.axes.spines['top']
2042 spinebbox = spine.get_transform().transform_path(
2043 spine.get_path()).get_extents()
2044 except KeyError:
2045 # use axes if spine doesn't exist
2046 spinebbox = self.axes.bbox
2047 bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox])
2048 top = bbox.y1
2050 self.label.set_position(
2051 (x, top + self.labelpad * self.figure.dpi / 72)
2052 )
2054 def _update_offset_text_position(self, bboxes, bboxes2):
2055 """
2056 Update the offset_text position based on the sequence of bounding
2057 boxes of all the ticklabels
2058 """
2059 x, y = self.offsetText.get_position()
2060 if not len(bboxes):
2061 bottom = self.axes.bbox.ymin
2062 else:
2063 bbox = mtransforms.Bbox.union(bboxes)
2064 bottom = bbox.y0
2065 self.offsetText.set_position(
2066 (x, bottom - self.OFFSETTEXTPAD * self.figure.dpi / 72)
2067 )
2069 def get_text_heights(self, renderer):
2070 """
2071 Returns the amount of space one should reserve for text
2072 above and below the axes. Returns a tuple (above, below)
2073 """
2074 bbox, bbox2 = self.get_ticklabel_extents(renderer)
2075 # MGDTODO: Need a better way to get the pad
2076 padPixels = self.majorTicks[0].get_pad_pixels()
2078 above = 0.0
2079 if bbox2.height:
2080 above += bbox2.height + padPixels
2081 below = 0.0
2082 if bbox.height:
2083 below += bbox.height + padPixels
2085 if self.get_label_position() == 'top':
2086 above += self.label.get_window_extent(renderer).height + padPixels
2087 else:
2088 below += self.label.get_window_extent(renderer).height + padPixels
2089 return above, below
2091 def set_ticks_position(self, position):
2092 """
2093 Set the ticks position (top, bottom, both, default or none)
2094 both sets the ticks to appear on both positions, but does not
2095 change the tick labels. 'default' resets the tick positions to
2096 the default: ticks on both positions, labels at bottom. 'none'
2097 can be used if you don't want any ticks. 'none' and 'both'
2098 affect only the ticks, not the labels.
2100 Parameters
2101 ----------
2102 position : {'top', 'bottom', 'both', 'default', 'none'}
2103 """
2104 if position == 'top':
2105 self.set_tick_params(which='both', top=True, labeltop=True,
2106 bottom=False, labelbottom=False)
2107 elif position == 'bottom':
2108 self.set_tick_params(which='both', top=False, labeltop=False,
2109 bottom=True, labelbottom=True)
2110 elif position == 'both':
2111 self.set_tick_params(which='both', top=True,
2112 bottom=True)
2113 elif position == 'none':
2114 self.set_tick_params(which='both', top=False,
2115 bottom=False)
2116 elif position == 'default':
2117 self.set_tick_params(which='both', top=True, labeltop=False,
2118 bottom=True, labelbottom=True)
2119 else:
2120 raise ValueError("invalid position: %s" % position)
2121 self.stale = True
2123 def tick_top(self):
2124 """
2125 Move ticks and ticklabels (if present) to the top of the axes.
2126 """
2127 label = True
2128 if 'label1On' in self._major_tick_kw:
2129 label = (self._major_tick_kw['label1On']
2130 or self._major_tick_kw['label2On'])
2131 self.set_ticks_position('top')
2132 # If labels were turned off before this was called, leave them off.
2133 self.set_tick_params(which='both', labeltop=label)
2135 def tick_bottom(self):
2136 """
2137 Move ticks and ticklabels (if present) to the bottom of the axes.
2138 """
2139 label = True
2140 if 'label1On' in self._major_tick_kw:
2141 label = (self._major_tick_kw['label1On']
2142 or self._major_tick_kw['label2On'])
2143 self.set_ticks_position('bottom')
2144 # If labels were turned off before this was called, leave them off.
2145 self.set_tick_params(which='both', labelbottom=label)
2147 def get_ticks_position(self):
2148 """
2149 Return the ticks position ("top", "bottom", "default", or "unknown").
2150 """
2151 return {1: "bottom", 2: "top",
2152 "default": "default", "unknown": "unknown"}[
2153 self._get_ticks_position()]
2155 get_view_interval, set_view_interval = _make_getset_interval(
2156 "view", "viewLim", "intervalx")
2157 get_data_interval, set_data_interval = _make_getset_interval(
2158 "data", "dataLim", "intervalx")
2160 def get_minpos(self):
2161 return self.axes.dataLim.minposx
2163 def set_inverted(self, inverted):
2164 # docstring inherited
2165 a, b = self.get_view_interval()
2166 # cast to bool to avoid bad interaction between python 3.8 and np.bool_
2167 self.axes.set_xlim(sorted((a, b), reverse=bool(inverted)), auto=None)
2169 def set_default_intervals(self):
2170 # docstring inherited
2171 xmin, xmax = 0., 1.
2172 dataMutated = self.axes.dataLim.mutatedx()
2173 viewMutated = self.axes.viewLim.mutatedx()
2174 if not dataMutated or not viewMutated:
2175 if self.converter is not None:
2176 info = self.converter.axisinfo(self.units, self)
2177 if info.default_limits is not None:
2178 valmin, valmax = info.default_limits
2179 xmin = self.converter.convert(valmin, self.units, self)
2180 xmax = self.converter.convert(valmax, self.units, self)
2181 if not dataMutated:
2182 self.axes.dataLim.intervalx = xmin, xmax
2183 if not viewMutated:
2184 self.axes.viewLim.intervalx = xmin, xmax
2185 self.stale = True
2187 def get_tick_space(self):
2188 ends = self.axes.transAxes.transform([[0, 0], [1, 0]])
2189 length = ((ends[1][0] - ends[0][0]) / self.axes.figure.dpi) * 72
2190 tick = self._get_tick(True)
2191 # There is a heuristic here that the aspect ratio of tick text
2192 # is no more than 3:1
2193 size = tick.label1.get_size() * 3
2194 if size > 0:
2195 return int(np.floor(length / size))
2196 else:
2197 return 2**31 - 1
2200class YAxis(Axis):
2201 __name__ = 'yaxis'
2202 axis_name = 'y' #: Read-only name identifying the axis.
2204 def contains(self, mouseevent):
2205 """Test whether the mouse event occurred in the y axis.
2207 Returns *True* | *False*
2208 """
2209 inside, info = self._default_contains(mouseevent)
2210 if inside is not None:
2211 return inside, info
2213 x, y = mouseevent.x, mouseevent.y
2214 try:
2215 trans = self.axes.transAxes.inverted()
2216 xaxes, yaxes = trans.transform((x, y))
2217 except ValueError:
2218 return False, {}
2219 (l, b), (r, t) = self.axes.transAxes.transform([(0, 0), (1, 1)])
2220 inaxis = 0 <= yaxes <= 1 and (
2221 l - self.pickradius < x < l or
2222 r < x < r + self.pickradius)
2223 return inaxis, {}
2225 def _get_tick(self, major):
2226 if major:
2227 tick_kw = self._major_tick_kw
2228 else:
2229 tick_kw = self._minor_tick_kw
2230 return YTick(self.axes, 0, '', major=major, **tick_kw)
2232 def _get_label(self):
2233 # x in display coords (updated by _update_label_position)
2234 # y in axes coords
2235 label = mtext.Text(x=0, y=0.5,
2236 # todo: get the label position
2237 fontproperties=font_manager.FontProperties(
2238 size=rcParams['axes.labelsize'],
2239 weight=rcParams['axes.labelweight']),
2240 color=rcParams['axes.labelcolor'],
2241 verticalalignment='bottom',
2242 horizontalalignment='center',
2243 rotation='vertical',
2244 rotation_mode='anchor')
2245 label.set_transform(mtransforms.blended_transform_factory(
2246 mtransforms.IdentityTransform(), self.axes.transAxes))
2248 self._set_artist_props(label)
2249 self.label_position = 'left'
2250 return label
2252 def _get_offset_text(self):
2253 # x in display coords, y in axes coords (to be updated at draw time)
2254 offsetText = mtext.Text(x=0, y=0.5,
2255 fontproperties=font_manager.FontProperties(
2256 size=rcParams['ytick.labelsize']
2257 ),
2258 color=rcParams['ytick.color'],
2259 verticalalignment='baseline',
2260 horizontalalignment='left')
2261 offsetText.set_transform(mtransforms.blended_transform_factory(
2262 self.axes.transAxes, mtransforms.IdentityTransform())
2263 )
2264 self._set_artist_props(offsetText)
2265 self.offset_text_position = 'left'
2266 return offsetText
2268 def set_label_position(self, position):
2269 """
2270 Set the label position (left or right)
2272 Parameters
2273 ----------
2274 position : {'left', 'right'}
2275 """
2276 self.label.set_rotation_mode('anchor')
2277 self.label.set_horizontalalignment('center')
2278 self.label.set_verticalalignment(cbook._check_getitem({
2279 'left': 'bottom', 'right': 'top',
2280 }, position=position))
2281 self.label_position = position
2282 self.stale = True
2284 def _get_tick_boxes_siblings(self, renderer):
2285 """
2286 Get the bounding boxes for this `.axis` and its siblings
2287 as set by `.Figure.align_xlabels` or `.Figure.align_ylablels`.
2289 By default it just gets bboxes for self.
2290 """
2291 bboxes = []
2292 bboxes2 = []
2293 # get the Grouper that keeps track of y-label groups for this figure
2294 grp = self.figure._align_ylabel_grp
2295 # if we want to align labels from other axes:
2296 for axx in grp.get_siblings(self.axes):
2297 ticks_to_draw = axx.yaxis._update_ticks()
2298 tlb, tlb2 = axx.yaxis._get_tick_bboxes(ticks_to_draw, renderer)
2299 bboxes.extend(tlb)
2300 bboxes2.extend(tlb2)
2301 return bboxes, bboxes2
2303 def _update_label_position(self, renderer):
2304 """
2305 Update the label position based on the bounding box enclosing
2306 all the ticklabels and axis spine
2307 """
2308 if not self._autolabelpos:
2309 return
2311 # get bounding boxes for this axis and any siblings
2312 # that have been set by `fig.align_ylabels()`
2313 bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer)
2315 x, y = self.label.get_position()
2316 if self.label_position == 'left':
2317 try:
2318 spine = self.axes.spines['left']
2319 spinebbox = spine.get_transform().transform_path(
2320 spine.get_path()).get_extents()
2321 except KeyError:
2322 # use axes if spine doesn't exist
2323 spinebbox = self.axes.bbox
2324 bbox = mtransforms.Bbox.union(bboxes + [spinebbox])
2325 left = bbox.x0
2326 self.label.set_position(
2327 (left - self.labelpad * self.figure.dpi / 72, y)
2328 )
2330 else:
2331 try:
2332 spine = self.axes.spines['right']
2333 spinebbox = spine.get_transform().transform_path(
2334 spine.get_path()).get_extents()
2335 except KeyError:
2336 # use axes if spine doesn't exist
2337 spinebbox = self.axes.bbox
2338 bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox])
2339 right = bbox.x1
2341 self.label.set_position(
2342 (right + self.labelpad * self.figure.dpi / 72, y)
2343 )
2345 def _update_offset_text_position(self, bboxes, bboxes2):
2346 """
2347 Update the offset_text position based on the sequence of bounding
2348 boxes of all the ticklabels
2349 """
2350 x, y = self.offsetText.get_position()
2351 top = self.axes.bbox.ymax
2352 self.offsetText.set_position(
2353 (x, top + self.OFFSETTEXTPAD * self.figure.dpi / 72)
2354 )
2356 def set_offset_position(self, position):
2357 """
2358 Parameters
2359 ----------
2360 position : {'left', 'right'}
2361 """
2362 x, y = self.offsetText.get_position()
2363 x = cbook._check_getitem({'left': 0, 'right': 1}, position=position)
2365 self.offsetText.set_ha(position)
2366 self.offsetText.set_position((x, y))
2367 self.stale = True
2369 def get_text_widths(self, renderer):
2370 bbox, bbox2 = self.get_ticklabel_extents(renderer)
2371 # MGDTODO: Need a better way to get the pad
2372 padPixels = self.majorTicks[0].get_pad_pixels()
2374 left = 0.0
2375 if bbox.width:
2376 left += bbox.width + padPixels
2377 right = 0.0
2378 if bbox2.width:
2379 right += bbox2.width + padPixels
2381 if self.get_label_position() == 'left':
2382 left += self.label.get_window_extent(renderer).width + padPixels
2383 else:
2384 right += self.label.get_window_extent(renderer).width + padPixels
2385 return left, right
2387 def set_ticks_position(self, position):
2388 """
2389 Set the ticks position (left, right, both, default or none)
2390 'both' sets the ticks to appear on both positions, but does not
2391 change the tick labels. 'default' resets the tick positions to
2392 the default: ticks on both positions, labels at left. 'none'
2393 can be used if you don't want any ticks. 'none' and 'both'
2394 affect only the ticks, not the labels.
2396 Parameters
2397 ----------
2398 position : {'left', 'right', 'both', 'default', 'none'}
2399 """
2400 if position == 'right':
2401 self.set_tick_params(which='both', right=True, labelright=True,
2402 left=False, labelleft=False)
2403 self.set_offset_position(position)
2404 elif position == 'left':
2405 self.set_tick_params(which='both', right=False, labelright=False,
2406 left=True, labelleft=True)
2407 self.set_offset_position(position)
2408 elif position == 'both':
2409 self.set_tick_params(which='both', right=True,
2410 left=True)
2411 elif position == 'none':
2412 self.set_tick_params(which='both', right=False,
2413 left=False)
2414 elif position == 'default':
2415 self.set_tick_params(which='both', right=True, labelright=False,
2416 left=True, labelleft=True)
2417 else:
2418 raise ValueError("invalid position: %s" % position)
2419 self.stale = True
2421 def tick_right(self):
2422 """
2423 Move ticks and ticklabels (if present) to the right of the axes.
2424 """
2425 label = True
2426 if 'label1On' in self._major_tick_kw:
2427 label = (self._major_tick_kw['label1On']
2428 or self._major_tick_kw['label2On'])
2429 self.set_ticks_position('right')
2430 # if labels were turned off before this was called
2431 # leave them off
2432 self.set_tick_params(which='both', labelright=label)
2434 def tick_left(self):
2435 """
2436 Move ticks and ticklabels (if present) to the left of the axes.
2437 """
2438 label = True
2439 if 'label1On' in self._major_tick_kw:
2440 label = (self._major_tick_kw['label1On']
2441 or self._major_tick_kw['label2On'])
2442 self.set_ticks_position('left')
2443 # if labels were turned off before this was called
2444 # leave them off
2445 self.set_tick_params(which='both', labelleft=label)
2447 def get_ticks_position(self):
2448 """
2449 Return the ticks position ("left", "right", "default", or "unknown").
2450 """
2451 return {1: "left", 2: "right",
2452 "default": "default", "unknown": "unknown"}[
2453 self._get_ticks_position()]
2455 get_view_interval, set_view_interval = _make_getset_interval(
2456 "view", "viewLim", "intervaly")
2457 get_data_interval, set_data_interval = _make_getset_interval(
2458 "data", "dataLim", "intervaly")
2460 def get_minpos(self):
2461 return self.axes.dataLim.minposy
2463 def set_inverted(self, inverted):
2464 # docstring inherited
2465 a, b = self.get_view_interval()
2466 # cast to bool to avoid bad interaction between python 3.8 and np.bool_
2467 self.axes.set_ylim(sorted((a, b), reverse=bool(inverted)), auto=None)
2469 def set_default_intervals(self):
2470 # docstring inherited
2471 ymin, ymax = 0., 1.
2472 dataMutated = self.axes.dataLim.mutatedy()
2473 viewMutated = self.axes.viewLim.mutatedy()
2474 if not dataMutated or not viewMutated:
2475 if self.converter is not None:
2476 info = self.converter.axisinfo(self.units, self)
2477 if info.default_limits is not None:
2478 valmin, valmax = info.default_limits
2479 ymin = self.converter.convert(valmin, self.units, self)
2480 ymax = self.converter.convert(valmax, self.units, self)
2481 if not dataMutated:
2482 self.axes.dataLim.intervaly = ymin, ymax
2483 if not viewMutated:
2484 self.axes.viewLim.intervaly = ymin, ymax
2485 self.stale = True
2487 def get_tick_space(self):
2488 ends = self.axes.transAxes.transform([[0, 0], [0, 1]])
2489 length = ((ends[1][1] - ends[0][1]) / self.axes.figure.dpi) * 72
2490 tick = self._get_tick(True)
2491 # Having a spacing of at least 2 just looks good.
2492 size = tick.label1.get_size() * 2.0
2493 if size > 0:
2494 return int(np.floor(length / size))
2495 else:
2496 return 2**31 - 1