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

Commit9c577ae

Browse files
committed
ENH: add outside kwarg to figure legend
1 parentc2946de commit9c577ae

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
@@ -273,6 +273,27 @@ def _make_layout_margins(fig, renderer, *, w_pad=0, h_pad=0,
273273
# pass the new margins down to the layout grid for the solution...
274274
gs._layoutgrid.edit_outer_margin_mins(margin,ss)
275275

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

277298
def_make_margin_suptitles(fig,renderer,*,w_pad=0,h_pad=0):
278299
# 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
@@ -1085,6 +1085,7 @@ def legend(self, *args, **kwargs):
10851085
# explicitly set the bbox transform if the user hasn't.
10861086
l=mlegend.Legend(self,handles,labels,*extra_args,
10871087
bbox_transform=transform,**kwargs)
1088+
l._outside=kwargs.pop('outside',False)
10881089
self.legends.append(l)
10891090
l._remove_method=self.legends.remove
10901091
self.stale=True

‎lib/matplotlib/legend.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,13 @@ def _update_bbox_to_anchor(self, loc_in_canvas):
263263
The custom dictionary mapping instances or types to a legend
264264
handler. This *handler_map* updates the default handler map
265265
found at `matplotlib.legend.Legend.get_legend_handler_map`.
266+
267+
outside : bool or string
268+
For `.Figure.legend` when used with `.Figure.set_constrained_layout`.
269+
If True, place the legend outside all the axes in the figure.
270+
loc='upper right/left' wil usually put beside the figure, but if
271+
outside='upper', then place above the axes; if loc='lower right/left'
272+
and outside='lower' then place below the axes.
266273
""")
267274

268275

@@ -332,6 +339,7 @@ def __init__(self, parent, handles, labels,
332339
bbox_transform=None,# transform for the bbox
333340
frameon=None,# draw frame
334341
handler_map=None,
342+
outside=False,
335343
):
336344
"""
337345
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
@@ -380,6 +381,51 @@ def test_warn_args_kwargs(self):
380381
"be discarded.")
381382

382383

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