@@ -28,39 +28,43 @@ For example, *ax.legend()* is equivalent to::
28
28
The :meth: `~matplotlib.axes.Axes.get_legend_handles_labels ` method
29
29
returns a tuple of two lists, i.e., list of artists and list of labels
30
30
(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
33
43
:class: `~matplotlib.collections.LineCollection ` or
34
44
:class: `~matplotlib.collections.RegularPolyCollection `. The label
35
45
attributes (returned by get_label() method) of collected artists are
36
46
used as text labels. If label attribute is empty string or starts with
37
- "_", that artist will be ignored.
47
+ "_", those artists will be ignored.
38
48
39
49
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.
42
56
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)
47
60
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
52
63
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
61
66
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.
64
68
65
69
66
70
Adjusting the Order of Legend items
@@ -180,3 +184,132 @@ legend.
180
184
181
185
.. plot :: users/plotting/examples/simple_legend02.py
182
186
: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