@@ -330,6 +330,12 @@ def _update_bbox_to_anchor(self, loc_in_canvas):
330330 _legend_kw_doc_base )
331331_docstring .interpd .update (_legend_kw_doc = _legend_kw_both_st )
332332
333+ _legend_kw_set_loc_st = (
334+ _loc_doc_base .format (parent = 'axes/figure' ,
335+ default = ":rc:`legend.loc` for Axes, 'upper right' for Figure" ,
336+ best = _loc_doc_best , outside = _outside_doc ))
337+ _docstring .interpd .update (_legend_kw_set_loc_doc = _legend_kw_set_loc_st )
338+
333339
334340class Legend (Artist ):
335341 """
@@ -503,58 +509,6 @@ def val_or_rc(val, rc_name):
503509 )
504510 self .parent = parent
505511
506- loc0 = loc
507- self ._loc_used_default = loc is None
508- if loc is None :
509- loc = mpl .rcParams ["legend.loc" ]
510- if not self .isaxes and loc in [0 , 'best' ]:
511- loc = 'upper right'
512-
513- type_err_message = ("loc must be string, coordinate tuple, or"
514- f" an integer 0-10, not { loc !r} " )
515-
516- # handle outside legends:
517- self ._outside_loc = None
518- if isinstance (loc , str ):
519- if loc .split ()[0 ] == 'outside' :
520- # strip outside:
521- loc = loc .split ('outside ' )[1 ]
522- # strip "center" at the beginning
523- self ._outside_loc = loc .replace ('center ' , '' )
524- # strip first
525- self ._outside_loc = self ._outside_loc .split ()[0 ]
526- locs = loc .split ()
527- if len (locs ) > 1 and locs [0 ] in ('right' , 'left' ):
528- # locs doesn't accept "left upper", etc, so swap
529- if locs [0 ] != 'center' :
530- locs = locs [::- 1 ]
531- loc = locs [0 ] + ' ' + locs [1 ]
532- # check that loc is in acceptable strings
533- loc = _api .check_getitem (self .codes , loc = loc )
534- elif np .iterable (loc ):
535- # coerce iterable into tuple
536- loc = tuple (loc )
537- # validate the tuple represents Real coordinates
538- if len (loc ) != 2 or not all (isinstance (e , numbers .Real ) for e in loc ):
539- raise ValueError (type_err_message )
540- elif isinstance (loc , int ):
541- # validate the integer represents a string numeric value
542- if loc < 0 or loc > 10 :
543- raise ValueError (type_err_message )
544- else :
545- # all other cases are invalid values of loc
546- raise ValueError (type_err_message )
547-
548- if self .isaxes and self ._outside_loc :
549- raise ValueError (
550- f"'outside' option for loc='{ loc0 } ' keyword argument only "
551- "works for figure legends" )
552-
553- if not self .isaxes and loc == 0 :
554- raise ValueError (
555- "Automatic legend placement (loc='best') not implemented for "
556- "figure legend" )
557-
558512 self ._mode = mode
559513 self .set_bbox_to_anchor (bbox_to_anchor , bbox_transform )
560514
@@ -598,9 +552,8 @@ def val_or_rc(val, rc_name):
598552 # init with null renderer
599553 self ._init_legend_box (handles , labels , markerfirst )
600554
601- tmp = self ._loc_used_default
602- self ._set_loc (loc )
603- self ._loc_used_default = tmp # ignore changes done by _set_loc
555+ # Set legend location
556+ self .set_loc (loc )
604557
605558 # figure out title font properties:
606559 if title_fontsize is not None and title_fontproperties is not None :
@@ -686,6 +639,73 @@ def _set_artist_props(self, a):
686639
687640 a .set_transform (self .get_transform ())
688641
642+ @_docstring .dedent_interpd
643+ def set_loc (self , loc = None ):
644+ """
645+ Set the location of the legend.
646+
647+ .. versionadded:: 3.8
648+
649+ Parameters
650+ ----------
651+ %(_legend_kw_set_loc_doc)s
652+ """
653+ loc0 = loc
654+ self ._loc_used_default = loc is None
655+ if loc is None :
656+ loc = mpl .rcParams ["legend.loc" ]
657+ if not self .isaxes and loc in [0 , 'best' ]:
658+ loc = 'upper right'
659+
660+ type_err_message = ("loc must be string, coordinate tuple, or"
661+ f" an integer 0-10, not { loc !r} " )
662+
663+ # handle outside legends:
664+ self ._outside_loc = None
665+ if isinstance (loc , str ):
666+ if loc .split ()[0 ] == 'outside' :
667+ # strip outside:
668+ loc = loc .split ('outside ' )[1 ]
669+ # strip "center" at the beginning
670+ self ._outside_loc = loc .replace ('center ' , '' )
671+ # strip first
672+ self ._outside_loc = self ._outside_loc .split ()[0 ]
673+ locs = loc .split ()
674+ if len (locs ) > 1 and locs [0 ] in ('right' , 'left' ):
675+ # locs doesn't accept "left upper", etc, so swap
676+ if locs [0 ] != 'center' :
677+ locs = locs [::- 1 ]
678+ loc = locs [0 ] + ' ' + locs [1 ]
679+ # check that loc is in acceptable strings
680+ loc = _api .check_getitem (self .codes , loc = loc )
681+ elif np .iterable (loc ):
682+ # coerce iterable into tuple
683+ loc = tuple (loc )
684+ # validate the tuple represents Real coordinates
685+ if len (loc ) != 2 or not all (isinstance (e , numbers .Real ) for e in loc ):
686+ raise ValueError (type_err_message )
687+ elif isinstance (loc , int ):
688+ # validate the integer represents a string numeric value
689+ if loc < 0 or loc > 10 :
690+ raise ValueError (type_err_message )
691+ else :
692+ # all other cases are invalid values of loc
693+ raise ValueError (type_err_message )
694+
695+ if self .isaxes and self ._outside_loc :
696+ raise ValueError (
697+ f"'outside' option for loc='{ loc0 } ' keyword argument only "
698+ "works for figure legends" )
699+
700+ if not self .isaxes and loc == 0 :
701+ raise ValueError (
702+ "Automatic legend placement (loc='best') not implemented for "
703+ "figure legend" )
704+
705+ tmp = self ._loc_used_default
706+ self ._set_loc (loc )
707+ self ._loc_used_default = tmp # ignore changes done by _set_loc
708+
689709 def _set_loc (self , loc ):
690710 # find_offset function will be provided to _legend_box and
691711 # _legend_box will draw itself at the location of the return
0 commit comments