Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4d46fec

Browse files
committed
refactor draggable legend to support annotation
svn path=/trunk/matplotlib/; revision=8103
1 parent fed9755 commit 4d46fec

4 files changed

Lines changed: 274 additions & 65 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import matplotlib.pyplot as plt
2+
3+
4+
ax = plt.subplot(111)
5+
ax.plot([1,2,3], label="test")
6+
7+
l = ax.legend()
8+
d1 = l.draggable()
9+
10+
xy = 1, 2
11+
txt = ax.annotate("Test", xy, xytext=(-30, 30),
12+
textcoords="offset points",
13+
bbox=dict(boxstyle="round",fc=(0.2, 1, 1)),
14+
arrowprops=dict(arrowstyle="->"))
15+
d2 = txt.draggable()
16+
17+
18+
from matplotlib._png import read_png
19+
from matplotlib.cbook import get_sample_data
20+
21+
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
22+
23+
fn = get_sample_data("lena.png", asfileobj=False)
24+
arr_lena = read_png(fn)
25+
26+
imagebox = OffsetImage(arr_lena, zoom=0.2)
27+
28+
ab = AnnotationBbox(imagebox, xy,
29+
xybox=(120., -80.),
30+
xycoords='data',
31+
boxcoords="offset points",
32+
pad=0.5,
33+
arrowprops=dict(arrowstyle="->",
34+
connectionstyle="angle,angleA=0,angleB=90,rad=3")
35+
)
36+
37+
38+
ax.add_artist(ab)
39+
40+
d3 = ab.draggable()
41+
42+
43+
plt.show()

lib/matplotlib/legend.py

Lines changed: 37 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -33,54 +33,26 @@
3333
from matplotlib.patches import Patch, Rectangle, Shadow, FancyBboxPatch
3434
from matplotlib.collections import LineCollection, RegularPolyCollection, \
3535
CircleCollection
36-
from matplotlib.transforms import Bbox, BboxBase, TransformedBbox, BboxTransformTo
36+
from matplotlib.transforms import Bbox, BboxBase, TransformedBbox, BboxTransformTo, BboxTransformFrom
3737

38-
from matplotlib.offsetbox import HPacker, VPacker, TextArea, DrawingArea
38+
from matplotlib.offsetbox import HPacker, VPacker, TextArea, DrawingArea, DraggableOffsetBox
3939

4040

41-
class DraggableLegend:
42-
"""helper code for a draggable legend -- see Legend.draggable"""
43-
41+
class DraggableLegend(DraggableOffsetBox):
4442
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)
43+
self.legend=legend
44+
DraggableOffsetBox.__init__(self, legend, legend._legend_box)
6545

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
46+
def artist_picker(self, legend, evt):
47+
return self.legend.legendPatch.contains(evt)
7548

76-
def on_release(self, event):
77-
if self.gotLegend:
78-
self.gotLegend = False
49+
def finalize_offset(self):
50+
loc_in_canvas = self.get_loc_in_canvas()
7951

80-
def disconnect(self):
81-
'disconnect the callbacks'
82-
for cid in self.cids:
83-
self.legend.figure.canvas.mpl_disconnect(cid)
52+
bbox = self.legend.get_bbox_to_anchor()
53+
_bbox_transform = BboxTransformFrom(bbox)
54+
self.legend._loc = tuple(_bbox_transform.transform_point(loc_in_canvas))
55+
8456

8557

8658
class Legend(Artist):
@@ -323,7 +295,6 @@ def __init__(self, parent, handles, labels,
323295
'Falling back on "upper right".')
324296
loc = 1
325297

326-
self._loc = loc
327298
self._mode = mode
328299
self.set_bbox_to_anchor(bbox_to_anchor, bbox_transform)
329300

@@ -357,6 +328,8 @@ def __init__(self, parent, handles, labels,
357328
# init with null renderer
358329
self._init_legend_box(handles, labels)
359330

331+
self._loc = loc
332+
360333
self.set_title(title)
361334

362335
self._last_fontsize_points = self._fontsize
@@ -373,6 +346,28 @@ def _set_artist_props(self, a):
373346
a.set_transform(self.get_transform())
374347

375348

349+
def _set_loc(self, loc):
350+
# find_offset function will be provided to _legend_box and
351+
# _legend_box will draw itself at the location of the return
352+
# value of the find_offset.
353+
self._loc_real = loc
354+
if loc == 0:
355+
_findoffset = self._findoffset_best
356+
else:
357+
_findoffset = self._findoffset_loc
358+
359+
#def findoffset(width, height, xdescent, ydescent):
360+
# return _findoffset(width, height, xdescent, ydescent, renderer)
361+
362+
self._legend_box.set_offset(_findoffset)
363+
364+
self._loc_real = loc
365+
366+
def _get_loc(self):
367+
return self._loc_real
368+
369+
_loc = property(_get_loc, _set_loc)
370+
376371
def _findoffset_best(self, width, height, xdescent, ydescent, renderer):
377372
"Helper function to locate the legend at its best position"
378373
ox, oy = self._find_best_position(width, height, renderer)
@@ -401,19 +396,6 @@ def draw(self, renderer):
401396
renderer.open_group('legend')
402397

403398

404-
# find_offset function will be provided to _legend_box and
405-
# _legend_box will draw itself at the location of the return
406-
# value of the find_offset.
407-
if self._loc == 0:
408-
_findoffset = self._findoffset_best
409-
else:
410-
_findoffset = self._findoffset_loc
411-
412-
def findoffset(width, height, xdescent, ydescent):
413-
return _findoffset(width, height, xdescent, ydescent, renderer)
414-
415-
self._legend_box.set_offset(findoffset)
416-
417399
fontsize = renderer.points_to_pixels(self._fontsize)
418400

419401
# if mode == fill, set the width of the legend_box to the

0 commit comments

Comments
 (0)