@@ -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,129 @@ 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
+ 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