Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9000e58

Browse filesBrowse files
authored
Merge pull request #7811 from dstansby/figure-legend
Allow figure.legend to be called without arguments
2 parents 5881e72 + 8a78947 commit 9000e58
Copy full SHA for 9000e58

File tree

Expand file treeCollapse file tree

8 files changed

+1087
-81
lines changed
Filter options
Expand file treeCollapse file tree

8 files changed

+1087
-81
lines changed
+6Lines 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.
+14-5Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/figure.py
+162-72Lines changed: 162 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,127 +1262,217 @@ 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 no arguments given, collect up all the artists on the figure
1416+
if len(args) == 0:
1417+
handles = []
1418+
labels = []
1419+
1420+
def in_handles(h, l):
1421+
# Method to check if we already have a given handle and label.
1422+
# Consider two handles to be the same if they share a label,
1423+
# color, facecolor, and edgecolor.
1424+
1425+
# Loop through each handle and label already collected
1426+
for f_h, f_l in zip(handles, labels):
1427+
if f_l != l:
1428+
continue
1429+
if type(f_h) != type(h):
1430+
continue
1431+
try:
1432+
if f_h.get_color() != h.get_color():
1433+
continue
1434+
except AttributeError:
1435+
pass
1436+
try:
1437+
if f_h.get_facecolor() != h.get_facecolor():
1438+
continue
1439+
except AttributeError:
1440+
pass
1441+
try:
1442+
if f_h.get_edgecolor() != h.get_edgecolor():
1443+
continue
1444+
except AttributeError:
1445+
pass
1446+
return True
1447+
return False
1448+
1449+
for ax in self.axes:
1450+
ax_handles, ax_labels = ax.get_legend_handles_labels()
1451+
for h, l in zip(ax_handles, ax_labels):
1452+
if not in_handles(h, l):
1453+
handles.append(h)
1454+
labels.append(l)
1455+
if len(handles) == 0:
1456+
warnings.warn("No labeled objects found. "
1457+
"Use label='...' kwarg on individual plots.")
1458+
return None
1459+
1460+
elif len(args) == 2:
1461+
# LINES, LABELS
1462+
handles, labels = args
1463+
1464+
elif len(args) == 3:
1465+
# LINES, LABELS, LOC
1466+
handles, labels, loc = args
1467+
kwargs['loc'] = loc
1468+
1469+
else:
1470+
raise TypeError('Invalid number of arguments passed to legend. '
1471+
'Please specify either 0 args, 2 args '
1472+
'(artist handles, figure labels) or 3 args '
1473+
'(artist handles, figure labels, legend location)')
1474+
1475+
l = Legend(self, handles, labels, **kwargs)
13861476
self.legends.append(l)
13871477
l._remove_method = lambda h: self.legends.remove(h)
13881478
self.stale = True

‎lib/matplotlib/pyplot.py

Copy file name to clipboardExpand all lines: lib/matplotlib/pyplot.py
+11-4Lines 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.
Loading

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.