axis_artist.py 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527
  1. """
  2. axis_artist.py module provides axis-related artists. They are
  3. * axis line
  4. * tick lines
  5. * tick labels
  6. * axis label
  7. * grid lines
  8. The main artist class is a AxisArtist and a GridlinesCollection. The
  9. GridlinesCollection is responsible for drawing grid lines and the
  10. AxisArtist is responsible for all other artists. The AxisArtist class
  11. has attributes that are associated with each type of artists.
  12. * line : axis line
  13. * major_ticks : major tick lines
  14. * major_ticklabels : major tick labels
  15. * minor_ticks : minor tick lines
  16. * minor_ticklabels : minor tick labels
  17. * label : axis label
  18. Typically, the AxisArtist associated with a axes will be accessed with
  19. the *axis* dictionary of the axes, i.e., the AxisArtist for the bottom
  20. axis is
  21. ax.axis["bottom"]
  22. where *ax* is an instance of axes (mpl_toolkits.axislines.Axes). Thus,
  23. ax.axis["bottom"].line is an artist associated with the axis line, and
  24. ax.axis["bottom"].major_ticks is an artist associated with the major tick
  25. lines.
  26. You can change the colors, fonts, line widths, etc. of these artists
  27. by calling suitable set method. For example, to change the color of the major
  28. ticks of the bottom axis to red,
  29. ax.axis["bottom"].major_ticks.set_color("r")
  30. However, things like the locations of ticks, and their ticklabels need
  31. to be changed from the side of the grid_helper.
  32. axis_direction
  33. --------------
  34. AxisArtist, AxisLabel, TickLabels have *axis_direction* attribute,
  35. which adjusts the location, angle, etc.,. The *axis_direction* must be
  36. one of [left, right, bottom, top] and they follow the matplotlib
  37. convention for the rectangle axis.
  38. For example, for the *bottom* axis (the left and right is relative to
  39. the direction of the increasing coordinate),
  40. * ticklabels and axislabel are on the right
  41. * ticklabels and axislabel have text angle of 0
  42. * ticklabels are baseline, center-aligned
  43. * axislabel is top, center-aligned
  44. The text angles are actually relative to (90 + angle of the direction
  45. to the ticklabel), which gives 0 for bottom axis.
  46. Parameter left bottom right top
  47. ticklabels location left right right left
  48. axislabel location left right right left
  49. ticklabels angle 90 0 -90 180
  50. axislabel angle 180 0 0 180
  51. ticklabel va center baseline center baseline
  52. axislabel va center top center bottom
  53. ticklabel ha right center right center
  54. axislabel ha right center right center
  55. Ticks are by default direct opposite side of the ticklabels. To make
  56. ticks to the same side of the ticklabels,
  57. ax.axis["bottom"].major_ticks.set_ticks_out(True)
  58. Following attributes can be customized (use set_xxx method)
  59. * Ticks : ticksize, tick_out
  60. * TickLabels : pad
  61. * AxisLabel : pad
  62. """
  63. from __future__ import (absolute_import, division, print_function,
  64. unicode_literals)
  65. import six
  66. # FIXME :
  67. # angles are given in data coordinate - need to convert it to canvas coordinate
  68. import matplotlib.artist as martist
  69. import matplotlib.text as mtext
  70. import matplotlib.font_manager as font_manager
  71. from matplotlib.path import Path
  72. from matplotlib.transforms import (
  73. Affine2D, Bbox, IdentityTransform, ScaledTranslation, TransformedPath)
  74. from matplotlib.collections import LineCollection
  75. from matplotlib import rcParams
  76. from matplotlib.artist import allow_rasterization
  77. import warnings
  78. import numpy as np
  79. import matplotlib.lines as mlines
  80. from .axisline_style import AxislineStyle
  81. class BezierPath(mlines.Line2D):
  82. def __init__(self, path, *kl, **kw):
  83. mlines.Line2D.__init__(self, [], [], *kl, **kw)
  84. self._path = path
  85. self._invalid = False
  86. def recache(self):
  87. self._transformed_path = TransformedPath(self._path, self.get_transform())
  88. self._invalid = False
  89. def set_path(self, path):
  90. self._path = path
  91. self._invalid = True
  92. def draw(self, renderer):
  93. if self._invalid:
  94. self.recache()
  95. if not self._visible: return
  96. renderer.open_group('line2d')
  97. gc = renderer.new_gc()
  98. self._set_gc_clip(gc)
  99. gc.set_foreground(self._color)
  100. gc.set_antialiased(self._antialiased)
  101. gc.set_linewidth(self._linewidth)
  102. gc.set_alpha(self._alpha)
  103. if self.is_dashed():
  104. cap = self._dashcapstyle
  105. join = self._dashjoinstyle
  106. else:
  107. cap = self._solidcapstyle
  108. join = self._solidjoinstyle
  109. gc.set_joinstyle(join)
  110. gc.set_capstyle(cap)
  111. gc.set_dashes(self._dashOffset, self._dashSeq)
  112. if self._lineStyles[self._linestyle] != '_draw_nothing':
  113. tpath, affine = (
  114. self._transformed_path.get_transformed_path_and_affine())
  115. renderer.draw_path(gc, tpath, affine.frozen())
  116. gc.restore()
  117. renderer.close_group('line2d')
  118. class UnimplementedException(Exception):
  119. pass
  120. from matplotlib.artist import Artist
  121. class AttributeCopier(object):
  122. def __init__(self, ref_artist, klass=Artist):
  123. self._klass = klass
  124. self._ref_artist = ref_artist
  125. super(AttributeCopier, self).__init__()
  126. def set_ref_artist(self, artist):
  127. self._ref_artist = artist
  128. def get_ref_artist(self):
  129. raise RuntimeError("get_ref_artist must overridden")
  130. #return self._ref_artist
  131. def get_attribute_from_ref_artist(self, attr_name, default_value):
  132. get_attr_method_name = "get_"+attr_name
  133. c = getattr(self._klass, get_attr_method_name)(self)
  134. if c == 'auto':
  135. ref_artist = self.get_ref_artist()
  136. if ref_artist:
  137. attr = getattr(ref_artist,
  138. get_attr_method_name)()
  139. return attr
  140. else:
  141. return default_value
  142. return c
  143. from matplotlib.lines import Line2D
  144. class Ticks(Line2D, AttributeCopier):
  145. """
  146. Ticks are derived from Line2D, and note that ticks themselves
  147. are markers. Thus, you should use set_mec, set_mew, etc.
  148. To change the tick size (length), you need to use
  149. set_ticksize. To change the direction of the ticks (ticks are
  150. in opposite direction of ticklabels by default), use
  151. set_tick_out(False).
  152. """
  153. def __init__(self, ticksize, tick_out=False, **kwargs):
  154. self._ticksize = ticksize
  155. self.locs_angles_labels = []
  156. self.set_tick_out(tick_out)
  157. self._axis = kwargs.pop("axis", None)
  158. if self._axis is not None:
  159. if "color" not in kwargs:
  160. kwargs["color"] = "auto"
  161. if ("mew" not in kwargs) and ("markeredgewidth" not in kwargs):
  162. kwargs["markeredgewidth"] = "auto"
  163. Line2D.__init__(self, [0.], [0.], **kwargs)
  164. AttributeCopier.__init__(self, self._axis, klass=Line2D)
  165. self.set_snap(True)
  166. def get_ref_artist(self):
  167. #return self._ref_artist.get_ticklines()[0]
  168. return self._ref_artist.majorTicks[0].tick1line
  169. def get_color(self):
  170. return self.get_attribute_from_ref_artist("color", "k")
  171. def get_markeredgecolor(self):
  172. if self._markeredgecolor == 'auto':
  173. return self.get_color()
  174. else:
  175. return self._markeredgecolor
  176. def get_markeredgewidth(self):
  177. return self.get_attribute_from_ref_artist("markeredgewidth", .5)
  178. def set_tick_out(self, b):
  179. """
  180. set True if tick need to be rotated by 180 degree.
  181. """
  182. self._tick_out = b
  183. def get_tick_out(self):
  184. """
  185. Return True if the tick will be rotated by 180 degree.
  186. """
  187. return self._tick_out
  188. def set_ticksize(self, ticksize):
  189. """
  190. set length of the ticks in points.
  191. """
  192. self._ticksize = ticksize
  193. def get_ticksize(self):
  194. """
  195. Return length of the ticks in points.
  196. """
  197. return self._ticksize
  198. def set_locs_angles(self, locs_angles):
  199. self.locs_angles = locs_angles
  200. def _update(self, renderer):
  201. pass
  202. _tickvert_path = Path([[0., 0.], [1., 0.]])
  203. def draw(self, renderer):
  204. if not self.get_visible():
  205. return
  206. self._update(renderer) # update the tick
  207. size = self._ticksize
  208. path_trans = self.get_transform()
  209. # set gc : copied from lines.py
  210. # gc = renderer.new_gc()
  211. # self._set_gc_clip(gc)
  212. # gc.set_foreground(self.get_color())
  213. # gc.set_antialiased(self._antialiased)
  214. # gc.set_linewidth(self._linewidth)
  215. # gc.set_alpha(self._alpha)
  216. # if self.is_dashed():
  217. # cap = self._dashcapstyle
  218. # join = self._dashjoinstyle
  219. # else:
  220. # cap = self._solidcapstyle
  221. # join = self._solidjoinstyle
  222. # gc.set_joinstyle(join)
  223. # gc.set_capstyle(cap)
  224. # gc.set_snap(self.get_snap())
  225. gc = renderer.new_gc()
  226. gc.set_foreground(self.get_markeredgecolor())
  227. gc.set_linewidth(self.get_markeredgewidth())
  228. gc.set_alpha(self._alpha)
  229. offset = renderer.points_to_pixels(size)
  230. marker_scale = Affine2D().scale(offset, offset)
  231. if self.get_tick_out():
  232. add_angle = 180
  233. else:
  234. add_angle = 0
  235. marker_rotation = Affine2D()
  236. marker_transform = marker_scale + marker_rotation
  237. for loc, angle in self.locs_angles:
  238. marker_rotation.clear().rotate_deg(angle+add_angle)
  239. locs = path_trans.transform_non_affine(np.array([loc]))
  240. if self.axes and not self.axes.viewLim.contains(*locs[0]):
  241. continue
  242. renderer.draw_markers(gc, self._tickvert_path, marker_transform,
  243. Path(locs), path_trans.get_affine())
  244. gc.restore()
  245. class LabelBase(mtext.Text):
  246. """
  247. A base class for AxisLabel and TickLabels. The position and angle
  248. of the text are calculated by to offset_ref_angle,
  249. text_ref_angle, and offset_radius attributes.
  250. """
  251. def __init__(self, *kl, **kwargs):
  252. self.locs_angles_labels = []
  253. self._ref_angle = 0
  254. self._offset_radius = 0.
  255. super(LabelBase, self).__init__(*kl,
  256. **kwargs)
  257. self.set_rotation_mode("anchor")
  258. self._text_follow_ref_angle = True
  259. #self._offset_ref_angle = 0
  260. def _set_ref_angle(self, a):
  261. self._ref_angle = a
  262. def _get_ref_angle(self):
  263. return self._ref_angle
  264. def _get_text_ref_angle(self):
  265. if self._text_follow_ref_angle:
  266. return self._get_ref_angle()+90
  267. else:
  268. return 0 #self.get_ref_angle()
  269. def _get_offset_ref_angle(self):
  270. return self._get_ref_angle()
  271. def _set_offset_radius(self, offset_radius):
  272. self._offset_radius = offset_radius
  273. def _get_offset_radius(self):
  274. return self._offset_radius
  275. _get_opposite_direction = {"left":"right",
  276. "right":"left",
  277. "top":"bottom",
  278. "bottom":"top"}.__getitem__
  279. def _update(self, renderer):
  280. pass
  281. def draw(self, renderer):
  282. if not self.get_visible(): return
  283. self._update(renderer)
  284. # save original and adjust some properties
  285. tr = self.get_transform()
  286. angle_orig = self.get_rotation()
  287. offset_tr = Affine2D()
  288. self.set_transform(tr+offset_tr)
  289. text_ref_angle = self._get_text_ref_angle()
  290. offset_ref_angle = self._get_offset_ref_angle()
  291. theta = (offset_ref_angle)/180.*np.pi
  292. dd = self._get_offset_radius()
  293. dx, dy = dd * np.cos(theta), dd * np.sin(theta)
  294. offset_tr.translate(dx, dy)
  295. self.set_rotation(text_ref_angle+angle_orig)
  296. super(LabelBase, self).draw(renderer)
  297. offset_tr.clear()
  298. # restore original properties
  299. self.set_transform(tr)
  300. self.set_rotation(angle_orig)
  301. def get_window_extent(self, renderer):
  302. self._update(renderer)
  303. # save original and adjust some properties
  304. tr = self.get_transform()
  305. angle_orig = self.get_rotation()
  306. offset_tr = Affine2D()
  307. self.set_transform(tr+offset_tr)
  308. text_ref_angle = self._get_text_ref_angle()
  309. offset_ref_angle = self._get_offset_ref_angle()
  310. theta = (offset_ref_angle)/180.*np.pi
  311. dd = self._get_offset_radius()
  312. dx, dy = dd * np.cos(theta), dd * np.sin(theta)
  313. offset_tr.translate(dx, dy)
  314. self.set_rotation(text_ref_angle+angle_orig)
  315. bbox = super(LabelBase, self).get_window_extent(renderer).frozen()
  316. offset_tr.clear()
  317. # restore original properties
  318. self.set_transform(tr)
  319. self.set_rotation(angle_orig)
  320. return bbox
  321. class AxisLabel(LabelBase, AttributeCopier):
  322. """
  323. Axis Label. Derived from Text. The position of the text is updated
  324. in the fly, so changing text position has no effect. Otherwise, the
  325. properties can be changed as a normal Text.
  326. To change the pad between ticklabels and axis label, use set_pad.
  327. """
  328. def __init__(self, *kl, **kwargs):
  329. axis_direction = kwargs.pop("axis_direction", "bottom")
  330. self._axis = kwargs.pop("axis", None)
  331. #super(AxisLabel, self).__init__(*kl, **kwargs)
  332. LabelBase.__init__(self, *kl, **kwargs)
  333. AttributeCopier.__init__(self, self._axis, klass=LabelBase)
  334. self.set_axis_direction(axis_direction)
  335. self._pad = 5
  336. self._extra_pad = 0
  337. def set_pad(self, pad):
  338. """
  339. Set the pad in points. Note that the actual pad will be the
  340. sum of the internal pad and the external pad (that are set
  341. automatically by the AxisArtist), and it only set the internal
  342. pad
  343. """
  344. self._pad = pad
  345. def get_pad(self):
  346. """
  347. return pad in points. See set_pad for more details.
  348. """
  349. return self._pad
  350. def _set_external_pad(self, p):
  351. """
  352. Set external pad IN PIXELS. This is intended to be set by the
  353. AxisArtist, bot by user..
  354. """
  355. self._extra_pad = p
  356. def _get_external_pad(self):
  357. """
  358. Get external pad.
  359. """
  360. return self._extra_pad
  361. def get_ref_artist(self):
  362. return self._axis.get_label()
  363. def get_text(self):
  364. t = super(AxisLabel, self).get_text()
  365. if t == "__from_axes__":
  366. return self._axis.get_label().get_text()
  367. return self._text
  368. _default_alignments = dict(left=("bottom", "center"),
  369. right=("top", "center"),
  370. bottom=("top", "center"),
  371. top=("bottom", "center"))
  372. def set_default_alignment(self, d):
  373. if d not in ["left", "right", "top", "bottom"]:
  374. raise ValueError('direction must be on of "left", "right", "top", "bottom"')
  375. va, ha = self._default_alignments[d]
  376. self.set_va(va)
  377. self.set_ha(ha)
  378. _default_angles = dict(left=180,
  379. right=0,
  380. bottom=0,
  381. top=180)
  382. def set_default_angle(self, d):
  383. if d not in ["left", "right", "top", "bottom"]:
  384. raise ValueError('direction must be on of "left", "right", "top", "bottom"')
  385. self.set_rotation(self._default_angles[d])
  386. def set_axis_direction(self, d):
  387. """
  388. Adjust the text angle and text alignment of axis label
  389. according to the matplotlib convention.
  390. ===================== ========== ========= ========== ==========
  391. property left bottom right top
  392. ===================== ========== ========= ========== ==========
  393. axislabel angle 180 0 0 180
  394. axislabel va center top center bottom
  395. axislabel ha right center right center
  396. ===================== ========== ========= ========== ==========
  397. Note that the text angles are actually relative to (90 + angle
  398. of the direction to the ticklabel), which gives 0 for bottom
  399. axis.
  400. """
  401. if d not in ["left", "right", "top", "bottom"]:
  402. raise ValueError('direction must be on of "left", "right", "top", "bottom"')
  403. self.set_default_alignment(d)
  404. self.set_default_angle(d)
  405. def get_color(self):
  406. return self.get_attribute_from_ref_artist("color", "k")
  407. def draw(self, renderer):
  408. if not self.get_visible():
  409. return
  410. pad = renderer.points_to_pixels(self.get_pad())
  411. r = self._get_external_pad() + pad
  412. self._set_offset_radius(r)
  413. super(AxisLabel, self).draw(renderer)
  414. def get_window_extent(self, renderer):
  415. if not self.get_visible():
  416. return
  417. pad = renderer.points_to_pixels(self.get_pad())
  418. r = self._get_external_pad() + pad
  419. self._set_offset_radius(r)
  420. bb = super(AxisLabel, self).get_window_extent(renderer)
  421. return bb
  422. class TickLabels(AxisLabel, AttributeCopier): # mtext.Text
  423. """
  424. Tick Labels. While derived from Text, this single artist draws all
  425. ticklabels. As in AxisLabel, the position of the text is updated
  426. in the fly, so changing text position has no effect. Otherwise,
  427. the properties can be changed as a normal Text. Unlike the
  428. ticklabels of the mainline matplotlib, properties of single
  429. ticklabel alone cannot modified.
  430. To change the pad between ticks and ticklabels, use set_pad.
  431. """
  432. def __init__(self, **kwargs):
  433. axis_direction = kwargs.pop("axis_direction", "bottom")
  434. AxisLabel.__init__(self, **kwargs)
  435. self.set_axis_direction(axis_direction)
  436. #self._axis_direction = axis_direction
  437. self._axislabel_pad = 0
  438. #self._extra_pad = 0
  439. # attribute copier
  440. def get_ref_artist(self):
  441. return self._axis.get_ticklabels()[0]
  442. def set_axis_direction(self, label_direction):
  443. """
  444. Adjust the text angle and text alignment of ticklabels
  445. according to the matplotlib convention.
  446. The *label_direction* must be one of [left, right, bottom,
  447. top].
  448. ===================== ========== ========= ========== ==========
  449. property left bottom right top
  450. ===================== ========== ========= ========== ==========
  451. ticklabels angle 90 0 -90 180
  452. ticklabel va center baseline center baseline
  453. ticklabel ha right center right center
  454. ===================== ========== ========= ========== ==========
  455. Note that the text angles are actually relative to (90 + angle
  456. of the direction to the ticklabel), which gives 0 for bottom
  457. axis.
  458. """
  459. if label_direction not in ["left", "right", "top", "bottom"]:
  460. raise ValueError('direction must be one of "left", "right", "top", "bottom"')
  461. self._axis_direction = label_direction
  462. self.set_default_alignment(label_direction)
  463. self.set_default_angle(label_direction)
  464. def invert_axis_direction(self):
  465. label_direction = self._get_opposite_direction(self._axis_direction)
  466. self.set_axis_direction(label_direction)
  467. def _get_ticklabels_offsets(self, renderer, label_direction):
  468. """
  469. Calculates the offsets of the ticklabels from the tick and
  470. their total heights. The offset only takes account the offset
  471. due to the vertical alignment of the ticklabels, i.e.,if axis
  472. direction is bottom and va is ;top', it will return 0. if va
  473. is 'baseline', it will return (height-descent).
  474. """
  475. whd_list = self.get_texts_widths_heights_descents(renderer)
  476. if not whd_list:
  477. return 0, 0
  478. r = 0
  479. va, ha = self.get_va(), self.get_ha()
  480. if label_direction == "left":
  481. pad = max(w for w, h, d in whd_list)
  482. if ha == "left":
  483. r = pad
  484. elif ha == "center":
  485. r = .5 * pad
  486. elif label_direction == "right":
  487. pad = max(w for w, h, d in whd_list)
  488. if ha == "right":
  489. r = pad
  490. elif ha == "center":
  491. r = .5 * pad
  492. elif label_direction == "bottom":
  493. pad = max(h for w, h, d in whd_list)
  494. if va == "bottom":
  495. r = pad
  496. elif va == "center":
  497. r =.5 * pad
  498. elif va == "baseline":
  499. max_ascent = max(h - d for w, h, d in whd_list)
  500. max_descent = max(d for w, h, d in whd_list)
  501. r = max_ascent
  502. pad = max_ascent + max_descent
  503. elif label_direction == "top":
  504. pad = max(h for w, h, d in whd_list)
  505. if va == "top":
  506. r = pad
  507. elif va == "center":
  508. r =.5 * pad
  509. elif va == "baseline":
  510. max_ascent = max(h - d for w, h, d in whd_list)
  511. max_descent = max(d for w, h, d in whd_list)
  512. r = max_descent
  513. pad = max_ascent + max_descent
  514. #tick_pad = renderer.points_to_pixels(self.get_pad())
  515. # r : offset
  516. # pad : total height of the ticklabels. This will be used to
  517. # calculate the pad for the axislabel.
  518. return r, pad
  519. _default_alignments = dict(left=("center", "right"),
  520. right=("center", "left"),
  521. bottom=("baseline", "center"),
  522. top=("baseline", "center"))
  523. # set_default_alignments(self, d)
  524. _default_angles = dict(left=90,
  525. right=-90,
  526. bottom=0,
  527. top=180)
  528. def draw(self, renderer):
  529. if not self.get_visible():
  530. self._axislabel_pad = self._get_external_pad()
  531. return
  532. r, total_width = self._get_ticklabels_offsets(renderer,
  533. self._axis_direction)
  534. #self._set_external_pad(r+self._get_external_pad())
  535. pad = self._get_external_pad() + \
  536. renderer.points_to_pixels(self.get_pad())
  537. self._set_offset_radius(r+pad)
  538. #self._set_offset_radius(r)
  539. for (x, y), a, l in self._locs_angles_labels:
  540. if not l.strip(): continue
  541. self._set_ref_angle(a) #+ add_angle
  542. self.set_x(x)
  543. self.set_y(y)
  544. self.set_text(l)
  545. LabelBase.draw(self, renderer)
  546. self._axislabel_pad = total_width \
  547. + pad # the value saved will be used to draw axislabel.
  548. def set_locs_angles_labels(self, locs_angles_labels):
  549. self._locs_angles_labels = locs_angles_labels
  550. def get_window_extents(self, renderer):
  551. if not self.get_visible():
  552. self._axislabel_pad = self._get_external_pad()
  553. return []
  554. bboxes = []
  555. r, total_width = self._get_ticklabels_offsets(renderer,
  556. self._axis_direction)
  557. pad = self._get_external_pad() + \
  558. renderer.points_to_pixels(self.get_pad())
  559. self._set_offset_radius(r+pad)
  560. for (x, y), a, l in self._locs_angles_labels:
  561. self._set_ref_angle(a) #+ add_angle
  562. self.set_x(x)
  563. self.set_y(y)
  564. self.set_text(l)
  565. bb = LabelBase.get_window_extent(self, renderer)
  566. bboxes.append(bb)
  567. self._axislabel_pad = total_width \
  568. + pad # the value saved will be used to draw axislabel.
  569. return bboxes
  570. def get_texts_widths_heights_descents(self, renderer):
  571. """
  572. return a list of width, height, descent for ticklabels.
  573. """
  574. whd_list = []
  575. for (x, y), a, l in self._locs_angles_labels:
  576. if not l.strip(): continue
  577. clean_line, ismath = self.is_math_text(l)
  578. whd = renderer.get_text_width_height_descent(
  579. clean_line, self._fontproperties, ismath=ismath)
  580. whd_list.append(whd)
  581. return whd_list
  582. class GridlinesCollection(LineCollection):
  583. def __init__(self, *kl, **kwargs):
  584. """
  585. *which* : "major" or "minor"
  586. *axis* : "both", "x" or "y"
  587. """
  588. self._which = kwargs.pop("which", "major")
  589. self._axis = kwargs.pop("axis", "both")
  590. super(GridlinesCollection, self).__init__(*kl, **kwargs)
  591. self.set_grid_helper(None)
  592. def set_which(self, which):
  593. self._which = which
  594. def set_axis(self, axis):
  595. self._axis = axis
  596. def set_grid_helper(self, grid_helper):
  597. self._grid_helper = grid_helper
  598. def draw(self, renderer):
  599. if self._grid_helper is not None:
  600. self._grid_helper.update_lim(self.axes)
  601. gl = self._grid_helper.get_gridlines(self._which, self._axis)
  602. if gl:
  603. self.set_segments([np.transpose(l) for l in gl])
  604. else:
  605. self.set_segments([])
  606. super(GridlinesCollection, self).draw(renderer)
  607. class AxisArtist(martist.Artist):
  608. """
  609. An artist which draws axis (a line along which the n-th axes coord
  610. is constant) line, ticks, ticklabels, and axis label.
  611. """
  612. ZORDER=2.5
  613. @property
  614. def LABELPAD(self):
  615. return self.label.get_pad()
  616. @LABELPAD.setter
  617. def LABELPAD(self, v):
  618. return self.label.set_pad(v)
  619. def __init__(self, axes,
  620. helper,
  621. offset=None,
  622. axis_direction="bottom",
  623. **kw):
  624. """
  625. *axes* : axes
  626. *helper* : an AxisArtistHelper instance.
  627. """
  628. #axes is also used to follow the axis attribute (tick color, etc).
  629. super(AxisArtist, self).__init__(**kw)
  630. self.axes = axes
  631. self._axis_artist_helper = helper
  632. if offset is None:
  633. offset = (0, 0)
  634. self.dpi_transform = Affine2D()
  635. self.offset_transform = ScaledTranslation(offset[0], offset[1],
  636. self.dpi_transform)
  637. self._label_visible = True
  638. self._majortick_visible = True
  639. self._majorticklabel_visible = True
  640. self._minortick_visible = True
  641. self._minorticklabel_visible = True
  642. #if self._axis_artist_helper._loc in ["left", "right"]:
  643. if axis_direction in ["left", "right"]:
  644. axis_name = "ytick"
  645. self.axis = axes.yaxis
  646. else:
  647. axis_name = "xtick"
  648. self.axis = axes.xaxis
  649. self._axisline_style = None
  650. self._axis_direction = axis_direction
  651. self._init_line()
  652. self._init_ticks(axis_name, **kw)
  653. self._init_offsetText(axis_direction)
  654. self._init_label()
  655. self.set_zorder(self.ZORDER)
  656. self._rotate_label_along_line = False
  657. # axis direction
  658. self._tick_add_angle = 180.
  659. self._ticklabel_add_angle = 0.
  660. self._axislabel_add_angle = 0.
  661. self.set_axis_direction(axis_direction)
  662. # axis direction
  663. def set_axis_direction(self, axis_direction):
  664. """
  665. Adjust the direction, text angle, text alignment of
  666. ticklabels, labels following the matplotlib convention for
  667. the rectangle axes.
  668. The *axis_direction* must be one of [left, right, bottom,
  669. top].
  670. ===================== ========== ========= ========== ==========
  671. property left bottom right top
  672. ===================== ========== ========= ========== ==========
  673. ticklabels location "-" "+" "+" "-"
  674. axislabel location "-" "+" "+" "-"
  675. ticklabels angle 90 0 -90 180
  676. ticklabel va center baseline center baseline
  677. ticklabel ha right center right center
  678. axislabel angle 180 0 0 180
  679. axislabel va center top center bottom
  680. axislabel ha right center right center
  681. ===================== ========== ========= ========== ==========
  682. Note that the direction "+" and "-" are relative to the direction of
  683. the increasing coordinate. Also, the text angles are actually
  684. relative to (90 + angle of the direction to the ticklabel),
  685. which gives 0 for bottom axis.
  686. """
  687. if axis_direction not in ["left", "right", "top", "bottom"]:
  688. raise ValueError('direction must be on of "left", "right", "top", "bottom"')
  689. self._axis_direction = axis_direction
  690. if axis_direction in ["left", "top"]:
  691. #self._set_tick_direction("+")
  692. self.set_ticklabel_direction("-")
  693. self.set_axislabel_direction("-")
  694. else:
  695. #self._set_tick_direction("-")
  696. self.set_ticklabel_direction("+")
  697. self.set_axislabel_direction("+")
  698. self.major_ticklabels.set_axis_direction(axis_direction)
  699. self.label.set_axis_direction(axis_direction)
  700. # def _set_tick_direction(self, d):
  701. # if d not in ["+", "-"]:
  702. # raise ValueError('direction must be on of "in", "out"')
  703. # if d == "+":
  704. # self._tick_add_angle = 0 #get_helper()._extremes=0, 10
  705. # else:
  706. # self._tick_add_angle = 180 #get_helper()._extremes=0, 10
  707. def set_ticklabel_direction(self, tick_direction):
  708. """
  709. Adjust the direction of the ticklabel.
  710. ACCEPTS: [ "+" | "-" ]
  711. Note that the label_direction '+' and '-' are relative to the
  712. direction of the increasing coordinate.
  713. """
  714. if tick_direction not in ["+", "-"]:
  715. raise ValueError('direction must be one of "+", "-"')
  716. if tick_direction == "-":
  717. self._ticklabel_add_angle = 180
  718. else:
  719. self._ticklabel_add_angle = 0
  720. def invert_ticklabel_direction(self):
  721. self._ticklabel_add_angle = (self._ticklabel_add_angle + 180) % 360
  722. self.major_ticklabels.invert_axis_direction()
  723. self.minor_ticklabels.invert_axis_direction()
  724. # def invert_ticks_direction(self):
  725. # self.major_ticks.set_tick_out(not self.major_ticks.get_tick_out())
  726. # self.minor_ticks.set_tick_out(not self.minor_ticks.get_tick_out())
  727. def set_axislabel_direction(self, label_direction):
  728. """
  729. Adjust the direction of the axislabel.
  730. ACCEPTS: [ "+" | "-" ]
  731. Note that the label_direction '+' and '-' are relative to the
  732. direction of the increasing coordinate.
  733. """
  734. if label_direction not in ["+", "-"]:
  735. raise ValueError('direction must be one of "+", "-"')
  736. if label_direction == "-":
  737. self._axislabel_add_angle = 180
  738. else:
  739. self._axislabel_add_angle = 0
  740. def get_transform(self):
  741. return self.axes.transAxes + self.offset_transform
  742. def get_helper(self):
  743. """
  744. Return axis artist helper instance.
  745. """
  746. return self._axis_artist_helper
  747. def set_axisline_style(self, axisline_style=None, **kw):
  748. """
  749. Set the axisline style.
  750. *axisline_style* can be a string with axisline style name with optional
  751. comma-separated attributes. Alternatively, the attrs can
  752. be provided as keywords.
  753. set_arrowstyle("->,size=1.5")
  754. set_arrowstyle("->", size=1.5)
  755. Old attrs simply are forgotten.
  756. Without argument (or with arrowstyle=None), return
  757. available styles as a list of strings.
  758. """
  759. if axisline_style==None:
  760. return AxislineStyle.pprint_styles()
  761. if isinstance(axisline_style, AxislineStyle._Base):
  762. self._axisline_style = axisline_style
  763. else:
  764. self._axisline_style = AxislineStyle(axisline_style, **kw)
  765. self._init_line()
  766. def get_axisline_style(self):
  767. """
  768. return the current axisline style.
  769. """
  770. return self._axisline_style
  771. def _init_line(self):
  772. """
  773. Initialize the *line* artist that is responsible to draw the axis line.
  774. """
  775. tran = self._axis_artist_helper.get_line_transform(self.axes) \
  776. + self.offset_transform
  777. axisline_style = self.get_axisline_style()
  778. if axisline_style is None:
  779. self.line = BezierPath(self._axis_artist_helper.get_line(self.axes),
  780. color=rcParams['axes.edgecolor'],
  781. linewidth=rcParams['axes.linewidth'],
  782. transform=tran)
  783. else:
  784. self.line = axisline_style(self, transform=tran)
  785. def _draw_line(self, renderer):
  786. self.line.set_path(self._axis_artist_helper.get_line(self.axes))
  787. if self.get_axisline_style() is not None:
  788. self.line.set_line_mutation_scale(self.major_ticklabels.get_size())
  789. self.line.draw(renderer)
  790. def _init_ticks(self, axis_name, **kw):
  791. trans=self._axis_artist_helper.get_tick_transform(self.axes) \
  792. + self.offset_transform
  793. major_tick_size = kw.get("major_tick_size",
  794. rcParams['%s.major.size'%axis_name])
  795. major_tick_pad = kw.get("major_tick_pad",
  796. rcParams['%s.major.pad'%axis_name])
  797. minor_tick_size = kw.get("minor_tick_size",
  798. rcParams['%s.minor.size'%axis_name])
  799. minor_tick_pad = kw.get("minor_tick_pad",
  800. rcParams['%s.minor.pad'%axis_name])
  801. self.major_ticks = Ticks(major_tick_size,
  802. axis=self.axis,
  803. transform=trans)
  804. self.minor_ticks = Ticks(minor_tick_size,
  805. axis=self.axis,
  806. transform=trans)
  807. if axis_name == "xaxis":
  808. size = rcParams['xtick.labelsize']
  809. else:
  810. size = rcParams['ytick.labelsize']
  811. fontprops = font_manager.FontProperties(size=size)
  812. self.major_ticklabels = TickLabels(size=size, axis=self.axis,
  813. axis_direction=self._axis_direction)
  814. self.minor_ticklabels = TickLabels(size=size, axis=self.axis,
  815. axis_direction=self._axis_direction)
  816. self.major_ticklabels.set(figure = self.axes.figure,
  817. transform=trans,
  818. fontproperties=fontprops)
  819. self.major_ticklabels.set_pad(major_tick_pad)
  820. self.minor_ticklabels.set(figure = self.axes.figure,
  821. transform=trans,
  822. fontproperties=fontprops)
  823. self.minor_ticklabels.set_pad(minor_tick_pad)
  824. def _get_tick_info(self, tick_iter):
  825. """
  826. return ticks_loc_angle, ticklabels_loc_angle_label
  827. ticks_loc_angle : list of locs and angles for ticks
  828. ticklabels_loc_angle_label : list of locs, angles and labels for tickslabels
  829. """
  830. ticks_loc_angle = []
  831. ticklabels_loc_angle_label = []
  832. tick_add_angle = self._tick_add_angle
  833. ticklabel_add_angle = self._ticklabel_add_angle
  834. for loc, angle_normal, angle_tangent, label in tick_iter:
  835. angle_label = angle_tangent - 90
  836. angle_label += ticklabel_add_angle
  837. if np.cos((angle_label - angle_normal)/180.*np.pi) < 0.:
  838. angle_tick = angle_normal
  839. else:
  840. angle_tick = angle_normal + 180
  841. ticks_loc_angle.append([loc, angle_tick])
  842. ticklabels_loc_angle_label.append([loc, angle_label, label])
  843. return ticks_loc_angle, ticklabels_loc_angle_label
  844. def _update_ticks(self, renderer):
  845. # set extra pad for major and minor ticklabels:
  846. # use ticksize of majorticks even for minor ticks. not clear what is best.
  847. dpi_cor = renderer.points_to_pixels(1.)
  848. if self.major_ticks.get_visible() and self.major_ticks.get_tick_out():
  849. self.major_ticklabels._set_external_pad(self.major_ticks._ticksize*dpi_cor)
  850. self.minor_ticklabels._set_external_pad(self.major_ticks._ticksize*dpi_cor)
  851. else:
  852. self.major_ticklabels._set_external_pad(0)
  853. self.minor_ticklabels._set_external_pad(0)
  854. majortick_iter, minortick_iter = \
  855. self._axis_artist_helper.get_tick_iterators(self.axes)
  856. tick_loc_angle, ticklabel_loc_angle_label \
  857. = self._get_tick_info(majortick_iter)
  858. self.major_ticks.set_locs_angles(tick_loc_angle)
  859. self.major_ticklabels.set_locs_angles_labels(ticklabel_loc_angle_label)
  860. #self.major_ticks.draw(renderer)
  861. #self.major_ticklabels.draw(renderer)
  862. # minor ticks
  863. tick_loc_angle, ticklabel_loc_angle_label \
  864. = self._get_tick_info(minortick_iter)
  865. self.minor_ticks.set_locs_angles(tick_loc_angle)
  866. self.minor_ticklabels.set_locs_angles_labels(ticklabel_loc_angle_label)
  867. #self.minor_ticks.draw(renderer)
  868. #self.minor_ticklabels.draw(renderer)
  869. #if (self.major_ticklabels.get_visible() or self.minor_ticklabels.get_visible()):
  870. # self._draw_offsetText(renderer)
  871. return self.major_ticklabels.get_window_extents(renderer)
  872. def _draw_ticks(self, renderer):
  873. extents = self._update_ticks(renderer)
  874. self.major_ticks.draw(renderer)
  875. self.major_ticklabels.draw(renderer)
  876. self.minor_ticks.draw(renderer)
  877. self.minor_ticklabels.draw(renderer)
  878. if (self.major_ticklabels.get_visible() or self.minor_ticklabels.get_visible()):
  879. self._draw_offsetText(renderer)
  880. return extents
  881. def _draw_ticks2(self, renderer):
  882. # set extra pad for major and minor ticklabels:
  883. # use ticksize of majorticks even for minor ticks. not clear what is best.
  884. dpi_cor = renderer.points_to_pixels(1.)
  885. if self.major_ticks.get_visible() and self.major_ticks.get_tick_out():
  886. self.major_ticklabels._set_external_pad(self.major_ticks._ticksize*dpi_cor)
  887. self.minor_ticklabels._set_external_pad(self.major_ticks._ticksize*dpi_cor)
  888. else:
  889. self.major_ticklabels._set_external_pad(0)
  890. self.minor_ticklabels._set_external_pad(0)
  891. majortick_iter, minortick_iter = \
  892. self._axis_artist_helper.get_tick_iterators(self.axes)
  893. tick_loc_angle, ticklabel_loc_angle_label \
  894. = self._get_tick_info(majortick_iter)
  895. self.major_ticks.set_locs_angles(tick_loc_angle)
  896. self.major_ticklabels.set_locs_angles_labels(ticklabel_loc_angle_label)
  897. self.major_ticks.draw(renderer)
  898. self.major_ticklabels.draw(renderer)
  899. # minor ticks
  900. tick_loc_angle, ticklabel_loc_angle_label \
  901. = self._get_tick_info(minortick_iter)
  902. self.minor_ticks.set_locs_angles(tick_loc_angle)
  903. self.minor_ticklabels.set_locs_angles_labels(ticklabel_loc_angle_label)
  904. self.minor_ticks.draw(renderer)
  905. self.minor_ticklabels.draw(renderer)
  906. if (self.major_ticklabels.get_visible() or self.minor_ticklabels.get_visible()):
  907. self._draw_offsetText(renderer)
  908. return self.major_ticklabels.get_window_extents(renderer)
  909. _offsetText_pos = dict(left=(0, 1, "bottom", "right"),
  910. right=(1, 1, "bottom", "left"),
  911. bottom=(1, 0, "top", "right"),
  912. top=(1, 1, "bottom", "right"))
  913. def _init_offsetText(self, direction):
  914. x,y,va,ha = self._offsetText_pos[direction]
  915. self.offsetText = mtext.Annotation("",
  916. xy=(x,y), xycoords="axes fraction",
  917. xytext=(0,0), textcoords="offset points",
  918. #fontproperties = fp,
  919. color = rcParams['xtick.color'],
  920. verticalalignment=va,
  921. horizontalalignment=ha,
  922. )
  923. self.offsetText.set_transform(IdentityTransform())
  924. self.axes._set_artist_props(self.offsetText)
  925. def _update_offsetText(self):
  926. self.offsetText.set_text( self.axis.major.formatter.get_offset() )
  927. self.offsetText.set_size(self.major_ticklabels.get_size())
  928. offset = self.major_ticklabels.get_pad() + self.major_ticklabels.get_size() + 2.
  929. self.offsetText.xyann= (0, offset)
  930. def _draw_offsetText(self, renderer):
  931. self._update_offsetText()
  932. self.offsetText.draw(renderer)
  933. def _init_label(self, **kw):
  934. # x in axes coords, y in display coords (to be updated at draw
  935. # time by _update_label_positions)
  936. labelsize = kw.get("labelsize",
  937. rcParams['axes.labelsize'])
  938. #labelcolor = kw.get("labelcolor",
  939. # rcParams['axes.labelcolor'])
  940. fontprops = font_manager.FontProperties(
  941. size=labelsize,
  942. weight=rcParams['axes.labelweight'])
  943. textprops = dict(fontproperties = fontprops)
  944. #color = labelcolor)
  945. tr = self._axis_artist_helper.get_axislabel_transform(self.axes) \
  946. + self.offset_transform
  947. self.label = AxisLabel(0, 0, "__from_axes__",
  948. color = "auto", #rcParams['axes.labelcolor'],
  949. fontproperties=fontprops,
  950. axis=self.axis,
  951. transform=tr,
  952. axis_direction=self._axis_direction,
  953. )
  954. self.label.set_figure(self.axes.figure)
  955. labelpad = kw.get("labelpad", 5)
  956. self.label.set_pad(labelpad)
  957. def _update_label(self, renderer):
  958. if not self.label.get_visible():
  959. return
  960. fontprops = font_manager.FontProperties(
  961. size=rcParams['axes.labelsize'],
  962. weight=rcParams['axes.labelweight'])
  963. #pad_points = self.major_tick_pad
  964. #if abs(self._ticklabel_add_angle - self._axislabel_add_angle)%360 > 90:
  965. if self._ticklabel_add_angle != self._axislabel_add_angle:
  966. if (self.major_ticks.get_visible() and not self.major_ticks.get_tick_out()) \
  967. or \
  968. (self.minor_ticks.get_visible() and not self.major_ticks.get_tick_out()):
  969. axislabel_pad = self.major_ticks._ticksize
  970. else:
  971. axislabel_pad = 0
  972. else:
  973. axislabel_pad = max(self.major_ticklabels._axislabel_pad,
  974. self.minor_ticklabels._axislabel_pad)
  975. #label_offset = axislabel_pad + self.LABELPAD
  976. #self.label._set_offset_radius(label_offset)
  977. self.label._set_external_pad(axislabel_pad)
  978. xy, angle_tangent = self._axis_artist_helper.get_axislabel_pos_angle(self.axes)
  979. if xy is None: return
  980. angle_label = angle_tangent - 90
  981. x, y = xy
  982. self.label._set_ref_angle(angle_label+self._axislabel_add_angle)
  983. self.label.set(x=x, y=y)
  984. def _draw_label(self, renderer):
  985. self._update_label(renderer)
  986. self.label.draw(renderer)
  987. def _draw_label2(self, renderer):
  988. if not self.label.get_visible():
  989. return
  990. fontprops = font_manager.FontProperties(
  991. size=rcParams['axes.labelsize'],
  992. weight=rcParams['axes.labelweight'])
  993. #pad_points = self.major_tick_pad
  994. #if abs(self._ticklabel_add_angle - self._axislabel_add_angle)%360 > 90:
  995. if self._ticklabel_add_angle != self._axislabel_add_angle:
  996. if (self.major_ticks.get_visible() and not self.major_ticks.get_tick_out()) \
  997. or \
  998. (self.minor_ticks.get_visible() and not self.major_ticks.get_tick_out()):
  999. axislabel_pad = self.major_ticks._ticksize
  1000. else:
  1001. axislabel_pad = 0
  1002. else:
  1003. axislabel_pad = max(self.major_ticklabels._axislabel_pad,
  1004. self.minor_ticklabels._axislabel_pad)
  1005. #label_offset = axislabel_pad + self.LABELPAD
  1006. #self.label._set_offset_radius(label_offset)
  1007. self.label._set_external_pad(axislabel_pad)
  1008. xy, angle_tangent = self._axis_artist_helper.get_axislabel_pos_angle(self.axes)
  1009. if xy is None: return
  1010. angle_label = angle_tangent - 90
  1011. x, y = xy
  1012. self.label._set_ref_angle(angle_label+self._axislabel_add_angle)
  1013. self.label.set(x=x, y=y)
  1014. self.label.draw(renderer)
  1015. def set_label(self, s):
  1016. self.label.set_text(s)
  1017. def get_tightbbox(self, renderer):
  1018. if not self.get_visible(): return
  1019. self._axis_artist_helper.update_lim(self.axes)
  1020. dpi_cor = renderer.points_to_pixels(1.)
  1021. self.dpi_transform.clear().scale(dpi_cor, dpi_cor)
  1022. bb = []
  1023. self._update_ticks(renderer)
  1024. #if self.major_ticklabels.get_visible():
  1025. bb.extend(self.major_ticklabels.get_window_extents(renderer))
  1026. #if self.minor_ticklabels.get_visible():
  1027. bb.extend(self.minor_ticklabels.get_window_extents(renderer))
  1028. self._update_label(renderer)
  1029. #if self.label.get_visible():
  1030. bb.append(self.label.get_window_extent(renderer))
  1031. bb.append(self.offsetText.get_window_extent(renderer))
  1032. bb = [b for b in bb if b and (b.width!=0 or b.height!=0)]
  1033. if bb:
  1034. _bbox = Bbox.union(bb)
  1035. return _bbox
  1036. else:
  1037. return None
  1038. #self._draw_line(renderer)
  1039. #self._draw_ticks(renderer)
  1040. #self._draw_offsetText(renderer)
  1041. #self._draw_label(renderer)
  1042. @allow_rasterization
  1043. def draw(self, renderer):
  1044. 'Draw the axis lines, tick lines and labels'
  1045. if not self.get_visible(): return
  1046. renderer.open_group(__name__)
  1047. self._axis_artist_helper.update_lim(self.axes)
  1048. dpi_cor = renderer.points_to_pixels(1.)
  1049. self.dpi_transform.clear().scale(dpi_cor, dpi_cor)
  1050. self._draw_ticks(renderer)
  1051. self._draw_line(renderer)
  1052. #self._draw_offsetText(renderer)
  1053. self._draw_label(renderer)
  1054. renderer.close_group(__name__)
  1055. #def get_ticklabel_extents(self, renderer):
  1056. # pass
  1057. def toggle(self, all=None, ticks=None, ticklabels=None, label=None):
  1058. """
  1059. Toggle visibility of ticks, ticklabels, and (axis) label.
  1060. To turn all off, ::
  1061. axis.toggle(all=False)
  1062. To turn all off but ticks on ::
  1063. axis.toggle(all=False, ticks=True)
  1064. To turn all on but (axis) label off ::
  1065. axis.toggle(all=True, label=False))
  1066. """
  1067. if all:
  1068. _ticks, _ticklabels, _label = True, True, True
  1069. elif all is not None:
  1070. _ticks, _ticklabels, _label = False, False, False
  1071. else:
  1072. _ticks, _ticklabels, _label = None, None, None
  1073. if ticks is not None:
  1074. _ticks = ticks
  1075. if ticklabels is not None:
  1076. _ticklabels = ticklabels
  1077. if label is not None:
  1078. _label = label
  1079. if _ticks is not None:
  1080. self.major_ticks.set_visible(_ticks)
  1081. self.minor_ticks.set_visible(_ticks)
  1082. if _ticklabels is not None:
  1083. self.major_ticklabels.set_visible(_ticklabels)
  1084. self.minor_ticklabels.set_visible(_ticklabels)
  1085. if _label is not None:
  1086. self.label.set_visible(_label)