@@ -332,6 +332,12 @@ def _update_bbox_to_anchor(self, loc_in_canvas):
332332 _legend_kw_doc_base )
333333_docstring .interpd .update (_legend_kw_doc = _legend_kw_both_st )
334334
335+ _legend_kw_set_loc_st = (
336+ _loc_doc_base .format (parent = 'axes/figure' ,
337+ default = ":rc:`legend.loc` for Axes, 'upper right' for Figure" ,
338+ best = _loc_doc_best , outside = _outside_doc ))
339+ _docstring .interpd .update (_legend_kw_set_loc_doc = _legend_kw_set_loc_st )
340+
335341
336342class Legend (Artist ):
337343 """
@@ -505,58 +511,6 @@ def val_or_rc(val, rc_name):
505511 )
506512 self .parent = parent
507513
508- loc0 = loc
509- self ._loc_used_default = loc is None
510- if loc is None :
511- loc = mpl .rcParams ["legend.loc" ]
512- if not self .isaxes and loc in [0 , 'best' ]:
513- loc = 'upper right'
514-
515- type_err_message = ("loc must be string, coordinate tuple, or"
516- f" an integer 0-10, not { loc !r} " )
517-
518- # handle outside legends:
519- self ._outside_loc = None
520- if isinstance (loc , str ):
521- if loc .split ()[0 ] == 'outside' :
522- # strip outside:
523- loc = loc .split ('outside ' )[1 ]
524- # strip "center" at the beginning
525- self ._outside_loc = loc .replace ('center ' , '' )
526- # strip first
527- self ._outside_loc = self ._outside_loc .split ()[0 ]
528- locs = loc .split ()
529- if len (locs ) > 1 and locs [0 ] in ('right' , 'left' ):
530- # locs doesn't accept "left upper", etc, so swap
531- if locs [0 ] != 'center' :
532- locs = locs [::- 1 ]
533- loc = locs [0 ] + ' ' + locs [1 ]
534- # check that loc is in acceptable strings
535- loc = _api .check_getitem (self .codes , loc = loc )
536- elif np .iterable (loc ):
537- # coerce iterable into tuple
538- loc = tuple (loc )
539- # validate the tuple represents Real coordinates
540- if len (loc ) != 2 or not all (isinstance (e , numbers .Real ) for e in loc ):
541- raise ValueError (type_err_message )
542- elif isinstance (loc , int ):
543- # validate the integer represents a string numeric value
544- if loc < 0 or loc > 10 :
545- raise ValueError (type_err_message )
546- else :
547- # all other cases are invalid values of loc
548- raise ValueError (type_err_message )
549-
550- if self .isaxes and self ._outside_loc :
551- raise ValueError (
552- f"'outside' option for loc='{ loc0 } ' keyword argument only "
553- "works for figure legends" )
554-
555- if not self .isaxes and loc == 0 :
556- raise ValueError (
557- "Automatic legend placement (loc='best') not implemented for "
558- "figure legend" )
559-
560514 self ._mode = mode
561515 self .set_bbox_to_anchor (bbox_to_anchor , bbox_transform )
562516
@@ -616,9 +570,8 @@ def val_or_rc(val, rc_name):
616570 # init with null renderer
617571 self ._init_legend_box (handles , labels , markerfirst )
618572
619- tmp = self ._loc_used_default
620- self ._set_loc (loc )
621- self ._loc_used_default = tmp # ignore changes done by _set_loc
573+ # Set legend location
574+ self .set_loc (loc )
622575
623576 # figure out title font properties:
624577 if title_fontsize is not None and title_fontproperties is not None :
@@ -704,6 +657,73 @@ def _set_artist_props(self, a):
704657
705658 a .set_transform (self .get_transform ())
706659
660+ @_docstring .dedent_interpd
661+ def set_loc (self , loc = None ):
662+ """
663+ Set the location of the legend.
664+
665+ .. versionadded:: 3.8
666+
667+ Parameters
668+ ----------
669+ %(_legend_kw_set_loc_doc)s
670+ """
671+ loc0 = loc
672+ self ._loc_used_default = loc is None
673+ if loc is None :
674+ loc = mpl .rcParams ["legend.loc" ]
675+ if not self .isaxes and loc in [0 , 'best' ]:
676+ loc = 'upper right'
677+
678+ type_err_message = ("loc must be string, coordinate tuple, or"
679+ f" an integer 0-10, not { loc !r} " )
680+
681+ # handle outside legends:
682+ self ._outside_loc = None
683+ if isinstance (loc , str ):
684+ if loc .split ()[0 ] == 'outside' :
685+ # strip outside:
686+ loc = loc .split ('outside ' )[1 ]
687+ # strip "center" at the beginning
688+ self ._outside_loc = loc .replace ('center ' , '' )
689+ # strip first
690+ self ._outside_loc = self ._outside_loc .split ()[0 ]
691+ locs = loc .split ()
692+ if len (locs ) > 1 and locs [0 ] in ('right' , 'left' ):
693+ # locs doesn't accept "left upper", etc, so swap
694+ if locs [0 ] != 'center' :
695+ locs = locs [::- 1 ]
696+ loc = locs [0 ] + ' ' + locs [1 ]
697+ # check that loc is in acceptable strings
698+ loc = _api .check_getitem (self .codes , loc = loc )
699+ elif np .iterable (loc ):
700+ # coerce iterable into tuple
701+ loc = tuple (loc )
702+ # validate the tuple represents Real coordinates
703+ if len (loc ) != 2 or not all (isinstance (e , numbers .Real ) for e in loc ):
704+ raise ValueError (type_err_message )
705+ elif isinstance (loc , int ):
706+ # validate the integer represents a string numeric value
707+ if loc < 0 or loc > 10 :
708+ raise ValueError (type_err_message )
709+ else :
710+ # all other cases are invalid values of loc
711+ raise ValueError (type_err_message )
712+
713+ if self .isaxes and self ._outside_loc :
714+ raise ValueError (
715+ f"'outside' option for loc='{ loc0 } ' keyword argument only "
716+ "works for figure legends" )
717+
718+ if not self .isaxes and loc == 0 :
719+ raise ValueError (
720+ "Automatic legend placement (loc='best') not implemented for "
721+ "figure legend" )
722+
723+ tmp = self ._loc_used_default
724+ self ._set_loc (loc )
725+ self ._loc_used_default = tmp # ignore changes done by _set_loc
726+
707727 def _set_loc (self , loc ):
708728 # find_offset function will be provided to _legend_box and
709729 # _legend_box will draw itself at the location of the return
0 commit comments