Movatterモバイル変換


[0]ホーム

URL:


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

Sign up
Appearance settings

Commit1ff75d5

Browse files
committed
ENH: add outside kwarg to figure legend
1 parent9c530bc commit1ff75d5

File tree

7 files changed

+139
-9
lines changed

7 files changed

+139
-9
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
:orphan:
2+
3+
Figure legends now have *outside* keyword argument
4+
--------------------------------------------------
5+
If a legend is made on a figure (or subfigure), and constrained_layout is being used,
6+
then setting the *outside* kwarg to *True* on `.Figure.legend` will move axes to
7+
make room for the legend. See:doc:`/tutorials/intermediate/legend_guide` for an
8+
example.

‎examples/text_labels_and_annotations/figlegend_demo.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,26 @@
2828

2929
plt.tight_layout()
3030
plt.show()
31+
32+
##############################################################################
33+
# Sometimes we do not want the legend to overlap the axes. If you use
34+
# constrained_layout you can specify ``outside=True``, the legend will
35+
# not overlap.
36+
37+
fig,axs=plt.subplots(1,2,constrained_layout=True)
38+
39+
x=np.arange(0.0,2.0,0.02)
40+
y1=np.sin(2*np.pi*x)
41+
y2=np.exp(-x)
42+
l1,=axs[0].plot(x,y1)
43+
l2,=axs[0].plot(x,y2,marker='o')
44+
45+
y3=np.sin(4*np.pi*x)
46+
y4=np.exp(-2*x)
47+
l3,=axs[1].plot(x,y3,color='tab:green')
48+
l4,=axs[1].plot(x,y4,color='tab:red',marker='^')
49+
50+
fig.legend((l1,l2), ('Line 1','Line 2'),'upper left')
51+
fig.legend((l3,l4), ('Line 3','Line 4'),'upper right',outside=True)
52+
53+
plt.show()

‎lib/matplotlib/_constrained_layout.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,27 @@ def _make_layout_margins(fig, renderer, *, w_pad=0, h_pad=0,
271271
# pass the new margins down to the layout grid for the solution...
272272
gs._layoutgrid.edit_outer_margin_mins(margin,ss)
273273

274+
# make margins for figure-level legends:
275+
forleginfig.legends:
276+
inv_trans_fig=None
277+
ifleg._outsideandleg._bbox_to_anchorisNone:
278+
ifinv_trans_figisNone:
279+
inv_trans_fig=fig.transFigure.inverted().transform_bbox
280+
bbox=inv_trans_fig(leg.get_tightbbox(renderer))
281+
w=bbox.width+2*w_pad
282+
h=bbox.height+2*h_pad
283+
margin='right'
284+
if ((leg._locin (3,4)andleg._outside=='lower')or
285+
(leg._loc==8)):
286+
fig._layoutgrid.edit_margin_min('bottom',h)
287+
elif ((leg._locin (1,2)andleg._outside=='upper')or
288+
(leg._loc==9)):
289+
fig._layoutgrid.edit_margin_min('top',h)
290+
elifleg._locin (1,4,5,7):
291+
fig._layoutgrid.edit_margin_min('right',w)
292+
elifleg._locin (2,3,6):
293+
fig._layoutgrid.edit_margin_min('left',w)
294+
274295

275296
def_make_margin_suptitles(fig,renderer,*,w_pad=0,h_pad=0):
276297
# Figure out how large the suptitle is and make the

‎lib/matplotlib/figure.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,7 @@ def legend(self, *args, **kwargs):
10791079
# explicitly set the bbox transform if the user hasn't.
10801080
l=mlegend.Legend(self,handles,labels,*extra_args,
10811081
bbox_transform=transform,**kwargs)
1082+
l._outside=kwargs.pop('outside',False)
10821083
self.legends.append(l)
10831084
l._remove_method=self.legends.remove
10841085
self.stale=True

‎lib/matplotlib/legend.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,13 @@ def _update_bbox_to_anchor(self, loc_in_canvas):
274274
The custom dictionary mapping instances or types to a legend
275275
handler. This *handler_map* updates the default handler map
276276
found at `matplotlib.legend.Legend.get_legend_handler_map`.
277+
278+
outside : bool or string
279+
For `.Figure.legend` when used with `.Figure.set_constrained_layout`.
280+
If True, place the legend outside all the axes in the figure.
281+
loc='upper right/left' wil usually put beside the figure, but if
282+
outside='upper', then place above the axes; if loc='lower right/left'
283+
and outside='lower' then place below the axes.
277284
""")
278285

279286

@@ -338,6 +345,7 @@ def __init__(
338345
frameon=None,# draw frame
339346
handler_map=None,
340347
title_fontproperties=None,# properties for the legend title
348+
outside=False,
341349
):
342350
"""
343351
Parameters

‎lib/matplotlib/tests/test_legend.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
fromunittestimportmock
44

55
importnumpyasnp
6+
fromnumpy.testingimportassert_allclose
67
importpytest
78

89
frommatplotlib.testing.decoratorsimportimage_comparison
@@ -381,6 +382,51 @@ def test_warn_args_kwargs(self):
381382
"be discarded.")
382383

383384

385+
deftest_figure_legend_outside():
386+
outside= [True]*9+ ['upper','upper','lower','lower']
387+
print(outside)
388+
todos= [1,2,3,4,5,6,7,8,9,1,2,3,4]
389+
axbb= [[20.347556,27.722556,659.249,588.833],# upper right
390+
[151.681556,27.722556,790.583,588.833],# upper left
391+
[151.681556,27.722556,790.583,588.833],# lower left
392+
[20.347556,27.722556,659.249,588.833],# lower right
393+
[20.347556,27.722556,659.249,588.833],# right
394+
[151.681556,27.722556,790.583,588.833],# center left
395+
[20.347556,27.722556,659.249,588.833],# center right
396+
[20.347556,71.056556,790.583,588.833],# lower center
397+
[20.347556,27.722556,790.583,545.499],# upper center
398+
[20.347556,27.722556,790.583,545.499],# up-right,'upper'
399+
[20.347556,27.722556,790.583,545.499],# up-left,'upper'
400+
[20.347556,71.056556,790.583,588.833],# low-left,'lower'
401+
[20.347556,71.056556,790.583,588.833],# low-right,'lower'
402+
]
403+
legbb= [[667.,555.,790.,590.],# upper right
404+
[10.,555.,133.,590.],# upper left
405+
[10.,10.,133.,45.],# lower left
406+
[667,10.,790.,45.],# lower right
407+
[667.,282.5,790.,317.5],
408+
[10.,282.5,133.,317.5],
409+
[667.,282.5,790.,317.5],
410+
[338.5,10.,461.5,45.],
411+
[338.5,555.,461.5,590.],
412+
[667.,555.,790.,590.],# upper right
413+
[10.,555.,133.,590.],# upper left
414+
[10.,10.,133.,45.],# lower left
415+
[667,10.,790.,45.],# lower right
416+
]
417+
fornn,todoinenumerate(todos):
418+
print(todo)
419+
fig,axs=plt.subplots(constrained_layout=True,dpi=100)
420+
axs.plot(range(10),label='Boo1')
421+
leg=fig.legend(loc=todo,outside=outside[nn])
422+
renderer=fig.canvas.get_renderer()
423+
fig.canvas.draw()
424+
assert_allclose(axs.get_window_extent(renderer=renderer).extents,
425+
axbb[nn])
426+
assert_allclose(leg.get_window_extent(renderer=renderer).extents,
427+
legbb[nn])
428+
429+
384430
@image_comparison(['legend_stackplot.png'])
385431
deftest_legend_stackplot():
386432
"""Test legend for PolyCollection using stackplot."""

‎tutorials/intermediate/legend_guide.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,20 +115,43 @@
115115
#
116116
# More examples of custom legend placement:
117117

118-
plt.subplot(211)
119-
plt.plot([1,2,3],label="test1")
120-
plt.plot([3,2,1],label="test2")
118+
fig,axs=plt.subplot_mosaic([['top','top'], ['left','right']])
119+
120+
axs['right'].remove()
121+
122+
axs['top'].plot([1,2,3],label="test1")
123+
axs['top'].plot([3,2,1],label="test2")
121124

122125
# Place a legend above this subplot, expanding itself to
123126
# fully use the given bounding box.
124-
plt.legend(bbox_to_anchor=(0.,1.02,1.,.102),loc='lower left',
125-
ncol=2,mode="expand",borderaxespad=0.)
127+
axs['top'].legend(bbox_to_anchor=(0.,1.02,1.,.102),loc='lower left',
128+
ncol=2,mode="expand",borderaxespad=0.)
129+
130+
axs['left'].plot([1,2,3],label="test1")
131+
axs['left'].plot([3,2,1],label="test2")
132+
# Place a legend to the right of this smaller subplot.
133+
axs['left'].legend(bbox_to_anchor=(1.05,1),loc='upper left',
134+
borderaxespad=0.)
135+
136+
plt.show()
137+
138+
##############################################################################
139+
# Figure legends
140+
# --------------
141+
#
142+
# Sometimes it makes more sense to place the axes relative to the (sub)figure
143+
# rather than individual axes. By using ``constrained_layout`` and
144+
# ``outside=True`` the legend is drawn outside the axes on the (sub)figure.
145+
146+
fig,axs=plt.subplot_mosaic([['left','right']],constrained_layout=True)
147+
148+
axs['left'].plot([1,2,3],label="test1")
149+
axs['left'].plot([3,2,1],label="test2")
126150

127-
plt.subplot(223)
128-
plt.plot([1,2,3],label="test1")
129-
plt.plot([3,2,1],label="test2")
151+
axs['right'].plot([1,2,3],'C2',label="test3")
152+
axs['right'].plot([3,2,1],'C3',label="test4")
130153
# Place a legend to the right of this smaller subplot.
131-
plt.legend(bbox_to_anchor=(1.05,1),loc='upperleft',borderaxespad=0.)
154+
fig.legend(loc='upperright',outside=True)
132155

133156
plt.show()
134157

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp