Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/matplotlib/axis.py: 20%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1236 statements  

1""" 

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

3""" 

4 

5import datetime 

6import functools 

7import logging 

8from numbers import Real 

9import warnings 

10 

11import numpy as np 

12 

13import matplotlib as mpl 

14from matplotlib import _api, cbook 

15import matplotlib.artist as martist 

16import matplotlib.colors as mcolors 

17import matplotlib.lines as mlines 

18import matplotlib.scale as mscale 

19import matplotlib.text as mtext 

20import matplotlib.ticker as mticker 

21import matplotlib.transforms as mtransforms 

22import matplotlib.units as munits 

23 

24_log = logging.getLogger(__name__) 

25 

26GRIDLINE_INTERPOLATION_STEPS = 180 

27 

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

29# allows all Line2D kwargs. 

30_line_inspector = martist.ArtistInspector(mlines.Line2D) 

31_line_param_names = _line_inspector.get_setters() 

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

33_gridline_param_names = ['grid_' + name 

34 for name in _line_param_names + _line_param_aliases] 

35 

36 

37class Tick(martist.Artist): 

38 """ 

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

40 

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

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

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

44 

45 Attributes 

46 ---------- 

47 tick1line : `~matplotlib.lines.Line2D` 

48 The left/bottom tick marker. 

49 tick2line : `~matplotlib.lines.Line2D` 

50 The right/top tick marker. 

51 gridline : `~matplotlib.lines.Line2D` 

52 The grid line associated with the label position. 

53 label1 : `~matplotlib.text.Text` 

54 The left/bottom tick label. 

55 label2 : `~matplotlib.text.Text` 

56 The right/top tick label. 

57 

58 """ 

59 def __init__( 

60 self, axes, loc, *, 

61 size=None, # points 

62 width=None, 

63 color=None, 

64 tickdir=None, 

65 pad=None, 

66 labelsize=None, 

67 labelcolor=None, 

68 labelfontfamily=None, 

69 zorder=None, 

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

71 tick1On=True, 

72 tick2On=True, 

73 label1On=True, 

74 label2On=False, 

75 major=True, 

76 labelrotation=0, 

77 grid_color=None, 

78 grid_linestyle=None, 

79 grid_linewidth=None, 

80 grid_alpha=None, 

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

82 ): 

83 """ 

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

85 loc is the tick location in data coords 

86 size is the tick size in points 

87 """ 

88 super().__init__() 

89 

90 if gridOn is None: 

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

92 if major and (which in ('both', 'major')): 

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

94 elif (not major) and (which in ('both', 'minor')): 

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

96 else: 

97 gridOn = False 

98 

99 self.set_figure(axes.figure) 

100 self.axes = axes 

101 

102 self._loc = loc 

103 self._major = major 

104 

105 name = self.__name__ 

106 major_minor = "major" if major else "minor" 

107 

108 if size is None: 

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

110 self._size = size 

111 

112 if width is None: 

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

114 self._width = width 

115 

116 if color is None: 

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

118 

119 if pad is None: 

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

121 self._base_pad = pad 

122 

123 if labelcolor is None: 

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

125 

126 if cbook._str_equal(labelcolor, 'inherit'): 

127 # inherit from tick color 

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

129 

130 if labelsize is None: 

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

132 

133 self._set_labelrotation(labelrotation) 

134 

135 if zorder is None: 

136 if major: 

137 zorder = mlines.Line2D.zorder + 0.01 

138 else: 

139 zorder = mlines.Line2D.zorder 

140 self._zorder = zorder 

141 

142 grid_color = mpl._val_or_rc(grid_color, "grid.color") 

143 grid_linestyle = mpl._val_or_rc(grid_linestyle, "grid.linestyle") 

144 grid_linewidth = mpl._val_or_rc(grid_linewidth, "grid.linewidth") 

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

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

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

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

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

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

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

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

153 

154 self.tick1line = mlines.Line2D( 

155 [], [], 

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

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

158 ) 

159 self.tick2line = mlines.Line2D( 

160 [], [], 

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

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

163 ) 

164 self.gridline = mlines.Line2D( 

165 [], [], 

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

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

168 **grid_kw, 

169 ) 

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

171 GRIDLINE_INTERPOLATION_STEPS 

172 self.label1 = mtext.Text( 

173 np.nan, np.nan, 

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

175 fontfamily=labelfontfamily, rotation=self._labelrotation[1]) 

176 self.label2 = mtext.Text( 

177 np.nan, np.nan, 

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

179 fontfamily=labelfontfamily, rotation=self._labelrotation[1]) 

180 

181 self._apply_tickdir(tickdir) 

182 

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

184 self.label1, self.label2]: 

185 self._set_artist_props(artist) 

186 

187 self.update_position(loc) 

188 

189 def _set_labelrotation(self, labelrotation): 

190 if isinstance(labelrotation, str): 

191 mode = labelrotation 

192 angle = 0 

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

194 mode, angle = labelrotation 

195 else: 

196 mode = 'default' 

197 angle = labelrotation 

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

199 self._labelrotation = (mode, angle) 

200 

201 @property 

202 def _pad(self): 

203 return self._base_pad + self.get_tick_padding() 

204 

205 def _apply_tickdir(self, tickdir): 

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

207 # This method is responsible for verifying input and, in subclasses, for setting 

208 # the tick{1,2}line markers. From the user perspective this should always be 

209 # called through _apply_params, which further updates ticklabel positions using 

210 # the new pads. 

211 if tickdir is None: 

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

213 else: 

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

215 self._tickdir = tickdir 

216 

217 def get_tickdir(self): 

218 return self._tickdir 

219 

220 def get_tick_padding(self): 

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

222 padding = { 

223 'in': 0.0, 

224 'inout': 0.5, 

225 'out': 1.0 

226 } 

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

228 

229 def get_children(self): 

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

231 self.gridline, self.label1, self.label2] 

232 return children 

233 

234 @_api.rename_parameter("3.8", "clippath", "path") 

235 def set_clip_path(self, path, transform=None): 

236 # docstring inherited 

237 super().set_clip_path(path, transform) 

238 self.gridline.set_clip_path(path, transform) 

239 self.stale = True 

240 

241 def contains(self, mouseevent): 

242 """ 

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

244 

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

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

247 """ 

248 return False, {} 

249 

250 def set_pad(self, val): 

251 """ 

252 Set the tick label pad in points 

253 

254 Parameters 

255 ---------- 

256 val : float 

257 """ 

258 self._apply_params(pad=val) 

259 self.stale = True 

260 

261 def get_pad(self): 

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

263 return self._base_pad 

264 

265 def get_loc(self): 

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

267 return self._loc 

268 

269 @martist.allow_rasterization 

270 def draw(self, renderer): 

271 if not self.get_visible(): 

272 self.stale = False 

273 return 

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

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

276 self.label1, self.label2]: 

277 artist.draw(renderer) 

278 renderer.close_group(self.__name__) 

279 self.stale = False 

280 

281 @_api.deprecated("3.8") 

282 def set_label1(self, s): 

283 """ 

284 Set the label1 text. 

285 

286 Parameters 

287 ---------- 

288 s : str 

289 """ 

290 self.label1.set_text(s) 

291 self.stale = True 

292 

293 set_label = set_label1 

294 

295 @_api.deprecated("3.8") 

296 def set_label2(self, s): 

297 """ 

298 Set the label2 text. 

299 

300 Parameters 

301 ---------- 

302 s : str 

303 """ 

304 self.label2.set_text(s) 

305 self.stale = True 

306 

307 def set_url(self, url): 

308 """ 

309 Set the url of label1 and label2. 

310 

311 Parameters 

312 ---------- 

313 url : str 

314 """ 

315 super().set_url(url) 

316 self.label1.set_url(url) 

317 self.label2.set_url(url) 

318 self.stale = True 

319 

320 def _set_artist_props(self, a): 

321 a.set_figure(self.figure) 

322 

323 def get_view_interval(self): 

324 """ 

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

326 """ 

327 raise NotImplementedError('Derived must override') 

328 

329 def _apply_params(self, **kwargs): 

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

331 ("tick1On", self.tick1line), 

332 ("tick2On", self.tick2line), 

333 ("label1On", self.label1), 

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

335 if name in kwargs: 

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

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

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

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

340 # convenient to leave it here. 

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

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

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

344 # sets the ticklines markers. 

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

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

347 line.set_markersize(self._size) 

348 line.set_markeredgewidth(self._width) 

349 # _get_text1_transform uses _pad from _apply_tickdir. 

350 trans = self._get_text1_transform()[0] 

351 self.label1.set_transform(trans) 

352 trans = self._get_text2_transform()[0] 

353 self.label2.set_transform(trans) 

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

355 if 'color' in kwargs: 

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

357 self.tick1line.set(**tick_kw) 

358 self.tick2line.set(**tick_kw) 

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

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

361 

362 if 'labelrotation' in kwargs: 

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

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

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

366 

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

368 if k in ['labelsize', 'labelcolor', 'labelfontfamily']} 

369 self.label1.set(**label_kw) 

370 self.label2.set(**label_kw) 

371 

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

373 if k in _gridline_param_names} 

374 self.gridline.set(**grid_kw) 

375 

376 def update_position(self, loc): 

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

378 raise NotImplementedError('Derived must override') 

379 

380 def _get_text1_transform(self): 

381 raise NotImplementedError('Derived must override') 

382 

383 def _get_text2_transform(self): 

384 raise NotImplementedError('Derived must override') 

385 

386 

387class XTick(Tick): 

388 """ 

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

390 the label text and the grid line 

391 """ 

392 __name__ = 'xtick' 

393 

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

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

396 # x in data coords, y in axes coords 

397 ax = self.axes 

398 self.tick1line.set( 

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

400 self.tick2line.set( 

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

402 self.gridline.set( 

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

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

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

406 self.label1.set( 

407 x=0, y=0, 

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

409 ) 

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

411 self.label2.set( 

412 x=0, y=1, 

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

414 ) 

415 

416 def _get_text1_transform(self): 

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

418 

419 def _get_text2_transform(self): 

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

421 

422 def _apply_tickdir(self, tickdir): 

423 # docstring inherited 

424 super()._apply_tickdir(tickdir) 

425 mark1, mark2 = { 

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

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

428 'inout': ('|', '|'), 

429 }[self._tickdir] 

430 self.tick1line.set_marker(mark1) 

431 self.tick2line.set_marker(mark2) 

432 

433 def update_position(self, loc): 

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

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

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

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

438 self.label1.set_x(loc) 

439 self.label2.set_x(loc) 

440 self._loc = loc 

441 self.stale = True 

442 

443 def get_view_interval(self): 

444 # docstring inherited 

445 return self.axes.viewLim.intervalx 

446 

447 

448class YTick(Tick): 

449 """ 

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

451 the label text and the grid line 

452 """ 

453 __name__ = 'ytick' 

454 

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

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

457 # x in axes coords, y in data coords 

458 ax = self.axes 

459 self.tick1line.set( 

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

461 self.tick2line.set( 

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

463 self.gridline.set( 

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

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

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

467 self.label1.set( 

468 x=0, y=0, 

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

470 ) 

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

472 self.label2.set( 

473 x=1, y=0, 

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

475 ) 

476 

477 def _get_text1_transform(self): 

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

479 

480 def _get_text2_transform(self): 

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

482 

483 def _apply_tickdir(self, tickdir): 

484 # docstring inherited 

485 super()._apply_tickdir(tickdir) 

486 mark1, mark2 = { 

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

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

489 'inout': ('_', '_'), 

490 }[self._tickdir] 

491 self.tick1line.set_marker(mark1) 

492 self.tick2line.set_marker(mark2) 

493 

494 def update_position(self, loc): 

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

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

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

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

499 self.label1.set_y(loc) 

500 self.label2.set_y(loc) 

501 self._loc = loc 

502 self.stale = True 

503 

504 def get_view_interval(self): 

505 # docstring inherited 

506 return self.axes.viewLim.intervaly 

507 

508 

509class Ticker: 

510 """ 

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

512 

513 Attributes 

514 ---------- 

515 locator : `~matplotlib.ticker.Locator` subclass 

516 Determines the positions of the ticks. 

517 formatter : `~matplotlib.ticker.Formatter` subclass 

518 Determines the format of the tick labels. 

519 """ 

520 

521 def __init__(self): 

522 self._locator = None 

523 self._formatter = None 

524 self._locator_is_default = True 

525 self._formatter_is_default = True 

526 

527 @property 

528 def locator(self): 

529 return self._locator 

530 

531 @locator.setter 

532 def locator(self, locator): 

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

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

535 'matplotlib.ticker.Locator') 

536 self._locator = locator 

537 

538 @property 

539 def formatter(self): 

540 return self._formatter 

541 

542 @formatter.setter 

543 def formatter(self, formatter): 

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

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

546 'matplotlib.ticker.Formatter') 

547 self._formatter = formatter 

548 

549 

550class _LazyTickList: 

551 """ 

552 A descriptor for lazy instantiation of tick lists. 

553 

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

555 attributes. 

556 """ 

557 

558 def __init__(self, major): 

559 self._major = major 

560 

561 def __get__(self, instance, owner): 

562 if instance is None: 

563 return self 

564 else: 

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

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

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

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

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

570 if self._major: 

571 instance.majorTicks = [] 

572 tick = instance._get_tick(major=True) 

573 instance.majorTicks.append(tick) 

574 return instance.majorTicks 

575 else: 

576 instance.minorTicks = [] 

577 tick = instance._get_tick(major=False) 

578 instance.minorTicks.append(tick) 

579 return instance.minorTicks 

580 

581 

582class Axis(martist.Artist): 

583 """ 

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

585 

586 Attributes 

587 ---------- 

588 isDefault_label : bool 

589 

590 axes : `~matplotlib.axes.Axes` 

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

592 major : `~matplotlib.axis.Ticker` 

593 Determines the major tick positions and their label format. 

594 minor : `~matplotlib.axis.Ticker` 

595 Determines the minor tick positions and their label format. 

596 callbacks : `~matplotlib.cbook.CallbackRegistry` 

597 

598 label : `~matplotlib.text.Text` 

599 The axis label. 

600 labelpad : float 

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

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

603 offsetText : `~matplotlib.text.Text` 

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

605 pickradius : float 

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

607 majorTicks : list of `.Tick` 

608 The major ticks. 

609 

610 .. warning:: 

611 

612 Ticks are not guaranteed to be persistent. Various operations 

613 can create, delete and modify the Tick instances. There is an 

614 imminent risk that changes to individual ticks will not 

615 survive if you work on the figure further (including also 

616 panning/zooming on a displayed figure). 

617 

618 Working on the individual ticks is a method of last resort. 

619 Use `.set_tick_params` instead if possible. 

620 

621 minorTicks : list of `.Tick` 

622 The minor ticks. 

623 """ 

624 OFFSETTEXTPAD = 3 

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

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

627 _tick_class = None 

628 

629 def __str__(self): 

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

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

632 

633 def __init__(self, axes, *, pickradius=15, clear=True): 

634 """ 

635 Parameters 

636 ---------- 

637 axes : `~matplotlib.axes.Axes` 

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

639 pickradius : float 

640 The acceptance radius for containment tests. See also 

641 `.Axis.contains`. 

642 clear : bool, default: True 

643 Whether to clear the Axis on creation. This is not required, e.g., when 

644 creating an Axis as part of an Axes, as ``Axes.clear`` will call 

645 ``Axis.clear``. 

646 .. versionadded:: 3.8 

647 """ 

648 super().__init__() 

649 self._remove_overlapping_locs = True 

650 

651 self.set_figure(axes.figure) 

652 

653 self.isDefault_label = True 

654 

655 self.axes = axes 

656 self.major = Ticker() 

657 self.minor = Ticker() 

658 self.callbacks = cbook.CallbackRegistry(signals=["units"]) 

659 

660 self._autolabelpos = True 

661 

662 self.label = mtext.Text( 

663 np.nan, np.nan, 

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

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

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

667 ) 

668 self._set_artist_props(self.label) 

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

670 self._set_artist_props(self.offsetText) 

671 

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

673 

674 self.pickradius = pickradius 

675 

676 # Initialize here for testing; later add API 

677 self._major_tick_kw = dict() 

678 self._minor_tick_kw = dict() 

679 

680 if clear: 

681 self.clear() 

682 else: 

683 self.converter = None 

684 self.units = None 

685 

686 self._autoscale_on = True 

687 

688 @property 

689 def isDefault_majloc(self): 

690 return self.major._locator_is_default 

691 

692 @isDefault_majloc.setter 

693 def isDefault_majloc(self, value): 

694 self.major._locator_is_default = value 

695 

696 @property 

697 def isDefault_majfmt(self): 

698 return self.major._formatter_is_default 

699 

700 @isDefault_majfmt.setter 

701 def isDefault_majfmt(self, value): 

702 self.major._formatter_is_default = value 

703 

704 @property 

705 def isDefault_minloc(self): 

706 return self.minor._locator_is_default 

707 

708 @isDefault_minloc.setter 

709 def isDefault_minloc(self, value): 

710 self.minor._locator_is_default = value 

711 

712 @property 

713 def isDefault_minfmt(self): 

714 return self.minor._formatter_is_default 

715 

716 @isDefault_minfmt.setter 

717 def isDefault_minfmt(self, value): 

718 self.minor._formatter_is_default = value 

719 

720 def _get_shared_axes(self): 

721 """Return Grouper of shared Axes for current axis.""" 

722 return self.axes._shared_axes[ 

723 self._get_axis_name()].get_siblings(self.axes) 

724 

725 def _get_shared_axis(self): 

726 """Return list of shared axis for current axis.""" 

727 name = self._get_axis_name() 

728 return [ax._axis_map[name] for ax in self._get_shared_axes()] 

729 

730 def _get_axis_name(self): 

731 """Return the axis name.""" 

732 return [name for name, axis in self.axes._axis_map.items() 

733 if axis is self][0] 

734 

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

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

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

738 majorTicks = _LazyTickList(major=True) 

739 minorTicks = _LazyTickList(major=False) 

740 

741 def get_remove_overlapping_locs(self): 

742 return self._remove_overlapping_locs 

743 

744 def set_remove_overlapping_locs(self, val): 

745 self._remove_overlapping_locs = bool(val) 

746 

747 remove_overlapping_locs = property( 

748 get_remove_overlapping_locs, set_remove_overlapping_locs, 

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

750 'ticker locations should be trimmed.')) 

751 

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

753 """ 

754 Set the coordinates of the label. 

755 

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

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

758 lead to poor alignment of multiple labels if there are multiple Axes. 

759 

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

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

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

763 """ 

764 self._autolabelpos = False 

765 if transform is None: 

766 transform = self.axes.transAxes 

767 

768 self.label.set_transform(transform) 

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

770 self.stale = True 

771 

772 def get_transform(self): 

773 """Return the transform used in the Axis' scale""" 

774 return self._scale.get_transform() 

775 

776 def get_scale(self): 

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

778 return self._scale.name 

779 

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

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

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

783 else: 

784 self._scale = value 

785 self._scale.set_default_locators_and_formatters(self) 

786 

787 self.isDefault_majloc = True 

788 self.isDefault_minloc = True 

789 self.isDefault_majfmt = True 

790 self.isDefault_minfmt = True 

791 

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

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

794 """ 

795 Set this Axis' scale. 

796 

797 Parameters 

798 ---------- 

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

800 The axis scale type to apply. 

801 

802 **kwargs 

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

804 See the respective class keyword arguments: 

805 

806 - `matplotlib.scale.LinearScale` 

807 - `matplotlib.scale.LogScale` 

808 - `matplotlib.scale.SymmetricalLogScale` 

809 - `matplotlib.scale.LogitScale` 

810 - `matplotlib.scale.FuncScale` 

811 - `matplotlib.scale.AsinhScale` 

812 

813 Notes 

814 ----- 

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

816 Additionally, custom scales may be registered using 

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

818 be used here. 

819 """ 

820 name = self._get_axis_name() 

821 old_default_lims = (self.get_major_locator() 

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

823 for ax in self._get_shared_axes(): 

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

825 ax._update_transScale() 

826 ax.stale = True 

827 new_default_lims = (self.get_major_locator() 

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

829 if old_default_lims != new_default_lims: 

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

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

832 self.axes.autoscale_view( 

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

834 

835 def limit_range_for_scale(self, vmin, vmax): 

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

837 

838 def _get_autoscale_on(self): 

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

840 return self._autoscale_on 

841 

842 def _set_autoscale_on(self, b): 

843 """ 

844 Set whether this Axis is autoscaled when drawing or by 

845 `.Axes.autoscale_view`. If b is None, then the value is not changed. 

846 

847 Parameters 

848 ---------- 

849 b : bool 

850 """ 

851 if b is not None: 

852 self._autoscale_on = b 

853 

854 def get_children(self): 

855 return [self.label, self.offsetText, 

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

857 

858 def _reset_major_tick_kw(self): 

859 self._major_tick_kw.clear() 

860 self._major_tick_kw['gridOn'] = ( 

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

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

863 

864 def _reset_minor_tick_kw(self): 

865 self._minor_tick_kw.clear() 

866 self._minor_tick_kw['gridOn'] = ( 

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

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

869 

870 def clear(self): 

871 """ 

872 Clear the axis. 

873 

874 This resets axis properties to their default values: 

875 

876 - the label 

877 - the scale 

878 - locators, formatters and ticks 

879 - major and minor grid 

880 - units 

881 - registered callbacks 

882 """ 

883 self.label._reset_visual_defaults() 

884 # The above resets the label formatting using text rcParams, 

885 # so we then update the formatting using axes rcParams 

886 self.label.set_color(mpl.rcParams['axes.labelcolor']) 

887 self.label.set_fontsize(mpl.rcParams['axes.labelsize']) 

888 self.label.set_fontweight(mpl.rcParams['axes.labelweight']) 

889 self.offsetText._reset_visual_defaults() 

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

891 

892 self._init() 

893 

894 self._set_scale('linear') 

895 

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

897 self.callbacks = cbook.CallbackRegistry(signals=["units"]) 

898 

899 # whether the grids are on 

900 self._major_tick_kw['gridOn'] = ( 

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

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

903 self._minor_tick_kw['gridOn'] = ( 

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

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

906 self.reset_ticks() 

907 

908 self.converter = None 

909 self.units = None 

910 self.stale = True 

911 

912 def reset_ticks(self): 

913 """ 

914 Re-initialize the major and minor Tick lists. 

915 

916 Each list starts with a single fresh Tick. 

917 """ 

918 # Restore the lazy tick lists. 

919 try: 

920 del self.majorTicks 

921 except AttributeError: 

922 pass 

923 try: 

924 del self.minorTicks 

925 except AttributeError: 

926 pass 

927 try: 

928 self.set_clip_path(self.axes.patch) 

929 except AttributeError: 

930 pass 

931 

932 def minorticks_on(self): 

933 """ 

934 Display default minor ticks on the Axis, depending on the scale 

935 (`~.axis.Axis.get_scale`). 

936 

937 Scales use specific minor locators: 

938 

939 - log: `~.LogLocator` 

940 - symlog: `~.SymmetricalLogLocator` 

941 - asinh: `~.AsinhLocator` 

942 - logit: `~.LogitLocator` 

943 - default: `~.AutoMinorLocator` 

944 

945 Displaying minor ticks may reduce performance; you may turn them off 

946 using `minorticks_off()` if drawing speed is a problem. 

947 """ 

948 scale = self.get_scale() 

949 if scale == 'log': 

950 s = self._scale 

951 self.set_minor_locator(mticker.LogLocator(s.base, s.subs)) 

952 elif scale == 'symlog': 

953 s = self._scale 

954 self.set_minor_locator( 

955 mticker.SymmetricalLogLocator(s._transform, s.subs)) 

956 elif scale == 'asinh': 

957 s = self._scale 

958 self.set_minor_locator( 

959 mticker.AsinhLocator(s.linear_width, base=s._base, 

960 subs=s._subs)) 

961 elif scale == 'logit': 

962 self.set_minor_locator(mticker.LogitLocator(minor=True)) 

963 else: 

964 self.set_minor_locator(mticker.AutoMinorLocator()) 

965 

966 def minorticks_off(self): 

967 """Remove minor ticks from the Axis.""" 

968 self.set_minor_locator(mticker.NullLocator()) 

969 

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

971 """ 

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

973 

974 For documentation of keyword arguments, see 

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

976 

977 See Also 

978 -------- 

979 .Axis.get_tick_params 

980 View the current style settings for ticks, ticklabels, and 

981 gridlines. 

982 """ 

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

984 kwtrans = self._translate_tick_params(kwargs) 

985 

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

987 # future new ticks will automatically get them 

988 if reset: 

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

990 self._reset_major_tick_kw() 

991 self._major_tick_kw.update(kwtrans) 

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

993 self._reset_minor_tick_kw() 

994 self._minor_tick_kw.update(kwtrans) 

995 self.reset_ticks() 

996 else: 

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

998 self._major_tick_kw.update(kwtrans) 

999 for tick in self.majorTicks: 

1000 tick._apply_params(**kwtrans) 

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

1002 self._minor_tick_kw.update(kwtrans) 

1003 for tick in self.minorTicks: 

1004 tick._apply_params(**kwtrans) 

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

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

1007 self.offsetText.set_visible( 

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

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

1010 if 'labelcolor' in kwtrans: 

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

1012 

1013 self.stale = True 

1014 

1015 def get_tick_params(self, which='major'): 

1016 """ 

1017 Get appearance parameters for ticks, ticklabels, and gridlines. 

1018 

1019 .. versionadded:: 3.7 

1020 

1021 Parameters 

1022 ---------- 

1023 which : {'major', 'minor'}, default: 'major' 

1024 The group of ticks for which the parameters are retrieved. 

1025 

1026 Returns 

1027 ------- 

1028 dict 

1029 Properties for styling tick elements added to the axis. 

1030 

1031 Notes 

1032 ----- 

1033 This method returns the appearance parameters for styling *new* 

1034 elements added to this axis and may be different from the values 

1035 on current elements if they were modified directly by the user 

1036 (e.g., via ``set_*`` methods on individual tick objects). 

1037 

1038 Examples 

1039 -------- 

1040 :: 

1041 

1042 >>> ax.yaxis.set_tick_params(labelsize=30, labelcolor='red', 

1043 ... direction='out', which='major') 

1044 >>> ax.yaxis.get_tick_params(which='major') 

1045 {'direction': 'out', 

1046 'left': True, 

1047 'right': False, 

1048 'labelleft': True, 

1049 'labelright': False, 

1050 'gridOn': False, 

1051 'labelsize': 30, 

1052 'labelcolor': 'red'} 

1053 >>> ax.yaxis.get_tick_params(which='minor') 

1054 {'left': True, 

1055 'right': False, 

1056 'labelleft': True, 

1057 'labelright': False, 

1058 'gridOn': False} 

1059 

1060 

1061 """ 

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

1063 if which == 'major': 

1064 return self._translate_tick_params( 

1065 self._major_tick_kw, reverse=True 

1066 ) 

1067 return self._translate_tick_params(self._minor_tick_kw, reverse=True) 

1068 

1069 @staticmethod 

1070 def _translate_tick_params(kw, reverse=False): 

1071 """ 

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

1073 supported by `.Tick._apply_params`. 

1074 

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

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

1077 are some other name translations. 

1078 

1079 Returns a new dict of translated kwargs. 

1080 

1081 Note: Use reverse=True to translate from those supported by 

1082 `.Tick._apply_params` back to those supported by 

1083 `.Axis.set_tick_params`. 

1084 """ 

1085 kw_ = {**kw} 

1086 

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

1088 allowed_keys = [ 

1089 'size', 'width', 'color', 'tickdir', 'pad', 

1090 'labelsize', 'labelcolor', 'labelfontfamily', 'zorder', 'gridOn', 

1091 'tick1On', 'tick2On', 'label1On', 'label2On', 

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

1093 'labelleft', 'labelbottom', 'labelright', 'labeltop', 

1094 'labelrotation', 

1095 *_gridline_param_names] 

1096 

1097 keymap = { 

1098 # tick_params key -> axis key 

1099 'length': 'size', 

1100 'direction': 'tickdir', 

1101 'rotation': 'labelrotation', 

1102 'left': 'tick1On', 

1103 'bottom': 'tick1On', 

1104 'right': 'tick2On', 

1105 'top': 'tick2On', 

1106 'labelleft': 'label1On', 

1107 'labelbottom': 'label1On', 

1108 'labelright': 'label2On', 

1109 'labeltop': 'label2On', 

1110 } 

1111 if reverse: 

1112 kwtrans = { 

1113 oldkey: kw_.pop(newkey) 

1114 for oldkey, newkey in keymap.items() if newkey in kw_ 

1115 } 

1116 else: 

1117 kwtrans = { 

1118 newkey: kw_.pop(oldkey) 

1119 for oldkey, newkey in keymap.items() if oldkey in kw_ 

1120 } 

1121 if 'colors' in kw_: 

1122 c = kw_.pop('colors') 

1123 kwtrans['color'] = c 

1124 kwtrans['labelcolor'] = c 

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

1126 for key in kw_: 

1127 if key not in allowed_keys: 

1128 raise ValueError( 

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

1130 % (key, allowed_keys)) 

1131 kwtrans.update(kw_) 

1132 return kwtrans 

1133 

1134 @_api.rename_parameter("3.8", "clippath", "path") 

1135 def set_clip_path(self, path, transform=None): 

1136 super().set_clip_path(path, transform) 

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

1138 child.set_clip_path(path, transform) 

1139 self.stale = True 

1140 

1141 def get_view_interval(self): 

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

1143 raise NotImplementedError('Derived must override') 

1144 

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

1146 """ 

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

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

1149 

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

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

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

1153 the orientation of the axis will not change. 

1154 

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

1156 vmax)`` in that order. 

1157 """ 

1158 raise NotImplementedError('Derived must override') 

1159 

1160 def get_data_interval(self): 

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

1162 raise NotImplementedError('Derived must override') 

1163 

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

1165 """ 

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

1167 

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

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

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

1171 the orientation of the axis will not change. 

1172 

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

1174 vmax)`` in that order. 

1175 """ 

1176 raise NotImplementedError('Derived must override') 

1177 

1178 def get_inverted(self): 

1179 """ 

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

1181 

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

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

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

1185 """ 

1186 low, high = self.get_view_interval() 

1187 return high < low 

1188 

1189 def set_inverted(self, inverted): 

1190 """ 

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

1192 

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

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

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

1196 """ 

1197 a, b = self.get_view_interval() 

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

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

1200 

1201 def set_default_intervals(self): 

1202 """ 

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

1204 have not been not mutated yet. 

1205 """ 

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

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

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

1209 # data and view limits. The unit conversion AxisInfo 

1210 # interface provides a hook for custom types to register 

1211 # default limits through the AxisInfo.default_limits 

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

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

1214 

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

1216 """ 

1217 Set view limits. 

1218 

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

1220 ``set_zlim`` methods. 

1221 

1222 Parameters 

1223 ---------- 

1224 v0, v1 : float 

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

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

1227 emit : bool, default: True 

1228 Whether to notify observers of limit change. 

1229 auto : bool or None, default: False 

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

1231 turns off, None leaves unchanged. 

1232 """ 

1233 name = self._get_axis_name() 

1234 

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

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

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

1238 

1239 if v0 is None or v1 is None: 

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

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

1242 old0, old1 = self.get_view_interval() 

1243 if v0 is None: 

1244 v0 = old0 

1245 if v1 is None: 

1246 v1 = old1 

1247 

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

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

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

1251 old0, old1 = self.get_view_interval() 

1252 if v0 <= 0: 

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

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

1255 v0 = old0 

1256 if v1 <= 0: 

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

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

1259 v1 = old1 

1260 if v0 == v1: 

1261 _api.warn_external( 

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

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

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

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

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

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

1268 

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

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

1271 for ax in self._get_shared_axes(): 

1272 ax._stale_viewlims[name] = False 

1273 self._set_autoscale_on(auto) 

1274 

1275 if emit: 

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

1277 # Call all of the other Axes that are shared with this one 

1278 for other in self._get_shared_axes(): 

1279 if other is self.axes: 

1280 continue 

1281 other._axis_map[name]._set_lim(v0, v1, emit=False, auto=auto) 

1282 if emit: 

1283 other.callbacks.process(f"{name}lim_changed", other) 

1284 if other.figure != self.figure: 

1285 other.figure.canvas.draw_idle() 

1286 

1287 self.stale = True 

1288 return v0, v1 

1289 

1290 def _set_artist_props(self, a): 

1291 if a is None: 

1292 return 

1293 a.set_figure(self.figure) 

1294 

1295 def _update_ticks(self): 

1296 """ 

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

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

1299 """ 

1300 major_locs = self.get_majorticklocs() 

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

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

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

1304 tick.update_position(loc) 

1305 tick.label1.set_text(label) 

1306 tick.label2.set_text(label) 

1307 minor_locs = self.get_minorticklocs() 

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

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

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

1311 tick.update_position(loc) 

1312 tick.label1.set_text(label) 

1313 tick.label2.set_text(label) 

1314 ticks = [*major_ticks, *minor_ticks] 

1315 

1316 view_low, view_high = self.get_view_interval() 

1317 if view_low > view_high: 

1318 view_low, view_high = view_high, view_low 

1319 

1320 if (hasattr(self, "axes") and self.axes.name == '3d' 

1321 and mpl.rcParams['axes3d.automargin']): 

1322 # In mpl3.8, the margin was 1/48. Due to the change in automargin 

1323 # behavior in mpl3.9, we need to adjust this to compensate for a 

1324 # zoom factor of 2/48, giving us a 23/24 modifier. So the new 

1325 # margin is 0.019965277777777776 = 1/48*23/24. 

1326 margin = 0.019965277777777776 

1327 delta = view_high - view_low 

1328 view_high = view_high - delta * margin 

1329 view_low = view_low + delta * margin 

1330 

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

1332 

1333 ticks_to_draw = [] 

1334 for tick in ticks: 

1335 try: 

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

1337 except AssertionError: 

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

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

1340 pass 

1341 else: 

1342 if mtransforms._interval_contains_close(interval_t, loc_t): 

1343 ticks_to_draw.append(tick) 

1344 

1345 return ticks_to_draw 

1346 

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

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

1349 if renderer is None: 

1350 renderer = self.figure._get_renderer() 

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

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

1353 [tick.label2.get_window_extent(renderer) 

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

1355 

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

1357 """ 

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

1359 tick labels, axis label, and offsetText. 

1360 

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

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

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

1364 too-long labels when doing their layout. 

1365 """ 

1366 if not self.get_visible(): 

1367 return 

1368 if renderer is None: 

1369 renderer = self.figure._get_renderer() 

1370 ticks_to_draw = self._update_ticks() 

1371 

1372 self._update_label_position(renderer) 

1373 

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

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

1376 

1377 self._update_offset_text_position(tlb1, tlb2) 

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

1379 

1380 bboxes = [ 

1381 *(a.get_window_extent(renderer) 

1382 for a in [self.offsetText] 

1383 if a.get_visible()), 

1384 *tlb1, *tlb2, 

1385 ] 

1386 # take care of label 

1387 if self.label.get_visible(): 

1388 bb = self.label.get_window_extent(renderer) 

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

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

1391 # this code collapses the relevant direction 

1392 if for_layout_only: 

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

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

1395 bb.x1 = bb.x0 + 1.0 

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

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

1398 bb.y1 = bb.y0 + 1.0 

1399 bboxes.append(bb) 

1400 bboxes = [b for b in bboxes 

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

1402 if bboxes: 

1403 return mtransforms.Bbox.union(bboxes) 

1404 else: 

1405 return None 

1406 

1407 def get_tick_padding(self): 

1408 values = [] 

1409 if len(self.majorTicks): 

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

1411 if len(self.minorTicks): 

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

1413 return max(values, default=0) 

1414 

1415 @martist.allow_rasterization 

1416 def draw(self, renderer): 

1417 # docstring inherited 

1418 

1419 if not self.get_visible(): 

1420 return 

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

1422 

1423 ticks_to_draw = self._update_ticks() 

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

1425 

1426 for tick in ticks_to_draw: 

1427 tick.draw(renderer) 

1428 

1429 # Shift label away from axes to avoid overlapping ticklabels. 

1430 self._update_label_position(renderer) 

1431 self.label.draw(renderer) 

1432 

1433 self._update_offset_text_position(tlb1, tlb2) 

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

1435 self.offsetText.draw(renderer) 

1436 

1437 renderer.close_group(__name__) 

1438 self.stale = False 

1439 

1440 def get_gridlines(self): 

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

1442 ticks = self.get_major_ticks() 

1443 return cbook.silent_list('Line2D gridline', 

1444 [tick.gridline for tick in ticks]) 

1445 

1446 def get_label(self): 

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

1448 return self.label 

1449 

1450 def get_offset_text(self): 

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

1452 return self.offsetText 

1453 

1454 def get_pickradius(self): 

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

1456 return self._pickradius 

1457 

1458 def get_majorticklabels(self): 

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

1460 self._update_ticks() 

1461 ticks = self.get_major_ticks() 

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

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

1464 return labels1 + labels2 

1465 

1466 def get_minorticklabels(self): 

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

1468 self._update_ticks() 

1469 ticks = self.get_minor_ticks() 

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

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

1472 return labels1 + labels2 

1473 

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

1475 """ 

1476 Get this Axis' tick labels. 

1477 

1478 Parameters 

1479 ---------- 

1480 minor : bool 

1481 Whether to return the minor or the major ticklabels. 

1482 

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

1484 Overrides *minor*. 

1485 

1486 Selects which ticklabels to return 

1487 

1488 Returns 

1489 ------- 

1490 list of `~matplotlib.text.Text` 

1491 """ 

1492 if which is not None: 

1493 if which == 'minor': 

1494 return self.get_minorticklabels() 

1495 elif which == 'major': 

1496 return self.get_majorticklabels() 

1497 elif which == 'both': 

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

1499 else: 

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

1501 if minor: 

1502 return self.get_minorticklabels() 

1503 return self.get_majorticklabels() 

1504 

1505 def get_majorticklines(self): 

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

1507 lines = [] 

1508 ticks = self.get_major_ticks() 

1509 for tick in ticks: 

1510 lines.append(tick.tick1line) 

1511 lines.append(tick.tick2line) 

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

1513 

1514 def get_minorticklines(self): 

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

1516 lines = [] 

1517 ticks = self.get_minor_ticks() 

1518 for tick in ticks: 

1519 lines.append(tick.tick1line) 

1520 lines.append(tick.tick2line) 

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

1522 

1523 def get_ticklines(self, minor=False): 

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

1525 if minor: 

1526 return self.get_minorticklines() 

1527 return self.get_majorticklines() 

1528 

1529 def get_majorticklocs(self): 

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

1531 return self.major.locator() 

1532 

1533 def get_minorticklocs(self): 

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

1535 # Remove minor ticks duplicating major ticks. 

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

1537 if self.remove_overlapping_locs: 

1538 major_locs = self.major.locator() 

1539 transform = self._scale.get_transform() 

1540 tr_minor_locs = transform.transform(minor_locs) 

1541 tr_major_locs = transform.transform(major_locs) 

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

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

1544 # rtol for np.isclose. 

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

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

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

1548 minor_locs = minor_locs[~mask] 

1549 return minor_locs 

1550 

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

1552 """ 

1553 Return this Axis' tick locations in data coordinates. 

1554 

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

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

1557 

1558 Parameters 

1559 ---------- 

1560 minor : bool, default: False 

1561 True to return the minor tick directions, 

1562 False to return the major tick directions. 

1563 

1564 Returns 

1565 ------- 

1566 array of tick locations 

1567 """ 

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

1569 

1570 def get_ticks_direction(self, minor=False): 

1571 """ 

1572 Return an array of this Axis' tick directions. 

1573 

1574 Parameters 

1575 ---------- 

1576 minor : bool, default: False 

1577 True to return the minor tick directions, 

1578 False to return the major tick directions. 

1579 

1580 Returns 

1581 ------- 

1582 array of tick directions 

1583 """ 

1584 if minor: 

1585 return np.array( 

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

1587 else: 

1588 return np.array( 

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

1590 

1591 def _get_tick(self, major): 

1592 """Return the default tick instance.""" 

1593 if self._tick_class is None: 

1594 raise NotImplementedError( 

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

1596 "_tick_class or reimplement _get_tick()") 

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

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

1599 

1600 def _get_tick_label_size(self, axis_name): 

1601 """ 

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

1603 

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

1605 `.get_tick_space`, since it is expensive. 

1606 """ 

1607 tick_kw = self._major_tick_kw 

1608 size = tick_kw.get('labelsize', 

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

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

1611 

1612 def _copy_tick_props(self, src, dest): 

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

1614 if src is None or dest is None: 

1615 return 

1616 dest.label1.update_from(src.label1) 

1617 dest.label2.update_from(src.label2) 

1618 dest.tick1line.update_from(src.tick1line) 

1619 dest.tick2line.update_from(src.tick2line) 

1620 dest.gridline.update_from(src.gridline) 

1621 dest.update_from(src) 

1622 dest._loc = src._loc 

1623 dest._size = src._size 

1624 dest._width = src._width 

1625 dest._base_pad = src._base_pad 

1626 dest._labelrotation = src._labelrotation 

1627 dest._zorder = src._zorder 

1628 dest._tickdir = src._tickdir 

1629 

1630 def get_label_text(self): 

1631 """Get the text of the label.""" 

1632 return self.label.get_text() 

1633 

1634 def get_major_locator(self): 

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

1636 return self.major.locator 

1637 

1638 def get_minor_locator(self): 

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

1640 return self.minor.locator 

1641 

1642 def get_major_formatter(self): 

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

1644 return self.major.formatter 

1645 

1646 def get_minor_formatter(self): 

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

1648 return self.minor.formatter 

1649 

1650 def get_major_ticks(self, numticks=None): 

1651 r""" 

1652 Return the list of major `.Tick`\s. 

1653 

1654 .. warning:: 

1655 

1656 Ticks are not guaranteed to be persistent. Various operations 

1657 can create, delete and modify the Tick instances. There is an 

1658 imminent risk that changes to individual ticks will not 

1659 survive if you work on the figure further (including also 

1660 panning/zooming on a displayed figure). 

1661 

1662 Working on the individual ticks is a method of last resort. 

1663 Use `.set_tick_params` instead if possible. 

1664 """ 

1665 if numticks is None: 

1666 numticks = len(self.get_majorticklocs()) 

1667 

1668 while len(self.majorTicks) < numticks: 

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

1670 tick = self._get_tick(major=True) 

1671 self.majorTicks.append(tick) 

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

1673 

1674 return self.majorTicks[:numticks] 

1675 

1676 def get_minor_ticks(self, numticks=None): 

1677 r""" 

1678 Return the list of minor `.Tick`\s. 

1679 

1680 .. warning:: 

1681 

1682 Ticks are not guaranteed to be persistent. Various operations 

1683 can create, delete and modify the Tick instances. There is an 

1684 imminent risk that changes to individual ticks will not 

1685 survive if you work on the figure further (including also 

1686 panning/zooming on a displayed figure). 

1687 

1688 Working on the individual ticks is a method of last resort. 

1689 Use `.set_tick_params` instead if possible. 

1690 """ 

1691 if numticks is None: 

1692 numticks = len(self.get_minorticklocs()) 

1693 

1694 while len(self.minorTicks) < numticks: 

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

1696 tick = self._get_tick(major=False) 

1697 self.minorTicks.append(tick) 

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

1699 

1700 return self.minorTicks[:numticks] 

1701 

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

1703 """ 

1704 Configure the grid lines. 

1705 

1706 Parameters 

1707 ---------- 

1708 visible : bool or None 

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

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

1711 

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

1713 visibility of the lines. 

1714 

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

1716 The grid lines to apply the changes on. 

1717 

1718 **kwargs : `~matplotlib.lines.Line2D` properties 

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

1720 

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

1722 """ 

1723 if kwargs: 

1724 if visible is None: 

1725 visible = True 

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

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

1728 'but line properties are supplied. The ' 

1729 'grid will be enabled.') 

1730 visible = True 

1731 which = which.lower() 

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

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

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

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

1736 if visible is None else visible) 

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

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

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

1740 if visible is None else visible) 

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

1742 self.stale = True 

1743 

1744 def update_units(self, data): 

1745 """ 

1746 Introspect *data* for units converter and update the 

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

1748 if *data* is registered for unit conversion. 

1749 """ 

1750 converter = munits.registry.get_converter(data) 

1751 if converter is None: 

1752 return False 

1753 

1754 neednew = self.converter != converter 

1755 self.converter = converter 

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

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

1758 self.set_units(default) 

1759 

1760 elif neednew: 

1761 self._update_axisinfo() 

1762 self.stale = True 

1763 return True 

1764 

1765 def _update_axisinfo(self): 

1766 """ 

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

1768 axis info needs to be updated. 

1769 """ 

1770 if self.converter is None: 

1771 return 

1772 

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

1774 

1775 if info is None: 

1776 return 

1777 if info.majloc is not None and \ 

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

1779 self.set_major_locator(info.majloc) 

1780 self.isDefault_majloc = True 

1781 if info.minloc is not None and \ 

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

1783 self.set_minor_locator(info.minloc) 

1784 self.isDefault_minloc = True 

1785 if info.majfmt is not None and \ 

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

1787 self.set_major_formatter(info.majfmt) 

1788 self.isDefault_majfmt = True 

1789 if info.minfmt is not None and \ 

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

1791 self.set_minor_formatter(info.minfmt) 

1792 self.isDefault_minfmt = True 

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

1794 self.set_label_text(info.label) 

1795 self.isDefault_label = True 

1796 

1797 self.set_default_intervals() 

1798 

1799 def have_units(self): 

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

1801 

1802 def convert_units(self, x): 

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

1804 if munits._is_natively_supported(x): 

1805 return x 

1806 

1807 if self.converter is None: 

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

1809 

1810 if self.converter is None: 

1811 return x 

1812 try: 

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

1814 except Exception as e: 

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

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

1817 return ret 

1818 

1819 def set_units(self, u): 

1820 """ 

1821 Set the units for axis. 

1822 

1823 Parameters 

1824 ---------- 

1825 u : units tag 

1826 

1827 Notes 

1828 ----- 

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

1830 """ 

1831 if u == self.units: 

1832 return 

1833 for axis in self._get_shared_axis(): 

1834 axis.units = u 

1835 axis._update_axisinfo() 

1836 axis.callbacks.process('units') 

1837 axis.stale = True 

1838 

1839 def get_units(self): 

1840 """Return the units for axis.""" 

1841 return self.units 

1842 

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

1844 """ 

1845 Set the text value of the axis label. 

1846 

1847 Parameters 

1848 ---------- 

1849 label : str 

1850 Text string. 

1851 fontdict : dict 

1852 Text properties. 

1853 

1854 .. admonition:: Discouraged 

1855 

1856 The use of *fontdict* is discouraged. Parameters should be passed as 

1857 individual keyword arguments or using dictionary-unpacking 

1858 ``set_label_text(..., **fontdict)``. 

1859 

1860 **kwargs 

1861 Merged into fontdict. 

1862 """ 

1863 self.isDefault_label = False 

1864 self.label.set_text(label) 

1865 if fontdict is not None: 

1866 self.label.update(fontdict) 

1867 self.label.update(kwargs) 

1868 self.stale = True 

1869 return self.label 

1870 

1871 def set_major_formatter(self, formatter): 

1872 """ 

1873 Set the formatter of the major ticker. 

1874 

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

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

1877 

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

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

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

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

1882 more information. 

1883 

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

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

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

1887 tick label. 

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

1889 more information. 

1890 

1891 Parameters 

1892 ---------- 

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

1894 """ 

1895 self._set_formatter(formatter, self.major) 

1896 

1897 def set_minor_formatter(self, formatter): 

1898 """ 

1899 Set the formatter of the minor ticker. 

1900 

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

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

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

1904 

1905 Parameters 

1906 ---------- 

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

1908 """ 

1909 self._set_formatter(formatter, self.minor) 

1910 

1911 def _set_formatter(self, formatter, level): 

1912 if isinstance(formatter, str): 

1913 formatter = mticker.StrMethodFormatter(formatter) 

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

1915 # like using a Locator instead of a Formatter. 

1916 elif (callable(formatter) and 

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

1918 formatter = mticker.FuncFormatter(formatter) 

1919 else: 

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

1921 

1922 if (isinstance(formatter, mticker.FixedFormatter) 

1923 and len(formatter.seq) > 0 

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

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

1926 'with FixedLocator') 

1927 

1928 if level == self.major: 

1929 self.isDefault_majfmt = False 

1930 else: 

1931 self.isDefault_minfmt = False 

1932 

1933 level.formatter = formatter 

1934 formatter.set_axis(self) 

1935 self.stale = True 

1936 

1937 def set_major_locator(self, locator): 

1938 """ 

1939 Set the locator of the major ticker. 

1940 

1941 Parameters 

1942 ---------- 

1943 locator : `~matplotlib.ticker.Locator` 

1944 """ 

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

1946 self.isDefault_majloc = False 

1947 self.major.locator = locator 

1948 if self.major.formatter: 

1949 self.major.formatter._set_locator(locator) 

1950 locator.set_axis(self) 

1951 self.stale = True 

1952 

1953 def set_minor_locator(self, locator): 

1954 """ 

1955 Set the locator of the minor ticker. 

1956 

1957 Parameters 

1958 ---------- 

1959 locator : `~matplotlib.ticker.Locator` 

1960 """ 

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

1962 self.isDefault_minloc = False 

1963 self.minor.locator = locator 

1964 if self.minor.formatter: 

1965 self.minor.formatter._set_locator(locator) 

1966 locator.set_axis(self) 

1967 self.stale = True 

1968 

1969 def set_pickradius(self, pickradius): 

1970 """ 

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

1972 

1973 Parameters 

1974 ---------- 

1975 pickradius : float 

1976 The acceptance radius for containment tests. 

1977 See also `.Axis.contains`. 

1978 """ 

1979 if not isinstance(pickradius, Real) or pickradius < 0: 

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

1981 self._pickradius = pickradius 

1982 

1983 pickradius = property( 

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

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

1986 

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

1988 @staticmethod 

1989 def _format_with_dict(tickd, x, pos): 

1990 return tickd.get(x, "") 

1991 

1992 def set_ticklabels(self, labels, *, minor=False, fontdict=None, **kwargs): 

1993 r""" 

1994 [*Discouraged*] Set this Axis' tick labels with list of string labels. 

1995 

1996 .. admonition:: Discouraged 

1997 

1998 The use of this method is discouraged, because of the dependency on 

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

2000 ``Axes.set_[x/y/z]ticks(positions, labels)`` or ``Axis.set_ticks`` 

2001 instead. 

2002 

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

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

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

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

2007 

2008 Parameters 

2009 ---------- 

2010 labels : sequence of str or of `.Text`\s 

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

2012 `.Axis.set_ticks`; the number of labels must match the number of locations. 

2013 The labels are used as is, via a `.FixedFormatter` (without further 

2014 formatting). 

2015 

2016 minor : bool 

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

2018 

2019 fontdict : dict, optional 

2020 

2021 .. admonition:: Discouraged 

2022 

2023 The use of *fontdict* is discouraged. Parameters should be passed as 

2024 individual keyword arguments or using dictionary-unpacking 

2025 ``set_ticklabels(..., **fontdict)``. 

2026 

2027 A dictionary controlling the appearance of the ticklabels. 

2028 The default *fontdict* is:: 

2029 

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

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

2032 'verticalalignment': 'baseline', 

2033 'horizontalalignment': loc} 

2034 

2035 **kwargs 

2036 Text properties. 

2037 

2038 .. warning:: 

2039 

2040 This only sets the properties of the current ticks, which is 

2041 only sufficient for static plots. 

2042 

2043 Ticks are not guaranteed to be persistent. Various operations 

2044 can create, delete and modify the Tick instances. There is an 

2045 imminent risk that these settings can get lost if you work on 

2046 the figure further (including also panning/zooming on a 

2047 displayed figure). 

2048 

2049 Use `.set_tick_params` instead if possible. 

2050 

2051 Returns 

2052 ------- 

2053 list of `.Text`\s 

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

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

2056 """ 

2057 try: 

2058 labels = [t.get_text() if hasattr(t, 'get_text') else t 

2059 for t in labels] 

2060 except TypeError: 

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

2062 locator = (self.get_minor_locator() if minor 

2063 else self.get_major_locator()) 

2064 if not labels: 

2065 # eg labels=[]: 

2066 formatter = mticker.NullFormatter() 

2067 elif isinstance(locator, mticker.FixedLocator): 

2068 # Passing [] as a list of labels is often used as a way to 

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

2070 if len(locator.locs) != len(labels) and len(labels) != 0: 

2071 raise ValueError( 

2072 "The number of FixedLocator locations" 

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

2074 " set_ticks, does not match" 

2075 f" the number of labels ({len(labels)}).") 

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

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

2078 formatter = mticker.FuncFormatter(func) 

2079 else: 

2080 _api.warn_external( 

2081 "set_ticklabels() should only be used with a fixed number of " 

2082 "ticks, i.e. after set_ticks() or using a FixedLocator.") 

2083 formatter = mticker.FixedFormatter(labels) 

2084 

2085 with warnings.catch_warnings(): 

2086 warnings.filterwarnings( 

2087 "ignore", 

2088 message="FixedFormatter should only be used together with FixedLocator") 

2089 if minor: 

2090 self.set_minor_formatter(formatter) 

2091 locs = self.get_minorticklocs() 

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

2093 else: 

2094 self.set_major_formatter(formatter) 

2095 locs = self.get_majorticklocs() 

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

2097 

2098 ret = [] 

2099 if fontdict is not None: 

2100 kwargs.update(fontdict) 

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

2102 tick.update_position(loc) 

2103 tick_label = formatter(loc, pos) 

2104 # deal with label1 

2105 tick.label1.set_text(tick_label) 

2106 tick.label1._internal_update(kwargs) 

2107 # deal with label2 

2108 tick.label2.set_text(tick_label) 

2109 tick.label2._internal_update(kwargs) 

2110 # only return visible tick labels 

2111 if tick.label1.get_visible(): 

2112 ret.append(tick.label1) 

2113 if tick.label2.get_visible(): 

2114 ret.append(tick.label2) 

2115 

2116 self.stale = True 

2117 return ret 

2118 

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

2120 # see docstring of set_ticks 

2121 

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

2123 ticks = self.convert_units(ticks) 

2124 locator = mticker.FixedLocator(ticks) # validate ticks early. 

2125 if len(ticks): 

2126 for axis in self._get_shared_axis(): 

2127 # set_view_interval maintains any preexisting inversion. 

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

2129 self.axes.stale = True 

2130 if minor: 

2131 self.set_minor_locator(locator) 

2132 return self.get_minor_ticks(len(ticks)) 

2133 else: 

2134 self.set_major_locator(locator) 

2135 return self.get_major_ticks(len(ticks)) 

2136 

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

2138 """ 

2139 Set this Axis' tick locations and optionally tick labels. 

2140 

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

2142 given ticks are visible. 

2143 

2144 Parameters 

2145 ---------- 

2146 ticks : 1D array-like 

2147 Array of tick locations (either floats or in axis units). The axis 

2148 `.Locator` is replaced by a `~.ticker.FixedLocator`. 

2149 

2150 Pass an empty list (``set_ticks([])``) to remove all ticks. 

2151 

2152 Some tick formatters will not label arbitrary tick positions; 

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

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

2155 using `.Axis.set_major_formatter` or provide formatted 

2156 *labels* yourself. 

2157 

2158 labels : list of str, optional 

2159 Tick labels for each location in *ticks*; must have the same length as 

2160 *ticks*. If set, the labels are used as is, via a `.FixedFormatter`. 

2161 If not set, the labels are generated using the axis tick `.Formatter`. 

2162 

2163 minor : bool, default: False 

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

2165 

2166 **kwargs 

2167 `.Text` properties for the labels. Using these is only allowed if 

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

2169 

2170 Notes 

2171 ----- 

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

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

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

2175 ticks. 

2176 """ 

2177 if labels is None and kwargs: 

2178 first_key = next(iter(kwargs)) 

2179 raise ValueError( 

2180 f"Incorrect use of keyword argument {first_key!r}. Keyword arguments " 

2181 "other than 'minor' modify the text labels and can only be used if " 

2182 "'labels' are passed as well.") 

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

2184 if labels is not None: 

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

2186 return result 

2187 

2188 def _get_tick_boxes_siblings(self, renderer): 

2189 """ 

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

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

2192 

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

2194 """ 

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

2196 name = self._get_axis_name() 

2197 if name not in self.figure._align_label_groups: 

2198 return [], [] 

2199 grouper = self.figure._align_label_groups[name] 

2200 bboxes = [] 

2201 bboxes2 = [] 

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

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

2204 axis = ax._axis_map[name] 

2205 ticks_to_draw = axis._update_ticks() 

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

2207 bboxes.extend(tlb) 

2208 bboxes2.extend(tlb2) 

2209 return bboxes, bboxes2 

2210 

2211 def _update_label_position(self, renderer): 

2212 """ 

2213 Update the label position based on the bounding box enclosing 

2214 all the ticklabels and axis spine. 

2215 """ 

2216 raise NotImplementedError('Derived must override') 

2217 

2218 def _update_offset_text_position(self, bboxes, bboxes2): 

2219 """ 

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

2221 boxes of all the ticklabels. 

2222 """ 

2223 raise NotImplementedError('Derived must override') 

2224 

2225 def axis_date(self, tz=None): 

2226 """ 

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

2228 

2229 Parameters 

2230 ---------- 

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

2232 The timezone used to create date labels. 

2233 """ 

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

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

2236 # which is the timezone, can be set. 

2237 if isinstance(tz, str): 

2238 import dateutil.tz 

2239 tz = dateutil.tz.gettz(tz) 

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

2241 

2242 def get_tick_space(self): 

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

2244 # Must be overridden in the subclass 

2245 raise NotImplementedError() 

2246 

2247 def _get_ticks_position(self): 

2248 """ 

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

2250 

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

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

2253 

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

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

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

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

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

2259 - "unknown" otherwise. 

2260 """ 

2261 major = self.majorTicks[0] 

2262 minor = self.minorTicks[0] 

2263 if all(tick.tick1line.get_visible() 

2264 and not tick.tick2line.get_visible() 

2265 and tick.label1.get_visible() 

2266 and not tick.label2.get_visible() 

2267 for tick in [major, minor]): 

2268 return 1 

2269 elif all(tick.tick2line.get_visible() 

2270 and not tick.tick1line.get_visible() 

2271 and tick.label2.get_visible() 

2272 and not tick.label1.get_visible() 

2273 for tick in [major, minor]): 

2274 return 2 

2275 elif all(tick.tick1line.get_visible() 

2276 and tick.tick2line.get_visible() 

2277 and tick.label1.get_visible() 

2278 and not tick.label2.get_visible() 

2279 for tick in [major, minor]): 

2280 return "default" 

2281 else: 

2282 return "unknown" 

2283 

2284 def get_label_position(self): 

2285 """ 

2286 Return the label position (top or bottom) 

2287 """ 

2288 return self.label_position 

2289 

2290 def set_label_position(self, position): 

2291 """ 

2292 Set the label position (top or bottom) 

2293 

2294 Parameters 

2295 ---------- 

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

2297 """ 

2298 raise NotImplementedError() 

2299 

2300 def get_minpos(self): 

2301 raise NotImplementedError() 

2302 

2303 

2304def _make_getset_interval(method_name, lim_name, attr_name): 

2305 """ 

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

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

2308 """ 

2309 

2310 def getter(self): 

2311 # docstring inherited. 

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

2313 

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

2315 # docstring inherited. 

2316 if ignore: 

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

2318 else: 

2319 oldmin, oldmax = getter(self) 

2320 if oldmin < oldmax: 

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

2322 ignore=True) 

2323 else: 

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

2325 ignore=True) 

2326 self.stale = True 

2327 

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

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

2330 

2331 return getter, setter 

2332 

2333 

2334class XAxis(Axis): 

2335 __name__ = 'xaxis' 

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

2337 _tick_class = XTick 

2338 

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

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

2341 self._init() 

2342 

2343 def _init(self): 

2344 """ 

2345 Initialize the label and offsetText instance values and 

2346 `label_position` / `offset_text_position`. 

2347 """ 

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

2349 # _update_label_positions and _update_offset_text_position). 

2350 self.label.set( 

2351 x=0.5, y=0, 

2352 verticalalignment='top', horizontalalignment='center', 

2353 transform=mtransforms.blended_transform_factory( 

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

2355 ) 

2356 self.label_position = 'bottom' 

2357 

2358 if mpl.rcParams['xtick.labelcolor'] == 'inherit': 

2359 tick_color = mpl.rcParams['xtick.color'] 

2360 else: 

2361 tick_color = mpl.rcParams['xtick.labelcolor'] 

2362 

2363 self.offsetText.set( 

2364 x=1, y=0, 

2365 verticalalignment='top', horizontalalignment='right', 

2366 transform=mtransforms.blended_transform_factory( 

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

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

2369 color=tick_color 

2370 ) 

2371 self.offset_text_position = 'bottom' 

2372 

2373 def contains(self, mouseevent): 

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

2375 if self._different_canvas(mouseevent): 

2376 return False, {} 

2377 x, y = mouseevent.x, mouseevent.y 

2378 try: 

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

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

2381 except ValueError: 

2382 return False, {} 

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

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

2385 b - self._pickradius < y < b or 

2386 t < y < t + self._pickradius) 

2387 return inaxis, {} 

2388 

2389 def set_label_position(self, position): 

2390 """ 

2391 Set the label position (top or bottom) 

2392 

2393 Parameters 

2394 ---------- 

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

2396 """ 

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

2398 'top': 'baseline', 'bottom': 'top', 

2399 }, position=position)) 

2400 self.label_position = position 

2401 self.stale = True 

2402 

2403 def _update_label_position(self, renderer): 

2404 """ 

2405 Update the label position based on the bounding box enclosing 

2406 all the ticklabels and axis spine 

2407 """ 

2408 if not self._autolabelpos: 

2409 return 

2410 

2411 # get bounding boxes for this axis and any siblings 

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

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

2414 

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

2416 if self.label_position == 'bottom': 

2417 try: 

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

2419 spinebbox = spine.get_window_extent() 

2420 except KeyError: 

2421 # use Axes if spine doesn't exist 

2422 spinebbox = self.axes.bbox 

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

2424 bottom = bbox.y0 

2425 

2426 self.label.set_position( 

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

2428 ) 

2429 else: 

2430 try: 

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

2432 spinebbox = spine.get_window_extent() 

2433 except KeyError: 

2434 # use Axes if spine doesn't exist 

2435 spinebbox = self.axes.bbox 

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

2437 top = bbox.y1 

2438 

2439 self.label.set_position( 

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

2441 ) 

2442 

2443 def _update_offset_text_position(self, bboxes, bboxes2): 

2444 """ 

2445 Update the offset_text position based on the sequence of bounding 

2446 boxes of all the ticklabels 

2447 """ 

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

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

2450 self._tick_position = 'bottom' 

2451 if self._tick_position == 'bottom': 

2452 if not len(bboxes): 

2453 bottom = self.axes.bbox.ymin 

2454 else: 

2455 bbox = mtransforms.Bbox.union(bboxes) 

2456 bottom = bbox.y0 

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

2458 else: 

2459 if not len(bboxes2): 

2460 top = self.axes.bbox.ymax 

2461 else: 

2462 bbox = mtransforms.Bbox.union(bboxes2) 

2463 top = bbox.y1 

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

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

2466 

2467 def set_ticks_position(self, position): 

2468 """ 

2469 Set the ticks position. 

2470 

2471 Parameters 

2472 ---------- 

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

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

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

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

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

2478 affect only the ticks, not the labels. 

2479 """ 

2480 if position == 'top': 

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

2482 bottom=False, labelbottom=False) 

2483 self._tick_position = 'top' 

2484 self.offsetText.set_verticalalignment('bottom') 

2485 elif position == 'bottom': 

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

2487 bottom=True, labelbottom=True) 

2488 self._tick_position = 'bottom' 

2489 self.offsetText.set_verticalalignment('top') 

2490 elif position == 'both': 

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

2492 bottom=True) 

2493 elif position == 'none': 

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

2495 bottom=False) 

2496 elif position == 'default': 

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

2498 bottom=True, labelbottom=True) 

2499 self._tick_position = 'bottom' 

2500 self.offsetText.set_verticalalignment('top') 

2501 else: 

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

2503 position=position) 

2504 self.stale = True 

2505 

2506 def tick_top(self): 

2507 """ 

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

2509 """ 

2510 label = True 

2511 if 'label1On' in self._major_tick_kw: 

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

2513 or self._major_tick_kw['label2On']) 

2514 self.set_ticks_position('top') 

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

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

2517 

2518 def tick_bottom(self): 

2519 """ 

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

2521 """ 

2522 label = True 

2523 if 'label1On' in self._major_tick_kw: 

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

2525 or self._major_tick_kw['label2On']) 

2526 self.set_ticks_position('bottom') 

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

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

2529 

2530 def get_ticks_position(self): 

2531 """ 

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

2533 """ 

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

2535 "default": "default", "unknown": "unknown"}[ 

2536 self._get_ticks_position()] 

2537 

2538 get_view_interval, set_view_interval = _make_getset_interval( 

2539 "view", "viewLim", "intervalx") 

2540 get_data_interval, set_data_interval = _make_getset_interval( 

2541 "data", "dataLim", "intervalx") 

2542 

2543 def get_minpos(self): 

2544 return self.axes.dataLim.minposx 

2545 

2546 def set_default_intervals(self): 

2547 # docstring inherited 

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

2549 # not changed the view: 

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

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

2552 if self.converter is not None: 

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

2554 if info.default_limits is not None: 

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

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

2557 self.stale = True 

2558 

2559 def get_tick_space(self): 

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

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

2562 length = ends.width * 72 

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

2564 # is no more than 3:1 

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

2566 if size > 0: 

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

2568 else: 

2569 return 2**31 - 1 

2570 

2571 

2572class YAxis(Axis): 

2573 __name__ = 'yaxis' 

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

2575 _tick_class = YTick 

2576 

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

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

2579 self._init() 

2580 

2581 def _init(self): 

2582 """ 

2583 Initialize the label and offsetText instance values and 

2584 `label_position` / `offset_text_position`. 

2585 """ 

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

2587 # _update_label_positions and _update_offset_text_position). 

2588 self.label.set( 

2589 x=0, y=0.5, 

2590 verticalalignment='bottom', horizontalalignment='center', 

2591 rotation='vertical', rotation_mode='anchor', 

2592 transform=mtransforms.blended_transform_factory( 

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

2594 ) 

2595 self.label_position = 'left' 

2596 

2597 if mpl.rcParams['ytick.labelcolor'] == 'inherit': 

2598 tick_color = mpl.rcParams['ytick.color'] 

2599 else: 

2600 tick_color = mpl.rcParams['ytick.labelcolor'] 

2601 

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

2603 self.offsetText.set( 

2604 x=0, y=0.5, 

2605 verticalalignment='baseline', horizontalalignment='left', 

2606 transform=mtransforms.blended_transform_factory( 

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

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

2609 color=tick_color 

2610 ) 

2611 self.offset_text_position = 'left' 

2612 

2613 def contains(self, mouseevent): 

2614 # docstring inherited 

2615 if self._different_canvas(mouseevent): 

2616 return False, {} 

2617 x, y = mouseevent.x, mouseevent.y 

2618 try: 

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

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

2621 except ValueError: 

2622 return False, {} 

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

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

2625 l - self._pickradius < x < l or 

2626 r < x < r + self._pickradius) 

2627 return inaxis, {} 

2628 

2629 def set_label_position(self, position): 

2630 """ 

2631 Set the label position (left or right) 

2632 

2633 Parameters 

2634 ---------- 

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

2636 """ 

2637 self.label.set_rotation_mode('anchor') 

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

2639 'left': 'bottom', 'right': 'top', 

2640 }, position=position)) 

2641 self.label_position = position 

2642 self.stale = True 

2643 

2644 def _update_label_position(self, renderer): 

2645 """ 

2646 Update the label position based on the bounding box enclosing 

2647 all the ticklabels and axis spine 

2648 """ 

2649 if not self._autolabelpos: 

2650 return 

2651 

2652 # get bounding boxes for this axis and any siblings 

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

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

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

2656 if self.label_position == 'left': 

2657 try: 

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

2659 spinebbox = spine.get_window_extent() 

2660 except KeyError: 

2661 # use Axes if spine doesn't exist 

2662 spinebbox = self.axes.bbox 

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

2664 left = bbox.x0 

2665 self.label.set_position( 

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

2667 ) 

2668 

2669 else: 

2670 try: 

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

2672 spinebbox = spine.get_window_extent() 

2673 except KeyError: 

2674 # use Axes if spine doesn't exist 

2675 spinebbox = self.axes.bbox 

2676 

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

2678 right = bbox.x1 

2679 self.label.set_position( 

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

2681 ) 

2682 

2683 def _update_offset_text_position(self, bboxes, bboxes2): 

2684 """ 

2685 Update the offset_text position based on the sequence of bounding 

2686 boxes of all the ticklabels 

2687 """ 

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

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

2690 # Special case for colorbars: 

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

2692 else: 

2693 bbox = self.axes.bbox 

2694 top = bbox.ymax 

2695 self.offsetText.set_position( 

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

2697 ) 

2698 

2699 def set_offset_position(self, position): 

2700 """ 

2701 Parameters 

2702 ---------- 

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

2704 """ 

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

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

2707 

2708 self.offsetText.set_ha(position) 

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

2710 self.stale = True 

2711 

2712 def set_ticks_position(self, position): 

2713 """ 

2714 Set the ticks position. 

2715 

2716 Parameters 

2717 ---------- 

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

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

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

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

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

2723 affect only the ticks, not the labels. 

2724 """ 

2725 if position == 'right': 

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

2727 left=False, labelleft=False) 

2728 self.set_offset_position(position) 

2729 elif position == 'left': 

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

2731 left=True, labelleft=True) 

2732 self.set_offset_position(position) 

2733 elif position == 'both': 

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

2735 left=True) 

2736 elif position == 'none': 

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

2738 left=False) 

2739 elif position == 'default': 

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

2741 left=True, labelleft=True) 

2742 else: 

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

2744 position=position) 

2745 self.stale = True 

2746 

2747 def tick_right(self): 

2748 """ 

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

2750 """ 

2751 label = True 

2752 if 'label1On' in self._major_tick_kw: 

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

2754 or self._major_tick_kw['label2On']) 

2755 self.set_ticks_position('right') 

2756 # if labels were turned off before this was called 

2757 # leave them off 

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

2759 

2760 def tick_left(self): 

2761 """ 

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

2763 """ 

2764 label = True 

2765 if 'label1On' in self._major_tick_kw: 

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

2767 or self._major_tick_kw['label2On']) 

2768 self.set_ticks_position('left') 

2769 # if labels were turned off before this was called 

2770 # leave them off 

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

2772 

2773 def get_ticks_position(self): 

2774 """ 

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

2776 """ 

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

2778 "default": "default", "unknown": "unknown"}[ 

2779 self._get_ticks_position()] 

2780 

2781 get_view_interval, set_view_interval = _make_getset_interval( 

2782 "view", "viewLim", "intervaly") 

2783 get_data_interval, set_data_interval = _make_getset_interval( 

2784 "data", "dataLim", "intervaly") 

2785 

2786 def get_minpos(self): 

2787 return self.axes.dataLim.minposy 

2788 

2789 def set_default_intervals(self): 

2790 # docstring inherited 

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

2792 # not changed the view: 

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

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

2795 if self.converter is not None: 

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

2797 if info.default_limits is not None: 

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

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

2800 self.stale = True 

2801 

2802 def get_tick_space(self): 

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

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

2805 length = ends.height * 72 

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

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

2808 if size > 0: 

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

2810 else: 

2811 return 2**31 - 1