@@ -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,132 @@ 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+ Artist Container
196+ ----------------
197+
198+ The Artist Container is simple class (derived from tuple) that
199+ contains multiple artists. This is introduced primarily to support
200+ legends for complex plot commands that create multiple artists.
201+
202+ Axes instances now have a "containers" attribute (which is a list, and
203+ this is only intended to be used for generating a legend). The items
204+ in this attribute are also returned by
205+ :meth: `~matplotlib.axes.Axes.get_legend_handles_labels `.
206+
207+ For example, "bar" command creates a series of Rectangle
208+ patches. Previously, it returned a list of these patches. With the
209+ current change, it creates a container object of these rectangle
210+ patches (and these patches are added to Axes.patches attribute as
211+ before) and return it instead. As the container class is derived from
212+ a tuple, it should be backward-compatible. Furthermore, the container
213+ object is added to the Axes.containers attributes so that legend
214+ command can properly create a legend for the bar. Thus, you may do ::
215+
216+ b1 = bar([0, 1, 2], [0.2, 0.3, 0.1], width=0.4,
217+ label="Bar 1", align="center")
218+ legend()
219+
220+ or ::
221+
222+ b1 = bar([0, 1, 2], [0.2, 0.3, 0.1], width=0.4, align="center")
223+ legend([b1], ["Bar 1"])
224+
225+
226+ At this time of writing, however, "bar" and "errorbar" are only
227+ supported (hopefully the list will increase). Here is an example.
228+
229+ .. plot :: mpl_examples/pylab_examples/legend_demo4.py
230+
231+ Legend Handler
232+ --------------
233+
234+ One of the change is that drawing of legend handles is delegated to
235+ legend handlers. For example, :class: `~matplotlib.lines.Line2D `
236+ instances are handled by
237+ :class: `~matplotlib.legend_handler.HandlerLine2D `. The mapping
238+ between the artists and their corresponding handlers are defined in a
239+ handler_map of the legend. The handler_map is a dictionary of
240+ key-handler pair, where key can be an artist instance or its
241+ class. And the handler is a Handler instance.
242+
243+ Let's consider the following sample code, ::
244+
245+ legend([p_1, p_2,..., p_i, ...], ["Test 1", "Test 2", ..., "Test i",...])
246+
247+ For each *p_i *, matplotlib
248+
249+ 1. check if *p_i * itself is in the handler_map
250+ 2. if not, iterate over type(p_i).mro() until a matching key is found in the handler_map
251+
252+
253+ Unless specified, the defaul handler_map is used. Below is a partial
254+ list of key-handler pairs included in the default handler map.
255+
256+ * Line2D : legend_handler.HandlerLine2D()
257+ * Patch : legend_handler.HandlerPatch()
258+ * LineCollection : legend_handler.HandlerLineCollection()
259+ * ...
260+
261+
262+ The legend command takes an optional argument of "handler_map". When
263+ provided, the default handler map will be updated (using dict.update
264+ method) with the provided one. ::
265+
266+ p1, = plot(x, "ro", label="test1")
267+ p2, = plot(y, "b+", ms=10, label="test2")
268+
269+ my_handler = HandlerLine2D(numpoints=1)
270+
271+ legend(handler_map={Line2D:my_handler})
272+
273+ The above example will use *my_handler * for any Line2D
274+ instances (p1 and p2). ::
275+
276+ legend(handler_map={p1:HandlerLine2D(numpoints=1)})
277+
278+ In the above example, only *p1 * will be handled by *my_handler *, while
279+ others will be handled by default handlers.
280+
281+ The curent default handler_map has handlers for errobar and bar
282+ plots. Also, it includes an entry for ¡°tuple¡± which is mapped to
283+ *HandlerTuple *. It simply overplots all the handles for items in the
284+ given tuple. For example,
285+
286+
287+ .. plot ::
288+ :include-source:
289+
290+ z = np.random.randn(10)
291+
292+ p1a, = plt.plot(z, "ro", ms=10, mfc="r", mew=2, mec="r") # red filled circle
293+ p1b, = plt.plot(z[:5], "w+", ms=10, mec="w", mew=2) # white cross
294+
295+ plt.legend([p1a, (p1a, p1b)], ["Attr A", "Attr A+B"])
296+
297+
298+
299+ Implement a Custom Handler
300+ --------------------------
301+
302+ Handler can be any callable object with following signature. ::
303+
304+ def __call__(self, legend, orig_handle,
305+ fontsize,
306+ handlebox):
307+
308+ Where *legend * is the legend itself, *orig_handle * is the original
309+ plot (*p_i * in the above example), *fontsize * is the fontsize in
310+ pixles, and *handlebox * is a OffsetBox instance. Within the call, you
311+ create relevant artists (using relevant properties from the *legend *
312+ and/or *orig_handle *) and add them into the handlebox. The artists
313+ needs to be scaled according to the fontsize (note that the size is in
314+ pixel, i.e., this is dpi-scaled value). See legend_handler.py for more
315+ details.
0 commit comments