legend_handler.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. """
  2. Default legend handlers.
  3. It is strongly encouraged to have read the :doc:`legend guide
  4. </tutorials/intermediate/legend_guide>` before this documentation.
  5. Legend handlers are expected to be a callable object with a following
  6. signature. ::
  7. legend_handler(legend, orig_handle, fontsize, handlebox)
  8. Where *legend* is the legend itself, *orig_handle* is the original
  9. plot, *fontsize* is the fontsize in pixels, and *handlebox* is a
  10. OffsetBox instance. Within the call, you should create relevant
  11. artists (using relevant properties from the *legend* and/or
  12. *orig_handle*) and add them into the handlebox. The artists needs to
  13. be scaled according to the fontsize (note that the size is in pixel,
  14. i.e., this is dpi-scaled value).
  15. This module includes definition of several legend handler classes
  16. derived from the base class (HandlerBase) with the following method::
  17. def legend_artist(self, legend, orig_handle, fontsize, handlebox)
  18. """
  19. from itertools import cycle
  20. import numpy as np
  21. from matplotlib import cbook
  22. from matplotlib.lines import Line2D
  23. from matplotlib.patches import Rectangle
  24. import matplotlib.collections as mcoll
  25. import matplotlib.colors as mcolors
  26. def update_from_first_child(tgt, src):
  27. first_child = next(iter(src.get_children()), None)
  28. if first_child is not None:
  29. tgt.update_from(first_child)
  30. class HandlerBase:
  31. """
  32. A Base class for default legend handlers.
  33. The derived classes are meant to override *create_artists* method, which
  34. has a following signature.::
  35. def create_artists(self, legend, orig_handle,
  36. xdescent, ydescent, width, height, fontsize,
  37. trans):
  38. The overridden method needs to create artists of the given
  39. transform that fits in the given dimension (xdescent, ydescent,
  40. width, height) that are scaled by fontsize if necessary.
  41. """
  42. def __init__(self, xpad=0., ypad=0., update_func=None):
  43. self._xpad, self._ypad = xpad, ypad
  44. self._update_prop_func = update_func
  45. def _update_prop(self, legend_handle, orig_handle):
  46. if self._update_prop_func is None:
  47. self._default_update_prop(legend_handle, orig_handle)
  48. else:
  49. self._update_prop_func(legend_handle, orig_handle)
  50. def _default_update_prop(self, legend_handle, orig_handle):
  51. legend_handle.update_from(orig_handle)
  52. def update_prop(self, legend_handle, orig_handle, legend):
  53. self._update_prop(legend_handle, orig_handle)
  54. legend._set_artist_props(legend_handle)
  55. legend_handle.set_clip_box(None)
  56. legend_handle.set_clip_path(None)
  57. def adjust_drawing_area(self, legend, orig_handle,
  58. xdescent, ydescent, width, height, fontsize,
  59. ):
  60. xdescent = xdescent - self._xpad * fontsize
  61. ydescent = ydescent - self._ypad * fontsize
  62. width = width - self._xpad * fontsize
  63. height = height - self._ypad * fontsize
  64. return xdescent, ydescent, width, height
  65. def legend_artist(self, legend, orig_handle,
  66. fontsize, handlebox):
  67. """
  68. Return the artist that this HandlerBase generates for the given
  69. original artist/handle.
  70. Parameters
  71. ----------
  72. legend : `~matplotlib.legend.Legend`
  73. The legend for which these legend artists are being created.
  74. orig_handle : :class:`matplotlib.artist.Artist` or similar
  75. The object for which these legend artists are being created.
  76. fontsize : int
  77. The fontsize in pixels. The artists being created should
  78. be scaled according to the given fontsize.
  79. handlebox : `matplotlib.offsetbox.OffsetBox`
  80. The box which has been created to hold this legend entry's
  81. artists. Artists created in the `legend_artist` method must
  82. be added to this handlebox inside this method.
  83. """
  84. xdescent, ydescent, width, height = self.adjust_drawing_area(
  85. legend, orig_handle,
  86. handlebox.xdescent, handlebox.ydescent,
  87. handlebox.width, handlebox.height,
  88. fontsize)
  89. artists = self.create_artists(legend, orig_handle,
  90. xdescent, ydescent, width, height,
  91. fontsize, handlebox.get_transform())
  92. # create_artists will return a list of artists.
  93. for a in artists:
  94. handlebox.add_artist(a)
  95. # we only return the first artist
  96. return artists[0]
  97. def create_artists(self, legend, orig_handle,
  98. xdescent, ydescent, width, height, fontsize,
  99. trans):
  100. raise NotImplementedError('Derived must override')
  101. class HandlerNpoints(HandlerBase):
  102. """
  103. A legend handler that shows *numpoints* points in the legend entry.
  104. """
  105. def __init__(self, marker_pad=0.3, numpoints=None, **kw):
  106. """
  107. Parameters
  108. ----------
  109. marker_pad : float
  110. Padding between points in legend entry.
  111. numpoints : int
  112. Number of points to show in legend entry.
  113. Notes
  114. -----
  115. Any other keyword arguments are given to `HandlerBase`.
  116. """
  117. HandlerBase.__init__(self, **kw)
  118. self._numpoints = numpoints
  119. self._marker_pad = marker_pad
  120. def get_numpoints(self, legend):
  121. if self._numpoints is None:
  122. return legend.numpoints
  123. else:
  124. return self._numpoints
  125. def get_xdata(self, legend, xdescent, ydescent, width, height, fontsize):
  126. numpoints = self.get_numpoints(legend)
  127. if numpoints > 1:
  128. # we put some pad here to compensate the size of the marker
  129. pad = self._marker_pad * fontsize
  130. xdata = np.linspace(-xdescent + pad,
  131. -xdescent + width - pad,
  132. numpoints)
  133. xdata_marker = xdata
  134. else:
  135. xdata = [-xdescent, -xdescent + width]
  136. xdata_marker = [-xdescent + 0.5 * width]
  137. return xdata, xdata_marker
  138. class HandlerNpointsYoffsets(HandlerNpoints):
  139. """
  140. A legend handler that shows *numpoints* in the legend, and allows them to
  141. be individually offset in the y-direction.
  142. """
  143. def __init__(self, numpoints=None, yoffsets=None, **kw):
  144. """
  145. Parameters
  146. ----------
  147. numpoints : int
  148. Number of points to show in legend entry.
  149. yoffsets : array of floats
  150. Length *numpoints* list of y offsets for each point in
  151. legend entry.
  152. Notes
  153. -----
  154. Any other keyword arguments are given to `HandlerNpoints`.
  155. """
  156. HandlerNpoints.__init__(self, numpoints=numpoints, **kw)
  157. self._yoffsets = yoffsets
  158. def get_ydata(self, legend, xdescent, ydescent, width, height, fontsize):
  159. if self._yoffsets is None:
  160. ydata = height * legend._scatteryoffsets
  161. else:
  162. ydata = height * np.asarray(self._yoffsets)
  163. return ydata
  164. class HandlerLine2D(HandlerNpoints):
  165. """
  166. Handler for `.Line2D` instances.
  167. """
  168. def __init__(self, marker_pad=0.3, numpoints=None, **kw):
  169. """
  170. Parameters
  171. ----------
  172. marker_pad : float
  173. Padding between points in legend entry.
  174. numpoints : int
  175. Number of points to show in legend entry.
  176. Notes
  177. -----
  178. Any other keyword arguments are given to `HandlerNpoints`.
  179. """
  180. HandlerNpoints.__init__(self, marker_pad=marker_pad,
  181. numpoints=numpoints, **kw)
  182. def create_artists(self, legend, orig_handle,
  183. xdescent, ydescent, width, height, fontsize,
  184. trans):
  185. xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent,
  186. width, height, fontsize)
  187. ydata = np.full_like(xdata, ((height - ydescent) / 2))
  188. legline = Line2D(xdata, ydata)
  189. self.update_prop(legline, orig_handle, legend)
  190. legline.set_drawstyle('default')
  191. legline.set_marker("")
  192. legline_marker = Line2D(xdata_marker, ydata[:len(xdata_marker)])
  193. self.update_prop(legline_marker, orig_handle, legend)
  194. legline_marker.set_linestyle('None')
  195. if legend.markerscale != 1:
  196. newsz = legline_marker.get_markersize() * legend.markerscale
  197. legline_marker.set_markersize(newsz)
  198. # we don't want to add this to the return list because
  199. # the texts and handles are assumed to be in one-to-one
  200. # correspondence.
  201. legline._legmarker = legline_marker
  202. legline.set_transform(trans)
  203. legline_marker.set_transform(trans)
  204. return [legline, legline_marker]
  205. class HandlerPatch(HandlerBase):
  206. """
  207. Handler for `.Patch` instances.
  208. """
  209. def __init__(self, patch_func=None, **kw):
  210. """
  211. Parameters
  212. ----------
  213. patch_func : callable, optional
  214. The function that creates the legend key artist.
  215. *patch_func* should have the signature::
  216. def patch_func(legend=legend, orig_handle=orig_handle,
  217. xdescent=xdescent, ydescent=ydescent,
  218. width=width, height=height, fontsize=fontsize)
  219. Subsequently the created artist will have its ``update_prop``
  220. method called and the appropriate transform will be applied.
  221. Notes
  222. -----
  223. Any other keyword arguments are given to `HandlerBase`.
  224. """
  225. HandlerBase.__init__(self, **kw)
  226. self._patch_func = patch_func
  227. def _create_patch(self, legend, orig_handle,
  228. xdescent, ydescent, width, height, fontsize):
  229. if self._patch_func is None:
  230. p = Rectangle(xy=(-xdescent, -ydescent),
  231. width=width, height=height)
  232. else:
  233. p = self._patch_func(legend=legend, orig_handle=orig_handle,
  234. xdescent=xdescent, ydescent=ydescent,
  235. width=width, height=height, fontsize=fontsize)
  236. return p
  237. def create_artists(self, legend, orig_handle,
  238. xdescent, ydescent, width, height, fontsize, trans):
  239. p = self._create_patch(legend, orig_handle,
  240. xdescent, ydescent, width, height, fontsize)
  241. self.update_prop(p, orig_handle, legend)
  242. p.set_transform(trans)
  243. return [p]
  244. class HandlerLineCollection(HandlerLine2D):
  245. """
  246. Handler for `.LineCollection` instances.
  247. """
  248. def get_numpoints(self, legend):
  249. if self._numpoints is None:
  250. return legend.scatterpoints
  251. else:
  252. return self._numpoints
  253. def _default_update_prop(self, legend_handle, orig_handle):
  254. lw = orig_handle.get_linewidths()[0]
  255. dashes = orig_handle._us_linestyles[0]
  256. color = orig_handle.get_colors()[0]
  257. legend_handle.set_color(color)
  258. legend_handle.set_linestyle(dashes)
  259. legend_handle.set_linewidth(lw)
  260. def create_artists(self, legend, orig_handle,
  261. xdescent, ydescent, width, height, fontsize, trans):
  262. xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent,
  263. width, height, fontsize)
  264. ydata = np.full_like(xdata, (height - ydescent) / 2)
  265. legline = Line2D(xdata, ydata)
  266. self.update_prop(legline, orig_handle, legend)
  267. legline.set_transform(trans)
  268. return [legline]
  269. class HandlerRegularPolyCollection(HandlerNpointsYoffsets):
  270. r"""Handler for `.RegularPolyCollection`\s."""
  271. def __init__(self, yoffsets=None, sizes=None, **kw):
  272. HandlerNpointsYoffsets.__init__(self, yoffsets=yoffsets, **kw)
  273. self._sizes = sizes
  274. def get_numpoints(self, legend):
  275. if self._numpoints is None:
  276. return legend.scatterpoints
  277. else:
  278. return self._numpoints
  279. def get_sizes(self, legend, orig_handle,
  280. xdescent, ydescent, width, height, fontsize):
  281. if self._sizes is None:
  282. handle_sizes = orig_handle.get_sizes()
  283. if not len(handle_sizes):
  284. handle_sizes = [1]
  285. size_max = max(handle_sizes) * legend.markerscale ** 2
  286. size_min = min(handle_sizes) * legend.markerscale ** 2
  287. numpoints = self.get_numpoints(legend)
  288. if numpoints < 4:
  289. sizes = [.5 * (size_max + size_min), size_max,
  290. size_min][:numpoints]
  291. else:
  292. rng = (size_max - size_min)
  293. sizes = rng * np.linspace(0, 1, numpoints) + size_min
  294. else:
  295. sizes = self._sizes
  296. return sizes
  297. def update_prop(self, legend_handle, orig_handle, legend):
  298. self._update_prop(legend_handle, orig_handle)
  299. legend_handle.set_figure(legend.figure)
  300. # legend._set_artist_props(legend_handle)
  301. legend_handle.set_clip_box(None)
  302. legend_handle.set_clip_path(None)
  303. def create_collection(self, orig_handle, sizes, offsets, transOffset):
  304. p = type(orig_handle)(orig_handle.get_numsides(),
  305. rotation=orig_handle.get_rotation(),
  306. sizes=sizes,
  307. offsets=offsets,
  308. transOffset=transOffset,
  309. )
  310. return p
  311. def create_artists(self, legend, orig_handle,
  312. xdescent, ydescent, width, height, fontsize,
  313. trans):
  314. xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent,
  315. width, height, fontsize)
  316. ydata = self.get_ydata(legend, xdescent, ydescent,
  317. width, height, fontsize)
  318. sizes = self.get_sizes(legend, orig_handle, xdescent, ydescent,
  319. width, height, fontsize)
  320. p = self.create_collection(orig_handle, sizes,
  321. offsets=list(zip(xdata_marker, ydata)),
  322. transOffset=trans)
  323. self.update_prop(p, orig_handle, legend)
  324. p._transOffset = trans
  325. return [p]
  326. class HandlerPathCollection(HandlerRegularPolyCollection):
  327. r"""Handler for `.PathCollection`\s, which are used by `~.Axes.scatter`."""
  328. def create_collection(self, orig_handle, sizes, offsets, transOffset):
  329. p = type(orig_handle)([orig_handle.get_paths()[0]],
  330. sizes=sizes,
  331. offsets=offsets,
  332. transOffset=transOffset,
  333. )
  334. return p
  335. class HandlerCircleCollection(HandlerRegularPolyCollection):
  336. r"""Handler for `.CircleCollection`\s."""
  337. def create_collection(self, orig_handle, sizes, offsets, transOffset):
  338. p = type(orig_handle)(sizes,
  339. offsets=offsets,
  340. transOffset=transOffset,
  341. )
  342. return p
  343. class HandlerErrorbar(HandlerLine2D):
  344. """Handler for Errorbars."""
  345. def __init__(self, xerr_size=0.5, yerr_size=None,
  346. marker_pad=0.3, numpoints=None, **kw):
  347. self._xerr_size = xerr_size
  348. self._yerr_size = yerr_size
  349. HandlerLine2D.__init__(self, marker_pad=marker_pad,
  350. numpoints=numpoints, **kw)
  351. def get_err_size(self, legend, xdescent, ydescent,
  352. width, height, fontsize):
  353. xerr_size = self._xerr_size * fontsize
  354. if self._yerr_size is None:
  355. yerr_size = xerr_size
  356. else:
  357. yerr_size = self._yerr_size * fontsize
  358. return xerr_size, yerr_size
  359. def create_artists(self, legend, orig_handle,
  360. xdescent, ydescent, width, height, fontsize,
  361. trans):
  362. plotlines, caplines, barlinecols = orig_handle
  363. xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent,
  364. width, height, fontsize)
  365. ydata = np.full_like(xdata, (height - ydescent) / 2)
  366. legline = Line2D(xdata, ydata)
  367. xdata_marker = np.asarray(xdata_marker)
  368. ydata_marker = np.asarray(ydata[:len(xdata_marker)])
  369. xerr_size, yerr_size = self.get_err_size(legend, xdescent, ydescent,
  370. width, height, fontsize)
  371. legline_marker = Line2D(xdata_marker, ydata_marker)
  372. # when plotlines are None (only errorbars are drawn), we just
  373. # make legline invisible.
  374. if plotlines is None:
  375. legline.set_visible(False)
  376. legline_marker.set_visible(False)
  377. else:
  378. self.update_prop(legline, plotlines, legend)
  379. legline.set_drawstyle('default')
  380. legline.set_marker('None')
  381. self.update_prop(legline_marker, plotlines, legend)
  382. legline_marker.set_linestyle('None')
  383. if legend.markerscale != 1:
  384. newsz = legline_marker.get_markersize() * legend.markerscale
  385. legline_marker.set_markersize(newsz)
  386. handle_barlinecols = []
  387. handle_caplines = []
  388. if orig_handle.has_xerr:
  389. verts = [((x - xerr_size, y), (x + xerr_size, y))
  390. for x, y in zip(xdata_marker, ydata_marker)]
  391. coll = mcoll.LineCollection(verts)
  392. self.update_prop(coll, barlinecols[0], legend)
  393. handle_barlinecols.append(coll)
  394. if caplines:
  395. capline_left = Line2D(xdata_marker - xerr_size, ydata_marker)
  396. capline_right = Line2D(xdata_marker + xerr_size, ydata_marker)
  397. self.update_prop(capline_left, caplines[0], legend)
  398. self.update_prop(capline_right, caplines[0], legend)
  399. capline_left.set_marker("|")
  400. capline_right.set_marker("|")
  401. handle_caplines.append(capline_left)
  402. handle_caplines.append(capline_right)
  403. if orig_handle.has_yerr:
  404. verts = [((x, y - yerr_size), (x, y + yerr_size))
  405. for x, y in zip(xdata_marker, ydata_marker)]
  406. coll = mcoll.LineCollection(verts)
  407. self.update_prop(coll, barlinecols[0], legend)
  408. handle_barlinecols.append(coll)
  409. if caplines:
  410. capline_left = Line2D(xdata_marker, ydata_marker - yerr_size)
  411. capline_right = Line2D(xdata_marker, ydata_marker + yerr_size)
  412. self.update_prop(capline_left, caplines[0], legend)
  413. self.update_prop(capline_right, caplines[0], legend)
  414. capline_left.set_marker("_")
  415. capline_right.set_marker("_")
  416. handle_caplines.append(capline_left)
  417. handle_caplines.append(capline_right)
  418. artists = [
  419. *handle_barlinecols, *handle_caplines, legline, legline_marker,
  420. ]
  421. for artist in artists:
  422. artist.set_transform(trans)
  423. return artists
  424. class HandlerStem(HandlerNpointsYoffsets):
  425. """
  426. Handler for plots produced by `~.Axes.stem`.
  427. """
  428. def __init__(self, marker_pad=0.3, numpoints=None,
  429. bottom=None, yoffsets=None, **kw):
  430. """
  431. Parameters
  432. ----------
  433. marker_pad : float, default: 0.3
  434. Padding between points in legend entry.
  435. numpoints : int, optional
  436. Number of points to show in legend entry.
  437. bottom : float, optional
  438. yoffsets : array of floats, optional
  439. Length *numpoints* list of y offsets for each point in
  440. legend entry.
  441. Notes
  442. -----
  443. Any other keyword arguments are given to `HandlerNpointsYoffsets`.
  444. """
  445. HandlerNpointsYoffsets.__init__(self, marker_pad=marker_pad,
  446. numpoints=numpoints,
  447. yoffsets=yoffsets,
  448. **kw)
  449. self._bottom = bottom
  450. def get_ydata(self, legend, xdescent, ydescent, width, height, fontsize):
  451. if self._yoffsets is None:
  452. ydata = height * (0.5 * legend._scatteryoffsets + 0.5)
  453. else:
  454. ydata = height * np.asarray(self._yoffsets)
  455. return ydata
  456. def create_artists(self, legend, orig_handle,
  457. xdescent, ydescent, width, height, fontsize,
  458. trans):
  459. markerline, stemlines, baseline = orig_handle
  460. # Check to see if the stemcontainer is storing lines as a list or a
  461. # LineCollection. Eventually using a list will be removed, and this
  462. # logic can also be removed.
  463. using_linecoll = isinstance(stemlines, mcoll.LineCollection)
  464. xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent,
  465. width, height, fontsize)
  466. ydata = self.get_ydata(legend, xdescent, ydescent,
  467. width, height, fontsize)
  468. if self._bottom is None:
  469. bottom = 0.
  470. else:
  471. bottom = self._bottom
  472. leg_markerline = Line2D(xdata_marker, ydata[:len(xdata_marker)])
  473. self.update_prop(leg_markerline, markerline, legend)
  474. leg_stemlines = [Line2D([x, x], [bottom, y])
  475. for x, y in zip(xdata_marker, ydata)]
  476. if using_linecoll:
  477. # change the function used by update_prop() from the default
  478. # to one that handles LineCollection
  479. with cbook._setattr_cm(
  480. self, _update_prop_func=self._copy_collection_props):
  481. for line in leg_stemlines:
  482. self.update_prop(line, stemlines, legend)
  483. else:
  484. for lm, m in zip(leg_stemlines, stemlines):
  485. self.update_prop(lm, m, legend)
  486. leg_baseline = Line2D([np.min(xdata), np.max(xdata)],
  487. [bottom, bottom])
  488. self.update_prop(leg_baseline, baseline, legend)
  489. artists = [*leg_stemlines, leg_baseline, leg_markerline]
  490. for artist in artists:
  491. artist.set_transform(trans)
  492. return artists
  493. def _copy_collection_props(self, legend_handle, orig_handle):
  494. """
  495. Copy properties from the `.LineCollection` *orig_handle* to the
  496. `.Line2D` *legend_handle*.
  497. """
  498. legend_handle.set_color(orig_handle.get_color()[0])
  499. legend_handle.set_linestyle(orig_handle.get_linestyle()[0])
  500. class HandlerTuple(HandlerBase):
  501. """
  502. Handler for Tuple.
  503. Additional kwargs are passed through to `HandlerBase`.
  504. Parameters
  505. ----------
  506. ndivide : int, default: 1
  507. The number of sections to divide the legend area into. If None,
  508. use the length of the input tuple.
  509. pad : float, default: :rc:`legend.borderpad`
  510. Padding in units of fraction of font size.
  511. """
  512. def __init__(self, ndivide=1, pad=None, **kwargs):
  513. self._ndivide = ndivide
  514. self._pad = pad
  515. HandlerBase.__init__(self, **kwargs)
  516. def create_artists(self, legend, orig_handle,
  517. xdescent, ydescent, width, height, fontsize,
  518. trans):
  519. handler_map = legend.get_legend_handler_map()
  520. if self._ndivide is None:
  521. ndivide = len(orig_handle)
  522. else:
  523. ndivide = self._ndivide
  524. if self._pad is None:
  525. pad = legend.borderpad * fontsize
  526. else:
  527. pad = self._pad * fontsize
  528. if ndivide > 1:
  529. width = (width - pad * (ndivide - 1)) / ndivide
  530. xds_cycle = cycle(xdescent - (width + pad) * np.arange(ndivide))
  531. a_list = []
  532. for handle1 in orig_handle:
  533. handler = legend.get_legend_handler(handler_map, handle1)
  534. _a_list = handler.create_artists(
  535. legend, handle1,
  536. next(xds_cycle), ydescent, width, height, fontsize, trans)
  537. a_list.extend(_a_list)
  538. return a_list
  539. class HandlerPolyCollection(HandlerBase):
  540. """
  541. Handler for `.PolyCollection` used in `~.Axes.fill_between` and
  542. `~.Axes.stackplot`.
  543. """
  544. def _update_prop(self, legend_handle, orig_handle):
  545. def first_color(colors):
  546. if colors is None:
  547. return None
  548. colors = mcolors.to_rgba_array(colors)
  549. if len(colors):
  550. return colors[0]
  551. else:
  552. return "none"
  553. def get_first(prop_array):
  554. if len(prop_array):
  555. return prop_array[0]
  556. else:
  557. return None
  558. edgecolor = getattr(orig_handle, '_original_edgecolor',
  559. orig_handle.get_edgecolor())
  560. legend_handle.set_edgecolor(first_color(edgecolor))
  561. facecolor = getattr(orig_handle, '_original_facecolor',
  562. orig_handle.get_facecolor())
  563. legend_handle.set_facecolor(first_color(facecolor))
  564. legend_handle.set_fill(orig_handle.get_fill())
  565. legend_handle.set_hatch(orig_handle.get_hatch())
  566. legend_handle.set_linewidth(get_first(orig_handle.get_linewidths()))
  567. legend_handle.set_linestyle(get_first(orig_handle.get_linestyles()))
  568. legend_handle.set_transform(get_first(orig_handle.get_transforms()))
  569. legend_handle.set_figure(orig_handle.get_figure())
  570. legend_handle.set_alpha(orig_handle.get_alpha())
  571. def create_artists(self, legend, orig_handle,
  572. xdescent, ydescent, width, height, fontsize, trans):
  573. p = Rectangle(xy=(-xdescent, -ydescent),
  574. width=width, height=height)
  575. self.update_prop(p, orig_handle, legend)
  576. p.set_transform(trans)
  577. return [p]