@@ -28,39 +28,43 @@ For example, *ax.legend()* is equivalent to::
2828The :meth: `~matplotlib.axes.Axes.get_legend_handles_labels ` method
2929returns a tuple of two lists, i.e., list of artists and list of labels
3030(python string). However, it does not return all of its child
31- artists. It returns all artists in *ax.lines * and *ax.patches * and
32- some artists in *ax.collection * which are instance of
31+ artists. It returns artists that are currently supported by matplotlib.
32+
33+ For matplotlib v1.0 and before, the supported artists are as follows.
34+
35+ * :class: `~matplotlib.lines.Line2D `
36+ * :class: `~matplotlib.patches.Patch `
37+ * :class: `~matplotlib.collections.LineCollection `
38+ * :class: `~matplotlib.collections.RegularPolyCollection `
39+
40+ And, :meth: `~matplotlib.axes.Axes.get_legend_handles_labels ` returns
41+ all artists in *ax.lines *, *ax.patches * and
42+ artists in *ax.collection * which are instance of
3343:class: `~matplotlib.collections.LineCollection ` or
3444:class: `~matplotlib.collections.RegularPolyCollection `. The label
3545attributes (returned by get_label() method) of collected artists are
3646used as text labels. If label attribute is empty string or starts with
37- "_", that artist will be ignored.
47+ "_", those artists will be ignored.
3848
3949
40- * Note that not all kind of artists are supported by the legend. The
41- following is the list of artists that are currently supported.
50+ Therefore, plots drawn by some *pyplot * commands are not supported by
51+ legend. For example, :func: `~matplotlib.pyplot.fill_between ` creates
52+ :class: `~matplotlib.collections.PolyCollection ` that is not
53+ supported. Also support is limted for some commands that creat
54+ multiple artists. For example, :func: `~matplotlib.pyplot.errorbar `
55+ creates multiples :class: `~matplotlib.lines.Line2D ` instances.
4256
43- * :class: `~matplotlib.lines.Line2D `
44- * :class: `~matplotlib.patches.Patch `
45- * :class: `~matplotlib.collections.LineCollection `
46- * :class: `~matplotlib.collections.RegularPolyCollection `
57+ Unfortunately, there is no easy workaround when you need legend for an
58+ artist not supported by matplotlib (You may use one of the supported
59+ artist as a proxy. See below)
4760
48- Unfortunately, there is no easy workaround when you need legend for
49- an artist not in the above list (You may use one of the supported
50- artist as a proxy. See below), or customize it beyond what is
51- supported by :class: `matplotlib.legend.Legend `.
61+ In newer version of matplotlib (v1.1 and later), the matplotlib
62+ internals are revised to support
5263
53- * Remember that some *pyplot * commands return artist not supported by
54- legend, e.g., :func: `~matplotlib.pyplot.fill_between ` returns
55- :class: `~matplotlib.collections.PolyCollection ` that is not
56- supported. Or some return multiple artists. For example,
57- :func: `~matplotlib.pyplot.plot ` returns list of
58- :class: `~matplotlib.lines.Line2D ` instances, and
59- :func: `~matplotlib.pyplot.errorbar ` returns a length 3 tuple of
60- :class: `~matplotlib.lines.Line2D ` instances.
64+ * complex plots that creates multiple artists (e.g., bar, errorbar, etc)
65+ * custom legend handles
6166
62- * The legend does not care about the axes that given artists belongs,
63- i.e., the artists may belong to other axes or even none.
67+ See below for details of new functionality.
6468
6569
6670Adjusting the Order of Legend items
@@ -180,3 +184,129 @@ legend.
180184
181185.. plot :: users/plotting/examples/simple_legend02.py
182186 :include-source:
187+
188+
189+ Legend of Complex Plots
190+ =======================
191+
192+ In matplotlib v1.1 (FIXME when released) and later, the legend is
193+ improved to support more plot commands and ease the customization.
194+
195+ Legend Handler
196+ --------------
197+
198+ One of the change is that drawing of legend handles is delegated to
199+ legend handlers. For example, :class: `~matplotlib.lines.Line2D `
200+ instances are handled by
201+ :class: `~matplotlib.legend_handler.HandlerLine2D `. The mapping
202+ between the artists and their corresponding handlers are defined in a
203+ the handler_map of the legend. The handler_map is a dictionary of
204+ key-handler pair, where key can be an artist instance or its
205+ class. And the handler is a Handler instance.
206+
207+ Let's consider the following sample code, ::
208+
209+ legend([p_1, p_2,..., p_i, ...], ["Test 1", "Test 2", ..., "Test i",...])
210+
211+ For each *p_i *, matplotlib
212+
213+ 1. check if *p_i * itself is in the handler_map
214+ 2. if not, iterate over type(p_i).mro() until a matching key is found in the handler_map
215+
216+
217+ For example, the default handler_map
218+ has following key-handler pairs (below is only a part of them).
219+
220+ * Line2D : legend_handler.HandlerLine2D()
221+ * Patch : legend_handler.HandlerPatch()
222+ * LineCollection : legend_handler.HandlerLineCollection()
223+ * ...
224+
225+ The legend command takes an optional argument of "handler_map". When
226+ provided, the default handler will be updated (using dict.update
227+ method) with the provided one. ::
228+
229+ p1, = plot(x, "ro", label="test1")
230+ p2, = plot(y, "b+", ms=10, label="test2")
231+
232+ my_handler = HandlerLine2D(numpoints=1)
233+
234+ legend(handler_map={Line2D:my_handler})
235+
236+ The above example will use *my_handler * for any Line2D
237+ instances (p1 and p2). ::
238+
239+ legend(handler_map={p1:HandlerLine2D(numpoints=1)})
240+
241+ In the above example, only *p1 * will be handled by *my_handler *, while
242+ others will be handled by default handlers.
243+
244+
245+ Artist Container
246+ ----------------
247+
248+ The Artist Container is simple class (derived from tuple) that
249+ contains multiple artists. This is introduced primarily to support
250+ legends for complex plot commands that create multiple artists.
251+
252+ Axes instances now have a "containers" attribute (which is a list, and
253+ this is only intended to be used for generating a legend). The items
254+ in this attribute are also returned by
255+ :meth: `~matplotlib.axes.Axes.get_legend_handles_labels `.
256+
257+ For example, "bar" command creates a series of Rectangle
258+ patches. Previously, it returned a list of these patches. With the
259+ current change, it creates a container object of these rectangle
260+ patches (and these patches are added to Axes.patches attribute as
261+ before) and return it instead. As the container class is derived from
262+ a tuple, it should be backward-compatible. Furthermore, the container
263+ object is added to the Axes.containers attributes so that legend
264+ command can properly create a legend for the bar. Thus, you may do ::
265+
266+ b1 = bar([0, 1, 2], [0.2, 0.3, 0.1], width=0.4,
267+ label="Bar 1", align="center")
268+ legend()
269+
270+ or ::
271+
272+ b1 = bar([0, 1, 2], [0.2, 0.3, 0.1], width=0.4, align="center")
273+ legend([b1], ["Bar 1"])
274+
275+
276+ At this time of writing, however, "bar" and "errorbar" are only
277+ supported (hopefully the list will increase). Here is an example.
278+
279+ .. plot :: mpl_examples/pylab_examples/legend_demo4.py
280+
281+ The default *handler_map * has an entry for "tuple" which is mapped to
282+ *HandlerTuple *. It simply overplots all the handles for items in the
283+ given tuple. For example,
284+
285+ .. plot ::
286+ :include-source:
287+
288+ z = np.random.randn(10)
289+
290+ p1a, = plt.plot(z, "ro", ms=10, mfc="r", mew=2, mec="r") # red filled circle
291+ p1b, = plt.plot(z, "w+", ms=10, mec="w", mew=2) # white cross
292+
293+ plt.legend([(p1a, p1b)], ["Attr A+B"])
294+
295+
296+ Implement a Custom Handler
297+ --------------------------
298+
299+ Handler can any callable object with following signature. ::
300+
301+ def __call__(self, legend, orig_handle,
302+ fontsize,
303+ handlebox):
304+
305+ Where *legend * is the legend itself, *orig_handle * is the original
306+ plot (*p_i * in the above example), *fontsize * is the fontsize in
307+ pixles, and *handlebox * is a OffsetBox instance. Within the call, you
308+ create relevant artists (using relevant properties from the *legend *
309+ and/or *orig_handle *) and add them into the handlebox. The artists
310+ needs to be scaled according to the fontsize (note that the size is in
311+ pixel, i.e., this is dpi-scaled value). See legend_handler.py for more
312+ details.
0 commit comments