Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2Classes for the ticks and x and y axis. 

3""" 

4 

5import datetime 

6import logging 

7 

8import numpy as np 

9 

10from matplotlib import rcParams 

11import matplotlib.artist as martist 

12import matplotlib.cbook as cbook 

13import matplotlib.font_manager as font_manager 

14import matplotlib.lines as mlines 

15import matplotlib.scale as mscale 

16import matplotlib.text as mtext 

17import matplotlib.ticker as mticker 

18import matplotlib.transforms as mtransforms 

19import matplotlib.units as munits 

20 

21_log = logging.getLogger(__name__) 

22 

23GRIDLINE_INTERPOLATION_STEPS = 180 

24 

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

26# allows all Line2D kwargs. 

27_line_AI = martist.ArtistInspector(mlines.Line2D) 

28_line_param_names = _line_AI.get_setters() 

29_line_param_aliases = [list(d)[0] for d in _line_AI.aliasd.values()] 

30_gridline_param_names = ['grid_' + name 

31 for name in _line_param_names + _line_param_aliases] 

32 

33 

34class Tick(martist.Artist): 

35 """ 

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

37 

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

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

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

41 

42 Attributes 

43 ---------- 

44 tick1line : `.Line2D` 

45 The left/bottom tick marker. 

46 tick2line : `.Line2D` 

47 The right/top tick marker. 

48 gridline : `.Line2D` 

49 The grid line associated with the label position. 

50 label1 : `.Text` 

51 The left/bottom tick label. 

52 label2 : `.Text` 

53 The right/top tick label. 

54 

55 """ 

56 def __init__(self, axes, loc, label, 

57 size=None, # points 

58 width=None, 

59 color=None, 

60 tickdir=None, 

61 pad=None, 

62 labelsize=None, 

63 labelcolor=None, 

64 zorder=None, 

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

66 # axes.grid.which 

67 tick1On=True, 

68 tick2On=True, 

69 label1On=True, 

70 label2On=False, 

71 major=True, 

72 labelrotation=0, 

73 grid_color=None, 

74 grid_linestyle=None, 

75 grid_linewidth=None, 

76 grid_alpha=None, 

77 **kw # Other Line2D kwargs applied to gridlines. 

78 ): 

79 """ 

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

81 loc is the tick location in data coords 

82 size is the tick size in points 

83 """ 

84 martist.Artist.__init__(self) 

85 

86 if gridOn is None: 

87 if major and (rcParams['axes.grid.which'] in ('both', 'major')): 

88 gridOn = rcParams['axes.grid'] 

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

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

91 gridOn = rcParams['axes.grid'] 

92 else: 

93 gridOn = False 

94 

95 self.set_figure(axes.figure) 

96 self.axes = axes 

97 

98 name = self.__name__.lower() 

99 

100 self._loc = loc 

101 

102 if size is None: 

103 if major: 

104 size = rcParams['%s.major.size' % name] 

105 else: 

106 size = rcParams['%s.minor.size' % name] 

107 self._size = size 

108 

109 if width is None: 

110 if major: 

111 width = rcParams['%s.major.width' % name] 

112 else: 

113 width = rcParams['%s.minor.width' % name] 

114 self._width = width 

115 

116 if color is None: 

117 color = rcParams['%s.color' % name] 

118 self._color = color 

119 

120 if pad is None: 

121 if major: 

122 pad = rcParams['%s.major.pad' % name] 

123 else: 

124 pad = rcParams['%s.minor.pad' % name] 

125 self._base_pad = pad 

126 

127 if labelcolor is None: 

128 labelcolor = rcParams['%s.color' % name] 

129 self._labelcolor = labelcolor 

130 

131 if labelsize is None: 

132 labelsize = rcParams['%s.labelsize' % name] 

133 self._labelsize = labelsize 

134 

135 self._set_labelrotation(labelrotation) 

136 

137 if zorder is None: 

138 if major: 

139 zorder = mlines.Line2D.zorder + 0.01 

140 else: 

141 zorder = mlines.Line2D.zorder 

142 self._zorder = zorder 

143 

144 self._grid_color = (rcParams['grid.color'] 

145 if grid_color is None else grid_color) 

146 self._grid_linestyle = (rcParams['grid.linestyle'] 

147 if grid_linestyle is None else grid_linestyle) 

148 self._grid_linewidth = (rcParams['grid.linewidth'] 

149 if grid_linewidth is None else grid_linewidth) 

150 self._grid_alpha = (rcParams['grid.alpha'] 

151 if grid_alpha is None else grid_alpha) 

152 

153 self._grid_kw = {k[5:]: v for k, v in kw.items()} 

154 

155 self.apply_tickdir(tickdir) 

156 

157 self.tick1line = self._get_tick1line() 

158 self.tick2line = self._get_tick2line() 

159 self.gridline = self._get_gridline() 

160 self.label1 = self._get_text1() 

161 self.label2 = self._get_text2() 

162 

163 self.gridline.set_visible(gridOn) 

164 self.tick1line.set_visible(tick1On) 

165 self.tick2line.set_visible(tick2On) 

166 self.label1.set_visible(label1On) 

167 self.label2.set_visible(label2On) 

168 

169 self.update_position(loc) 

170 

171 for _old_name, _new_name in [ 

172 ("gridOn", "gridline"), 

173 ("tick1On", "tick1line"), 

174 ("tick2On", "tick2line"), 

175 ("label1On", "label1"), 

176 ("label2On", "label2")]: 

177 locals()[_old_name] = property( 

178 cbook.deprecated( 

179 "3.1", 

180 name=_old_name, 

181 alternative="Tick.{}.get_visible".format(_new_name))( 

182 lambda self, _new_name=_new_name: 

183 getattr(self, _new_name).get_visible()), 

184 cbook.deprecated( 

185 "3.1", 

186 name=_old_name, 

187 alternative="Tick.{}.set_visible".format(_new_name))( 

188 lambda self, value, _new_name=_new_name: 

189 getattr(self, _new_name).set_visible(value))) 

190 del _old_name, _new_name 

191 

192 @property 

193 @cbook.deprecated("3.1", alternative="Tick.label1", pending=True) 

194 def label(self): 

195 return self.label1 

196 

197 def _set_labelrotation(self, labelrotation): 

198 if isinstance(labelrotation, str): 

199 mode = labelrotation 

200 angle = 0 

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

202 mode, angle = labelrotation 

203 else: 

204 mode = 'default' 

205 angle = labelrotation 

206 cbook._check_in_list(['auto', 'default'], labelrotation=mode) 

207 self._labelrotation = (mode, angle) 

208 

209 def apply_tickdir(self, tickdir): 

210 """Calculate self._pad and self._tickmarkers.""" 

211 

212 def get_tickdir(self): 

213 return self._tickdir 

214 

215 def get_tick_padding(self): 

216 """Get the length of the tick outside of the axes.""" 

217 padding = { 

218 'in': 0.0, 

219 'inout': 0.5, 

220 'out': 1.0 

221 } 

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

223 

224 def get_children(self): 

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

226 self.gridline, self.label1, self.label2] 

227 return children 

228 

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

230 # docstring inherited 

231 martist.Artist.set_clip_path(self, clippath, transform) 

232 self.gridline.set_clip_path(clippath, transform) 

233 self.stale = True 

234 

235 def get_pad_pixels(self): 

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

237 

238 def contains(self, mouseevent): 

239 """ 

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

241 

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

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

244 """ 

245 inside, info = self._default_contains(mouseevent) 

246 if inside is not None: 

247 return inside, info 

248 return False, {} 

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_text1(self): 

266 'Get the default Text 1 instance' 

267 pass 

268 

269 def _get_text2(self): 

270 'Get the default Text 2 instance' 

271 pass 

272 

273 def _get_tick1line(self): 

274 'Get the default line2D instance for tick1' 

275 pass 

276 

277 def _get_tick2line(self): 

278 'Get the default line2D instance for tick2' 

279 pass 

280 

281 def _get_gridline(self): 

282 'Get the default grid Line2d instance for this tick' 

283 pass 

284 

285 def get_loc(self): 

286 'Return the tick location (data coords) as a scalar' 

287 return self._loc 

288 

289 @martist.allow_rasterization 

290 def draw(self, renderer): 

291 if not self.get_visible(): 

292 self.stale = False 

293 return 

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

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

296 self.label1, self.label2]: 

297 artist.draw(renderer) 

298 renderer.close_group(self.__name__) 

299 self.stale = False 

300 

301 def set_label1(self, s): 

302 """ 

303 Set the label1 text. 

304 

305 Parameters 

306 ---------- 

307 s : str 

308 """ 

309 self.label1.set_text(s) 

310 self.stale = True 

311 

312 set_label = set_label1 

313 

314 def set_label2(self, s): 

315 """ 

316 Set the label2 text. 

317 

318 Parameters 

319 ---------- 

320 s : str 

321 """ 

322 self.label2.set_text(s) 

323 self.stale = True 

324 

325 def _set_artist_props(self, a): 

326 a.set_figure(self.figure) 

327 

328 def get_view_interval(self): 

329 """ 

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

331 """ 

332 raise NotImplementedError('Derived must override') 

333 

334 def _apply_params(self, **kw): 

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

336 ("tick1On", self.tick1line), 

337 ("tick2On", self.tick2line), 

338 ("label1On", self.label1), 

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

340 if name in kw: 

341 target.set_visible(kw.pop(name)) 

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

343 self._size = kw.pop('size', self._size) 

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

345 # convenient to leave it here. 

346 self._width = kw.pop('width', self._width) 

347 self._base_pad = kw.pop('pad', self._base_pad) 

348 # apply_tickdir uses _size and _base_pad to make _pad, 

349 # and also makes _tickmarkers. 

350 self.apply_tickdir(kw.pop('tickdir', self._tickdir)) 

351 self.tick1line.set_marker(self._tickmarkers[0]) 

352 self.tick2line.set_marker(self._tickmarkers[1]) 

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

354 line.set_markersize(self._size) 

355 line.set_markeredgewidth(self._width) 

356 # _get_text1_transform uses _pad from apply_tickdir. 

357 trans = self._get_text1_transform()[0] 

358 self.label1.set_transform(trans) 

359 trans = self._get_text2_transform()[0] 

360 self.label2.set_transform(trans) 

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

362 self.tick1line.set(**tick_kw) 

363 self.tick2line.set(**tick_kw) 

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

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

366 

367 if 'labelrotation' in kw: 

368 self._set_labelrotation(kw.pop('labelrotation')) 

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

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

371 

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

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

374 self.label1.set(**label_kw) 

375 self.label2.set(**label_kw) 

376 for k, v in label_kw.items(): 

377 # for labelsize the text objects covert str ('small') 

378 # -> points. grab the integer from the `Text` object 

379 # instead of saving the string representation 

380 v = getattr(self.label1, 'get_' + k)() 

381 setattr(self, '_label' + k, v) 

382 

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

384 if k in _gridline_param_names} 

385 self.gridline.set(**grid_kw) 

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

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

388 

389 def update_position(self, loc): 

390 'Set the location of tick in data coords with scalar *loc*' 

391 raise NotImplementedError('Derived must override') 

392 

393 def _get_text1_transform(self): 

394 raise NotImplementedError('Derived must override') 

395 

396 def _get_text2_transform(self): 

397 raise NotImplementedError('Derived must override') 

398 

399 

400class XTick(Tick): 

401 """ 

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

403 the label text and the grid line 

404 """ 

405 __name__ = 'xtick' 

406 

407 def _get_text1_transform(self): 

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

409 

410 def _get_text2_transform(self): 

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

412 

413 def apply_tickdir(self, tickdir): 

414 if tickdir is None: 

415 tickdir = rcParams['%s.direction' % self.__name__.lower()] 

416 self._tickdir = tickdir 

417 

418 if self._tickdir == 'in': 

419 self._tickmarkers = (mlines.TICKUP, mlines.TICKDOWN) 

420 elif self._tickdir == 'inout': 

421 self._tickmarkers = ('|', '|') 

422 else: 

423 self._tickmarkers = (mlines.TICKDOWN, mlines.TICKUP) 

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

425 self.stale = True 

426 

427 def _get_text1(self): 

428 'Get the default Text instance' 

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

430 # get the affine as an a, b, c, d, tx, ty list 

431 # x in data coords, y in axes coords 

432 trans, vert, horiz = self._get_text1_transform() 

433 t = mtext.Text( 

434 x=0, y=0, 

435 fontproperties=font_manager.FontProperties(size=self._labelsize), 

436 color=self._labelcolor, 

437 verticalalignment=vert, 

438 horizontalalignment=horiz, 

439 ) 

440 t.set_transform(trans) 

441 self._set_artist_props(t) 

442 return t 

443 

444 def _get_text2(self): 

445 'Get the default Text 2 instance' 

446 # x in data coords, y in axes coords 

447 trans, vert, horiz = self._get_text2_transform() 

448 t = mtext.Text( 

449 x=0, y=1, 

450 fontproperties=font_manager.FontProperties(size=self._labelsize), 

451 color=self._labelcolor, 

452 verticalalignment=vert, 

453 horizontalalignment=horiz, 

454 ) 

455 t.set_transform(trans) 

456 self._set_artist_props(t) 

457 return t 

458 

459 def _get_tick1line(self): 

460 'Get the default line2D instance' 

461 # x in data coords, y in axes coords 

462 l = mlines.Line2D(xdata=(0,), ydata=(0,), color=self._color, 

463 linestyle='None', marker=self._tickmarkers[0], 

464 markersize=self._size, 

465 markeredgewidth=self._width, zorder=self._zorder) 

466 l.set_transform(self.axes.get_xaxis_transform(which='tick1')) 

467 self._set_artist_props(l) 

468 return l 

469 

470 def _get_tick2line(self): 

471 'Get the default line2D instance' 

472 # x in data coords, y in axes coords 

473 l = mlines.Line2D(xdata=(0,), ydata=(1,), 

474 color=self._color, 

475 linestyle='None', 

476 marker=self._tickmarkers[1], 

477 markersize=self._size, 

478 markeredgewidth=self._width, 

479 zorder=self._zorder) 

480 

481 l.set_transform(self.axes.get_xaxis_transform(which='tick2')) 

482 self._set_artist_props(l) 

483 return l 

484 

485 def _get_gridline(self): 

486 'Get the default line2D instance' 

487 # x in data coords, y in axes coords 

488 l = mlines.Line2D(xdata=(0.0, 0.0), ydata=(0, 1.0), 

489 color=self._grid_color, 

490 linestyle=self._grid_linestyle, 

491 linewidth=self._grid_linewidth, 

492 alpha=self._grid_alpha, 

493 markersize=0, 

494 **self._grid_kw) 

495 l.set_transform(self.axes.get_xaxis_transform(which='grid')) 

496 l.get_path()._interpolation_steps = GRIDLINE_INTERPOLATION_STEPS 

497 self._set_artist_props(l) 

498 

499 return l 

500 

501 def update_position(self, loc): 

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

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

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

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

506 self.label1.set_x(loc) 

507 self.label2.set_x(loc) 

508 self._loc = loc 

509 self.stale = True 

510 

511 def get_view_interval(self): 

512 # docstring inherited 

513 return self.axes.viewLim.intervalx 

514 

515 

516class YTick(Tick): 

517 """ 

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

519 the label text and the grid line 

520 """ 

521 __name__ = 'ytick' 

522 

523 def _get_text1_transform(self): 

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

525 

526 def _get_text2_transform(self): 

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

528 

529 def apply_tickdir(self, tickdir): 

530 if tickdir is None: 

531 tickdir = rcParams['%s.direction' % self.__name__.lower()] 

532 self._tickdir = tickdir 

533 

534 if self._tickdir == 'in': 

535 self._tickmarkers = (mlines.TICKRIGHT, mlines.TICKLEFT) 

536 elif self._tickdir == 'inout': 

537 self._tickmarkers = ('_', '_') 

538 else: 

539 self._tickmarkers = (mlines.TICKLEFT, mlines.TICKRIGHT) 

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

541 self.stale = True 

542 

543 # how far from the y axis line the right of the ticklabel are 

544 def _get_text1(self): 

545 'Get the default Text instance' 

546 # x in axes coords, y in data coords 

547 trans, vert, horiz = self._get_text1_transform() 

548 t = mtext.Text( 

549 x=0, y=0, 

550 fontproperties=font_manager.FontProperties(size=self._labelsize), 

551 color=self._labelcolor, 

552 verticalalignment=vert, 

553 horizontalalignment=horiz, 

554 ) 

555 t.set_transform(trans) 

556 self._set_artist_props(t) 

557 return t 

558 

559 def _get_text2(self): 

560 'Get the default Text instance' 

561 # x in axes coords, y in data coords 

562 trans, vert, horiz = self._get_text2_transform() 

563 t = mtext.Text( 

564 x=1, y=0, 

565 fontproperties=font_manager.FontProperties(size=self._labelsize), 

566 color=self._labelcolor, 

567 verticalalignment=vert, 

568 horizontalalignment=horiz, 

569 ) 

570 t.set_transform(trans) 

571 self._set_artist_props(t) 

572 return t 

573 

574 def _get_tick1line(self): 

575 'Get the default line2D instance' 

576 # x in axes coords, y in data coords 

577 

578 l = mlines.Line2D((0,), (0,), 

579 color=self._color, 

580 marker=self._tickmarkers[0], 

581 linestyle='None', 

582 markersize=self._size, 

583 markeredgewidth=self._width, 

584 zorder=self._zorder) 

585 l.set_transform(self.axes.get_yaxis_transform(which='tick1')) 

586 self._set_artist_props(l) 

587 return l 

588 

589 def _get_tick2line(self): 

590 'Get the default line2D instance' 

591 # x in axes coords, y in data coords 

592 l = mlines.Line2D((1,), (0,), 

593 color=self._color, 

594 marker=self._tickmarkers[1], 

595 linestyle='None', 

596 markersize=self._size, 

597 markeredgewidth=self._width, 

598 zorder=self._zorder) 

599 l.set_transform(self.axes.get_yaxis_transform(which='tick2')) 

600 self._set_artist_props(l) 

601 return l 

602 

603 def _get_gridline(self): 

604 'Get the default line2D instance' 

605 # x in axes coords, y in data coords 

606 l = mlines.Line2D(xdata=(0, 1), ydata=(0, 0), 

607 color=self._grid_color, 

608 linestyle=self._grid_linestyle, 

609 linewidth=self._grid_linewidth, 

610 alpha=self._grid_alpha, 

611 markersize=0, 

612 **self._grid_kw) 

613 l.set_transform(self.axes.get_yaxis_transform(which='grid')) 

614 l.get_path()._interpolation_steps = GRIDLINE_INTERPOLATION_STEPS 

615 self._set_artist_props(l) 

616 return l 

617 

618 def update_position(self, loc): 

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

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

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

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

623 self.label1.set_y(loc) 

624 self.label2.set_y(loc) 

625 self._loc = loc 

626 self.stale = True 

627 

628 def get_view_interval(self): 

629 # docstring inherited 

630 return self.axes.viewLim.intervaly 

631 

632 

633class Ticker: 

634 """ 

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

636 

637 Attributes 

638 ---------- 

639 locator : `matplotlib.ticker.Locator` subclass 

640 Determines the positions of the ticks. 

641 formatter : `matplotlib.ticker.Formatter` subclass 

642 Determines the format of the tick labels. 

643 """ 

644 

645 def __init__(self): 

646 self._locator = None 

647 self._formatter = None 

648 

649 @property 

650 def locator(self): 

651 return self._locator 

652 

653 @locator.setter 

654 def locator(self, locator): 

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

656 cbook.warn_deprecated( 

657 "3.2", message="Support for locators that do not subclass " 

658 "matplotlib.ticker.Locator is deprecated since %(since)s and " 

659 "support for them will be removed %(removal)s.") 

660 self._locator = locator 

661 

662 @property 

663 def formatter(self): 

664 return self._formatter 

665 

666 @formatter.setter 

667 def formatter(self, formatter): 

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

669 cbook.warn_deprecated( 

670 "3.2", message="Support for formatters that do not subclass " 

671 "matplotlib.ticker.Formatter is deprecated since %(since)s " 

672 "and support for them will be removed %(removal)s.") 

673 self._formatter = formatter 

674 

675 

676class _LazyTickList: 

677 """ 

678 A descriptor for lazy instantiation of tick lists. 

679 

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

681 attributes. 

682 """ 

683 

684 def __init__(self, major): 

685 self._major = major 

686 

687 def __get__(self, instance, cls): 

688 if instance is None: 

689 return self 

690 else: 

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

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

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

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

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

696 if self._major: 

697 instance.majorTicks = [] 

698 tick = instance._get_tick(major=True) 

699 instance.majorTicks.append(tick) 

700 return instance.majorTicks 

701 else: 

702 instance.minorTicks = [] 

703 tick = instance._get_tick(major=False) 

704 instance.minorTicks.append(tick) 

705 return instance.minorTicks 

706 

707 

708class Axis(martist.Artist): 

709 """ 

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

711 

712 Attributes 

713 ---------- 

714 isDefault_label : bool 

715 

716 axes : `matplotlib.axes.Axes` 

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

718 major : `matplotlib.axis.Ticker` 

719 Determines the major tick positions and their label format. 

720 minor : `matplotlib.axis.Ticker` 

721 Determines the minor tick positions and their label format. 

722 callbacks : `matplotlib.cbook.CallbackRegistry` 

723 

724 label : `.Text` 

725 The axis label. 

726 labelpad : float 

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

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

729 offsetText : `.Text` 

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

731 pickradius : float 

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

733 majorTicks : list of `.Tick` 

734 The major ticks. 

735 minorTicks : list of `.Tick` 

736 The minor ticks. 

737 """ 

738 OFFSETTEXTPAD = 3 

739 

740 def __str__(self): 

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

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

743 

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

745 """ 

746 Parameters 

747 ---------- 

748 axes : `matplotlib.axes.Axes` 

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

750 pickradius : float 

751 The acceptance radius for containment tests. See also 

752 `.Axis.contains`. 

753 """ 

754 martist.Artist.__init__(self) 

755 self._remove_overlapping_locs = True 

756 

757 self.set_figure(axes.figure) 

758 

759 self.isDefault_label = True 

760 

761 self.axes = axes 

762 self.major = Ticker() 

763 self.minor = Ticker() 

764 self.callbacks = cbook.CallbackRegistry() 

765 

766 self._autolabelpos = True 

767 self._smart_bounds = False # Deprecated in 3.2 

768 

769 self.label = self._get_label() 

770 self.labelpad = rcParams['axes.labelpad'] 

771 self.offsetText = self._get_offset_text() 

772 

773 self.pickradius = pickradius 

774 

775 # Initialize here for testing; later add API 

776 self._major_tick_kw = dict() 

777 self._minor_tick_kw = dict() 

778 

779 self.cla() 

780 self._set_scale('linear') 

781 

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

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

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

785 majorTicks = _LazyTickList(major=True) 

786 minorTicks = _LazyTickList(major=False) 

787 

788 def get_remove_overlapping_locs(self): 

789 return self._remove_overlapping_locs 

790 

791 def set_remove_overlapping_locs(self, val): 

792 self._remove_overlapping_locs = bool(val) 

793 

794 remove_overlapping_locs = property( 

795 get_remove_overlapping_locs, set_remove_overlapping_locs, 

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

797 'ticker locations should be trimmed.')) 

798 

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

800 """ 

801 Set the coordinates of the label. 

802 

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

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

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

806 

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

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

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

810 """ 

811 self._autolabelpos = False 

812 if transform is None: 

813 transform = self.axes.transAxes 

814 

815 self.label.set_transform(transform) 

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

817 self.stale = True 

818 

819 def get_transform(self): 

820 return self._scale.get_transform() 

821 

822 def get_scale(self): 

823 return self._scale.name 

824 

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

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

827 self._scale.set_default_locators_and_formatters(self) 

828 

829 self.isDefault_majloc = True 

830 self.isDefault_minloc = True 

831 self.isDefault_majfmt = True 

832 self.isDefault_minfmt = True 

833 

834 def limit_range_for_scale(self, vmin, vmax): 

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

836 

837 def get_children(self): 

838 return [self.label, self.offsetText, 

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

840 

841 def cla(self): 

842 """Clear this axis.""" 

843 

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

845 

846 self._set_scale('linear') 

847 

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

849 self.callbacks = cbook.CallbackRegistry() 

850 

851 # whether the grids are on 

852 self._gridOnMajor = (rcParams['axes.grid'] and 

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

854 self._gridOnMinor = (rcParams['axes.grid'] and 

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

856 

857 self.reset_ticks() 

858 

859 self.converter = None 

860 self.units = None 

861 self.set_units(None) 

862 self.stale = True 

863 

864 def reset_ticks(self): 

865 """ 

866 Re-initialize the major and minor Tick lists. 

867 

868 Each list starts with a single fresh Tick. 

869 """ 

870 # Restore the lazy tick lists. 

871 try: 

872 del self.majorTicks 

873 except AttributeError: 

874 pass 

875 try: 

876 del self.minorTicks 

877 except AttributeError: 

878 pass 

879 try: 

880 self.set_clip_path(self.axes.patch) 

881 except AttributeError: 

882 pass 

883 

884 def set_tick_params(self, which='major', reset=False, **kw): 

885 """ 

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

887 

888 For documentation of keyword arguments, see 

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

890 """ 

891 dicts = [] 

892 if which == 'major' or which == 'both': 

893 dicts.append(self._major_tick_kw) 

894 if which == 'minor' or which == 'both': 

895 dicts.append(self._minor_tick_kw) 

896 kwtrans = self._translate_tick_kw(kw) 

897 

898 # this stashes the parameter changes so any new ticks will 

899 # automatically get them 

900 for d in dicts: 

901 if reset: 

902 d.clear() 

903 d.update(kwtrans) 

904 

905 if reset: 

906 self.reset_ticks() 

907 else: 

908 # apply the new kwargs to the existing ticks 

909 if which == 'major' or which == 'both': 

910 for tick in self.majorTicks: 

911 tick._apply_params(**kwtrans) 

912 if which == 'minor' or which == 'both': 

913 for tick in self.minorTicks: 

914 tick._apply_params(**kwtrans) 

915 # special-case label color to also apply to the offset 

916 # text 

917 if 'labelcolor' in kwtrans: 

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

919 

920 self.stale = True 

921 

922 @staticmethod 

923 def _translate_tick_kw(kw): 

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

925 kwkeys = ['size', 'width', 'color', 'tickdir', 'pad', 

926 'labelsize', 'labelcolor', 'zorder', 'gridOn', 

927 'tick1On', 'tick2On', 'label1On', 'label2On', 

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

929 'labelleft', 'labelbottom', 'labelright', 'labeltop', 

930 'labelrotation'] + _gridline_param_names 

931 kwtrans = {} 

932 if 'length' in kw: 

933 kwtrans['size'] = kw.pop('length') 

934 if 'direction' in kw: 

935 kwtrans['tickdir'] = kw.pop('direction') 

936 if 'rotation' in kw: 

937 kwtrans['labelrotation'] = kw.pop('rotation') 

938 if 'left' in kw: 

939 kwtrans['tick1On'] = kw.pop('left') 

940 if 'bottom' in kw: 

941 kwtrans['tick1On'] = kw.pop('bottom') 

942 if 'right' in kw: 

943 kwtrans['tick2On'] = kw.pop('right') 

944 if 'top' in kw: 

945 kwtrans['tick2On'] = kw.pop('top') 

946 if 'labelleft' in kw: 

947 kwtrans['label1On'] = kw.pop('labelleft') 

948 if 'labelbottom' in kw: 

949 kwtrans['label1On'] = kw.pop('labelbottom') 

950 if 'labelright' in kw: 

951 kwtrans['label2On'] = kw.pop('labelright') 

952 if 'labeltop' in kw: 

953 kwtrans['label2On'] = kw.pop('labeltop') 

954 if 'colors' in kw: 

955 c = kw.pop('colors') 

956 kwtrans['color'] = c 

957 kwtrans['labelcolor'] = c 

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

959 for key in kw: 

960 if key not in kwkeys: 

961 raise ValueError( 

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

963 % (key, kwkeys)) 

964 kwtrans.update(kw) 

965 return kwtrans 

966 

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

968 martist.Artist.set_clip_path(self, clippath, transform) 

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

970 child.set_clip_path(clippath, transform) 

971 self.stale = True 

972 

973 def get_view_interval(self): 

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

975 raise NotImplementedError('Derived must override') 

976 

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

978 """ 

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

980 users should typically use e.g. `~Axes.set_xlim` and `~Axes.set_ylim`. 

981 

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

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

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

985 the orientation of the axis will not change. 

986 

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

988 vmax)`` in that order. 

989 """ 

990 raise NotImplementedError('Derived must override') 

991 

992 def get_data_interval(self): 

993 """Return the Interval instance for this axis data limits.""" 

994 raise NotImplementedError('Derived must override') 

995 

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

997 """ 

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

999 

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

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

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

1003 the orientation of the axis will not change. 

1004 

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

1006 vmax)`` in that order. 

1007 """ 

1008 raise NotImplementedError('Derived must override') 

1009 

1010 def get_inverted(self): 

1011 """ 

1012 Return whether the axis is oriented in the "inverse" direction. 

1013 

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

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

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

1017 """ 

1018 low, high = self.get_view_interval() 

1019 return high < low 

1020 

1021 def set_inverted(self, inverted): 

1022 """ 

1023 Set whether the axis is oriented in the "inverse" direction. 

1024 

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

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

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

1028 """ 

1029 # Currently, must be implemented in subclasses using set_xlim/set_ylim 

1030 # rather than generically using set_view_interval, so that shared 

1031 # axes get updated as well. 

1032 raise NotImplementedError('Derived must override') 

1033 

1034 def set_default_intervals(self): 

1035 """ 

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

1037 have not been not mutated yet. 

1038 """ 

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

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

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

1042 # data and view limits. The unit conversion AxisInfo 

1043 # interface provides a hook for custom types to register 

1044 # default limits through the AxisInfo.default_limits 

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

1046 # and use it if is available (else just use 0..1) 

1047 

1048 def _set_artist_props(self, a): 

1049 if a is None: 

1050 return 

1051 a.set_figure(self.figure) 

1052 

1053 @cbook.deprecated("3.1") 

1054 def iter_ticks(self): 

1055 """ 

1056 Yield ``(Tick, location, label)`` tuples for major and minor ticks. 

1057 """ 

1058 major_locs = self.get_majorticklocs() 

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

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

1061 yield from zip(major_ticks, major_locs, major_labels) 

1062 minor_locs = self.get_minorticklocs() 

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

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

1065 yield from zip(minor_ticks, minor_locs, minor_labels) 

1066 

1067 def get_ticklabel_extents(self, renderer): 

1068 """ 

1069 Get the extents of the tick labels on either side 

1070 of the axes. 

1071 """ 

1072 

1073 ticks_to_draw = self._update_ticks() 

1074 ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw, 

1075 renderer) 

1076 

1077 if len(ticklabelBoxes): 

1078 bbox = mtransforms.Bbox.union(ticklabelBoxes) 

1079 else: 

1080 bbox = mtransforms.Bbox.from_extents(0, 0, 0, 0) 

1081 if len(ticklabelBoxes2): 

1082 bbox2 = mtransforms.Bbox.union(ticklabelBoxes2) 

1083 else: 

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

1085 return bbox, bbox2 

1086 

1087 @cbook.deprecated("3.2") 

1088 def set_smart_bounds(self, value): 

1089 """Set the axis to have smart bounds.""" 

1090 self._smart_bounds = value 

1091 self.stale = True 

1092 

1093 @cbook.deprecated("3.2") 

1094 def get_smart_bounds(self): 

1095 """Return whether the axis has smart bounds.""" 

1096 return self._smart_bounds 

1097 

1098 def _update_ticks(self): 

1099 """ 

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

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

1102 """ 

1103 major_locs = self.get_majorticklocs() 

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

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

1106 self.major.formatter.set_locs(major_locs) 

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

1108 tick.update_position(loc) 

1109 tick.set_label1(label) 

1110 tick.set_label2(label) 

1111 minor_locs = self.get_minorticklocs() 

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

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

1114 self.minor.formatter.set_locs(minor_locs) 

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

1116 tick.update_position(loc) 

1117 tick.set_label1(label) 

1118 tick.set_label2(label) 

1119 ticks = [*major_ticks, *minor_ticks] 

1120 

1121 view_low, view_high = self.get_view_interval() 

1122 if view_low > view_high: 

1123 view_low, view_high = view_high, view_low 

1124 

1125 if self._smart_bounds and ticks: # _smart_bounds is deprecated in 3.2 

1126 # handle inverted limits 

1127 data_low, data_high = sorted(self.get_data_interval()) 

1128 locs = np.sort([tick.get_loc() for tick in ticks]) 

1129 if data_low <= view_low: 

1130 # data extends beyond view, take view as limit 

1131 ilow = view_low 

1132 else: 

1133 # data stops within view, take best tick 

1134 good_locs = locs[locs <= data_low] 

1135 if len(good_locs): 

1136 # last tick prior or equal to first data point 

1137 ilow = good_locs[-1] 

1138 else: 

1139 # No ticks (why not?), take first tick 

1140 ilow = locs[0] 

1141 if data_high >= view_high: 

1142 # data extends beyond view, take view as limit 

1143 ihigh = view_high 

1144 else: 

1145 # data stops within view, take best tick 

1146 good_locs = locs[locs >= data_high] 

1147 if len(good_locs): 

1148 # first tick after or equal to last data point 

1149 ihigh = good_locs[0] 

1150 else: 

1151 # No ticks (why not?), take last tick 

1152 ihigh = locs[-1] 

1153 ticks = [tick for tick in ticks if ilow <= tick.get_loc() <= ihigh] 

1154 

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

1156 

1157 ticks_to_draw = [] 

1158 for tick in ticks: 

1159 try: 

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

1161 except AssertionError: 

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

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

1164 pass 

1165 else: 

1166 if mtransforms._interval_contains_close(interval_t, loc_t): 

1167 ticks_to_draw.append(tick) 

1168 

1169 return ticks_to_draw 

1170 

1171 def _get_tick_bboxes(self, ticks, renderer): 

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

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

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

1175 [tick.label2.get_window_extent(renderer) 

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

1177 

1178 def get_tightbbox(self, renderer): 

1179 """ 

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

1181 tick labels, axis label, and offsetText. 

1182 """ 

1183 if not self.get_visible(): 

1184 return 

1185 

1186 ticks_to_draw = self._update_ticks() 

1187 

1188 self._update_label_position(renderer) 

1189 

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

1191 ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes( 

1192 ticks_to_draw, renderer) 

1193 

1194 self._update_offset_text_position(ticklabelBoxes, ticklabelBoxes2) 

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

1196 

1197 bboxes = [ 

1198 *(a.get_window_extent(renderer) 

1199 for a in [self.label, self.offsetText] 

1200 if a.get_visible()), 

1201 *ticklabelBoxes, 

1202 *ticklabelBoxes2, 

1203 ] 

1204 bboxes = [b for b in bboxes 

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

1206 if bboxes: 

1207 return mtransforms.Bbox.union(bboxes) 

1208 else: 

1209 return None 

1210 

1211 def get_tick_padding(self): 

1212 values = [] 

1213 if len(self.majorTicks): 

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

1215 if len(self.minorTicks): 

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

1217 return max(values, default=0) 

1218 

1219 @martist.allow_rasterization 

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

1221 'Draw the axis lines, grid lines, tick lines and labels' 

1222 

1223 if not self.get_visible(): 

1224 return 

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

1226 

1227 ticks_to_draw = self._update_ticks() 

1228 ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw, 

1229 renderer) 

1230 

1231 for tick in ticks_to_draw: 

1232 tick.draw(renderer) 

1233 

1234 # scale up the axis label box to also find the neighbors, not 

1235 # just the tick labels that actually overlap note we need a 

1236 # *copy* of the axis label box because we don't wan't to scale 

1237 # the actual bbox 

1238 

1239 self._update_label_position(renderer) 

1240 

1241 self.label.draw(renderer) 

1242 

1243 self._update_offset_text_position(ticklabelBoxes, ticklabelBoxes2) 

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

1245 self.offsetText.draw(renderer) 

1246 

1247 renderer.close_group(__name__) 

1248 self.stale = False 

1249 

1250 def _get_label(self): 

1251 raise NotImplementedError('Derived must override') 

1252 

1253 def _get_offset_text(self): 

1254 raise NotImplementedError('Derived must override') 

1255 

1256 def get_gridlines(self): 

1257 'Return the grid lines as a list of Line2D instance' 

1258 ticks = self.get_major_ticks() 

1259 return cbook.silent_list('Line2D gridline', 

1260 [tick.gridline for tick in ticks]) 

1261 

1262 def get_label(self): 

1263 'Return the axis label as a Text instance' 

1264 return self.label 

1265 

1266 def get_offset_text(self): 

1267 'Return the axis offsetText as a Text instance' 

1268 return self.offsetText 

1269 

1270 def get_pickradius(self): 

1271 'Return the depth of the axis used by the picker' 

1272 return self.pickradius 

1273 

1274 def get_majorticklabels(self): 

1275 'Return a list of Text instances for the major ticklabels.' 

1276 ticks = self.get_major_ticks() 

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

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

1279 return cbook.silent_list('Text major ticklabel', labels1 + labels2) 

1280 

1281 def get_minorticklabels(self): 

1282 'Return a list of Text instances for the minor ticklabels.' 

1283 ticks = self.get_minor_ticks() 

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

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

1286 return cbook.silent_list('Text minor ticklabel', labels1 + labels2) 

1287 

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

1289 """ 

1290 Get the tick labels as a list of `~matplotlib.text.Text` instances. 

1291 

1292 Parameters 

1293 ---------- 

1294 minor : bool 

1295 If True return the minor ticklabels, 

1296 else return the major ticklabels 

1297 

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

1299 Overrides *minor*. 

1300 

1301 Selects which ticklabels to return 

1302 

1303 Returns 

1304 ------- 

1305 ret : list 

1306 List of `~matplotlib.text.Text` instances. 

1307 """ 

1308 

1309 if which is not None: 

1310 if which == 'minor': 

1311 return self.get_minorticklabels() 

1312 elif which == 'major': 

1313 return self.get_majorticklabels() 

1314 elif which == 'both': 

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

1316 else: 

1317 cbook._check_in_list(['major', 'minor', 'both'], which=which) 

1318 if minor: 

1319 return self.get_minorticklabels() 

1320 return self.get_majorticklabels() 

1321 

1322 def get_majorticklines(self): 

1323 'Return the major tick lines as a list of Line2D instances' 

1324 lines = [] 

1325 ticks = self.get_major_ticks() 

1326 for tick in ticks: 

1327 lines.append(tick.tick1line) 

1328 lines.append(tick.tick2line) 

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

1330 

1331 def get_minorticklines(self): 

1332 'Return the minor tick lines as a list of Line2D instances' 

1333 lines = [] 

1334 ticks = self.get_minor_ticks() 

1335 for tick in ticks: 

1336 lines.append(tick.tick1line) 

1337 lines.append(tick.tick2line) 

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

1339 

1340 def get_ticklines(self, minor=False): 

1341 'Return the tick lines as a list of Line2D instances' 

1342 if minor: 

1343 return self.get_minorticklines() 

1344 return self.get_majorticklines() 

1345 

1346 def get_majorticklocs(self): 

1347 """Get the array of major tick locations in data coordinates.""" 

1348 return self.major.locator() 

1349 

1350 def get_minorticklocs(self): 

1351 """Get the array of minor tick locations in data coordinates.""" 

1352 # Remove minor ticks duplicating major ticks. 

1353 major_locs = self.major.locator() 

1354 minor_locs = self.minor.locator() 

1355 transform = self._scale.get_transform() 

1356 tr_minor_locs = transform.transform(minor_locs) 

1357 tr_major_locs = transform.transform(major_locs) 

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

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

1360 # for np.isclose. 

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

1362 if self.remove_overlapping_locs: 

1363 minor_locs = [ 

1364 loc for loc, tr_loc in zip(minor_locs, tr_minor_locs) 

1365 if ~np.isclose(tr_loc, tr_major_locs, atol=tol, rtol=0).any()] 

1366 return minor_locs 

1367 

1368 def get_ticklocs(self, minor=False): 

1369 """Get the array of tick locations in data coordinates.""" 

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

1371 

1372 def get_ticks_direction(self, minor=False): 

1373 """ 

1374 Get the tick directions as a numpy array 

1375 

1376 Parameters 

1377 ---------- 

1378 minor : boolean 

1379 True to return the minor tick directions, 

1380 False to return the major tick directions, 

1381 Default is False 

1382 

1383 Returns 

1384 ------- 

1385 numpy array of tick directions 

1386 """ 

1387 if minor: 

1388 return np.array( 

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

1390 else: 

1391 return np.array( 

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

1393 

1394 def _get_tick(self, major): 

1395 """Return the default tick instance.""" 

1396 raise NotImplementedError('derived must override') 

1397 

1398 def _copy_tick_props(self, src, dest): 

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

1400 if src is None or dest is None: 

1401 return 

1402 dest.label1.update_from(src.label1) 

1403 dest.label2.update_from(src.label2) 

1404 dest.tick1line.update_from(src.tick1line) 

1405 dest.tick2line.update_from(src.tick2line) 

1406 dest.gridline.update_from(src.gridline) 

1407 

1408 def get_label_text(self): 

1409 'Get the text of the label' 

1410 return self.label.get_text() 

1411 

1412 def get_major_locator(self): 

1413 'Get the locator of the major ticker' 

1414 return self.major.locator 

1415 

1416 def get_minor_locator(self): 

1417 'Get the locator of the minor ticker' 

1418 return self.minor.locator 

1419 

1420 def get_major_formatter(self): 

1421 'Get the formatter of the major ticker' 

1422 return self.major.formatter 

1423 

1424 def get_minor_formatter(self): 

1425 'Get the formatter of the minor ticker' 

1426 return self.minor.formatter 

1427 

1428 def get_major_ticks(self, numticks=None): 

1429 'Get the tick instances; grow as necessary.' 

1430 if numticks is None: 

1431 numticks = len(self.get_majorticklocs()) 

1432 

1433 while len(self.majorTicks) < numticks: 

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

1435 tick = self._get_tick(major=True) 

1436 self.majorTicks.append(tick) 

1437 tick.gridline.set_visible(self._gridOnMajor) 

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

1439 

1440 return self.majorTicks[:numticks] 

1441 

1442 def get_minor_ticks(self, numticks=None): 

1443 'Get the minor tick instances; grow as necessary.' 

1444 if numticks is None: 

1445 numticks = len(self.get_minorticklocs()) 

1446 

1447 while len(self.minorTicks) < numticks: 

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

1449 tick = self._get_tick(major=False) 

1450 self.minorTicks.append(tick) 

1451 tick.gridline.set_visible(self._gridOnMinor) 

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

1453 

1454 return self.minorTicks[:numticks] 

1455 

1456 def grid(self, b=None, which='major', **kwargs): 

1457 """ 

1458 Configure the grid lines. 

1459 

1460 Parameters 

1461 ---------- 

1462 b : bool or None 

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

1464 it is assumed you want the grid on and *b* will be set to True. 

1465 

1466 If *b* is *None* and there are no *kwargs*, this toggles the 

1467 visibility of the lines. 

1468 

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

1470 The grid lines to apply the changes on. 

1471 

1472 **kwargs : `.Line2D` properties 

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

1474 

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

1476 

1477 """ 

1478 if len(kwargs): 

1479 if not b and b is not None: # something false-like but not None 

1480 cbook._warn_external('First parameter to grid() is false, ' 

1481 'but line properties are supplied. The ' 

1482 'grid will be enabled.') 

1483 b = True 

1484 which = which.lower() 

1485 cbook._check_in_list(['major', 'minor', 'both'], which=which) 

1486 gridkw = {'grid_' + item[0]: item[1] for item in kwargs.items()} 

1487 

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

1489 if b is None: 

1490 self._gridOnMinor = not self._gridOnMinor 

1491 else: 

1492 self._gridOnMinor = b 

1493 self.set_tick_params(which='minor', gridOn=self._gridOnMinor, 

1494 **gridkw) 

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

1496 if b is None: 

1497 self._gridOnMajor = not self._gridOnMajor 

1498 else: 

1499 self._gridOnMajor = b 

1500 self.set_tick_params(which='major', gridOn=self._gridOnMajor, 

1501 **gridkw) 

1502 self.stale = True 

1503 

1504 def update_units(self, data): 

1505 """ 

1506 Introspect *data* for units converter and update the 

1507 axis.converter instance if necessary. Return *True* 

1508 if *data* is registered for unit conversion. 

1509 """ 

1510 converter = munits.registry.get_converter(data) 

1511 if converter is None: 

1512 return False 

1513 

1514 neednew = self.converter != converter 

1515 self.converter = converter 

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

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

1518 self.set_units(default) 

1519 

1520 if neednew: 

1521 self._update_axisinfo() 

1522 self.stale = True 

1523 return True 

1524 

1525 def _update_axisinfo(self): 

1526 """ 

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

1528 axis info needs to be updated. 

1529 """ 

1530 if self.converter is None: 

1531 return 

1532 

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

1534 

1535 if info is None: 

1536 return 

1537 if info.majloc is not None and \ 

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

1539 self.set_major_locator(info.majloc) 

1540 self.isDefault_majloc = True 

1541 if info.minloc is not None and \ 

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

1543 self.set_minor_locator(info.minloc) 

1544 self.isDefault_minloc = True 

1545 if info.majfmt is not None and \ 

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

1547 self.set_major_formatter(info.majfmt) 

1548 self.isDefault_majfmt = True 

1549 if info.minfmt is not None and \ 

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

1551 self.set_minor_formatter(info.minfmt) 

1552 self.isDefault_minfmt = True 

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

1554 self.set_label_text(info.label) 

1555 self.isDefault_label = True 

1556 

1557 self.set_default_intervals() 

1558 

1559 def have_units(self): 

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

1561 

1562 def convert_units(self, x): 

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

1564 if munits._is_natively_supported(x): 

1565 return x 

1566 

1567 if self.converter is None: 

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

1569 

1570 if self.converter is None: 

1571 return x 

1572 try: 

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

1574 except Exception as e: 

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

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

1577 return ret 

1578 

1579 def set_units(self, u): 

1580 """ 

1581 Set the units for axis. 

1582 

1583 Parameters 

1584 ---------- 

1585 u : units tag 

1586 """ 

1587 if u == self.units: 

1588 return 

1589 self.units = u 

1590 self._update_axisinfo() 

1591 self.callbacks.process('units') 

1592 self.callbacks.process('units finalize') 

1593 self.stale = True 

1594 

1595 def get_units(self): 

1596 """Return the units for axis.""" 

1597 return self.units 

1598 

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

1600 """ 

1601 Set the text value of the axis label. 

1602 

1603 Parameters 

1604 ---------- 

1605 label : str 

1606 Text string. 

1607 fontdict : dict 

1608 Text properties. 

1609 **kwargs 

1610 Merged into fontdict. 

1611 """ 

1612 self.isDefault_label = False 

1613 self.label.set_text(label) 

1614 if fontdict is not None: 

1615 self.label.update(fontdict) 

1616 self.label.update(kwargs) 

1617 self.stale = True 

1618 return self.label 

1619 

1620 def set_major_formatter(self, formatter): 

1621 """ 

1622 Set the formatter of the major ticker. 

1623 

1624 Parameters 

1625 ---------- 

1626 formatter : `~matplotlib.ticker.Formatter` 

1627 """ 

1628 cbook._check_isinstance(mticker.Formatter, formatter=formatter) 

1629 self.isDefault_majfmt = False 

1630 self.major.formatter = formatter 

1631 formatter.set_axis(self) 

1632 self.stale = True 

1633 

1634 def set_minor_formatter(self, formatter): 

1635 """ 

1636 Set the formatter of the minor ticker. 

1637 

1638 Parameters 

1639 ---------- 

1640 formatter : `~matplotlib.ticker.Formatter` 

1641 """ 

1642 cbook._check_isinstance(mticker.Formatter, formatter=formatter) 

1643 self.isDefault_minfmt = False 

1644 self.minor.formatter = formatter 

1645 formatter.set_axis(self) 

1646 self.stale = True 

1647 

1648 def set_major_locator(self, locator): 

1649 """ 

1650 Set the locator of the major ticker. 

1651 

1652 Parameters 

1653 ---------- 

1654 locator : `~matplotlib.ticker.Locator` 

1655 """ 

1656 cbook._check_isinstance(mticker.Locator, locator=locator) 

1657 self.isDefault_majloc = False 

1658 self.major.locator = locator 

1659 if self.major.formatter: 

1660 self.major.formatter._set_locator(locator) 

1661 locator.set_axis(self) 

1662 self.stale = True 

1663 

1664 def set_minor_locator(self, locator): 

1665 """ 

1666 Set the locator of the minor ticker. 

1667 

1668 Parameters 

1669 ---------- 

1670 locator : `~matplotlib.ticker.Locator` 

1671 """ 

1672 cbook._check_isinstance(mticker.Locator, locator=locator) 

1673 self.isDefault_minloc = False 

1674 self.minor.locator = locator 

1675 if self.minor.formatter: 

1676 self.minor.formatter._set_locator(locator) 

1677 locator.set_axis(self) 

1678 self.stale = True 

1679 

1680 def set_pickradius(self, pickradius): 

1681 """ 

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

1683 

1684 Parameters 

1685 ---------- 

1686 pickradius : float 

1687 """ 

1688 self.pickradius = pickradius 

1689 

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

1691 r""" 

1692 Set the text values of the tick labels. 

1693 

1694 Parameters 

1695 ---------- 

1696 ticklabels : sequence of str or of `Text`\s 

1697 List of texts for tick labels; must include values for non-visible 

1698 labels. 

1699 minor : bool 

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

1701 **kwargs 

1702 Text properties. 

1703 

1704 Returns 

1705 ------- 

1706 labels : list of `Text`\s 

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

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

1709 """ 

1710 if args: 

1711 cbook.warn_deprecated( 

1712 "3.1", message="Additional positional arguments to " 

1713 "set_ticklabels are ignored, and deprecated since Matplotlib " 

1714 "3.1; passing them will raise a TypeError in Matplotlib 3.3.") 

1715 get_labels = [] 

1716 for t in ticklabels: 

1717 # try calling get_text() to check whether it is Text object 

1718 # if it is Text, get label content 

1719 try: 

1720 get_labels.append(t.get_text()) 

1721 # otherwise add the label to the list directly 

1722 except AttributeError: 

1723 get_labels.append(t) 

1724 # replace the ticklabels list with the processed one 

1725 ticklabels = get_labels 

1726 

1727 if minor: 

1728 self.set_minor_formatter(mticker.FixedFormatter(ticklabels)) 

1729 ticks = self.get_minor_ticks() 

1730 else: 

1731 self.set_major_formatter(mticker.FixedFormatter(ticklabels)) 

1732 ticks = self.get_major_ticks() 

1733 ret = [] 

1734 for tick_label, tick in zip(ticklabels, ticks): 

1735 # deal with label1 

1736 tick.label1.set_text(tick_label) 

1737 tick.label1.update(kwargs) 

1738 # deal with label2 

1739 tick.label2.set_text(tick_label) 

1740 tick.label2.update(kwargs) 

1741 # only return visible tick labels 

1742 if tick.label1.get_visible(): 

1743 ret.append(tick.label1) 

1744 if tick.label2.get_visible(): 

1745 ret.append(tick.label2) 

1746 

1747 self.stale = True 

1748 return ret 

1749 

1750 @cbook._make_keyword_only("3.2", "minor") 

1751 def set_ticks(self, ticks, minor=False): 

1752 """ 

1753 Set the locations of the tick marks from sequence ticks 

1754 

1755 Parameters 

1756 ---------- 

1757 ticks : sequence of floats 

1758 minor : bool 

1759 """ 

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

1761 ticks = self.convert_units(ticks) 

1762 if len(ticks) > 1: 

1763 xleft, xright = self.get_view_interval() 

1764 if xright > xleft: 

1765 self.set_view_interval(min(ticks), max(ticks)) 

1766 else: 

1767 self.set_view_interval(max(ticks), min(ticks)) 

1768 if minor: 

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

1770 return self.get_minor_ticks(len(ticks)) 

1771 else: 

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

1773 return self.get_major_ticks(len(ticks)) 

1774 

1775 def _get_tick_boxes_siblings(self, xdir, renderer): 

1776 """ 

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

1778 as set by `.Figure.align_xlabels` or `.Figure.align_ylablels`. 

1779 

1780 By default it just gets bboxes for self. 

1781 """ 

1782 raise NotImplementedError('Derived must override') 

1783 

1784 def _update_label_position(self, renderer): 

1785 """ 

1786 Update the label position based on the bounding box enclosing 

1787 all the ticklabels and axis spine. 

1788 """ 

1789 raise NotImplementedError('Derived must override') 

1790 

1791 def _update_offset_text_position(self, bboxes, bboxes2): 

1792 """ 

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

1794 boxes of all the ticklabels. 

1795 """ 

1796 raise NotImplementedError('Derived must override') 

1797 

1798 def pan(self, numsteps): 

1799 """Pan by *numsteps* (can be positive or negative).""" 

1800 self.major.locator.pan(numsteps) 

1801 

1802 def zoom(self, direction): 

1803 """Zoom in/out on axis; if *direction* is >0 zoom in, else zoom out.""" 

1804 self.major.locator.zoom(direction) 

1805 

1806 def axis_date(self, tz=None): 

1807 """ 

1808 Sets up axis ticks and labels treating data along this axis as dates. 

1809 

1810 Parameters 

1811 ---------- 

1812 tz : tzinfo or str or None 

1813 The timezone used to create date labels. 

1814 """ 

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

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

1817 # which is the timezone, can be set. 

1818 if isinstance(tz, str): 

1819 import dateutil.tz 

1820 tz = dateutil.tz.gettz(tz) 

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

1822 

1823 def get_tick_space(self): 

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

1825 # Must be overridden in the subclass 

1826 raise NotImplementedError() 

1827 

1828 def _get_ticks_position(self): 

1829 """ 

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

1831 

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

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

1834 

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

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

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

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

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

1840 - "unknown" otherwise. 

1841 """ 

1842 major = self.majorTicks[0] 

1843 minor = self.minorTicks[0] 

1844 if all(tick.tick1line.get_visible() 

1845 and not tick.tick2line.get_visible() 

1846 and tick.label1.get_visible() 

1847 and not tick.label2.get_visible() 

1848 for tick in [major, minor]): 

1849 return 1 

1850 elif all(tick.tick2line.get_visible() 

1851 and not tick.tick1line.get_visible() 

1852 and tick.label2.get_visible() 

1853 and not tick.label1.get_visible() 

1854 for tick in [major, minor]): 

1855 return 2 

1856 elif all(tick.tick1line.get_visible() 

1857 and tick.tick2line.get_visible() 

1858 and tick.label1.get_visible() 

1859 and not tick.label2.get_visible() 

1860 for tick in [major, minor]): 

1861 return "default" 

1862 else: 

1863 return "unknown" 

1864 

1865 def get_label_position(self): 

1866 """ 

1867 Return the label position (top or bottom) 

1868 """ 

1869 return self.label_position 

1870 

1871 def set_label_position(self, position): 

1872 """ 

1873 Set the label position (top or bottom) 

1874 

1875 Parameters 

1876 ---------- 

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

1878 """ 

1879 raise NotImplementedError() 

1880 

1881 def get_minpos(self): 

1882 raise NotImplementedError() 

1883 

1884 

1885def _make_getset_interval(method_name, lim_name, attr_name): 

1886 """ 

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

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

1889 """ 

1890 

1891 def getter(self): 

1892 # docstring inherited. 

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

1894 

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

1896 # docstring inherited. 

1897 if ignore: 

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

1899 else: 

1900 oldmin, oldmax = getter(self) 

1901 if oldmin < oldmax: 

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

1903 ignore=True) 

1904 else: 

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

1906 ignore=True) 

1907 self.stale = True 

1908 

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

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

1911 

1912 return getter, setter 

1913 

1914 

1915class XAxis(Axis): 

1916 __name__ = 'xaxis' 

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

1918 

1919 def contains(self, mouseevent): 

1920 """Test whether the mouse event occurred in the x axis. 

1921 """ 

1922 inside, info = self._default_contains(mouseevent) 

1923 if inside is not None: 

1924 return inside, info 

1925 

1926 x, y = mouseevent.x, mouseevent.y 

1927 try: 

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

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

1930 except ValueError: 

1931 return False, {} 

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

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

1934 b - self.pickradius < y < b or 

1935 t < y < t + self.pickradius) 

1936 return inaxis, {} 

1937 

1938 def _get_tick(self, major): 

1939 if major: 

1940 tick_kw = self._major_tick_kw 

1941 else: 

1942 tick_kw = self._minor_tick_kw 

1943 return XTick(self.axes, 0, '', major=major, **tick_kw) 

1944 

1945 def _get_label(self): 

1946 # x in axes coords, y in display coords (to be updated at draw 

1947 # time by _update_label_positions) 

1948 label = mtext.Text(x=0.5, y=0, 

1949 fontproperties=font_manager.FontProperties( 

1950 size=rcParams['axes.labelsize'], 

1951 weight=rcParams['axes.labelweight']), 

1952 color=rcParams['axes.labelcolor'], 

1953 verticalalignment='top', 

1954 horizontalalignment='center') 

1955 

1956 label.set_transform(mtransforms.blended_transform_factory( 

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

1958 

1959 self._set_artist_props(label) 

1960 self.label_position = 'bottom' 

1961 return label 

1962 

1963 def _get_offset_text(self): 

1964 # x in axes coords, y in display coords (to be updated at draw time) 

1965 offsetText = mtext.Text(x=1, y=0, 

1966 fontproperties=font_manager.FontProperties( 

1967 size=rcParams['xtick.labelsize']), 

1968 color=rcParams['xtick.color'], 

1969 verticalalignment='top', 

1970 horizontalalignment='right') 

1971 offsetText.set_transform(mtransforms.blended_transform_factory( 

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

1973 ) 

1974 self._set_artist_props(offsetText) 

1975 self.offset_text_position = 'bottom' 

1976 return offsetText 

1977 

1978 def set_label_position(self, position): 

1979 """ 

1980 Set the label position (top or bottom) 

1981 

1982 Parameters 

1983 ---------- 

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

1985 """ 

1986 self.label.set_verticalalignment(cbook._check_getitem({ 

1987 'top': 'baseline', 'bottom': 'top', 

1988 }, position=position)) 

1989 self.label_position = position 

1990 self.stale = True 

1991 

1992 def _get_tick_boxes_siblings(self, renderer): 

1993 """ 

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

1995 as set by `.Figure.align_xlabels` or `.Figure.align_ylablels`. 

1996 

1997 By default it just gets bboxes for self. 

1998 """ 

1999 bboxes = [] 

2000 bboxes2 = [] 

2001 # get the Grouper that keeps track of x-label groups for this figure 

2002 grp = self.figure._align_xlabel_grp 

2003 # if we want to align labels from other axes: 

2004 for nn, axx in enumerate(grp.get_siblings(self.axes)): 

2005 ticks_to_draw = axx.xaxis._update_ticks() 

2006 tlb, tlb2 = axx.xaxis._get_tick_bboxes(ticks_to_draw, renderer) 

2007 bboxes.extend(tlb) 

2008 bboxes2.extend(tlb2) 

2009 return bboxes, bboxes2 

2010 

2011 def _update_label_position(self, renderer): 

2012 """ 

2013 Update the label position based on the bounding box enclosing 

2014 all the ticklabels and axis spine 

2015 """ 

2016 if not self._autolabelpos: 

2017 return 

2018 

2019 # get bounding boxes for this axis and any siblings 

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

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

2022 

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

2024 if self.label_position == 'bottom': 

2025 try: 

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

2027 spinebbox = spine.get_transform().transform_path( 

2028 spine.get_path()).get_extents() 

2029 except KeyError: 

2030 # use axes if spine doesn't exist 

2031 spinebbox = self.axes.bbox 

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

2033 bottom = bbox.y0 

2034 

2035 self.label.set_position( 

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

2037 ) 

2038 

2039 else: 

2040 try: 

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

2042 spinebbox = spine.get_transform().transform_path( 

2043 spine.get_path()).get_extents() 

2044 except KeyError: 

2045 # use axes if spine doesn't exist 

2046 spinebbox = self.axes.bbox 

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

2048 top = bbox.y1 

2049 

2050 self.label.set_position( 

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

2052 ) 

2053 

2054 def _update_offset_text_position(self, bboxes, bboxes2): 

2055 """ 

2056 Update the offset_text position based on the sequence of bounding 

2057 boxes of all the ticklabels 

2058 """ 

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

2060 if not len(bboxes): 

2061 bottom = self.axes.bbox.ymin 

2062 else: 

2063 bbox = mtransforms.Bbox.union(bboxes) 

2064 bottom = bbox.y0 

2065 self.offsetText.set_position( 

2066 (x, bottom - self.OFFSETTEXTPAD * self.figure.dpi / 72) 

2067 ) 

2068 

2069 def get_text_heights(self, renderer): 

2070 """ 

2071 Returns the amount of space one should reserve for text 

2072 above and below the axes. Returns a tuple (above, below) 

2073 """ 

2074 bbox, bbox2 = self.get_ticklabel_extents(renderer) 

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

2076 padPixels = self.majorTicks[0].get_pad_pixels() 

2077 

2078 above = 0.0 

2079 if bbox2.height: 

2080 above += bbox2.height + padPixels 

2081 below = 0.0 

2082 if bbox.height: 

2083 below += bbox.height + padPixels 

2084 

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

2086 above += self.label.get_window_extent(renderer).height + padPixels 

2087 else: 

2088 below += self.label.get_window_extent(renderer).height + padPixels 

2089 return above, below 

2090 

2091 def set_ticks_position(self, position): 

2092 """ 

2093 Set the ticks position (top, bottom, both, default or none) 

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

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

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

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

2098 affect only the ticks, not the labels. 

2099 

2100 Parameters 

2101 ---------- 

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

2103 """ 

2104 if position == 'top': 

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

2106 bottom=False, labelbottom=False) 

2107 elif position == 'bottom': 

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

2109 bottom=True, labelbottom=True) 

2110 elif position == 'both': 

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

2112 bottom=True) 

2113 elif position == 'none': 

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

2115 bottom=False) 

2116 elif position == 'default': 

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

2118 bottom=True, labelbottom=True) 

2119 else: 

2120 raise ValueError("invalid position: %s" % position) 

2121 self.stale = True 

2122 

2123 def tick_top(self): 

2124 """ 

2125 Move ticks and ticklabels (if present) to the top of the axes. 

2126 """ 

2127 label = True 

2128 if 'label1On' in self._major_tick_kw: 

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

2130 or self._major_tick_kw['label2On']) 

2131 self.set_ticks_position('top') 

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

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

2134 

2135 def tick_bottom(self): 

2136 """ 

2137 Move ticks and ticklabels (if present) to the bottom of the axes. 

2138 """ 

2139 label = True 

2140 if 'label1On' in self._major_tick_kw: 

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

2142 or self._major_tick_kw['label2On']) 

2143 self.set_ticks_position('bottom') 

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

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

2146 

2147 def get_ticks_position(self): 

2148 """ 

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

2150 """ 

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

2152 "default": "default", "unknown": "unknown"}[ 

2153 self._get_ticks_position()] 

2154 

2155 get_view_interval, set_view_interval = _make_getset_interval( 

2156 "view", "viewLim", "intervalx") 

2157 get_data_interval, set_data_interval = _make_getset_interval( 

2158 "data", "dataLim", "intervalx") 

2159 

2160 def get_minpos(self): 

2161 return self.axes.dataLim.minposx 

2162 

2163 def set_inverted(self, inverted): 

2164 # docstring inherited 

2165 a, b = self.get_view_interval() 

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

2167 self.axes.set_xlim(sorted((a, b), reverse=bool(inverted)), auto=None) 

2168 

2169 def set_default_intervals(self): 

2170 # docstring inherited 

2171 xmin, xmax = 0., 1. 

2172 dataMutated = self.axes.dataLim.mutatedx() 

2173 viewMutated = self.axes.viewLim.mutatedx() 

2174 if not dataMutated or not viewMutated: 

2175 if self.converter is not None: 

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

2177 if info.default_limits is not None: 

2178 valmin, valmax = info.default_limits 

2179 xmin = self.converter.convert(valmin, self.units, self) 

2180 xmax = self.converter.convert(valmax, self.units, self) 

2181 if not dataMutated: 

2182 self.axes.dataLim.intervalx = xmin, xmax 

2183 if not viewMutated: 

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

2185 self.stale = True 

2186 

2187 def get_tick_space(self): 

2188 ends = self.axes.transAxes.transform([[0, 0], [1, 0]]) 

2189 length = ((ends[1][0] - ends[0][0]) / self.axes.figure.dpi) * 72 

2190 tick = self._get_tick(True) 

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

2192 # is no more than 3:1 

2193 size = tick.label1.get_size() * 3 

2194 if size > 0: 

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

2196 else: 

2197 return 2**31 - 1 

2198 

2199 

2200class YAxis(Axis): 

2201 __name__ = 'yaxis' 

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

2203 

2204 def contains(self, mouseevent): 

2205 """Test whether the mouse event occurred in the y axis. 

2206 

2207 Returns *True* | *False* 

2208 """ 

2209 inside, info = self._default_contains(mouseevent) 

2210 if inside is not None: 

2211 return inside, info 

2212 

2213 x, y = mouseevent.x, mouseevent.y 

2214 try: 

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

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

2217 except ValueError: 

2218 return False, {} 

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

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

2221 l - self.pickradius < x < l or 

2222 r < x < r + self.pickradius) 

2223 return inaxis, {} 

2224 

2225 def _get_tick(self, major): 

2226 if major: 

2227 tick_kw = self._major_tick_kw 

2228 else: 

2229 tick_kw = self._minor_tick_kw 

2230 return YTick(self.axes, 0, '', major=major, **tick_kw) 

2231 

2232 def _get_label(self): 

2233 # x in display coords (updated by _update_label_position) 

2234 # y in axes coords 

2235 label = mtext.Text(x=0, y=0.5, 

2236 # todo: get the label position 

2237 fontproperties=font_manager.FontProperties( 

2238 size=rcParams['axes.labelsize'], 

2239 weight=rcParams['axes.labelweight']), 

2240 color=rcParams['axes.labelcolor'], 

2241 verticalalignment='bottom', 

2242 horizontalalignment='center', 

2243 rotation='vertical', 

2244 rotation_mode='anchor') 

2245 label.set_transform(mtransforms.blended_transform_factory( 

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

2247 

2248 self._set_artist_props(label) 

2249 self.label_position = 'left' 

2250 return label 

2251 

2252 def _get_offset_text(self): 

2253 # x in display coords, y in axes coords (to be updated at draw time) 

2254 offsetText = mtext.Text(x=0, y=0.5, 

2255 fontproperties=font_manager.FontProperties( 

2256 size=rcParams['ytick.labelsize'] 

2257 ), 

2258 color=rcParams['ytick.color'], 

2259 verticalalignment='baseline', 

2260 horizontalalignment='left') 

2261 offsetText.set_transform(mtransforms.blended_transform_factory( 

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

2263 ) 

2264 self._set_artist_props(offsetText) 

2265 self.offset_text_position = 'left' 

2266 return offsetText 

2267 

2268 def set_label_position(self, position): 

2269 """ 

2270 Set the label position (left or right) 

2271 

2272 Parameters 

2273 ---------- 

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

2275 """ 

2276 self.label.set_rotation_mode('anchor') 

2277 self.label.set_horizontalalignment('center') 

2278 self.label.set_verticalalignment(cbook._check_getitem({ 

2279 'left': 'bottom', 'right': 'top', 

2280 }, position=position)) 

2281 self.label_position = position 

2282 self.stale = True 

2283 

2284 def _get_tick_boxes_siblings(self, renderer): 

2285 """ 

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

2287 as set by `.Figure.align_xlabels` or `.Figure.align_ylablels`. 

2288 

2289 By default it just gets bboxes for self. 

2290 """ 

2291 bboxes = [] 

2292 bboxes2 = [] 

2293 # get the Grouper that keeps track of y-label groups for this figure 

2294 grp = self.figure._align_ylabel_grp 

2295 # if we want to align labels from other axes: 

2296 for axx in grp.get_siblings(self.axes): 

2297 ticks_to_draw = axx.yaxis._update_ticks() 

2298 tlb, tlb2 = axx.yaxis._get_tick_bboxes(ticks_to_draw, renderer) 

2299 bboxes.extend(tlb) 

2300 bboxes2.extend(tlb2) 

2301 return bboxes, bboxes2 

2302 

2303 def _update_label_position(self, renderer): 

2304 """ 

2305 Update the label position based on the bounding box enclosing 

2306 all the ticklabels and axis spine 

2307 """ 

2308 if not self._autolabelpos: 

2309 return 

2310 

2311 # get bounding boxes for this axis and any siblings 

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

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

2314 

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

2316 if self.label_position == 'left': 

2317 try: 

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

2319 spinebbox = spine.get_transform().transform_path( 

2320 spine.get_path()).get_extents() 

2321 except KeyError: 

2322 # use axes if spine doesn't exist 

2323 spinebbox = self.axes.bbox 

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

2325 left = bbox.x0 

2326 self.label.set_position( 

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

2328 ) 

2329 

2330 else: 

2331 try: 

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

2333 spinebbox = spine.get_transform().transform_path( 

2334 spine.get_path()).get_extents() 

2335 except KeyError: 

2336 # use axes if spine doesn't exist 

2337 spinebbox = self.axes.bbox 

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

2339 right = bbox.x1 

2340 

2341 self.label.set_position( 

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

2343 ) 

2344 

2345 def _update_offset_text_position(self, bboxes, bboxes2): 

2346 """ 

2347 Update the offset_text position based on the sequence of bounding 

2348 boxes of all the ticklabels 

2349 """ 

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

2351 top = self.axes.bbox.ymax 

2352 self.offsetText.set_position( 

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

2354 ) 

2355 

2356 def set_offset_position(self, position): 

2357 """ 

2358 Parameters 

2359 ---------- 

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

2361 """ 

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

2363 x = cbook._check_getitem({'left': 0, 'right': 1}, position=position) 

2364 

2365 self.offsetText.set_ha(position) 

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

2367 self.stale = True 

2368 

2369 def get_text_widths(self, renderer): 

2370 bbox, bbox2 = self.get_ticklabel_extents(renderer) 

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

2372 padPixels = self.majorTicks[0].get_pad_pixels() 

2373 

2374 left = 0.0 

2375 if bbox.width: 

2376 left += bbox.width + padPixels 

2377 right = 0.0 

2378 if bbox2.width: 

2379 right += bbox2.width + padPixels 

2380 

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

2382 left += self.label.get_window_extent(renderer).width + padPixels 

2383 else: 

2384 right += self.label.get_window_extent(renderer).width + padPixels 

2385 return left, right 

2386 

2387 def set_ticks_position(self, position): 

2388 """ 

2389 Set the ticks position (left, right, both, default or none) 

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

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

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

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

2394 affect only the ticks, not the labels. 

2395 

2396 Parameters 

2397 ---------- 

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

2399 """ 

2400 if position == 'right': 

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

2402 left=False, labelleft=False) 

2403 self.set_offset_position(position) 

2404 elif position == 'left': 

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

2406 left=True, labelleft=True) 

2407 self.set_offset_position(position) 

2408 elif position == 'both': 

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

2410 left=True) 

2411 elif position == 'none': 

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

2413 left=False) 

2414 elif position == 'default': 

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

2416 left=True, labelleft=True) 

2417 else: 

2418 raise ValueError("invalid position: %s" % position) 

2419 self.stale = True 

2420 

2421 def tick_right(self): 

2422 """ 

2423 Move ticks and ticklabels (if present) to the right of the axes. 

2424 """ 

2425 label = True 

2426 if 'label1On' in self._major_tick_kw: 

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

2428 or self._major_tick_kw['label2On']) 

2429 self.set_ticks_position('right') 

2430 # if labels were turned off before this was called 

2431 # leave them off 

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

2433 

2434 def tick_left(self): 

2435 """ 

2436 Move ticks and ticklabels (if present) to the left of the axes. 

2437 """ 

2438 label = True 

2439 if 'label1On' in self._major_tick_kw: 

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

2441 or self._major_tick_kw['label2On']) 

2442 self.set_ticks_position('left') 

2443 # if labels were turned off before this was called 

2444 # leave them off 

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

2446 

2447 def get_ticks_position(self): 

2448 """ 

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

2450 """ 

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

2452 "default": "default", "unknown": "unknown"}[ 

2453 self._get_ticks_position()] 

2454 

2455 get_view_interval, set_view_interval = _make_getset_interval( 

2456 "view", "viewLim", "intervaly") 

2457 get_data_interval, set_data_interval = _make_getset_interval( 

2458 "data", "dataLim", "intervaly") 

2459 

2460 def get_minpos(self): 

2461 return self.axes.dataLim.minposy 

2462 

2463 def set_inverted(self, inverted): 

2464 # docstring inherited 

2465 a, b = self.get_view_interval() 

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

2467 self.axes.set_ylim(sorted((a, b), reverse=bool(inverted)), auto=None) 

2468 

2469 def set_default_intervals(self): 

2470 # docstring inherited 

2471 ymin, ymax = 0., 1. 

2472 dataMutated = self.axes.dataLim.mutatedy() 

2473 viewMutated = self.axes.viewLim.mutatedy() 

2474 if not dataMutated or not viewMutated: 

2475 if self.converter is not None: 

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

2477 if info.default_limits is not None: 

2478 valmin, valmax = info.default_limits 

2479 ymin = self.converter.convert(valmin, self.units, self) 

2480 ymax = self.converter.convert(valmax, self.units, self) 

2481 if not dataMutated: 

2482 self.axes.dataLim.intervaly = ymin, ymax 

2483 if not viewMutated: 

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

2485 self.stale = True 

2486 

2487 def get_tick_space(self): 

2488 ends = self.axes.transAxes.transform([[0, 0], [0, 1]]) 

2489 length = ((ends[1][1] - ends[0][1]) / self.axes.figure.dpi) * 72 

2490 tick = self._get_tick(True) 

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

2492 size = tick.label1.get_size() * 2.0 

2493 if size > 0: 

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

2495 else: 

2496 return 2**31 - 1