19
19
impo
10000
rt matplotlib .artist as martist
20
20
import matplotlib .text as mtext
21
21
import numpy as np
22
+ from matplotlib .transforms import Bbox , TransformedBbox , BboxTransformTo
23
+
24
+ from matplotlib .font_manager import FontProperties
25
+ from matplotlib .patches import FancyBboxPatch
26
+ from matplotlib import rcParams
22
27
23
28
from matplotlib .patches import bbox_artist as mbbox_artist
24
29
DEBUG = False
@@ -291,7 +296,7 @@ def get_extent_offsets(self, renderer):
291
296
for c in self .get_visible_children ():
292
297
if isinstance (c , PackerBase ) and c .mode == "expand" :
293
298
c .set_width (self .width )
294
-
299
+
295
300
whd_list = [c .get_extent (renderer ) for c in self .get_visible_children ()]
296
301
whd_list = [(w , h , xd , (h - yd )) for w , h , xd , yd in whd_list ]
297
302
@@ -752,7 +757,7 @@ def get_extent(self, renderer):
752
757
self .ref_offset_transform .translate (- ub .x0 , - ub .y0 )
753
758
# restor offset transform
754
759
self .offset_transform .matrix_from_values (* _off )
755
-
760
+
756
761
return ub .width , ub .height , 0. , 0.
757
762
758
763
@@ -767,14 +772,50 @@ def draw(self, renderer):
767
772
bbox_artist (self , renderer , fill = False , props = dict (pad = 0. ))
768
773
769
774
770
- from matplotlib .font_manager import FontProperties
771
- from matplotlib .patches import FancyBboxPatch
772
- from matplotlib import rcParams
773
- from matplotlib .transforms import Bbox
774
775
775
776
class AnchoredOffsetbox (OffsetBox ):
777
+ """
778
+ An offset box placed according to the legend location
779
+ loc. AnchoredOffsetbox has a single child. When multiple children
780
+ is needed, use other OffsetBox class to enlose them. By default,
781
+ the offset box is anchored against its parent axes. You may
782
+ explicitly specify the bbox_to_anchor.
783
+ """
784
+
776
785
def __init__ (self , loc , pad = 0.4 , borderpad = 0.5 ,
777
- child = None , prop = None , frameon = True ):
786
+ child = None , prop = None , frameon = True ,
787
+ bbox_to_anchor = None ,
788
+ bbox_transform = None ):
789
+ """
790
+ loc is a string or an integer specifying the legend location.
791
+ The valid location codes are::
792
+
793
+ 'upper right' : 1,
794
+ 'upper left' : 2,
795
+ 'lower left' : 3,
796
+ 'lower right' : 4,
797
+ 'right' : 5,
798
+ 'center left' : 6,
799
+ 'center right' : 7,
800
+ 'lower center' : 8,
801
+ 'upper center' : 9,
802
+ 'center' : 10,
803
+
804
+
805
+ pad : pad around the child for drawing a frame. given in
806
+ fraction of fontsize.
807
+
808
+ borderpad : pad between offsetbox frame and the bbox_to_anchor,
809
+
810
+ child : OffsetBox instance that will be anchored.
811
+
812
+ prop : font property. This is only used as a reference for paddings.
813
+
814
+ frameon : draw a frame box if True.
815
+
816
+ bbox_to_anchor : bbox to anchor. If None, use axes.bbox.
817
+
818
+ """
778
819
779
820
super (AnchoredOffsetbox , self ).__init__ ()
780
821
@@ -788,7 +829,7 @@ def __init__(self, loc, pad=0.4, borderpad=0.5,
788
829
self .prop = FontProperties (size = rcParams ["legend.fontsize" ])
789
830
else :
790
831
self .prop = prop
791
-
832
+
792
833
self .patch = FancyBboxPatch (
793
834
xy = (0.0 , 0.0 ), width = 1. , height = 1. ,
794
835
facecolor = 'w' , edgecolor = 'k' ,
@@ -797,48 +838,121 @@ def __init__(self, loc, pad=0.4, borderpad=0.5,
797
838
)
798
839
self .patch .set_boxstyle ("square" ,pad = 0 )
799
840
self ._drawFrame = frameon
841
+ #self._parent_bbox = bbox_to_anchor
842
+ self .set_bbox_to_anchor (bbox_to_anchor , bbox_transform )
843
+
844
+
845
+
800
846
801
847
def set_child (self , child ):
848
+ "set the child to be anchored"
802
849
self ._child = child
803
850
851
+ def get_child (self ):
852
+ "return the child"
853
+ return self ._child
854
+
804
855
def get_children (self ):
856
+ "return the list of children"
805
857
return [self ._child ]
806
858
807
- def get_child (self ):
808
- return self ._child
809
859
810
860
def get_extent (self , renderer ):
861
+ """
862
+ return the extent of the artist. The extent of the child
863
+ added with the pad is returned
864
+ """
811
865
w , h , xd , yd = self .get_child ().get_extent (renderer )
812
866
fontsize = renderer .points_to_pixels (self .prop .get_size_in_points ())
813
867
pad = self .pad * fontsize
814
868
815
869
return w + 2 * pad , h + 2 * pad , xd + pad , yd + pad
816
870
871
+
872
+ def get_bbox_to_anchor (self ):
873
+ """
874
+ return the bbox that the legend will be anchored
875
+ """
876
+ if self ._bbox_to_anchor is None :
877
+ return self .axes .bbox
878
+ else :
879
+ transform = self ._bbox_to_anchor_transform
880
+ if transform is None :
881
+ transform = BboxTransformTo (self .axes .bbox )
882
+
883
+ return TransformedBbox (self ._bbox_to_anchor ,
884
+ transform )
885
+
886
+
887
+
888
+ def set_bbox_to_anchor (self , bbox , transform = None ):
889
+ """
890
+ set the bbox that the child will be anchored.
891
+
892
+ *bbox* can be a Bbox instance, a list of [left, bottom, width,
893
+ height], or a list of [left, bottom] where the width and
894
+ height will be assumed to be zero. The bbox will be
895
+ transformed to display coordinate by the given transform. If
896
+ transform is None, axes.transAxes will be use.
897
+ """
898
+ if bbox is None :
899
+ self ._bbox_to_anchor = None
900
+ elif isinstance (bbox , Bbox ):
901
+ self ._bbox_to_anchor = bbox
902
+ else :
903
+ try :
904
+ l = len (bbox )
905
+ except TypeError :
906
+ raise ValueError ("Invalid argument for bbox : %s" % str (bbox ))
907
+
908
+ if l == 2 :
909
+ bbox = [bbox [0 ], bbox [1 ], 0 , 0 ]
910
+
911
+ self ._bbox_to_anchor = Bbox .from_bounds (* bbox )
912
+
913
+ self ._bbox_to_anchor_transform = transform
914
+
915
+
817
916
def get_window_extent (self , renderer ):
818
917
'''
819
918
get the bounding box in display space.
820
919
'''
920
+ self ._update_offset_func (renderer )
821
921
10000
w , h , xd , yd = self .get_extent (renderer )
822
922
ox , oy = self .get_offset (w , h , xd , yd )
823
923
return Bbox .from_bounds (ox - xd , oy - yd , w , h )
824
924
825
- def draw (self , renderer ):
826
-
827
- if not self .get_visible (): return
828
925
829
- fontsize = renderer .points_to_pixels (self .prop .get_size_in_points ())
926
+ def _update_offset_func (self , renderer , fontsize = None ):
927
+ """
928
+ Update the offset func which depends on the dpi of the
929
+ renderer (because of the padding).
930
+ """
931
+ if fontsize is None :
932
+ fontsize = renderer .points_to_pixels (self .prop .get_size_in_points ())
830
933
831
934
def _offset (w , h , xd , yd , fontsize = fontsize , self = self ):
832
935
bbox = Bbox .from_bounds (0 , 0 , w , h )
833
936
borderpad = self .borderpad * fontsize
937
+ bbox_to_anchor = self .get_bbox_to_anchor ()
938
+
834
939
x0 , y0 = self ._get_anchored_bbox (self .loc ,
835
940
bbox ,
836
- self . axes . bbox ,
941
+ bbox_to_anchor ,
837
942
borderpad )
838
943
return x0 + xd , y0 + yd
839
944
840
945
self .set_offset (_offset )
841
946
947
+
948
+ def draw (self , renderer ):
949
+ "draw the artist"
950
+
951
+ if not self .get_visible (): return
952
+
953
+ fontsize = renderer .points_to_pixels (self .prop .get_size_in_points ())
954
+ self ._update_offset_func (renderer , fontsize )
955
+
842
956
if self ._drawFrame :
843
957
# update the location and size of the legend
844
958
bbox = self .get_window_extent (renderer )
@@ -860,6 +974,10 @@ def _offset(w, h, xd, yd, fontsize=fontsize, self=self):
860
974
861
975
862
976
def _get_anchored_bbox (self , loc , bbox , parentbbox , borderpad ):
977
+ """
978
+ return the position of the bbox anchored at the parentbbox
979
+ with the loc code, with the borderpad.
980
+ """
863
981
assert loc in range (1 ,11 ) # called only internally
864
982
865
983
BEST , UR , UL , LL , LR , R , CL , CR , LC , UC , C = range (11 )
0 commit comments