Skip to content

Commit 12c5be9

Browse files
committed
Allow figure.legend to take no arguments
1 parent 9edcb03 commit 12c5be9

File tree

8 files changed

+1083
-81
lines changed

8 files changed

+1083
-81
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
figure.legend() can be called without arguments
2+
-----------------------------------------------
3+
4+
Calling :func:`figure.legend` can now be done with no arguments. In this case a
5+
legend will be created that contains all the artists on all the axes contained
6+
within the figure.
Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
1+
"""
2+
==================
3+
Figure legend demo
4+
==================
5+
6+
Instead of plotting a legend on each axis, a legend for all the artists on all
7+
the sub-axes of a figure can be plotted instead.
8+
"""
9+
110
import numpy as np
211
import matplotlib.pyplot as plt
312

4-
fig = plt.figure()
5-
ax1 = fig.add_axes([0.1, 0.1, 0.4, 0.7])
6-
ax2 = fig.add_axes([0.55, 0.1, 0.4, 0.7])
13+
fig, axs = plt.subplots(1, 2)
714

815
x = np.arange(0.0, 2.0, 0.02)
916
y1 = np.sin(2*np.pi*x)
1017
y2 = np.exp(-x)
11-
l1, l2 = ax1.plot(x, y1, 'rs-', x, y2, 'go')
18+
l1, l2 = axs[0].plot(x, y1, 'rs-', x, y2, 'go')
1219

1320
y3 = np.sin(4*np.pi*x)
1421
y4 = np.exp(-2*x)
15-
l3, l4 = ax2.plot(x, y3, 'yd-', x, y4, 'k^')
22+
l3, l4 = axs[1].plot(x, y3, 'yd-', x, y4, 'k^')
1623

1724
fig.legend((l1, l2), ('Line 1', 'Line 2'), 'upper left')
1825
fig.legend((l3, l4), ('Line 3', 'Line 4'), 'upper right')
26+
27+
plt.tight_layout()
1928
plt.show()

lib/matplotlib/figure.py

Lines changed: 158 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,127 +1262,213 @@ def draw_artist(self, a):
12621262
def get_axes(self):
12631263
return self.axes
12641264

1265-
def legend(self, handles, labels, *args, **kwargs):
1265+
def legend(self, *args, **kwargs):
12661266
"""
1267-
Place a legend in the figure. Labels are a sequence of
1268-
strings, handles is a sequence of
1269-
:class:`~matplotlib.lines.Line2D` or
1270-
:class:`~matplotlib.patches.Patch` instances, and loc can be a
1271-
string or an integer specifying the legend location
1267+
Place a legend on the figure.
12721268
1273-
USAGE::
1269+
To make a legend from existing artists on every axes::
1270+
1271+
legend()
1272+
1273+
To make a legend for a list of lines and labels::
12741274
12751275
legend( (line1, line2, line3),
12761276
('label1', 'label2', 'label3'),
12771277
'upper right')
12781278
1279-
The *loc* location codes are::
1280-
1281-
'best' : 0, (currently not supported for figure legends)
1282-
'upper right' : 1,
1283-
'upper left' : 2,
1284-
'lower left' : 3,
1285-
'lower right' : 4,
1286-
'right' : 5,
1287-
'center left' : 6,
1288-
'center right' : 7,
1289-
'lower center' : 8,
1290-
'upper center' : 9,
1291-
'center' : 10,
1292-
1293-
*loc* can also be an (x,y) tuple in figure coords, which
1294-
specifies the lower left of the legend box. figure coords are
1295-
(0,0) is the left, bottom of the figure and 1,1 is the right,
1296-
top.
1297-
1298-
Keyword arguments:
1299-
1300-
prop: [ *None* | FontProperties | dict ]
1301-
A :class:`matplotlib.font_manager.FontProperties`
1302-
instance. If *prop* is a dictionary, a new instance will be
1303-
created with *prop*. If *None*, use rc settings.
1304-
1305-
numpoints: integer
1279+
Parameters
1280+
----------
1281+
loc : string or integer
1282+
The location of the legend. Possible codes are:
1283+
1284+
=============== =============
1285+
Location String Location Code
1286+
=============== =============
1287+
'upper right' 1
1288+
'upper left' 2
1289+
'lower left' 3
1290+
'lower right' 4
1291+
'right' 5
1292+
'center left' 6
1293+
'center right' 7
1294+
'lower center' 8
1295+
'upper center' 9
1296+
'center' 10
1297+
=============== =============
1298+
1299+
*loc* can also be an (x,y) tuple in figure coords, which specifies
1300+
the lower left of the legend box. In figure coords (0,0) is the
1301+
bottom left of the figure, and (1,1) is the top right.
1302+
1303+
prop : None or FontProperties or dict
1304+
A :class:`matplotlib.font_manager.FontProperties` instance. If
1305+
*prop* is a dictionary, a new instance will be created with *prop*.
1306+
If *None*, use rc settings.
1307+
1308+
numpoints : integer
13061309
The number of points in the legend line, default is 4
13071310
1308-
scatterpoints: integer
1311+
scatterpoints : integer
13091312
The number of points in the legend line, default is 4
13101313
1311-
scatteryoffsets: list of floats
1312-
a list of yoffsets for scatter symbols in legend
1314+
scatteryoffsets : list of floats
1315+
A list of yoffsets for scatter symbols in legend
13131316
1314-
markerscale: [ *None* | scalar ]
1317+
markerscale : None or scalar
13151318
The relative size of legend markers vs. original. If *None*, use rc
13161319
settings.
13171320
1318-
markerfirst: [ *True* | *False* ]
1319-
if *True*, legend marker is placed to the left of the legend label
1320-
if *False*, legend marker is placed to the right of the legend
1321-
label
1321+
markerfirst : bool
1322+
If *True*, legend marker is placed to the left of the legend label.
1323+
If *False*, legend marker is placed to the right of the legend
1324+
label.
13221325
1323-
frameon: [ *None* | bool ]
1326+
frameon : None or bool
13241327
Control whether the legend should be drawn on a patch (frame).
13251328
Default is *None* which will take the value from the
13261329
``legend.frameon`` :data:`rcParam<matplotlib.rcParams>`.
13271330
1328-
fancybox: [ *None* | *False* | *True* ]
1329-
if *True*, draw a frame with a round fancybox. If *None*, use rc
1331+
fancybox : None or bool
1332+
If *True*, draw a frame with a round fancybox. If *None*, use rc
1333+
settings.
13301334
1331-
shadow: [ *None* | *False* | *True* ]
1335+
shadow : None or bool
13321336
If *True*, draw a shadow behind legend. If *None*, use rc settings.
13331337
1334-
framealpha: [ *None* | float ]
1338+
framealpha : None or float
13351339
Control the alpha transparency of the legend's background.
13361340
Default is *None* which will take the value from the
13371341
``legend.framealpha`` :data:`rcParam<matplotlib.rcParams>`.
13381342
1339-
facecolor: [ *None* | "inherit" | a color spec ]
1343+
facecolor : None or "inherit" or a color spec
13401344
Control the legend's background color.
13411345
Default is *None* which will take the value from the
13421346
``legend.facecolor`` :data:`rcParam<matplotlib.rcParams>`.
13431347
If ``"inherit"``, it will take the ``axes.facecolor``
13441348
:data:`rcParam<matplotlib.rcParams>`.
13451349
1346-
edgecolor: [ *None* | "inherit" | a color spec ]
1350+
edgecolor : None or "inherit" or a color spec
13471351
Control the legend's background patch edge color.
13481352
Default is *None* which will take the value from the
13491353
``legend.edgecolor`` :data:`rcParam<matplotlib.rcParams>`.
13501354
If ``"inherit"``, it will take the ``axes.edgecolor``
13511355
:data:`rcParam<matplotlib.rcParams>`.
13521356
1353-
ncol : integer
1354-
number of columns. default is 1
1357+
ncol : integer
1358+
Number of columns. Default is 1.
13551359
1356-
mode : [ "expand" | *None* ]
1357-
if mode is "expand", the legend will be horizontally expanded
1360+
mode : "expand" or None
1361+
If mode is "expand", the legend will be horizontally expanded
13581362
to fill the axes area (or *bbox_to_anchor*)
13591363
1360-
title : string
1361-
the legend title
1364+
title : string
1365+
The legend title
1366+
1367+
borderpad : float or None
1368+
The fractional whitespace inside the legend border, measured in
1369+
font-size units.
1370+
Default is *None* which will take the value from the
1371+
``legend.borderpad`` :data:`rcParam<matplotlib.rcParams>`.
1372+
1373+
labelspacing : float or None
1374+
The vertical space between the legend entries, measured in
1375+
font-size units.
1376+
Default is *None* which will take the value from the
1377+
``legend.labelspacing`` :data:`rcParam<matplotlib.rcParams>`.
1378+
1379+
handlelength : float or None
1380+
The length of the legend handles, measured in font-size units.
1381+
Default is *None* which will take the value from the
1382+
``legend.handlelength`` :data:`rcParam<matplotlib.rcParams>`.
1383+
1384+
handletextpad : float or None
1385+
The padding between the legend handle and text, measured in
1386+
font-size units.
1387+
Default is *None* which will take the value from the
1388+
``legend.handletextpad`` :data:`rcParam<matplotlib.rcParams>`.
13621389
1363-
Padding and spacing between various elements use following keywords
1364-
parameters. The dimensions of these values are given as a fraction
1365-
of the fontsize. Values from rcParams will be used if None.
1390+
borderaxespad : float or None
1391+
The padding between the axes and legend border, measured in
1392+
font-size units.
1393+
Default is *None* which will take the value from the
1394+
``legend.borderaxespad`` :data:`rcParam<matplotlib.rcParams>`.
13661395
1367-
================ ====================================================
1368-
Keyword Description
1369-
================ ====================================================
1370-
borderpad the fractional whitespace inside the legend border
1371-
labelspacing the vertical space between the legend entries
1372-
handlelength the length of the legend handles
1373-
handletextpad the pad between the legend handle and text
1374-
borderaxespad the pad between the axes and legend border
1375-
columnspacing the spacing between columns
1376-
================ ====================================================
1396+
columnspacing : float or None
1397+
The spacing between columns, measured in font-size units.
1398+
Default is *None* which will take the value from the
1399+
``legend.columnspacing`` :data:`rcParam<matplotlib.rcParams>`.
13771400
1378-
.. Note:: Not all kinds of artist are supported by the legend.
1379-
See LINK (FIXME) for details.
1401+
Returns
1402+
-------
1403+
:class:`matplotlib.legend.Legend` instance
13801404
1381-
**Example:**
1405+
Notes
1406+
-----
1407+
Not all kinds of artist are supported by the legend command.
1408+
See :ref:`plotting-guide-legend` for details.
13821409
1410+
Examples
1411+
--------
13831412
.. plot:: mpl_examples/pylab_examples/figlegend_demo.py
13841413
"""
1385-
l = Legend(self, handles, labels, *args, **kwargs)
1414+
1415+
if len(args) == 0:
1416+
handles = []
1417+
labels = []
1418+
1419+
def in_handles(h, l):
1420+
for f_h, f_l in zip(handles, labels):
1421+
if f_l != l:
1422+
continue
1423+
if type(f_h) != type(h):
1424+
continue
1425+
1426+
try:
1427+
if f_h.get_color() != h.get_color():
1428+
continue
1429+
except AttributeError:
1430+
pass
1431+
try:
1432+
if f_h.get_facecolor() != h.get_facecolor():
1433+
continue
1434+
except AttributeError:
1435+
pass
1436+
try:
1437+
if f_h.get_edgecolor() != h.get_edgecolor():
1438+
continue
1439+
except AttributeError:
1440+
pass
1441+
1442+
return True
1443+
return False
1444+
1445+
for ax in self.axes:
1446+
ax_handles, ax_labels = ax.get_legend_handles_labels()
1447+
for h, l in zip(ax_handles, ax_labels):
1448+
if not in_handles(h, l):
1449+
handles.append(h)
1450+
labels.append(l)
1451+
if len(handles) == 0:
1452+
warnings.warn("No labeled objects found. "
1453+
"Use label='...' kwarg on individual plots.")
1454+
return None
1455+
1456+
elif len(args) == 2:
1457+
# LINES, LABELS
1458+
handles, labels = args
1459+
1460+
elif len(args) == 3:
1461+
# LINES, LABELS, LOC
1462+
handles, labels, loc = args
1463+
kwargs['loc'] = loc
1464+
1465+
else:
1466+
raise TypeError('Invalid number of arguments passed to legend. '
1467+
'Please specify either 0 args, 2 args '
1468+
'(artist handles, figure labels) or 3 args '
1469+
'(artist handles, figure labels, legend location)')
1470+
1471+
l = Legend(self, handles, labels, **kwargs)
13861472
self.legends.append(l)
13871473
l._remove_method = lambda h: self.legends.remove(h)
13881474
self.stale = True

lib/matplotlib/pyplot.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ def figimage(*args, **kwargs):
740740
return gcf().figimage(*args, **kwargs)
741741

742742

743-
def figlegend(handles, labels, loc, **kwargs):
743+
def figlegend(*args, **kwargs):
744744
"""
745745
Place a legend in the figure.
746746
@@ -757,7 +757,14 @@ def figlegend(handles, labels, loc, **kwargs):
757757
758758
A :class:`matplotlib.legend.Legend` instance is returned.
759759
760-
Example::
760+
Examples
761+
--------
762+
763+
To make a legend from existing artists on every axes::
764+
765+
figlegend()
766+
767+
To make a legend for a list of lines and labels::
761768
762769
figlegend( (line1, line2, line3),
763770
('label1', 'label2', 'label3'),
@@ -768,7 +775,7 @@ def figlegend(handles, labels, loc, **kwargs):
768775
:func:`~matplotlib.pyplot.legend`
769776
770777
"""
771-
return gcf().legend(handles, labels, loc, **kwargs)
778+
return gcf().legend(*args, **kwargs)
772779

773780

774781
## Figure and Axes hybrid ##
@@ -976,7 +983,7 @@ def subplot(*args, **kwargs):
976983
977984
.. note::
978985
979-
Creating a subplot will delete any pre-existing subplot that overlaps
986+
Creating a subplot will delete any pre-existing subplot that overlaps
980987
with it beyond sharing a boundary::
981988
982989
import matplotlib.pyplot as plt
Binary file not shown.

0 commit comments

Comments
 (0)