26
26
27
27
import numpy as np
28
28
29
+
29
30
from matplotlib import rcParams
30
31
from matplotlib import cbook , docstring
31
32
from matplotlib .artist import Artist , allow_rasterization
32
- from matplotlib .cbook import silent_list , is_hashable , warn_deprecated
33
+ from matplotlib .cbook import (silent_list , is_hashable , warn_deprecated ,
34
+ _valid_compass , _map_loc_to_compass )
33
35
from matplotlib .font_manager import FontProperties
34
36
from matplotlib .lines import Line2D
35
37
from matplotlib .patches import Patch , Rectangle , Shadow , FancyBboxPatch
@@ -113,23 +115,23 @@ def _update_bbox_to_anchor(self, loc_in_canvas):
113
115
loc : int or string or pair of floats, default: :rc:`legend.loc` ('best' for \
114
116
axes, 'upper right' for figures)
115
117
The location of the legend.
116
- Possible (case-insensitive ) strings and codes are:
117
-
118
- =============== ============== =============
119
- Location String Compass String Location Code
120
- =============== ============== =============
121
- 'best' 0
122
- 'upper right ' 'NE' 1
123
- 'upper left ' 'NW ' 2
124
- 'lower left ' 'SW ' 3
125
- 'lower right ' 'SE' 4
126
- 'right' 5
127
- 'center left ' 'W ' 6
128
- 'center right ' 'E ' 7
129
- 'lower center ' 'S ' 8
130
- 'upper center ' 'N ' 9
131
- 'center ' 'C ' 10
132
- =============== ============== =============
118
+ Possible (case-sensitive ) strings and codes are:
119
+
120
+ ============ ============== = ============== =============
121
+ Compass Code Compass String Location String Location Code
122
+ ============ ============== = ============== =============
123
+ .. 'best' 0
124
+ 'NE ' 'northeast' 'upper right' 1
125
+ 'NW ' 'northwest ' 'upper left' 2
126
+ 'SW ' 'southwest ' 'lower left' 3
127
+ 'SE ' 'southeast' 'lower right' 4
128
+ .. 'right' 5
129
+ 'W ' 'west ' 'center left' 6
130
+ 'E ' 'east ' 'center right' 7
131
+ 'S ' 'south ' 'lower center' 8
132
+ 'N ' 'north ' 'upper center' 9
133
+ 'C ' 'center ' 'center' 10
134
+ ============ ============== = ============== =============
133
135
134
136
135
137
Alternatively can be a 2-tuple giving ``x, y`` of the lower-left
@@ -309,21 +311,7 @@ class Legend(Artist):
309
311
Place a legend on the axes at location loc.
310
312
311
313
"""
312
- codes = {'best' : 0 , # only implemented for axes legends
313
- 'upper right' : 1 ,
314
- 'upper left' : 2 ,
315
- 'lower left' : 3 ,
316
- 'lower right' : 4 ,
317
- 'right' : 5 ,
318
- 'center left' : 6 ,
319
- 'center right' : 7 ,
320
- 'lower center' : 8 ,
321
- 'upper center' : 9 ,
322
- 'center' : 10 ,
323
- }
324
- compasscodes = {'nw' : 2 , 'n' : 9 , 'ne' : 1 , 'w' : 6 , 'c' : 10 , 'e' : 7 ,
325
- 'sw' : 3 , 's' : 8 , 'se' : 4 }
326
- allcodes = {** codes , ** compasscodes }
314
+
327
315
zorder = 5
328
316
329
317
def __str__ (self ):
@@ -487,35 +475,6 @@ def __init__(self, parent, handles, labels,
487
475
raise TypeError ("Legend needs either Axes or Figure as parent" )
488
476
self .parent = parent
489
477
490
- if loc is None :
491
- loc = rcParams ["legend.loc" ]
492
- if not self .isaxes and loc in [0 , 'best' ]:
493
- loc = 'upper right'
494
- if isinstance (loc , str ):
495
- if loc .lower () not in self .allcodes :
496
- if self .isaxes :
497
- cbook .warn_deprecated (
498
- "3.1" , message = "Unrecognized location {!r}. Falling "
499
- "back on 'best'; valid locations are\n \t {}\n "
500
- "This will raise an exception %(removal)s."
501
- .format (loc , '\n \t ' .join (self .allcodes )))
502
- loc = 0
503
- else :
504
- cbook .warn_deprecated (
505
- "3.1" , message = "Unrecognized location {!r}. Falling "
506
- "back on 'upper right'; valid locations are\n \t {}\n '"
507
- "This will raise an exception %(removal)s."
508
- .format (loc , '\n \t ' .join (self .allcodes )))
509
- loc = 1
510
- else :
511
- loc = self .allcodes [loc .lower ()]
512
- if not self .isaxes and loc == 0 :
513
- cbook .warn_deprecated (
514
- "3.1" , message = "Automatic legend placement (loc='best') not "
515
- "implemented for figure legend. Falling back on 'upper "
516
- "right'. This will raise an exception %(removal)s." )
517
- loc = 1
518
-
519
478
self ._mode = mode
520
479
self .set_bbox_to_anchor (bbox_to_anchor , bbox_transform )
521
480
@@ -561,6 +520,9 @@ def __init__(self, parent, handles, labels,
561
520
# init with null renderer
562
521
self ._init_legend_box (handles , labels , markerfirst )
563
522
523
+ # location must be set after _legend_box is created.
524
+ self ._loc = loc
525
+
564
526
# If shadow is activated use framealpha if not
565
527
# explicitly passed. See Issue 8943
566
528
if framealpha is None :
@@ -571,7 +533,6 @@ def __init__(self, parent, handles, labels,
571
533
else :
572
534
self .get_frame ().set_alpha (framealpha )
573
535
574
- self ._loc = loc
575
536
# figure out title fontsize:
576
537
if title_fontsize is None :
577
538
title_fontsize = rcParams ['legend.title_fontsize' ]
@@ -592,6 +553,16 @@ def _set_artist_props(self, a):
592
553
a .set_transform (self .get_transform ())
593
554
594
555
def _set_loc (self , loc ):
556
+ if loc is None :
557
+ loc = rcParams ["legend.loc" ]
558
+ if not self .isaxes and loc in [0 , 'best' ]:
559
+ loc = 'NE'
560
+ if self .isaxes :
561
+ loc = _map_loc_to_compass (loc , allowtuple = True , allowbest = True ,
562
+ fallback = "best" , warnonly = True )
563
+ else :
564
+ loc = _map_loc_to_compass (loc , allowtuple = True , allowbest = False ,
565
+ fallback = "NE" , warnonly = True )
595
566
# find_offset function will be provided to _legend_box and
596
567
# _legend_box will draw itself at the location of the return
597
568
# value of the find_offset.
@@ -604,12 +575,26 @@ def _get_loc(self):
604
575
605
576
_loc = property (_get_loc , _set_loc )
606
577
578
+ def set_loc (self , loc ):
579
+ """
580
+ Set the legend location. For possible values see the `~.Axes.legend`
581
+ docstring.
582
+ """
583
+ self ._set_loc (loc )
584
+
585
+ def get_loc (self ):
586
+ """
587
+ Get the legend location. This will be one of 'best', 'NE', 'NW',
588
+ 'SW', 'SE', 'E', 'W', 'E', 'S', 'N', 'C' or a tuple of floats.
589
+ """
590
+ return self ._get_loc ()
591
+
607
592
def _findoffset (self , width , height , xdescent , ydescent , renderer ):
608
593
"Helper function to locate the legend."
609
594
610
- if self ._loc == 0 : # "best".
595
+ if self ._loc == 'best' : # "best".
611
596
x , y = self ._find_best_position (width , height , renderer )
612
- elif self ._loc in Legend . codes . values ( ): # Fixed location.
597
+ elif isinstance ( self ._loc , str ): # Fixed location.
613
598
bbox = Bbox .from_bounds (0 , 0 , width , height )
614
599
x , y = self ._get_anchored_bbox (self ._loc , bbox ,
615
600
self .get_bbox_to_anchor (),
@@ -1079,26 +1064,12 @@ def _get_anchored_bbox(self, loc, bbox, parentbbox, renderer):
1079
1064
- parentbbox: a parent box which will contain the bbox. In
1080
1065
display coordinates.
1081
1066
"""
1082
- assert loc in range (1 , 11 ) # called only internally
1083
-
1084
- BEST , UR , UL , LL , LR , R , CL , CR , LC , UC , C = range (11 )
1085
-
1086
- anchor_coefs = {UR : "NE" ,
1087
- UL : "NW" ,
1088
- LL : "SW" ,
1089
- LR : "SE" ,
1090
- R : "E" ,
1091
- CL : "W" ,
1092
- CR : "E" ,
1093
- LC : "S" ,
1094
- UC : "N" ,
1095
- C : "C" }
1096
1067
1097
- c = anchor_coefs [ loc ]
1068
+ assert loc in _valid_compass # as this is called only internally
1098
1069
1099
1070
fontsize = renderer .points_to_pixels (self ._fontsize )
1100
1071
container = parentbbox .padded (- (self .borderaxespad ) * fontsize )
1101
- anchored_box = bbox .anchored (c , container = container )
1072
+ anchored_box = bbox .anchored (loc , container = container )
1102
1073
return anchored_box .x0 , anchored_box .y0
1103
1074
1104
1075
def _find_best_position (self , width , height , renderer , consider = None ):
@@ -1115,10 +1086,10 @@ def _find_best_position(self, width, height, renderer, consider=None):
1115
1086
1116
1087
bbox = Bbox .from_bounds (0 , 0 , width , height )
1117
1088
if consider is None :
1118
- consider = [self ._get_anchored_bbox (x , bbox ,
1089
+ consider = [self ._get_anchored_bbox (loc , bbox ,
1119
1090
self .get_bbox_to_anchor (),
1120
1091
renderer )
1121
- for x in range ( 1 , len ( self . codes )) ]
1092
+ for loc in _valid_compass ]
1122
1093
1123
1094
candidates = []
1124
1095
for idx , (l , b ) in enumerate (consider ):
@@ -1199,7 +1170,7 @@ def draggable(self, state=None, use_blit=False, update="loc"):
1199
1170
is changed. If "bbox", the *bbox_to_anchor* parameter is changed.
1200
1171
"""
1201
1172
warn_deprecated ("2.2" ,
1202
- message = "Legend.draggable() is drepecated in "
1173
+ message = "Legend.draggable() is deprecated in "
1203
1174
"favor of Legend.set_draggable(). "
1204
1175
"Legend.draggable may be reintroduced as a "
1205
1176
"property in future releases." )
0 commit comments