Coverage for /usr/lib/python3/dist-packages/matplotlib/axis.py: 62%

1238 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1""" 

2Classes for the ticks and x- and y-axis. 

3""" 

4 

5import datetime 

6import functools 

7import logging 

8from numbers import Number 

9 

10import numpy as np 

11 

12import matplotlib as mpl 

13from matplotlib import _api, cbook 

14import matplotlib.artist as martist 

15import matplotlib.colors as mcolors 

16import matplotlib.lines as mlines 

17import matplotlib.scale as mscale 

18import matplotlib.text as mtext 

19import matplotlib.ticker as mticker 

20import matplotlib.transforms as mtransforms 

21import matplotlib.units as munits 

22 

23_log = logging.getLogger(__name__) 

24 

25GRIDLINE_INTERPOLATION_STEPS = 180 

26 

27# This list is being used for compatibility with Axes.grid, which 

28# allows all Line2D kwargs. 

29_line_inspector = martist.ArtistInspector(mlines.Line2D) 

30_line_param_names = _line_inspector.get_setters() 

31_line_param_aliases = [list(d)[0] for d in _line_inspector.aliasd.values()] 

32_gridline_param_names = ['grid_' + name 

33 for name in _line_param_names + _line_param_aliases] 

34 

35 

36class Tick(martist.Artist): 

37 """ 

38 Abstract base class for the axis ticks, grid lines and labels. 

39 

40 Ticks mark a position on an Axis. They contain two lines as markers and 

41 two labels; one each for the bottom and top positions (in case of an 

42 `.XAxis`) or for the left and right positions (in case of a `.YAxis`). 

43 

44 Attributes 

45 ---------- 

46 tick1line : `.Line2D` 

47 The left/bottom tick marker. 

48 tick2line : `.Line2D` 

49 The right/top tick marker. 

50 gridline : `.Line2D` 

51 The grid line associated with the label position. 

52 label1 : `.Text` 

53 The left/bottom tick label. 

54 label2 : `.Text` 

55 The right/top tick label. 

56 

57 """ 

58 def __init__( 

59 self, axes, loc, *, 

60 size=None, # points 

61 width=None, 

62 color=None, 

63 tickdir=None, 

64 pad=None, 

65 labelsize=None, 

66 labelcolor=None, 

67 zorder=None, 

68 gridOn=None, # defaults to axes.grid depending on axes.grid.which 

69 tick1On=True, 

70 tick2On=True, 

71 label1On=True, 

72 label2On=False, 

73 major=True, 

74 labelrotation=0, 

75 grid_color=None, 

76 grid_linestyle=None, 

77 grid_linewidth=None, 

78 grid_alpha=None, 

79 **kwargs, # Other Line2D kwargs applied to gridlines. 

80 ): 

81 """ 

82 bbox is the Bound2D bounding box in display coords of the Axes 

83 loc is the tick location in data coords 

84 size is the tick size in points 

85 """ 

86 super().__init__() 

87 

88 if gridOn is None: 

89 if major and (mpl.rcParams['axes.grid.which'] 

90 in ('both', 'major')): 

91 gridOn = mpl.rcParams['axes.grid'] 

92 elif (not major) and (mpl.rcParams['axes.grid.which'] 

93 in ('both', 'minor')): 

94 gridOn = mpl.rcParams['axes.grid'] 

95 else: 

96 gridOn = False 

97 

98 self.set_figure(axes.figure) 

99 self.axes = axes 

100 

101 self._loc = loc 

102 self._major = major 

103 

104 name = self.__name__ 

105 major_minor = "major" if major else "minor" 

106 

107 if size is None: 

108 size = mpl.rcParams[f"{name}.{major_minor}.size"] 

109 self._size = size 

110 

111 if width is None: 

112 width = mpl.rcParams[f"{name}.{major_minor}.width"] 

113 self._width = width 

114 

115 if color is None: 

116 color = mpl.rcParams[f"{name}.color"] 

117 

118 if pad is None: 

119 pad = mpl.rcParams[f"{name}.{major_minor}.pad"] 

120 self._base_pad = pad 

121 

122 if labelcolor is None: 

123 labelcolor = mpl.rcParams[f"{name}.labelcolor"] 

124 

125 if labelcolor == 'inherit': 

126 # inherit from tick color 

127 labelcolor = mpl.rcParams[f"{name}.color"] 

128 

129 if labelsize is None: 

130 labelsize = mpl.rcParams[f"{name}.labelsize"] 

131 

132 self._set_labelrotation(labelrotation) 

133 

134 if zorder is None: 

135 if major: 

136 zorder = mlines.Line2D.zorder + 0.01 

137 else: 

138 zorder = mlines.Line2D.zorder 

139 self._zorder = zorder 

140 

141 if grid_color is None: 

142 grid_color = mpl.rcParams["grid.color"] 

143 if grid_linestyle is None: 

144 grid_linestyle = mpl.rcParams["grid.linestyle"] 

145 if grid_linewidth is None: 

146 grid_linewidth = mpl.rcParams["grid.linewidth"] 

147 if grid_alpha is None and not mcolors._has_alpha_channel(grid_color): 

148 # alpha precedence: kwarg > color alpha > rcParams['grid.alpha'] 

149 # Note: only resolve to rcParams if the color does not have alpha 

150 # otherwise `grid(color=(1, 1, 1, 0.5))` would work like 

151 # grid(color=(1, 1, 1, 0.5), alpha=rcParams['grid.alpha']) 

152 # so the that the rcParams default would override color alpha. 

153 grid_alpha = mpl.rcParams["grid.alpha"] 

154 grid_kw = {k[5:]: v for k, v in kwargs.items()} 

155 

156 self.tick1line = mlines.Line2D( 

157 [], [], 

158 color=color, linestyle="none", zorder=zorder, visible=tick1On, 

159 markeredgecolor=color, markersize=size, markeredgewidth=width, 

160 ) 

161 self.tick2line = mlines.Line2D( 

162 [], [], 

163 color=color, linestyle="none", zorder=zorder, visible=tick2On, 

164 markeredgecolor=color, markersize=size, markeredgewidth=width, 

165 ) 

166 self.gridline = mlines.Line2D( 

167 [], [], 

168 color=grid_color, alpha=grid_alpha, visible=gridOn, 

169 linestyle=grid_linestyle, linewidth=grid_linewidth, marker="", 

170 **grid_kw, 

171 ) 

172 self.gridline.get_path()._interpolation_steps = \ 

173 GRIDLINE_INTERPOLATION_STEPS 

174 self.label1 = mtext.Text( 

175 np.nan, np.nan, 

176 fontsize=labelsize, color=labelcolor, visible=label1On, 

177 rotation=self._labelrotation[1]) 

178 self.label2 = mtext.Text( 

179 np.nan, np.nan, 

180 fontsize=labelsize, color=labelcolor, visible=label2On, 

181 rotation=self._labelrotation[1]) 

182 

183 self._apply_tickdir(tickdir) 

184 

185 for artist in [self.tick1line, self.tick2line, self.gridline, 

186 self.label1, self.label2]: 

187 self._set_artist_props(artist) 

188 

189 self.update_position(loc) 

190 

191 @property 

192 @_api.deprecated("3.1", alternative="Tick.label1", removal="3.8") 

193 def label(self): 

194 return self.label1 

195 

196 def _set_labelrotation(self, labelrotation): 

197 if isinstance(labelrotation, str): 

198 mode = labelrotation 

199 angle = 0 

200 elif isinstance(labelrotation, (tuple, list)): 

201 mode, angle = labelrotation 

202 else: 

203 mode = 'default' 

204 angle = labelrotation 

205 _api.check_in_list(['auto', 'default'], labelrotation=mode) 

206 self._labelrotation = (mode, angle) 

207 

208 def _apply_tickdir(self, tickdir): 

209 """Set tick direction. Valid values are 'out', 'in', 'inout'.""" 

210 # This method is responsible for updating `_pad`, and, in subclasses, 

211 # for setting the tick{1,2}line markers as well. From the user 

212 # perspective this should always be called though _apply_params, which 

213 # further updates ticklabel positions using the new pads. 

214 if tickdir is None: 

215 tickdir = mpl.rcParams[f'{self.__name__}.direction'] 

216 _api.check_in_list(['in', 'out', 'inout'], tickdir=tickdir) 

217 self._tickdir = tickdir 

218 self._pad = self._base_pad + self.get_tick_padding() 

219 

220 @_api.deprecated("3.5", alternative="`.Axis.set_tick_params`") 

221 def apply_tickdir(self, tickdir): 

222 self._apply_tickdir(tickdir) 

223 self.stale = True 

224 

225 def get_tickdir(self): 

226 return self._tickdir 

227 

228 def get_tick_padding(self): 

229 """Get the length of the tick outside of the Axes.""" 

230 padding = { 

231 'in': 0.0, 

232 'inout': 0.5, 

233 'out': 1.0 

234 } 

235 return self._size * padding[self._tickdir] 

236 

237 def get_children(self): 

238 children = [self.tick1line, self.tick2line, 

239 self.gridline, self.label1, self.label2] 

240 return children 

241 

242 def set_clip_path(self, clippath, transform=None): 

243 # docstring inherited 

244 super().set_clip_path(clippath, transform) 

245 self.gridline.set_clip_path(clippath, transform) 

246 self.stale = True 

247 

248 @_api.deprecated("3.6") 

249 def get_pad_pixels(self): 

250 return self.figure.dpi * self._base_pad / 72 

251 

252 def contains(self, mouseevent): 

253 """ 

254 Test whether the mouse event occurred in the Tick marks. 

255 

256 This function always returns false. It is more useful to test if the 

257 axis as a whole contains the mouse rather than the set of tick marks. 

258 """ 

259 inside, info = self._default_contains(mouseevent) 

260 if inside is not None: 

261 return inside, info 

262 return False, {} 

263 

264 def set_pad(self, val): 

265 """ 

266 Set the tick label pad in points 

267 

268 Parameters 

269 ---------- 

270 val : float 

271 """ 

272 self._apply_params(pad=val) 

273 self.stale = True 

274 

275 def get_pad(self): 

276 """Get the value of the tick label pad in points.""" 

277 return self._base_pad 

278 

279 def _get_text1(self): 

280 """Get the default Text 1 instance.""" 

281 

282 def _get_text2(self): 

283 """Get the default Text 2 instance.""" 

284 

285 def _get_tick1line(self): 

286 """Get the default `.Line2D` instance for tick1.""" 

287 

288 def _get_tick2line(self): 

289 """Get the default `.Line2D` instance for tick2.""" 

290 

291 def _get_gridline(self): 

292 """Get the default grid `.Line2D` instance for this tick.""" 

293 

294 def get_loc(self): 

295 """Return the tick location (data coords) as a scalar.""" 

296 return self._loc 

297 

298 @martist.allow_rasterization 

299 def draw(self, renderer): 

300 if not self.get_visible(): 

301 self.stale = False 

302 return 

303 renderer.open_group(self.__name__, gid=self.get_gid()) 

304 for artist in [self.gridline, self.tick1line, self.tick2line, 

305 self.label1, self.label2]: 

306 artist.draw(renderer) 

307 renderer.close_group(self.__name__) 

308 self.stale = False 

309 

310 def set_label1(self, s): 

311 """ 

312 Set the label1 text. 

313 

314 Parameters 

315 ---------- 

316 s : str 

317 """ 

318 self.label1.set_text(s) 

319 self.stale = True 

320 

321 set_label = set_label1 

322 

323 def set_label2(self, s): 

324 """ 

325 Set the label2 text. 

326 

327 Parameters 

328 ---------- 

329 s : str 

330 """ 

331 self.label2.set_text(s) 

332 self.stale = True 

333 

334 def set_url(self, url): 

335 """ 

336 Set the url of label1 and label2. 

337 

338 Parameters 

339 ---------- 

340 url : str 

341 """ 

342 super().set_url(url) 

343 self.label1.set_url(url) 

344 self.label2.set_url(url) 

345 self.stale = True 

346 

347 def _set_artist_props(self, a): 

348 a.set_figure(self.figure) 

349 

350 def get_view_interval(self): 

351 """ 

352 Return the view limits ``(min, max)`` of the axis the tick belongs to. 

353 """ 

354 raise NotImplementedError('Derived must override') 

355 

356 def _apply_params(self, **kwargs): 

357 for name, target in [("gridOn", self.gridline), 

358 ("tick1On", self.tick1line), 

359 ("tick2On", self.tick2line), 

360 ("label1On", self.label1), 

361 ("label2On", self.label2)]: 

362 if name in kwargs: 

363 target.set_visible(kwargs.pop(name)) 

364 if any(k in kwargs for k in ['size', 'width', 'pad', 'tickdir']): 

365 self._size = kwargs.pop('size', self._size) 

366 # Width could be handled outside this block, but it is 

367 # convenient to leave it here. 

368 self._width = kwargs.pop('width', self._width) 

369 self._base_pad = kwargs.pop('pad', self._base_pad) 

370 # _apply_tickdir uses _size and _base_pad to make _pad, and also 

371 # sets the ticklines markers. 

372 self._apply_tickdir(kwargs.pop('tickdir', self._tickdir)) 

373 for line in (self.tick1line, self.tick2line): 

374 line.set_markersize(self._size) 

375 line.set_markeredgewidth(self._width) 

376 # _get_text1_transform uses _pad from _apply_tickdir. 

377 trans = self._get_text1_transform()[0] 

378 self.label1.set_transform(trans) 

379 trans = self._get_text2_transform()[0] 

380 self.label2.set_transform(trans) 

381 tick_kw = {k: v for k, v in kwargs.items() if k in ['color', 'zorder']} 

382 if 'color' in kwargs: 

383 tick_kw['markeredgecolor'] = kwargs['color'] 

384 self.tick1line.set(**tick_kw) 

385 self.tick2line.set(**tick_kw) 

386 for k, v in tick_kw.items(): 

387 setattr(self, '_' + k, v) 

388 

389 if 'labelrotation' in kwargs: 

390 self._set_labelrotation(kwargs.pop('labelrotation')) 

391 self.label1.set(rotation=self._labelrotation[1]) 

392 self.label2.set(rotation=self._labelrotation[1]) 

393 

394 label_kw = {k[5:]: v for k, v in kwargs.items() 

395 if k in ['labelsize', 'labelcolor']} 

396 self.label1.set(**label_kw) 

397 self.label2.set(**label_kw) 

398 

399 grid_kw = {k[5:]: v for k, v in kwargs.items() 

400 if k in _gridline_param_names} 

401 self.gridline.set(**grid_kw) 

402 

403 def update_position(self, loc): 

404 """Set the location of tick in data coords with scalar *loc*.""" 

405 raise NotImplementedError('Derived must override') 

406 

407 def _get_text1_transform(self): 

408 raise NotImplementedError('Derived must override') 

409 

410 def _get_text2_transform(self): 

411 raise NotImplementedError('Derived must override') 

412 

413 

414class XTick(Tick): 

415 """ 

416 Contains all the Artists needed to make an x tick - the tick line, 

417 the label text and the grid line 

418 """ 

419 __name__ = 'xtick' 

420 

421 def __init__(self, *args, **kwargs): 

422 super().__init__(*args, **kwargs) 

423 # x in data coords, y in axes coords 

424 ax = self.axes 

425 self.tick1line.set( 

426 data=([0], [0]), transform=ax.get_xaxis_transform("tick1")) 

427 self.tick2line.set( 

428 data=([0], [1]), transform=ax.get_xaxis_transform("tick2")) 

429 self.gridline.set( 

430 data=([0, 0], [0, 1]), transform=ax.get_xaxis_transform("grid")) 

431 # the y loc is 3 points below the min of y axis 

432 trans, va, ha = self._get_text1_transform() 

433 self.label1.set( 

434 x=0, y=0, 

435 verticalalignment=va, horizontalalignment=ha, transform=trans, 

436 ) 

437 trans, va, ha = self._get_text2_transform() 

438 self.label2.set( 

439 x=0, y=1, 

440 verticalalignment=va, horizontalalignment=ha, transform=trans, 

441 ) 

442 

443 def _get_text1_transform(self): 

444 return self.axes.get_xaxis_text1_transform(self._pad) 

445 

446 def _get_text2_transform(self): 

447 return self.axes.get_xaxis_text2_transform(self._pad) 

448 

449 def _apply_tickdir(self, tickdir): 

450 # docstring inherited 

451 super()._apply_tickdir(tickdir) 

452 mark1, mark2 = { 

453 'out': (mlines.TICKDOWN, mlines.TICKUP), 

454 'in': (mlines.TICKUP, mlines.TICKDOWN), 

455 'inout': ('|', '|'), 

456 }[self._tickdir] 

457 self.tick1line.set_marker(mark1) 

458 self.tick2line.set_marker(mark2) 

459 

460 def update_position(self, loc): 

461 """Set the location of tick in data coords with scalar *loc*.""" 

462 self.tick1line.set_xdata((loc,)) 

463 self.tick2line.set_xdata((loc,)) 

464 self.gridline.set_xdata((loc,)) 

465 self.label1.set_x(loc) 

466 self.label2.set_x(loc) 

467 self._loc = loc 

468 self.stale = True 

469 

470 def get_view_interval(self): 

471 # docstring inherited 

472 return self.axes.viewLim.intervalx 

473 

474 

475class YTick(Tick): 

476 """ 

477 Contains all the Artists needed to make a Y tick - the tick line, 

478 the label text and the grid line 

479 """ 

480 __name__ = 'ytick' 

481 

482 def __init__(self, *args, **kwargs): 

483 super().__init__(*args, **kwargs) 

484 # x in axes coords, y in data coords 

485 ax = self.axes 

486 self.tick1line.set( 

487 data=([0], [0]), transform=ax.get_yaxis_transform("tick1")) 

488 self.tick2line.set( 

489 data=([1], [0]), transform=ax.get_yaxis_transform("tick2")) 

490 self.gridline.set( 

491 data=([0, 1], [0, 0]), transform=ax.get_yaxis_transform("grid")) 

492 # the y loc is 3 points below the min of y axis 

493 trans, va, ha = self._get_text1_transform() 

494 self.label1.set( 

495 x=0, y=0, 

496 verticalalignment=va, horizontalalignment=ha, transform=trans, 

497 ) 

498 trans, va, ha = self._get_text2_transform() 

499 self.label2.set( 

500 x=1, y=0, 

501 verticalalignment=va, horizontalalignment=ha, transform=trans, 

502 ) 

503 

504 def _get_text1_transform(self): 

505 return self.axes.get_yaxis_text1_transform(self._pad) 

506 

507 def _get_text2_transform(self): 

508 return self.axes.get_yaxis_text2_transform(self._pad) 

509 

510 def _apply_tickdir(self, tickdir): 

511 # docstring inherited 

512 super()._apply_tickdir(tickdir) 

513 mark1, mark2 = { 

514 'out': (mlines.TICKLEFT, mlines.TICKRIGHT), 

515 'in': (mlines.TICKRIGHT, mlines.TICKLEFT), 

516 'inout': ('_', '_'), 

517 }[self._tickdir] 

518 self.tick1line.set_marker(mark1) 

519 self.tick2line.set_marker(mark2) 

520 

521 def update_position(self, loc): 

522 """Set the location of tick in data coords with scalar *loc*.""" 

523 self.tick1line.set_ydata((loc,)) 

524 self.tick2line.set_ydata((loc,)) 

525 self.gridline.set_ydata((loc,)) 

526 self.label1.set_y(loc) 

527 self.label2.set_y(loc) 

528 self._loc = loc 

529 self.stale = True 

530 

531 def get_view_interval(self): 

532 # docstring inherited 

533 return self.axes.viewLim.intervaly 

534 

535 

536class Ticker: 

537 """ 

538 A container for the objects defining tick position and format. 

539 

540 Attributes 

541 ---------- 

542 locator : `matplotlib.ticker.Locator` subclass 

543 Determines the positions of the ticks. 

544 formatter : `matplotlib.ticker.Formatter` subclass 

545 Determines the format of the tick labels. 

546 """ 

547 

548 def __init__(self): 

549 self._locator = None 

550 self._formatter = None 

551 self._locator_is_default = True 

552 self._formatter_is_default = True 

553 

554 @property 

555 def locator(self): 

556 return self._locator 

557 

558 @locator.setter 

559 def locator(self, locator): 

560 if not isinstance(locator, mticker.Locator): 

561 raise TypeError('locator must be a subclass of ' 

562 'matplotlib.ticker.Locator') 

563 self._locator = locator 

564 

565 @property 

566 def formatter(self): 

567 return self._formatter 

568 

569 @formatter.setter 

570 def formatter(self, formatter): 

571 if not isinstance(formatter, mticker.Formatter): 

572 raise TypeError('formatter must be a subclass of ' 

573 'matplotlib.ticker.Formatter') 

574 self._formatter = formatter 

575 

576 

577class _LazyTickList: 

578 """ 

579 A descriptor for lazy instantiation of tick lists. 

580 

581 See comment above definition of the ``majorTicks`` and ``minorTicks`` 

582 attributes. 

583 """ 

584 

585 def __init__(self, major): 

586 self._major = major 

587 

588 def __get__(self, instance, cls): 

589 if instance is None: 

590 return self 

591 else: 

592 # instance._get_tick() can itself try to access the majorTicks 

593 # attribute (e.g. in certain projection classes which override 

594 # e.g. get_xaxis_text1_transform). In order to avoid infinite 

595 # recursion, first set the majorTicks on the instance to an empty 

596 # list, then create the tick and append it. 

597 if self._major: 

598 instance.majorTicks = [] 

599 tick = instance._get_tick(major=True) 

600 instance.majorTicks.append(tick) 

601 return instance.majorTicks 

602 else: 

603 instance.minorTicks = [] 

604 tick = instance._get_tick(major=False) 

605 instance.minorTicks.append(tick) 

606 return instance.minorTicks 

607 

608 

609class Axis(martist.Artist): 

610 """ 

611 Base class for `.XAxis` and `.YAxis`. 

612 

613 Attributes 

614 ---------- 

615 isDefault_label : bool 

616 

617 axes : `matplotlib.axes.Axes` 

618 The `~.axes.Axes` to which the Axis belongs. 

619 major : `matplotlib.axis.Ticker` 

620 Determines the major tick positions and their label format. 

621 minor : `matplotlib.axis.Ticker` 

622 Determines the minor tick positions and their label format. 

623 callbacks : `matplotlib.cbook.CallbackRegistry` 

624 

625 label : `.Text` 

626 The axis label. 

627 labelpad : float 

628 The distance between the axis label and the tick labels. 

629 Defaults to :rc:`axes.labelpad` = 4. 

630 offsetText : `.Text` 

631 A `.Text` object containing the data offset of the ticks (if any). 

632 pickradius : float 

633 The acceptance radius for containment tests. See also `.Axis.contains`. 

634 majorTicks : list of `.Tick` 

635 The major ticks. 

636 minorTicks : list of `.Tick` 

637 The minor ticks. 

638 """ 

639 OFFSETTEXTPAD = 3 

640 # The class used in _get_tick() to create tick instances. Must either be 

641 # overwritten in subclasses, or subclasses must reimplement _get_tick(). 

642 _tick_class = None 

643 

644 def __str__(self): 

645 return "{}({},{})".format( 

646 type(self).__name__, *self.axes.transAxes.transform((0, 0))) 

647 

648 @_api.make_keyword_only("3.6", name="pickradius") 

649 def __init__(self, axes, pickradius=15): 

650 """ 

651 Parameters 

652 ---------- 

653 axes : `matplotlib.axes.Axes` 

654 The `~.axes.Axes` to which the created Axis belongs. 

655 pickradius : float 

656 The acceptance radius for containment tests. See also 

657 `.Axis.contains`. 

658 """ 

659 super().__init__() 

660 self._remove_overlapping_locs = True 

661 

662 self.set_figure(axes.figure) 

663 

664 self.isDefault_label = True 

665 

666 self.axes = axes 

667 self.major = Ticker() 

668 self.minor = Ticker() 

669 self.callbacks = cbook.CallbackRegistry( 

670 signals=["units", "units finalize"]) 

671 

672 self._autolabelpos = True 

673 

674 self.label = mtext.Text( 

675 np.nan, np.nan, 

676 fontsize=mpl.rcParams['axes.labelsize'], 

677 fontweight=mpl.rcParams['axes.labelweight'], 

678 color=mpl.rcParams['axes.labelcolor'], 

679 ) 

680 self._set_artist_props(self.label) 

681 self.offsetText = mtext.Text(np.nan, np.nan) 

682 self._set_artist_props(self.offsetText) 

683 

684 self.labelpad = mpl.rcParams['axes.labelpad'] 

685 

686 self.pickradius = pickradius 

687 

688 # Initialize here for testing; later add API 

689 self._major_tick_kw = dict() 

690 self._minor_tick_kw = dict() 

691 

692 self.clear() 

693 self._autoscale_on = True 

694 

695 @property 

696 def isDefault_majloc(self): 

697 return self.major._locator_is_default 

698 

699 @isDefault_majloc.setter 

700 def isDefault_majloc(self, value): 

701 self.major._locator_is_default = value 

702 

703 @property 

704 def isDefault_majfmt(self): 

705 return self.major._formatter_is_default 

706 

707 @isDefault_majfmt.setter 

708 def isDefault_majfmt(self, value): 

709 self.major._formatter_is_default = value 

710 

711 @property 

712 def isDefault_minloc(self): 

713 return self.minor._locator_is_default 

714 

715 @isDefault_minloc.setter 

716 def isDefault_minloc(self, value): 

717 self.minor._locator_is_default = value 

718 

719 @property 

720 def isDefault_minfmt(self): 

721 return self.minor._formatter_is_default 

722 

723 @isDefault_minfmt.setter 

724 def isDefault_minfmt(self, value): 

725 self.minor._formatter_is_default = value 

726 

727 # During initialization, Axis objects often create ticks that are later 

728 # unused; this turns out to be a very slow step. Instead, use a custom 

729 # descriptor to make the tick lists lazy and instantiate them as needed. 

730 majorTicks = _LazyTickList(major=True) 

731 minorTicks = _LazyTickList(major=False) 

732 

733 def get_remove_overlapping_locs(self): 

734 return self._remove_overlapping_locs 

735 

736 def set_remove_overlapping_locs(self, val): 

737 self._remove_overlapping_locs = bool(val) 

738 

739 remove_overlapping_locs = property( 

740 get_remove_overlapping_locs, set_remove_overlapping_locs, 

741 doc=('If minor ticker locations that overlap with major ' 

742 'ticker locations should be trimmed.')) 

743 

744 def set_label_coords(self, x, y, transform=None): 

745 """ 

746 Set the coordinates of the label. 

747 

748 By default, the x coordinate of the y label and the y coordinate of the 

749 x label are determined by the tick label bounding boxes, but this can 

750 lead to poor alignment of multiple labels if there are multiple axes. 

751 

752 You can also specify the coordinate system of the label with the 

753 transform. If None, the default coordinate system will be the axes 

754 coordinate system: (0, 0) is bottom left, (0.5, 0.5) is center, etc. 

755 """ 

756 self._autolabelpos = False 

757 if transform is None: 

758 transform = self.axes.transAxes 

759 

760 self.label.set_transform(transform) 

761 self.label.set_position((x, y)) 

762 self.stale = True 

763 

764 def get_transform(self): 

765 return self._scale.get_transform() 

766 

767 def get_scale(self): 

768 """Return this Axis' scale (as a str).""" 

769 return self._scale.name 

770 

771 def _set_scale(self, value, **kwargs): 

772 if not isinstance(value, mscale.ScaleBase): 

773 self._scale = mscale.scale_factory(value, self, **kwargs) 

774 else: 

775 self._scale = value 

776 self._scale.set_default_locators_and_formatters(self) 

777 

778 self.isDefault_majloc = True 

779 self.isDefault_minloc = True 

780 self.isDefault_majfmt = True 

781 self.isDefault_minfmt = True 

782 

783 # This method is directly wrapped by Axes.set_{x,y}scale. 

784 def _set_axes_scale(self, value, **kwargs): 

785 """ 

786 Set this Axis' scale. 

787 

788 Parameters 

789 ---------- 

790 value : {"linear", "log", "symlog", "logit", ...} or `.ScaleBase` 

791 The axis scale type to apply. 

792 

793 **kwargs 

794 Different keyword arguments are accepted, depending on the scale. 

795 See the respective class keyword arguments: 

796 

797 - `matplotlib.scale.LinearScale` 

798 - `matplotlib.scale.LogScale` 

799 - `matplotlib.scale.SymmetricalLogScale` 

800 - `matplotlib.scale.LogitScale` 

801 - `matplotlib.scale.FuncScale` 

802 

803 Notes 

804 ----- 

805 By default, Matplotlib supports the above-mentioned scales. 

806 Additionally, custom scales may be registered using 

807 `matplotlib.scale.register_scale`. These scales can then also 

808 be used here. 

809 """ 

810 name, = [name for name, axis in self.axes._axis_map.items() 

811 if axis is self] # The axis name. 

812 old_default_lims = (self.get_major_locator() 

813 .nonsingular(-np.inf, np.inf)) 

814 g = self.axes._shared_axes[name] 

815 for ax in g.get_siblings(self.axes): 

816 ax._axis_map[name]._set_scale(value, **kwargs) 

817 ax._update_transScale() 

818 ax.stale = True 

819 new_default_lims = (self.get_major_locator() 

820 .nonsingular(-np.inf, np.inf)) 

821 if old_default_lims != new_default_lims: 

822 # Force autoscaling now, to take advantage of the scale locator's 

823 # nonsingular() before it possibly gets swapped out by the user. 

824 self.axes.autoscale_view( 

825 **{f"scale{k}": k == name for k in self.axes._axis_names}) 

826 

827 def limit_range_for_scale(self, vmin, vmax): 

828 return self._scale.limit_range_for_scale(vmin, vmax, self.get_minpos()) 

829 

830 def _get_autoscale_on(self): 

831 """Return whether this Axis is autoscaled.""" 

832 return self._autoscale_on 

833 

834 def _set_autoscale_on(self, b): 

835 """ 

836 Set whether this Axis is autoscaled when drawing or by 

837 `.Axes.autoscale_view`. 

838 

839 Parameters 

840 ---------- 

841 b : bool 

842 """ 

843 self._autoscale_on = b 

844 

845 def get_children(self): 

846 return [self.label, self.offsetText, 

847 *self.get_major_ticks(), *self.get_minor_ticks()] 

848 

849 def _reset_major_tick_kw(self): 

850 self._major_tick_kw.clear() 

851 self._major_tick_kw['gridOn'] = ( 

852 mpl.rcParams['axes.grid'] and 

853 mpl.rcParams['axes.grid.which'] in ('both', 'major')) 

854 

855 def _reset_minor_tick_kw(self): 

856 self._minor_tick_kw.clear() 

857 self._minor_tick_kw['gridOn'] = ( 

858 mpl.rcParams['axes.grid'] and 

859 mpl.rcParams['axes.grid.which'] in ('both', 'minor')) 

860 

861 def clear(self): 

862 """ 

863 Clear the axis. 

864 

865 This resets axis properties to their default values: 

866 

867 - the label 

868 - the scale 

869 - locators, formatters and ticks 

870 - major and minor grid 

871 - units 

872 - registered callbacks 

873 """ 

874 

875 self.label.set_text('') # self.set_label_text would change isDefault_ 

876 

877 self._set_scale('linear') 

878 

879 # Clear the callback registry for this axis, or it may "leak" 

880 self.callbacks = cbook.CallbackRegistry( 

881 signals=["units", "units finalize"]) 

882 

883 # whether the grids are on 

884 self._major_tick_kw['gridOn'] = ( 

885 mpl.rcParams['axes.grid'] and 

886 mpl.rcParams['axes.grid.which'] in ('both', 'major')) 

887 self._minor_tick_kw['gridOn'] = ( 

888 mpl.rcParams['axes.grid'] and 

889 mpl.rcParams['axes.grid.which'] in ('both', 'minor')) 

890 self.reset_ticks() 

891 

892 self.converter = None 

893 self.units = None 

894 self.set_units(None) 

895 self.stale = True 

896 

897 def reset_ticks(self): 

898 """ 

899 Re-initialize the major and minor Tick lists. 

900 

901 Each list starts with a single fresh Tick. 

902 """ 

903 # Restore the lazy tick lists. 

904 try: 

905 del self.majorTicks 

906 except AttributeError: 

907 pass 

908 try: 

909 del self.minorTicks 

910 except AttributeError: 

911 pass 

912 try: 

913 self.set_clip_path(self.axes.patch) 

914 except AttributeError: 

915 pass 

916 

917 def set_tick_params(self, which='major', reset=False, **kwargs): 

918 """ 

919 Set appearance parameters for ticks, ticklabels, and gridlines. 

920 

921 For documentation of keyword arguments, see 

922 :meth:`matplotlib.axes.Axes.tick_params`. 

923 """ 

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

925 kwtrans = self._translate_tick_params(kwargs) 

926 

927 # the kwargs are stored in self._major/minor_tick_kw so that any 

928 # future new ticks will automatically get them 

929 if reset: 

930 if which in ['major', 'both']: 

931 self._reset_major_tick_kw() 

932 self._major_tick_kw.update(kwtrans) 

933 if which in ['minor', 'both']: 

934 self._reset_minor_tick_kw() 

935 self._minor_tick_kw.update(kwtrans) 

936 self.reset_ticks() 

937 else: 

938 if which in ['major', 'both']: 

939 self._major_tick_kw.update(kwtrans) 

940 for tick in self.majorTicks: 

941 tick._apply_params(**kwtrans) 

942 if which in ['minor', 'both']: 

943 self._minor_tick_kw.update(kwtrans) 

944 for tick in self.minorTicks: 

945 tick._apply_params(**kwtrans) 

946 # labelOn and labelcolor also apply to the offset text. 

947 if 'label1On' in kwtrans or 'label2On' in kwtrans: 

948 self.offsetText.set_visible( 

949 self._major_tick_kw.get('label1On', False) 

950 or self._major_tick_kw.get('label2On', False)) 

951 if 'labelcolor' in kwtrans: 

952 self.offsetText.set_color(kwtrans['labelcolor']) 

953 

954 self.stale = True 

955 

956 @staticmethod 

957 def _translate_tick_params(kw): 

958 """ 

959 Translate the kwargs supported by `.Axis.set_tick_params` to kwargs 

960 supported by `.Tick._apply_params`. 

961 

962 In particular, this maps axis specific names like 'top', 'left' 

963 to the generic tick1, tick2 logic of the axis. Additionally, there 

964 are some other name translations. 

965 

966 Returns a new dict of translated kwargs. 

967 

968 Note: The input *kwargs* are currently modified, but that's ok for 

969 the only caller. 

970 """ 

971 # The following lists may be moved to a more accessible location. 

972 allowed_keys = [ 

973 'size', 'width', 'color', 'tickdir', 'pad', 

974 'labelsize', 'labelcolor', 'zorder', 'gridOn', 

975 'tick1On', 'tick2On', 'label1On', 'label2On', 

976 'length', 'direction', 'left', 'bottom', 'right', 'top', 

977 'labelleft', 'labelbottom', 'labelright', 'labeltop', 

978 'labelrotation', 

979 *_gridline_param_names] 

980 

981 keymap = { 

982 # tick_params key -> axis key 

983 'length': 'size', 

984 'direction': 'tickdir', 

985 'rotation': 'labelrotation', 

986 'left': 'tick1On', 

987 'bottom': 'tick1On', 

988 'right': 'tick2On', 

989 'top': 'tick2On', 

990 'labelleft': 'label1On', 

991 'labelbottom': 'label1On', 

992 'labelright': 'label2On', 

993 'labeltop': 'label2On', 

994 } 

995 kwtrans = {newkey: kw.pop(oldkey) 

996 for oldkey, newkey in keymap.items() if oldkey in kw} 

997 if 'colors' in kw: 

998 c = kw.pop('colors') 

999 kwtrans['color'] = c 

1000 kwtrans['labelcolor'] = c 

1001 # Maybe move the checking up to the caller of this method. 

1002 for key in kw: 

1003 if key not in allowed_keys: 

1004 raise ValueError( 

1005 "keyword %s is not recognized; valid keywords are %s" 

1006 % (key, allowed_keys)) 

1007 kwtrans.update(kw) 

1008 return kwtrans 

1009 

1010 def set_clip_path(self, clippath, transform=None): 

1011 super().set_clip_path(clippath, transform) 

1012 for child in self.majorTicks + self.minorTicks: 

1013 child.set_clip_path(clippath, transform) 

1014 self.stale = True 

1015 

1016 def get_view_interval(self): 

1017 """Return the ``(min, max)`` view limits of this axis.""" 

1018 raise NotImplementedError('Derived must override') 

1019 

1020 def set_view_interval(self, vmin, vmax, ignore=False): 

1021 """ 

1022 Set the axis view limits. This method is for internal use; Matplotlib 

1023 users should typically use e.g. `~.Axes.set_xlim` or `~.Axes.set_ylim`. 

1024 

1025 If *ignore* is False (the default), this method will never reduce the 

1026 preexisting view limits, only expand them if *vmin* or *vmax* are not 

1027 within them. Moreover, the order of *vmin* and *vmax* does not matter; 

1028 the orientation of the axis will not change. 

1029 

1030 If *ignore* is True, the view limits will be set exactly to ``(vmin, 

1031 vmax)`` in that order. 

1032 """ 

1033 raise NotImplementedError('Derived must override') 

1034 

1035 def get_data_interval(self): 

1036 """Return the ``(min, max)`` data limits of this axis.""" 

1037 raise NotImplementedError('Derived must override') 

1038 

1039 def set_data_interval(self, vmin, vmax, ignore=False): 

1040 """ 

1041 Set the axis data limits. This method is for internal use. 

1042 

1043 If *ignore* is False (the default), this method will never reduce the 

1044 preexisting data limits, only expand them if *vmin* or *vmax* are not 

1045 within them. Moreover, the order of *vmin* and *vmax* does not matter; 

1046 the orientation of the axis will not change. 

1047 

1048 If *ignore* is True, the data limits will be set exactly to ``(vmin, 

1049 vmax)`` in that order. 

1050 """ 

1051 raise NotImplementedError('Derived must override') 

1052 

1053 def get_inverted(self): 

1054 """ 

1055 Return whether this Axis is oriented in the "inverse" direction. 

1056 

1057 The "normal" direction is increasing to the right for the x-axis and to 

1058 the top for the y-axis; the "inverse" direction is increasing to the 

1059 left for the x-axis and to the bottom for the y-axis. 

1060 """ 

1061 low, high = self.get_view_interval() 

1062 return high < low 

1063 

1064 def set_inverted(self, inverted): 

1065 """ 

1066 Set whether this Axis is oriented in the "inverse" direction. 

1067 

1068 The "normal" direction is increasing to the right for the x-axis and to 

1069 the top for the y-axis; the "inverse" direction is increasing to the 

1070 left for the x-axis and to the bottom for the y-axis. 

1071 """ 

1072 a, b = self.get_view_interval() 

1073 # cast to bool to avoid bad interaction between python 3.8 and np.bool_ 

1074 self._set_lim(*sorted((a, b), reverse=bool(inverted)), auto=None) 

1075 

1076 def set_default_intervals(self): 

1077 """ 

1078 Set the default limits for the axis data and view interval if they 

1079 have not been not mutated yet. 

1080 """ 

1081 # this is mainly in support of custom object plotting. For 

1082 # example, if someone passes in a datetime object, we do not 

1083 # know automagically how to set the default min/max of the 

1084 # data and view limits. The unit conversion AxisInfo 

1085 # interface provides a hook for custom types to register 

1086 # default limits through the AxisInfo.default_limits 

1087 # attribute, and the derived code below will check for that 

1088 # and use it if it's available (else just use 0..1) 

1089 

1090 def _set_lim(self, v0, v1, *, emit=True, auto): 

1091 """ 

1092 Set view limits. 

1093 

1094 This method is a helper for the Axes ``set_xlim``, ``set_ylim``, and 

1095 ``set_zlim`` methods. 

1096 

1097 Parameters 

1098 ---------- 

1099 v0, v1 : float 

1100 The view limits. (Passing *v0* as a (low, high) pair is not 

1101 supported; normalization must occur in the Axes setters.) 

1102 emit : bool, default: True 

1103 Whether to notify observers of limit change. 

1104 auto : bool or None, default: False 

1105 Whether to turn on autoscaling of the x-axis. True turns on, False 

1106 turns off, None leaves unchanged. 

1107 """ 

1108 name, = [name for name, axis in self.axes._axis_map.items() 

1109 if axis is self] # The axis name. 

1110 

1111 self.axes._process_unit_info([(name, (v0, v1))], convert=False) 

1112 v0 = self.axes._validate_converted_limits(v0, self.convert_units) 

1113 v1 = self.axes._validate_converted_limits(v1, self.convert_units) 

1114 

1115 if v0 is None or v1 is None: 

1116 # Axes init calls set_xlim(0, 1) before get_xlim() can be called, 

1117 # so only grab the limits if we really need them. 

1118 old0, old1 = self.get_view_interval() 

1119 if v0 is None: 

1120 v0 = old0 

1121 if v1 is None: 

1122 v1 = old1 

1123 

1124 if self.get_scale() == 'log' and (v0 <= 0 or v1 <= 0): 

1125 # Axes init calls set_xlim(0, 1) before get_xlim() can be called, 

1126 # so only grab the limits if we really need them. 

1127 old0, old1 = self.get_view_interval() 

1128 if v0 <= 0: 

1129 _api.warn_external(f"Attempt to set non-positive {name}lim on " 

1130 f"a log-scaled axis will be ignored.") 

1131 v0 = old0 

1132 if v1 <= 0: 

1133 _api.warn_external(f"Attempt to set non-positive {name}lim on " 

1134 f"a log-scaled axis will be ignored.") 

1135 v1 = old1 

1136 if v0 == v1: 

1137 _api.warn_external( 

1138 f"Attempting to set identical low and high {name}lims " 

1139 f"makes transformation singular; automatically expanding.") 

1140 reverse = bool(v0 > v1) # explicit cast needed for python3.8+np.bool_. 

1141 v0, v1 = self.get_major_locator().nonsingular(v0, v1) 

1142 v0, v1 = self.limit_range_for_scale(v0, v1) 

1143 v0, v1 = sorted([v0, v1], reverse=bool(reverse)) 

1144 

1145 self.set_view_interval(v0, v1, ignore=True) 

1146 # Mark viewlims as no longer stale without triggering an autoscale. 

1147 for ax in self.axes._shared_axes[name].get_siblings(self.axes): 

1148 ax._stale_viewlims[name] = False 

1149 if auto is not None: 

1150 self._set_autoscale_on(bool(auto)) 

1151 

1152 if emit: 

1153 self.axes.callbacks.process(f"{name}lim_changed", self.axes) 

1154 # Call all of the other axes that are shared with this one 

1155 for other in self.axes._shared_axes[name].get_siblings(self.axes): 

1156 if other is not self.axes: 

1157 other._axis_map[name]._set_lim( 

1158 v0, v1, emit=False, auto=auto) 

1159 if other.figure != self.figure: 

1160 other.figure.canvas.draw_idle() 

1161 

1162 self.stale = True 

1163 return v0, v1 

1164 

1165 def _set_artist_props(self, a): 

1166 if a is None: 

1167 return 

1168 a.set_figure(self.figure) 

1169 

1170 @_api.deprecated("3.6") 

1171 def get_ticklabel_extents(self, renderer): 

1172 """Get the extents of the tick labels on either side of the axes.""" 

1173 ticks_to_draw = self._update_ticks() 

1174 tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer) 

1175 if len(tlb1): 

1176 bbox1 = mtransforms.Bbox.union(tlb1) 

1177 else: 

1178 bbox1 = mtransforms.Bbox.from_extents(0, 0, 0, 0) 

1179 if len(tlb2): 

1180 bbox2 = mtransforms.Bbox.union(tlb2) 

1181 else: 

1182 bbox2 = mtransforms.Bbox.from_extents(0, 0, 0, 0) 

1183 return bbox1, bbox2 

1184 

1185 def _update_ticks(self): 

1186 """ 

1187 Update ticks (position and labels) using the current data interval of 

1188 the axes. Return the list of ticks that will be drawn. 

1189 """ 

1190 major_locs = self.get_majorticklocs() 

1191 major_labels = self.major.formatter.format_ticks(major_locs) 

1192 major_ticks = self.get_major_ticks(len(major_locs)) 

1193 self.major.formatter.set_locs(major_locs) 

1194 for tick, loc, label in zip(major_ticks, major_locs, major_labels): 

1195 tick.update_position(loc) 

1196 tick.set_label1(label) 

1197 tick.set_label2(label) 

1198 minor_locs = self.get_minorticklocs() 

1199 minor_labels = self.minor.formatter.format_ticks(minor_locs) 

1200 minor_ticks = self.get_minor_ticks(len(minor_locs)) 

1201 self.minor.formatter.set_locs(minor_locs) 

1202 for tick, loc, label in zip(minor_ticks, minor_locs, minor_labels): 

1203 tick.update_position(loc) 

1204 tick.set_label1(label) 

1205 tick.set_label2(label) 

1206 ticks = [*major_ticks, *minor_ticks] 

1207 

1208 view_low, view_high = self.get_view_interval() 

1209 if view_low > view_high: 

1210 view_low, view_high = view_high, view_low 

1211 

1212 interval_t = self.get_transform().transform([view_low, view_high]) 

1213 

1214 ticks_to_draw = [] 

1215 for tick in ticks: 

1216 try: 

1217 loc_t = self.get_transform().transform(tick.get_loc()) 

1218 except AssertionError: 

1219 # transforms.transform doesn't allow masked values but 

1220 # some scales might make them, so we need this try/except. 

1221 pass 

1222 else: 

1223 if mtransforms._interval_contains_close(interval_t, loc_t): 

1224 ticks_to_draw.append(tick) 

1225 

1226 return ticks_to_draw 

1227 

1228 def _get_ticklabel_bboxes(self, ticks, renderer=None): 

1229 """Return lists of bboxes for ticks' label1's and label2's.""" 

1230 if renderer is None: 

1231 renderer = self.figure._get_renderer() 

1232 return ([tick.label1.get_window_extent(renderer) 

1233 for tick in ticks if tick.label1.get_visible()], 

1234 [tick.label2.get_window_extent(renderer) 

1235 for tick in ticks if tick.label2.get_visible()]) 

1236 

1237 def get_tightbbox(self, renderer=None, *, for_layout_only=False): 

1238 """ 

1239 Return a bounding box that encloses the axis. It only accounts 

1240 tick labels, axis label, and offsetText. 

1241 

1242 If *for_layout_only* is True, then the width of the label (if this 

1243 is an x-axis) or the height of the label (if this is a y-axis) is 

1244 collapsed to near zero. This allows tight/constrained_layout to ignore 

1245 too-long labels when doing their layout. 

1246 """ 

1247 if not self.get_visible(): 

1248 return 

1249 if renderer is None: 

1250 renderer = self.figure._get_renderer() 

1251 ticks_to_draw = self._update_ticks() 

1252 

1253 self._update_label_position(renderer) 

1254 

1255 # go back to just this axis's tick labels 

1256 tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer) 

1257 

1258 self._update_offset_text_position(tlb1, tlb2) 

1259 self.offsetText.set_text(self.major.formatter.get_offset()) 

1260 

1261 bboxes = [ 

1262 *(a.get_window_extent(renderer) 

1263 for a in [self.offsetText] 

1264 if a.get_visible()), 

1265 *tlb1, *tlb2, 

1266 ] 

1267 # take care of label 

1268 if self.label.get_visible(): 

1269 bb = self.label.get_window_extent(renderer) 

1270 # for constrained/tight_layout, we want to ignore the label's 

1271 # width/height because the adjustments they make can't be improved. 

1272 # this code collapses the relevant direction 

1273 if for_layout_only: 

1274 if self.axis_name == "x" and bb.width > 0: 

1275 bb.x0 = (bb.x0 + bb.x1) / 2 - 0.5 

1276 bb.x1 = bb.x0 + 1.0 

1277 if self.axis_name == "y" and bb.height > 0: 

1278 bb.y0 = (bb.y0 + bb.y1) / 2 - 0.5 

1279 bb.y1 = bb.y0 + 1.0 

1280 bboxes.append(bb) 

1281 bboxes = [b for b in bboxes 

1282 if 0 < b.width < np.inf and 0 < b.height < np.inf] 

1283 if bboxes: 

1284 return mtransforms.Bbox.union(bboxes) 

1285 else: 

1286 return None 

1287 

1288 def get_tick_padding(self): 

1289 values = [] 

1290 if len(self.majorTicks): 

1291 values.append(self.majorTicks[0].get_tick_padding()) 

1292 if len(self.minorTicks): 

1293 values.append(self.minorTicks[0].get_tick_padding()) 

1294 return max(values, default=0) 

1295 

1296 @martist.allow_rasterization 

1297 def draw(self, renderer, *args, **kwargs): 

1298 # docstring inherited 

1299 

1300 if not self.get_visible(): 

1301 return 

1302 renderer.open_group(__name__, gid=self.get_gid()) 

1303 

1304 ticks_to_draw = self._update_ticks() 

1305 tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer) 

1306 

1307 for tick in ticks_to_draw: 

1308 tick.draw(renderer) 

1309 

1310 # Scale up the axis label box to also find the neighbors, not just the 

1311 # tick labels that actually overlap. We need a *copy* of the axis 

1312 # label box because we don't want to scale the actual bbox. 

1313 

1314 self._update_label_position(renderer) 

1315 

1316 self.label.draw(renderer) 

1317 

1318 self._update_offset_text_position(tlb1, tlb2) 

1319 self.offsetText.set_text(self.major.formatter.get_offset()) 

1320 self.offsetText.draw(renderer) 

1321 

1322 renderer.close_group(__name__) 

1323 self.stale = False 

1324 

1325 def get_gridlines(self): 

1326 r"""Return this Axis' grid lines as a list of `.Line2D`\s.""" 

1327 ticks = self.get_major_ticks() 

1328 return cbook.silent_list('Line2D gridline', 

1329 [tick.gridline for tick in ticks]) 

1330 

1331 def get_label(self): 

1332 """Return the axis label as a Text instance.""" 

1333 return self.label 

1334 

1335 def get_offset_text(self): 

1336 """Return the axis offsetText as a Text instance.""" 

1337 return self.offsetText 

1338 

1339 def get_pickradius(self): 

1340 """Return the depth of the axis used by the picker.""" 

1341 return self._pickradius 

1342 

1343 def get_majorticklabels(self): 

1344 """Return this Axis' major tick labels, as a list of `~.text.Text`.""" 

1345 self._update_ticks() 

1346 ticks = self.get_major_ticks() 

1347 labels1 = [tick.label1 for tick in ticks if tick.label1.get_visible()] 

1348 labels2 = [tick.label2 for tick in ticks if tick.label2.get_visible()] 

1349 return labels1 + labels2 

1350 

1351 def get_minorticklabels(self): 

1352 """Return this Axis' minor tick labels, as a list of `~.text.Text`.""" 

1353 self._update_ticks() 

1354 ticks = self.get_minor_ticks() 

1355 labels1 = [tick.label1 for tick in ticks if tick.label1.get_visible()] 

1356 labels2 = [tick.label2 for tick in ticks if tick.label2.get_visible()] 

1357 return labels1 + labels2 

1358 

1359 def get_ticklabels(self, minor=False, which=None): 

1360 """ 

1361 Get this Axis' tick labels. 

1362 

1363 Parameters 

1364 ---------- 

1365 minor : bool 

1366 Whether to return the minor or the major ticklabels. 

1367 

1368 which : None, ('minor', 'major', 'both') 

1369 Overrides *minor*. 

1370 

1371 Selects which ticklabels to return 

1372 

1373 Returns 

1374 ------- 

1375 list of `~matplotlib.text.Text` 

1376 """ 

1377 if which is not None: 

1378 if which == 'minor': 

1379 return self.get_minorticklabels() 

1380 elif which == 'major': 

1381 return self.get_majorticklabels() 

1382 elif which == 'both': 

1383 return self.get_majorticklabels() + self.get_minorticklabels() 

1384 else: 

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

1386 if minor: 

1387 return self.get_minorticklabels() 

1388 return self.get_majorticklabels() 

1389 

1390 def get_majorticklines(self): 

1391 r"""Return this Axis' major tick lines as a list of `.Line2D`\s.""" 

1392 lines = [] 

1393 ticks = self.get_major_ticks() 

1394 for tick in ticks: 

1395 lines.append(tick.tick1line) 

1396 lines.append(tick.tick2line) 

1397 return cbook.silent_list('Line2D ticklines', lines) 

1398 

1399 def get_minorticklines(self): 

1400 r"""Return this Axis' minor tick lines as a list of `.Line2D`\s.""" 

1401 lines = [] 

1402 ticks = self.get_minor_ticks() 

1403 for tick in ticks: 

1404 lines.append(tick.tick1line) 

1405 lines.append(tick.tick2line) 

1406 return cbook.silent_list('Line2D ticklines', lines) 

1407 

1408 def get_ticklines(self, minor=False): 

1409 r"""Return this Axis' tick lines as a list of `.Line2D`\s.""" 

1410 if minor: 

1411 return self.get_minorticklines() 

1412 return self.get_majorticklines() 

1413 

1414 def get_majorticklocs(self): 

1415 """Return this Axis' major tick locations in data coordinates.""" 

1416 return self.major.locator() 

1417 

1418 def get_minorticklocs(self): 

1419 """Return this Axis' minor tick locations in data coordinates.""" 

1420 # Remove minor ticks duplicating major ticks. 

1421 minor_locs = np.asarray(self.minor.locator()) 

1422 if self.remove_overlapping_locs: 

1423 major_locs = self.major.locator() 

1424 transform = self._scale.get_transform() 

1425 tr_minor_locs = transform.transform(minor_locs) 

1426 tr_major_locs = transform.transform(major_locs) 

1427 lo, hi = sorted(transform.transform(self.get_view_interval())) 

1428 # Use the transformed view limits as scale. 1e-5 is the default 

1429 # rtol for np.isclose. 

1430 tol = (hi - lo) * 1e-5 

1431 mask = np.isclose(tr_minor_locs[:, None], tr_major_locs[None, :], 

1432 atol=tol, rtol=0).any(axis=1) 

1433 minor_locs = minor_locs[~mask] 

1434 return minor_locs 

1435 

1436 def get_ticklocs(self, *, minor=False): 

1437 """ 

1438 Return this Axis' tick locations in data coordinates. 

1439 

1440 The locations are not clipped to the current axis limits and hence 

1441 may contain locations that are not visible in the output. 

1442 

1443 Parameters 

1444 ---------- 

1445 minor : bool, default: False 

1446 True to return the minor tick directions, 

1447 False to return the major tick directions. 

1448 

1449 Returns 

1450 ------- 

1451 numpy array of tick locations 

1452 """ 

1453 return self.get_minorticklocs() if minor else self.get_majorticklocs() 

1454 

1455 def get_ticks_direction(self, minor=False): 

1456 """ 

1457 Get the tick directions as a numpy array 

1458 

1459 Parameters 

1460 ---------- 

1461 minor : bool, default: False 

1462 True to return the minor tick directions, 

1463 False to return the major tick directions. 

1464 

1465 Returns 

1466 ------- 

1467 numpy array of tick directions 

1468 """ 

1469 if minor: 

1470 return np.array( 

1471 [tick._tickdir for tick in self.get_minor_ticks()]) 

1472 else: 

1473 return np.array( 

1474 [tick._tickdir for tick in self.get_major_ticks()]) 

1475 

1476 def _get_tick(self, major): 

1477 """Return the default tick instance.""" 

1478 if self._tick_class is None: 

1479 raise NotImplementedError( 

1480 f"The Axis subclass {self.__class__.__name__} must define " 

1481 "_tick_class or reimplement _get_tick()") 

1482 tick_kw = self._major_tick_kw if major else self._minor_tick_kw 

1483 return self._tick_class(self.axes, 0, major=major, **tick_kw) 

1484 

1485 def _get_tick_label_size(self, axis_name): 

1486 """ 

1487 Return the text size of tick labels for this Axis. 

1488 

1489 This is a convenience function to avoid having to create a `Tick` in 

1490 `.get_tick_space`, since it is expensive. 

1491 """ 

1492 tick_kw = self._major_tick_kw 

1493 size = tick_kw.get('labelsize', 

1494 mpl.rcParams[f'{axis_name}tick.labelsize']) 

1495 return mtext.FontProperties(size=size).get_size_in_points() 

1496 

1497 def _copy_tick_props(self, src, dest): 

1498 """Copy the properties from *src* tick to *dest* tick.""" 

1499 if src is None or dest is None: 

1500 return 

1501 dest.label1.update_from(src.label1) 

1502 dest.label2.update_from(src.label2) 

1503 dest.tick1line.update_from(src.tick1line) 

1504 dest.tick2line.update_from(src.tick2line) 

1505 dest.gridline.update_from(src.gridline) 

1506 

1507 def get_label_text(self): 

1508 """Get the text of the label.""" 

1509 return self.label.get_text() 

1510 

1511 def get_major_locator(self): 

1512 """Get the locator of the major ticker.""" 

1513 return self.major.locator 

1514 

1515 def get_minor_locator(self): 

1516 """Get the locator of the minor ticker.""" 

1517 return self.minor.locator 

1518 

1519 def get_major_formatter(self): 

1520 """Get the formatter of the major ticker.""" 

1521 return self.major.formatter 

1522 

1523 def get_minor_formatter(self): 

1524 """Get the formatter of the minor ticker.""" 

1525 return self.minor.formatter 

1526 

1527 def get_major_ticks(self, numticks=None): 

1528 r"""Return the list of major `.Tick`\s.""" 

1529 if numticks is None: 

1530 numticks = len(self.get_majorticklocs()) 

1531 

1532 while len(self.majorTicks) < numticks: 

1533 # Update the new tick label properties from the old. 

1534 tick = self._get_tick(major=True) 

1535 self.majorTicks.append(tick) 

1536 self._copy_tick_props(self.majorTicks[0], tick) 

1537 

1538 return self.majorTicks[:numticks] 

1539 

1540 def get_minor_ticks(self, numticks=None): 

1541 r"""Return the list of minor `.Tick`\s.""" 

1542 if numticks is None: 

1543 numticks = len(self.get_minorticklocs()) 

1544 

1545 while len(self.minorTicks) < numticks: 

1546 # Update the new tick label properties from the old. 

1547 tick = self._get_tick(major=False) 

1548 self.minorTicks.append(tick) 

1549 self._copy_tick_props(self.minorTicks[0], tick) 

1550 

1551 return self.minorTicks[:numticks] 

1552 

1553 @_api.rename_parameter("3.5", "b", "visible") 

1554 def grid(self, visible=None, which='major', **kwargs): 

1555 """ 

1556 Configure the grid lines. 

1557 

1558 Parameters 

1559 ---------- 

1560 visible : bool or None 

1561 Whether to show the grid lines. If any *kwargs* are supplied, it 

1562 is assumed you want the grid on and *visible* will be set to True. 

1563 

1564 If *visible* is *None* and there are no *kwargs*, this toggles the 

1565 visibility of the lines. 

1566 

1567 which : {'major', 'minor', 'both'} 

1568 The grid lines to apply the changes on. 

1569 

1570 **kwargs : `.Line2D` properties 

1571 Define the line properties of the grid, e.g.:: 

1572 

1573 grid(color='r', linestyle='-', linewidth=2) 

1574 """ 

1575 if kwargs: 

1576 if visible is None: 

1577 visible = True 

1578 elif not visible: # something false-like but not None 

1579 _api.warn_external('First parameter to grid() is false, ' 

1580 'but line properties are supplied. The ' 

1581 'grid will be enabled.') 

1582 visible = True 

1583 which = which.lower() 

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

1585 gridkw = {f'grid_{name}': value for name, value in kwargs.items()} 

1586 if which in ['minor', 'both']: 

1587 gridkw['gridOn'] = (not self._minor_tick_kw['gridOn'] 

1588 if visible is None else visible) 

1589 self.set_tick_params(which='minor', **gridkw) 

1590 if which in ['major', 'both']: 

1591 gridkw['gridOn'] = (not self._major_tick_kw['gridOn'] 

1592 if visible is None else visible) 

1593 self.set_tick_params(which='major', **gridkw) 

1594 self.stale = True 

1595 

1596 def update_units(self, data): 

1597 """ 

1598 Introspect *data* for units converter and update the 

1599 ``axis.converter`` instance if necessary. Return *True* 

1600 if *data* is registered for unit conversion. 

1601 """ 

1602 converter = munits.registry.get_converter(data) 

1603 if converter is None: 

1604 return False 

1605 

1606 neednew = self.converter != converter 

1607 self.converter = converter 

1608 default = self.converter.default_units(data, self) 

1609 if default is not None and self.units is None: 

1610 self.set_units(default) 

1611 

1612 elif neednew: 

1613 self._update_axisinfo() 

1614 self.stale = True 

1615 return True 

1616 

1617 def _update_axisinfo(self): 

1618 """ 

1619 Check the axis converter for the stored units to see if the 

1620 axis info needs to be updated. 

1621 """ 

1622 if self.converter is None: 

1623 return 

1624 

1625 info = self.converter.axisinfo(self.units, self) 

1626 

1627 if info is None: 

1628 return 

1629 if info.majloc is not None and \ 

1630 self.major.locator != info.majloc and self.isDefault_majloc: 

1631 self.set_major_locator(info.majloc) 

1632 self.isDefault_majloc = True 

1633 if info.minloc is not None and \ 

1634 self.minor.locator != info.minloc and self.isDefault_minloc: 

1635 self.set_minor_locator(info.minloc) 

1636 self.isDefault_minloc = True 

1637 if info.majfmt is not None and \ 

1638 self.major.formatter != info.majfmt and self.isDefault_majfmt: 

1639 self.set_major_formatter(info.majfmt) 

1640 self.isDefault_majfmt = True 

1641 if info.minfmt is not None and \ 

1642 self.minor.formatter != info.minfmt and self.isDefault_minfmt: 

1643 self.set_minor_formatter(info.minfmt) 

1644 self.isDefault_minfmt = True 

1645 if info.label is not None and self.isDefault_label: 

1646 self.set_label_text(info.label) 

1647 self.isDefault_label = True 

1648 

1649 self.set_default_intervals() 

1650 

1651 def have_units(self): 

1652 return self.converter is not None or self.units is not None 

1653 

1654 def convert_units(self, x): 

1655 # If x is natively supported by Matplotlib, doesn't need converting 

1656 if munits._is_natively_supported(x): 

1657 return x 

1658 

1659 if self.converter is None: 

1660 self.converter = munits.registry.get_converter(x) 

1661 

1662 if self.converter is None: 

1663 return x 

1664 try: 

1665 ret = self.converter.convert(x, self.units, self) 

1666 except Exception as e: 

1667 raise munits.ConversionError('Failed to convert value(s) to axis ' 

1668 f'units: {x!r}') from e 

1669 return ret 

1670 

1671 def set_units(self, u): 

1672 """ 

1673 Set the units for axis. 

1674 

1675 Parameters 

1676 ---------- 

1677 u : units tag 

1678 

1679 Notes 

1680 ----- 

1681 The units of any shared axis will also be updated. 

1682 """ 

1683 if u == self.units: 

1684 return 

1685 for name, axis in self.axes._axis_map.items(): 

1686 if self is axis: 

1687 shared = [ 

1688 getattr(ax, f"{name}axis") 

1689 for ax 

1690 in self.axes._shared_axes[name].get_siblings(self.axes)] 

1691 break 

1692 else: 

1693 shared = [self] 

1694 for axis in shared: 

1695 axis.units = u 

1696 axis._update_axisinfo() 

1697 axis.callbacks.process('units') 

1698 axis.callbacks.process('units finalize') 

1699 axis.stale = True 

1700 

1701 def get_units(self): 

1702 """Return the units for axis.""" 

1703 return self.units 

1704 

1705 def set_label_text(self, label, fontdict=None, **kwargs): 

1706 """ 

1707 Set the text value of the axis label. 

1708 

1709 Parameters 

1710 ---------- 

1711 label : str 

1712 Text string. 

1713 fontdict : dict 

1714 Text properties. 

1715 **kwargs 

1716 Merged into fontdict. 

1717 """ 

1718 self.isDefault_label = False 

1719 self.label.set_text(label) 

1720 if fontdict is not None: 

1721 self.label.update(fontdict) 

1722 self.label.update(kwargs) 

1723 self.stale = True 

1724 return self.label 

1725 

1726 def set_major_formatter(self, formatter): 

1727 """ 

1728 Set the formatter of the major ticker. 

1729 

1730 In addition to a `~matplotlib.ticker.Formatter` instance, 

1731 this also accepts a ``str`` or function. 

1732 

1733 For a ``str`` a `~matplotlib.ticker.StrMethodFormatter` is used. 

1734 The field used for the value must be labeled ``'x'`` and the field used 

1735 for the position must be labeled ``'pos'``. 

1736 See the `~matplotlib.ticker.StrMethodFormatter` documentation for 

1737 more information. 

1738 

1739 For a function, a `~matplotlib.ticker.FuncFormatter` is used. 

1740 The function must take two inputs (a tick value ``x`` and a 

1741 position ``pos``), and return a string containing the corresponding 

1742 tick label. 

1743 See the `~matplotlib.ticker.FuncFormatter` documentation for 

1744 more information. 

1745 

1746 Parameters 

1747 ---------- 

1748 formatter : `~matplotlib.ticker.Formatter`, ``str``, or function 

1749 """ 

1750 self._set_formatter(formatter, self.major) 

1751 

1752 def set_minor_formatter(self, formatter): 

1753 """ 

1754 Set the formatter of the minor ticker. 

1755 

1756 In addition to a `~matplotlib.ticker.Formatter` instance, 

1757 this also accepts a ``str`` or function. 

1758 See `.Axis.set_major_formatter` for more information. 

1759 

1760 Parameters 

1761 ---------- 

1762 formatter : `~matplotlib.ticker.Formatter`, ``str``, or function 

1763 """ 

1764 self._set_formatter(formatter, self.minor) 

1765 

1766 def _set_formatter(self, formatter, level): 

1767 if isinstance(formatter, str): 

1768 formatter = mticker.StrMethodFormatter(formatter) 

1769 # Don't allow any other TickHelper to avoid easy-to-make errors, 

1770 # like using a Locator instead of a Formatter. 

1771 elif (callable(formatter) and 

1772 not isinstance(formatter, mticker.TickHelper)): 

1773 formatter = mticker.FuncFormatter(formatter) 

1774 else: 

1775 _api.check_isinstance(mticker.Formatter, formatter=formatter) 

1776 

1777 if (isinstance(formatter, mticker.FixedFormatter) 

1778 and len(formatter.seq) > 0 

1779 and not isinstance(level.locator, mticker.FixedLocator)): 

1780 _api.warn_external('FixedFormatter should only be used together ' 

1781 'with FixedLocator') 

1782 

1783 if level == self.major: 

1784 self.isDefault_majfmt = False 

1785 else: 

1786 self.isDefault_minfmt = False 

1787 

1788 level.formatter = formatter 

1789 formatter.set_axis(self) 

1790 self.stale = True 

1791 

1792 def set_major_locator(self, locator): 

1793 """ 

1794 Set the locator of the major ticker. 

1795 

1796 Parameters 

1797 ---------- 

1798 locator : `~matplotlib.ticker.Locator` 

1799 """ 

1800 _api.check_isinstance(mticker.Locator, locator=locator) 

1801 self.isDefault_majloc = False 

1802 self.major.locator = locator 

1803 if self.major.formatter: 

1804 self.major.formatter._set_locator(locator) 

1805 locator.set_axis(self) 

1806 self.stale = True 

1807 

1808 def set_minor_locator(self, locator): 

1809 """ 

1810 Set the locator of the minor ticker. 

1811 

1812 Parameters 

1813 ---------- 

1814 locator : `~matplotlib.ticker.Locator` 

1815 """ 

1816 _api.check_isinstance(mticker.Locator, locator=locator) 

1817 self.isDefault_minloc = False 

1818 self.minor.locator = locator 

1819 if self.minor.formatter: 

1820 self.minor.formatter._set_locator(locator) 

1821 locator.set_axis(self) 

1822 self.stale = True 

1823 

1824 def set_pickradius(self, pickradius): 

1825 """ 

1826 Set the depth of the axis used by the picker. 

1827 

1828 Parameters 

1829 ---------- 

1830 pickradius : float 

1831 The acceptance radius for containment tests. 

1832 See also `.Axis.contains`. 

1833 """ 

1834 if not isinstance(pickradius, Number) or pickradius < 0: 

1835 raise ValueError("pick radius should be a distance") 

1836 self._pickradius = pickradius 

1837 

1838 pickradius = property( 

1839 get_pickradius, set_pickradius, doc="The acceptance radius for " 

1840 "containment tests. See also `.Axis.contains`.") 

1841 

1842 # Helper for set_ticklabels. Defining it here makes it picklable. 

1843 @staticmethod 

1844 def _format_with_dict(tickd, x, pos): 

1845 return tickd.get(x, "") 

1846 

1847 def set_ticklabels(self, ticklabels, *, minor=False, **kwargs): 

1848 r""" 

1849 [*Discouraged*] Set the text values of the tick labels. 

1850 

1851 .. admonition:: Discouraged 

1852 

1853 The use of this method is discouraged, because of the dependency 

1854 on tick positions. In most cases, you'll want to use 

1855 ``set_[x/y]ticks(positions, labels)`` instead. 

1856 

1857 If you are using this method, you should always fix the tick 

1858 positions before, e.g. by using `.Axis.set_ticks` or by explicitly 

1859 setting a `~.ticker.FixedLocator`. Otherwise, ticks are free to 

1860 move and the labels may end up in unexpected positions. 

1861 

1862 Parameters 

1863 ---------- 

1864 ticklabels : sequence of str or of `.Text`\s 

1865 Texts for labeling each tick location in the sequence set by 

1866 `.Axis.set_ticks`; the number of labels must match the number of 

1867 locations. 

1868 minor : bool 

1869 If True, set minor ticks instead of major ticks. 

1870 **kwargs 

1871 Text properties. 

1872 

1873 Returns 

1874 ------- 

1875 list of `.Text`\s 

1876 For each tick, includes ``tick.label1`` if it is visible, then 

1877 ``tick.label2`` if it is visible, in that order. 

1878 """ 

1879 try: 

1880 ticklabels = [t.get_text() if hasattr(t, 'get_text') else t 

1881 for t in ticklabels] 

1882 except TypeError: 

1883 raise TypeError(f"{ticklabels:=} must be a sequence") from None 

1884 locator = (self.get_minor_locator() if minor 

1885 else self.get_major_locator()) 

1886 if isinstance(locator, mticker.FixedLocator): 

1887 # Passing [] as a list of ticklabels is often used as a way to 

1888 # remove all tick labels, so only error for > 0 ticklabels 

1889 if len(locator.locs) != len(ticklabels) and len(ticklabels) != 0: 

1890 raise ValueError( 

1891 "The number of FixedLocator locations" 

1892 f" ({len(locator.locs)}), usually from a call to" 

1893 " set_ticks, does not match" 

1894 f" the number of ticklabels ({len(ticklabels)}).") 

1895 tickd = {loc: lab for loc, lab in zip(locator.locs, ticklabels)} 

1896 func = functools.partial(self._format_with_dict, tickd) 

1897 formatter = mticker.FuncFormatter(func) 

1898 else: 

1899 formatter = mticker.FixedFormatter(ticklabels) 

1900 

1901 if minor: 

1902 self.set_minor_formatter(formatter) 

1903 locs = self.get_minorticklocs() 

1904 ticks = self.get_minor_ticks(len(locs)) 

1905 else: 

1906 self.set_major_formatter(formatter) 

1907 locs = self.get_majorticklocs() 

1908 ticks = self.get_major_ticks(len(locs)) 

1909 

1910 ret = [] 

1911 for pos, (loc, tick) in enumerate(zip(locs, ticks)): 

1912 tick.update_position(loc) 

1913 tick_label = formatter(loc, pos) 

1914 # deal with label1 

1915 tick.label1.set_text(tick_label) 

1916 tick.label1._internal_update(kwargs) 

1917 # deal with label2 

1918 tick.label2.set_text(tick_label) 

1919 tick.label2._internal_update(kwargs) 

1920 # only return visible tick labels 

1921 if tick.label1.get_visible(): 

1922 ret.append(tick.label1) 

1923 if tick.label2.get_visible(): 

1924 ret.append(tick.label2) 

1925 

1926 self.stale = True 

1927 return ret 

1928 

1929 # Wrapper around set_ticklabels used to generate Axes.set_x/ytickabels; can 

1930 # go away once the API of Axes.set_x/yticklabels becomes consistent. 

1931 def _set_ticklabels(self, labels, *, fontdict=None, minor=False, **kwargs): 

1932 """ 

1933 Set this Axis' labels with list of string labels. 

1934 

1935 .. warning:: 

1936 This method should only be used after fixing the tick positions 

1937 using `.Axis.set_ticks`. Otherwise, the labels may end up in 

1938 unexpected positions. 

1939 

1940 Parameters 

1941 ---------- 

1942 labels : list of str 

1943 The label texts. 

1944 

1945 fontdict : dict, optional 

1946 A dictionary controlling the appearance of the ticklabels. 

1947 The default *fontdict* is:: 

1948 

1949 {'fontsize': rcParams['axes.titlesize'], 

1950 'fontweight': rcParams['axes.titleweight'], 

1951 'verticalalignment': 'baseline', 

1952 'horizontalalignment': loc} 

1953 

1954 minor : bool, default: False 

1955 Whether to set the minor ticklabels rather than the major ones. 

1956 

1957 Returns 

1958 ------- 

1959 list of `.Text` 

1960 The labels. 

1961 

1962 Other Parameters 

1963 ---------------- 

1964 **kwargs : `~.text.Text` properties. 

1965 """ 

1966 if fontdict is not None: 

1967 kwargs.update(fontdict) 

1968 return self.set_ticklabels(labels, minor=minor, **kwargs) 

1969 

1970 def _set_tick_locations(self, ticks, *, minor=False): 

1971 # see docstring of set_ticks 

1972 

1973 # XXX if the user changes units, the information will be lost here 

1974 ticks = self.convert_units(ticks) 

1975 for name, axis in self.axes._axis_map.items(): 

1976 if self is axis: 

1977 shared = [ 

1978 getattr(ax, f"{name}axis") 

1979 for ax 

1980 in self.axes._shared_axes[name].get_siblings(self.axes)] 

1981 break 

1982 else: 

1983 shared = [self] 

1984 if len(ticks): 

1985 for axis in shared: 

1986 # set_view_interval maintains any preexisting inversion. 

1987 axis.set_view_interval(min(ticks), max(ticks)) 

1988 self.axes.stale = True 

1989 if minor: 

1990 self.set_minor_locator(mticker.FixedLocator(ticks)) 

1991 return self.get_minor_ticks(len(ticks)) 

1992 else: 

1993 self.set_major_locator(mticker.FixedLocator(ticks)) 

1994 return self.get_major_ticks(len(ticks)) 

1995 

1996 def set_ticks(self, ticks, labels=None, *, minor=False, **kwargs): 

1997 """ 

1998 Set this Axis' tick locations and optionally labels. 

1999 

2000 If necessary, the view limits of the Axis are expanded so that all 

2001 given ticks are visible. 

2002 

2003 Parameters 

2004 ---------- 

2005 ticks : list of floats 

2006 List of tick locations. The axis `.Locator` is replaced by a 

2007 `~.ticker.FixedLocator`. 

2008 

2009 Some tick formatters will not label arbitrary tick positions; 

2010 e.g. log formatters only label decade ticks by default. In 

2011 such a case you can set a formatter explicitly on the axis 

2012 using `.Axis.set_major_formatter` or provide formatted 

2013 *labels* yourself. 

2014 labels : list of str, optional 

2015 List of tick labels. If not set, the labels are generated with 

2016 the axis tick `.Formatter`. 

2017 minor : bool, default: False 

2018 If ``False``, set the major ticks; if ``True``, the minor ticks. 

2019 **kwargs 

2020 `.Text` properties for the labels. These take effect only if you 

2021 pass *labels*. In other cases, please use `~.Axes.tick_params`. 

2022 

2023 Notes 

2024 ----- 

2025 The mandatory expansion of the view limits is an intentional design 

2026 choice to prevent the surprise of a non-visible tick. If you need 

2027 other limits, you should set the limits explicitly after setting the 

2028 ticks. 

2029 """ 

2030 result = self._set_tick_locations(ticks, minor=minor) 

2031 if labels is not None: 

2032 self.set_ticklabels(labels, minor=minor, **kwargs) 

2033 return result 

2034 

2035 def _get_tick_boxes_siblings(self, renderer): 

2036 """ 

2037 Get the bounding boxes for this `.axis` and its siblings 

2038 as set by `.Figure.align_xlabels` or `.Figure.align_ylabels`. 

2039 

2040 By default, it just gets bboxes for *self*. 

2041 """ 

2042 # Get the Grouper keeping track of x or y label groups for this figure. 

2043 axis_names = [ 

2044 name for name, axis in self.axes._axis_map.items() 

2045 if name in self.figure._align_label_groups and axis is self] 

2046 if len(axis_names) != 1: 

2047 return [], [] 

2048 axis_name, = axis_names 

2049 grouper = self.figure._align_label_groups[axis_name] 

2050 bboxes = [] 

2051 bboxes2 = [] 

2052 # If we want to align labels from other Axes: 

2053 for ax in grouper.get_siblings(self.axes): 

2054 axis = getattr(ax, f"{axis_name}axis") 

2055 ticks_to_draw = axis._update_ticks() 

2056 tlb, tlb2 = axis._get_ticklabel_bboxes(ticks_to_draw, renderer) 

2057 bboxes.extend(tlb) 

2058 bboxes2.extend(tlb2) 

2059 return bboxes, bboxes2 

2060 

2061 def _update_label_position(self, renderer): 

2062 """ 

2063 Update the label position based on the bounding box enclosing 

2064 all the ticklabels and axis spine. 

2065 """ 

2066 raise NotImplementedError('Derived must override') 

2067 

2068 def _update_offset_text_position(self, bboxes, bboxes2): 

2069 """ 

2070 Update the offset text position based on the sequence of bounding 

2071 boxes of all the ticklabels. 

2072 """ 

2073 raise NotImplementedError('Derived must override') 

2074 

2075 def axis_date(self, tz=None): 

2076 """ 

2077 Set up axis ticks and labels to treat data along this Axis as dates. 

2078 

2079 Parameters 

2080 ---------- 

2081 tz : str or `datetime.tzinfo`, default: :rc:`timezone` 

2082 The timezone used to create date labels. 

2083 """ 

2084 # By providing a sample datetime instance with the desired timezone, 

2085 # the registered converter can be selected, and the "units" attribute, 

2086 # which is the timezone, can be set. 

2087 if isinstance(tz, str): 

2088 import dateutil.tz 

2089 tz = dateutil.tz.gettz(tz) 

2090 self.update_units(datetime.datetime(2009, 1, 1, 0, 0, 0, 0, tz)) 

2091 

2092 def get_tick_space(self): 

2093 """Return the estimated number of ticks that can fit on the axis.""" 

2094 # Must be overridden in the subclass 

2095 raise NotImplementedError() 

2096 

2097 def _get_ticks_position(self): 

2098 """ 

2099 Helper for `XAxis.get_ticks_position` and `YAxis.get_ticks_position`. 

2100 

2101 Check the visibility of tick1line, label1, tick2line, and label2 on 

2102 the first major and the first minor ticks, and return 

2103 

2104 - 1 if only tick1line and label1 are visible (which corresponds to 

2105 "bottom" for the x-axis and "left" for the y-axis); 

2106 - 2 if only tick2line and label2 are visible (which corresponds to 

2107 "top" for the x-axis and "right" for the y-axis); 

2108 - "default" if only tick1line, tick2line and label1 are visible; 

2109 - "unknown" otherwise. 

2110 """ 

2111 major = self.majorTicks[0] 

2112 minor = self.minorTicks[0] 

2113 if all(tick.tick1line.get_visible() 

2114 and not tick.tick2line.get_visible() 

2115 and tick.label1.get_visible() 

2116 and not tick.label2.get_visible() 

2117 for tick in [major, minor]): 

2118 return 1 

2119 elif all(tick.tick2line.get_visible() 

2120 and not tick.tick1line.get_visible() 

2121 and tick.label2.get_visible() 

2122 and not tick.label1.get_visible() 

2123 for tick in [major, minor]): 

2124 return 2 

2125 elif all(tick.tick1line.get_visible() 

2126 and tick.tick2line.get_visible() 

2127 and tick.label1.get_visible() 

2128 and not tick.label2.get_visible() 

2129 for tick in [major, minor]): 

2130 return "default" 

2131 else: 

2132 return "unknown" 

2133 

2134 def get_label_position(self): 

2135 """ 

2136 Return the label position (top or bottom) 

2137 """ 

2138 return self.label_position 

2139 

2140 def set_label_position(self, position): 

2141 """ 

2142 Set the label position (top or bottom) 

2143 

2144 Parameters 

2145 ---------- 

2146 position : {'top', 'bottom'} 

2147 """ 

2148 raise NotImplementedError() 

2149 

2150 def get_minpos(self): 

2151 raise NotImplementedError() 

2152 

2153 

2154def _make_getset_interval(method_name, lim_name, attr_name): 

2155 """ 

2156 Helper to generate ``get_{data,view}_interval`` and 

2157 ``set_{data,view}_interval`` implementations. 

2158 """ 

2159 

2160 def getter(self): 

2161 # docstring inherited. 

2162 return getattr(getattr(self.axes, lim_name), attr_name) 

2163 

2164 def setter(self, vmin, vmax, ignore=False): 

2165 # docstring inherited. 

2166 if ignore: 

2167 setattr(getattr(self.axes, lim_name), attr_name, (vmin, vmax)) 

2168 else: 

2169 oldmin, oldmax = getter(self) 

2170 if oldmin < oldmax: 

2171 setter(self, min(vmin, vmax, oldmin), max(vmin, vmax, oldmax), 

2172 ignore=True) 

2173 else: 

2174 setter(self, max(vmin, vmax, oldmin), min(vmin, vmax, oldmax), 

2175 ignore=True) 

2176 self.stale = True 

2177 

2178 getter.__name__ = f"get_{method_name}_interval" 

2179 setter.__name__ = f"set_{method_name}_interval" 

2180 

2181 return getter, setter 

2182 

2183 

2184class XAxis(Axis): 

2185 __name__ = 'xaxis' 

2186 axis_name = 'x' #: Read-only name identifying the axis. 

2187 _tick_class = XTick 

2188 

2189 def __init__(self, *args, **kwargs): 

2190 super().__init__(*args, **kwargs) 

2191 # x in axes coords, y in display coords (to be updated at draw time by 

2192 # _update_label_positions and _update_offset_text_position). 

2193 self.label.set( 

2194 x=0.5, y=0, 

2195 verticalalignment='top', horizontalalignment='center', 

2196 transform=mtransforms.blended_transform_factory( 

2197 self.axes.transAxes, mtransforms.IdentityTransform()), 

2198 ) 

2199 self.label_position = 'bottom' 

2200 self.offsetText.set( 

2201 x=1, y=0, 

2202 verticalalignment='top', horizontalalignment='right', 

2203 transform=mtransforms.blended_transform_factory( 

2204 self.axes.transAxes, mtransforms.IdentityTransform()), 

2205 fontsize=mpl.rcParams['xtick.labelsize'], 

2206 color=mpl.rcParams['xtick.color'], 

2207 ) 

2208 self.offset_text_position = 'bottom' 

2209 

2210 def contains(self, mouseevent): 

2211 """Test whether the mouse event occurred in the x-axis.""" 

2212 inside, info = self._default_contains(mouseevent) 

2213 if inside is not None: 

2214 return inside, info 

2215 

2216 x, y = mouseevent.x, mouseevent.y 

2217 try: 

2218 trans = self.axes.transAxes.inverted() 

2219 xaxes, yaxes = trans.transform((x, y)) 

2220 except ValueError: 

2221 return False, {} 

2222 (l, b), (r, t) = self.axes.transAxes.transform([(0, 0), (1, 1)]) 

2223 inaxis = 0 <= xaxes <= 1 and ( 

2224 b - self._pickradius < y < b or 

2225 t < y < t + self._pickradius) 

2226 return inaxis, {} 

2227 

2228 def set_label_position(self, position): 

2229 """ 

2230 Set the label position (top or bottom) 

2231 

2232 Parameters 

2233 ---------- 

2234 position : {'top', 'bottom'} 

2235 """ 

2236 self.label.set_verticalalignment(_api.check_getitem({ 

2237 'top': 'baseline', 'bottom': 'top', 

2238 }, position=position)) 

2239 self.label_position = position 

2240 self.stale = True 

2241 

2242 def _update_label_position(self, renderer): 

2243 """ 

2244 Update the label position based on the bounding box enclosing 

2245 all the ticklabels and axis spine 

2246 """ 

2247 if not self._autolabelpos: 

2248 return 

2249 

2250 # get bounding boxes for this axis and any siblings 

2251 # that have been set by `fig.align_xlabels()` 

2252 bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer) 

2253 

2254 x, y = self.label.get_position() 

2255 if self.label_position == 'bottom': 

2256 try: 

2257 spine = self.axes.spines['bottom'] 

2258 spinebbox = spine.get_window_extent() 

2259 except KeyError: 

2260 # use Axes if spine doesn't exist 

2261 spinebbox = self.axes.bbox 

2262 bbox = mtransforms.Bbox.union(bboxes + [spinebbox]) 

2263 bottom = bbox.y0 

2264 

2265 self.label.set_position( 

2266 (x, bottom - self.labelpad * self.figure.dpi / 72) 

2267 ) 

2268 else: 

2269 try: 

2270 spine = self.axes.spines['top'] 

2271 spinebbox = spine.get_window_extent() 

2272 except KeyError: 

2273 # use Axes if spine doesn't exist 

2274 spinebbox = self.axes.bbox 

2275 bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox]) 

2276 top = bbox.y1 

2277 

2278 self.label.set_position( 

2279 (x, top + self.labelpad * self.figure.dpi / 72) 

2280 ) 

2281 

2282 def _update_offset_text_position(self, bboxes, bboxes2): 

2283 """ 

2284 Update the offset_text position based on the sequence of bounding 

2285 boxes of all the ticklabels 

2286 """ 

2287 x, y = self.offsetText.get_position() 

2288 if not hasattr(self, '_tick_position'): 

2289 self._tick_position = 'bottom' 

2290 if self._tick_position == 'bottom': 

2291 if not len(bboxes): 

2292 bottom = self.axes.bbox.ymin 

2293 else: 

2294 bbox = mtransforms.Bbox.union(bboxes) 

2295 bottom = bbox.y0 

2296 y = bottom - self.OFFSETTEXTPAD * self.figure.dpi / 72 

2297 else: 

2298 if not len(bboxes2): 

2299 top = self.axes.bbox.ymax 

2300 else: 

2301 bbox = mtransforms.Bbox.union(bboxes2) 

2302 top = bbox.y1 

2303 y = top + self.OFFSETTEXTPAD * self.figure.dpi / 72 

2304 self.offsetText.set_position((x, y)) 

2305 

2306 @_api.deprecated("3.6") 

2307 def get_text_heights(self, renderer): 

2308 """ 

2309 Return how much space should be reserved for text above and below the 

2310 Axes, as a pair of floats. 

2311 """ 

2312 bbox, bbox2 = self.get_ticklabel_extents(renderer) 

2313 # MGDTODO: Need a better way to get the pad 

2314 pad_pixels = self.majorTicks[0].get_pad_pixels() 

2315 

2316 above = 0.0 

2317 if bbox2.height: 

2318 above += bbox2.height + pad_pixels 

2319 below = 0.0 

2320 if bbox.height: 

2321 below += bbox.height + pad_pixels 

2322 

2323 if self.get_label_position() == 'top': 

2324 above += self.label.get_window_extent(renderer).height + pad_pixels 

2325 else: 

2326 below += self.label.get_window_extent(renderer).height + pad_pixels 

2327 return above, below 

2328 

2329 def set_ticks_position(self, position): 

2330 """ 

2331 Set the ticks position. 

2332 

2333 Parameters 

2334 ---------- 

2335 position : {'top', 'bottom', 'both', 'default', 'none'} 

2336 'both' sets the ticks to appear on both positions, but does not 

2337 change the tick labels. 'default' resets the tick positions to 

2338 the default: ticks on both positions, labels at bottom. 'none' 

2339 can be used if you don't want any ticks. 'none' and 'both' 

2340 affect only the ticks, not the labels. 

2341 """ 

2342 _api.check_in_list(['top', 'bottom', 'both', 'default', 'none'], 

2343 position=position) 

2344 if position == 'top': 

2345 self.set_tick_params(which='both', top=True, labeltop=True, 

2346 bottom=False, labelbottom=False) 

2347 self._tick_position = 'top' 

2348 self.offsetText.set_verticalalignment('bottom') 

2349 elif position == 'bottom': 

2350 self.set_tick_params(which='both', top=False, labeltop=False, 

2351 bottom=True, labelbottom=True) 

2352 self._tick_position = 'bottom' 

2353 self.offsetText.set_verticalalignment('top') 

2354 elif position == 'both': 

2355 self.set_tick_params(which='both', top=True, 

2356 bottom=True) 

2357 elif position == 'none': 

2358 self.set_tick_params(which='both', top=False, 

2359 bottom=False) 

2360 elif position == 'default': 

2361 self.set_tick_params(which='both', top=True, labeltop=False, 

2362 bottom=True, labelbottom=True) 

2363 self._tick_position = 'bottom' 

2364 self.offsetText.set_verticalalignment('top') 

2365 else: 

2366 assert False, "unhandled parameter not caught by _check_in_list" 

2367 self.stale = True 

2368 

2369 def tick_top(self): 

2370 """ 

2371 Move ticks and ticklabels (if present) to the top of the Axes. 

2372 """ 

2373 label = True 

2374 if 'label1On' in self._major_tick_kw: 

2375 label = (self._major_tick_kw['label1On'] 

2376 or self._major_tick_kw['label2On']) 

2377 self.set_ticks_position('top') 

2378 # If labels were turned off before this was called, leave them off. 

2379 self.set_tick_params(which='both', labeltop=label) 

2380 

2381 def tick_bottom(self): 

2382 """ 

2383 Move ticks and ticklabels (if present) to the bottom of the Axes. 

2384 """ 

2385 label = True 

2386 if 'label1On' in self._major_tick_kw: 

2387 label = (self._major_tick_kw['label1On'] 

2388 or self._major_tick_kw['label2On']) 

2389 self.set_ticks_position('bottom') 

2390 # If labels were turned off before this was called, leave them off. 

2391 self.set_tick_params(which='both', labelbottom=label) 

2392 

2393 def get_ticks_position(self): 

2394 """ 

2395 Return the ticks position ("top", "bottom", "default", or "unknown"). 

2396 """ 

2397 return {1: "bottom", 2: "top", 

2398 "default": "default", "unknown": "unknown"}[ 

2399 self._get_ticks_position()] 

2400 

2401 get_view_interval, set_view_interval = _make_getset_interval( 

2402 "view", "viewLim", "intervalx") 

2403 get_data_interval, set_data_interval = _make_getset_interval( 

2404 "data", "dataLim", "intervalx") 

2405 

2406 def get_minpos(self): 

2407 return self.axes.dataLim.minposx 

2408 

2409 def set_default_intervals(self): 

2410 # docstring inherited 

2411 # only change view if dataLim has not changed and user has 

2412 # not changed the view: 

2413 if (not self.axes.dataLim.mutatedx() and 

2414 not self.axes.viewLim.mutatedx()): 

2415 if self.converter is not None: 

2416 info = self.converter.axisinfo(self.units, self) 

2417 if info.default_limits is not None: 

2418 xmin, xmax = self.convert_units(info.default_limits) 

2419 self.axes.viewLim.intervalx = xmin, xmax 

2420 self.stale = True 

2421 

2422 def get_tick_space(self): 

2423 ends = mtransforms.Bbox.unit().transformed( 

2424 self.axes.transAxes - self.figure.dpi_scale_trans) 

2425 length = ends.width * 72 

2426 # There is a heuristic here that the aspect ratio of tick text 

2427 # is no more than 3:1 

2428 size = self._get_tick_label_size('x') * 3 

2429 if size > 0: 

2430 return int(np.floor(length / size)) 

2431 else: 

2432 return 2**31 - 1 

2433 

2434 

2435class YAxis(Axis): 

2436 __name__ = 'yaxis' 

2437 axis_name = 'y' #: Read-only name identifying the axis. 

2438 _tick_class = YTick 

2439 

2440 def __init__(self, *args, **kwargs): 

2441 super().__init__(*args, **kwargs) 

2442 # x in display coords, y in axes coords (to be updated at draw time by 

2443 # _update_label_positions and _update_offset_text_position). 

2444 self.label.set( 

2445 x=0, y=0.5, 

2446 verticalalignment='bottom', horizontalalignment='center', 

2447 rotation='vertical', rotation_mode='anchor', 

2448 transform=mtransforms.blended_transform_factory( 

2449 mtransforms.IdentityTransform(), self.axes.transAxes), 

2450 ) 

2451 self.label_position = 'left' 

2452 # x in axes coords, y in display coords(!). 

2453 self.offsetText.set( 

2454 x=0, y=0.5, 

2455 verticalalignment='baseline', horizontalalignment='left', 

2456 transform=mtransforms.blended_transform_factory( 

2457 self.axes.transAxes, mtransforms.IdentityTransform()), 

2458 fontsize=mpl.rcParams['ytick.labelsize'], 

2459 color=mpl.rcParams['ytick.color'], 

2460 ) 

2461 self.offset_text_position = 'left' 

2462 

2463 def contains(self, mouseevent): 

2464 # docstring inherited 

2465 inside, info = self._default_contains(mouseevent) 

2466 if inside is not None: 

2467 return inside, info 

2468 

2469 x, y = mouseevent.x, mouseevent.y 

2470 try: 

2471 trans = self.axes.transAxes.inverted() 

2472 xaxes, yaxes = trans.transform((x, y)) 

2473 except ValueError: 

2474 return False, {} 

2475 (l, b), (r, t) = self.axes.transAxes.transform([(0, 0), (1, 1)]) 

2476 inaxis = 0 <= yaxes <= 1 and ( 

2477 l - self._pickradius < x < l or 

2478 r < x < r + self._pickradius) 

2479 return inaxis, {} 

2480 

2481 def set_label_position(self, position): 

2482 """ 

2483 Set the label position (left or right) 

2484 

2485 Parameters 

2486 ---------- 

2487 position : {'left', 'right'} 

2488 """ 

2489 self.label.set_rotation_mode('anchor') 

2490 self.label.set_verticalalignment(_api.check_getitem({ 

2491 'left': 'bottom', 'right': 'top', 

2492 }, position=position)) 

2493 self.label_position = position 

2494 self.stale = True 

2495 

2496 def _update_label_position(self, renderer): 

2497 """ 

2498 Update the label position based on the bounding box enclosing 

2499 all the ticklabels and axis spine 

2500 """ 

2501 if not self._autolabelpos: 

2502 return 

2503 

2504 # get bounding boxes for this axis and any siblings 

2505 # that have been set by `fig.align_ylabels()` 

2506 bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer) 

2507 x, y = self.label.get_position() 

2508 if self.label_position == 'left': 

2509 try: 

2510 spine = self.axes.spines['left'] 

2511 spinebbox = spine.get_window_extent() 

2512 except KeyError: 

2513 # use Axes if spine doesn't exist 

2514 spinebbox = self.axes.bbox 

2515 bbox = mtransforms.Bbox.union(bboxes + [spinebbox]) 

2516 left = bbox.x0 

2517 self.label.set_position( 

2518 (left - self.labelpad * self.figure.dpi / 72, y) 

2519 ) 

2520 

2521 else: 

2522 try: 

2523 spine = self.axes.spines['right'] 

2524 spinebbox = spine.get_window_extent() 

2525 except KeyError: 

2526 # use Axes if spine doesn't exist 

2527 spinebbox = self.axes.bbox 

2528 

2529 bbox = mtransforms.Bbox.union(bboxes2 + [spinebbox]) 

2530 right = bbox.x1 

2531 self.label.set_position( 

2532 (right + self.labelpad * self.figure.dpi / 72, y) 

2533 ) 

2534 

2535 def _update_offset_text_position(self, bboxes, bboxes2): 

2536 """ 

2537 Update the offset_text position based on the sequence of bounding 

2538 boxes of all the ticklabels 

2539 """ 

2540 x, _ = self.offsetText.get_position() 

2541 if 'outline' in self.axes.spines: 

2542 # Special case for colorbars: 

2543 bbox = self.axes.spines['outline'].get_window_extent() 

2544 else: 

2545 bbox = self.axes.bbox 

2546 top = bbox.ymax 

2547 self.offsetText.set_position( 

2548 (x, top + self.OFFSETTEXTPAD * self.figure.dpi / 72) 

2549 ) 

2550 

2551 def set_offset_position(self, position): 

2552 """ 

2553 Parameters 

2554 ---------- 

2555 position : {'left', 'right'} 

2556 """ 

2557 x, y = self.offsetText.get_position() 

2558 x = _api.check_getitem({'left': 0, 'right': 1}, position=position) 

2559 

2560 self.offsetText.set_ha(position) 

2561 self.offsetText.set_position((x, y)) 

2562 self.stale = True 

2563 

2564 @_api.deprecated("3.6") 

2565 def get_text_widths(self, renderer): 

2566 bbox, bbox2 = self.get_ticklabel_extents(renderer) 

2567 # MGDTODO: Need a better way to get the pad 

2568 pad_pixels = self.majorTicks[0].get_pad_pixels() 

2569 

2570 left = 0.0 

2571 if bbox.width: 

2572 left += bbox.width + pad_pixels 

2573 right = 0.0 

2574 if bbox2.width: 

2575 right += bbox2.width + pad_pixels 

2576 

2577 if self.get_label_position() == 'left': 

2578 left += self.label.get_window_extent(renderer).width + pad_pixels 

2579 else: 

2580 right += self.label.get_window_extent(renderer).width + pad_pixels 

2581 return left, right 

2582 

2583 def set_ticks_position(self, position): 

2584 """ 

2585 Set the ticks position. 

2586 

2587 Parameters 

2588 ---------- 

2589 position : {'left', 'right', 'both', 'default', 'none'} 

2590 'both' sets the ticks to appear on both positions, but does not 

2591 change the tick labels. 'default' resets the tick positions to 

2592 the default: ticks on both positions, labels at left. 'none' 

2593 can be used if you don't want any ticks. 'none' and 'both' 

2594 affect only the ticks, not the labels. 

2595 """ 

2596 _api.check_in_list(['left', 'right', 'both', 'default', 'none'], 

2597 position=position) 

2598 if position == 'right': 

2599 self.set_tick_params(which='both', right=True, labelright=True, 

2600 left=False, labelleft=False) 

2601 self.set_offset_position(position) 

2602 elif position == 'left': 

2603 self.set_tick_params(which='both', right=False, labelright=False, 

2604 left=True, labelleft=True) 

2605 self.set_offset_position(position) 

2606 elif position == 'both': 

2607 self.set_tick_params(which='both', right=True, 

2608 left=True) 

2609 elif position == 'none': 

2610 self.set_tick_params(which='both', right=False, 

2611 left=False) 

2612 elif position == 'default': 

2613 self.set_tick_params(which='both', right=True, labelright=False, 

2614 left=True, labelleft=True) 

2615 else: 

2616 assert False, "unhandled parameter not caught by _check_in_list" 

2617 self.stale = True 

2618 

2619 def tick_right(self): 

2620 """ 

2621 Move ticks and ticklabels (if present) to the right of the Axes. 

2622 """ 

2623 label = True 

2624 if 'label1On' in self._major_tick_kw: 

2625 label = (self._major_tick_kw['label1On'] 

2626 or self._major_tick_kw['label2On']) 

2627 self.set_ticks_position('right') 

2628 # if labels were turned off before this was called 

2629 # leave them off 

2630 self.set_tick_params(which='both', labelright=label) 

2631 

2632 def tick_left(self): 

2633 """ 

2634 Move ticks and ticklabels (if present) to the left of the Axes. 

2635 """ 

2636 label = True 

2637 if 'label1On' in self._major_tick_kw: 

2638 label = (self._major_tick_kw['label1On'] 

2639 or self._major_tick_kw['label2On']) 

2640 self.set_ticks_position('left') 

2641 # if labels were turned off before this was called 

2642 # leave them off 

2643 self.set_tick_params(which='both', labelleft=label) 

2644 

2645 def get_ticks_position(self): 

2646 """ 

2647 Return the ticks position ("left", "right", "default", or "unknown"). 

2648 """ 

2649 return {1: "left", 2: "right", 

2650 "default": "default", "unknown": "unknown"}[ 

2651 self._get_ticks_position()] 

2652 

2653 get_view_interval, set_view_interval = _make_getset_interval( 

2654 "view", "viewLim", "intervaly") 

2655 get_data_interval, set_data_interval = _make_getset_interval( 

2656 "data", "dataLim", "intervaly") 

2657 

2658 def get_minpos(self): 

2659 return self.axes.dataLim.minposy 

2660 

2661 def set_default_intervals(self): 

2662 # docstring inherited 

2663 # only change view if dataLim has not changed and user has 

2664 # not changed the view: 

2665 if (not self.axes.dataLim.mutatedy() and 

2666 not self.axes.viewLim.mutatedy()): 

2667 if self.converter is not None: 

2668 info = self.converter.axisinfo(self.units, self) 

2669 if info.default_limits is not None: 

2670 ymin, ymax = self.convert_units(info.default_limits) 

2671 self.axes.viewLim.intervaly = ymin, ymax 

2672 self.stale = True 

2673 

2674 def get_tick_space(self): 

2675 ends = mtransforms.Bbox.unit().transformed( 

2676 self.axes.transAxes - self.figure.dpi_scale_trans) 

2677 length = ends.height * 72 

2678 # Having a spacing of at least 2 just looks good. 

2679 size = self._get_tick_label_size('y') * 2 

2680 if size > 0: 

2681 return int(np.floor(length / size)) 

2682 else: 

2683 return 2**31 - 1