3838from matplotlib .offsetbox import HPacker , VPacker , TextArea , DrawingArea
3939
4040
41+ class DraggableLegend :
42+ """helper code for a draggable legend -- see Legend.draggable"""
43+
44+ def __init__ (self , legend ):
45+ self .legend = legend
46+ self .gotLegend = False
47+
48+ c1 = legend .figure .canvas .mpl_connect ('motion_notify_event' , self .on_motion )
49+ c2 = legend .figure .canvas .mpl_connect ('pick_event' , self .on_pick )
50+ c3 = legend .figure .canvas .mpl_connect ('button_release_event' , self .on_release )
51+ legend .set_picker (self .my_legend_picker )
52+ self .cids = [c1 , c2 , c3 ]
53+
54+ def on_motion (self , evt ):
55+ if self .gotLegend :
56+ dx = evt .x - self .mouse_x
57+ dy = evt .y - self .mouse_y
58+ loc_in_canvas = self .legend_x + dx , self .legend_y + dy
59+ loc_in_norm_axes = self .legend .parent .transAxes .inverted ().transform_point (loc_in_canvas )
60+ self .legend ._loc = tuple (loc_in_norm_axes )
61+ self .legend .figure .canvas .draw ()
62+
63+ def my_legend_picker (self , legend , evt ):
64+ return self .legend .legendPatch .contains (evt )
65+
66+ def on_pick (self , evt ):
67+ legend = self .legend
68+ if evt .artist == legend :
69+ bbox = self .legend .get_window_extent ()
70+ self .mouse_x = evt .mouseevent .x
71+ self .mouse_y = evt .mouseevent .y
72+ self .legend_x = bbox .xmin
73+ self .legend_y = bbox .ymin
74+ self .gotLegend = 1
75+
76+ def on_release (self , event ):
77+ if self .gotLegend :
78+ self .gotLegend = False
79+
80+ def disconnect (self ):
81+ 'disconnect the callbacks'
82+ for cid in self .cids :
83+ self .legend .figure .canvas .mpl_disconnect (cid )
84+
85+
4186class Legend (Artist ):
4287 """
4388 Place a legend on the axes at location loc. Labels are a
@@ -182,7 +227,7 @@ def __init__(self, parent, handles, labels,
182227 self .texts = []
183228 self .legendHandles = []
184229 self ._legend_title_box = None
185-
230+
186231 localdict = locals ()
187232
188233 for name in propnames :
@@ -284,7 +329,7 @@ def __init__(self, parent, handles, labels,
284329
285330 # We use FancyBboxPatch to draw a legend frame. The location
286331 # and size of the box will be updated during the drawing time.
287-
332+
288333 self .legendPatch = FancyBboxPatch (
289334 xy = (0.0 , 0.0 ), width = 1. , height = 1. ,
290335 facecolor = rcParams ["axes.facecolor" ],
@@ -316,6 +361,7 @@ def __init__(self, parent, handles, labels,
316361
317362 self ._last_fontsize_points = self ._fontsize
318363
364+ self ._draggable = None
319365
320366 def _set_artist_props (self , a ):
321367 """
@@ -584,7 +630,7 @@ def _init_legend_box(self, handles, labels):
584630 textbox = TextArea (lab , textprops = label_prop ,
585631 multilinebaseline = True , minimumdescent = True )
586632 text_list .append (textbox ._text )
587-
633+
588634 labelboxes .append (textbox )
589635
590636 handlebox = DrawingArea (width = self .handlelength * fontsize ,
@@ -597,7 +643,7 @@ def _init_legend_box(self, handles, labels):
597643 handleboxes .append (handlebox )
598644
599645
600- if len (handleboxes ) > 0 :
646+ if len (handleboxes ) > 0 :
601647
602648 # We calculate number of lows in each column. The first
603649 # (num_largecol) columns will have (nrows+1) rows, and remaing
@@ -613,7 +659,7 @@ def _init_legend_box(self, handles, labels):
613659 [nrows ] * num_smallcol )
614660 else :
615661 largecol , smallcol = [], []
616-
662+
617663 handle_label = safezip (handleboxes , labelboxes )
618664 columnbox = []
619665 for i0 , di in largecol + smallcol :
@@ -889,3 +935,16 @@ def _find_best_position(self, width, height, renderer, consider=None):
889935 return ox , oy
890936
891937
938+ def draggable (self ):
939+ """
940+ toggle the draggable state; if on, you can drag the legend on
941+ the canvas. The DraggableLegend helper class is returned
942+ """
943+ if self ._draggable is not None :
944+ self ._draggable .disconnect ()
945+ self ._draggable = None
946+ else :
947+
948+ self ._draggable = DraggableLegend (self )
949+
950+ return self ._draggable
0 commit comments