33matplotlib event handling to interact with objects on the canvas
44
55"""
6+ from matplotlib .artist import Artist
67from matplotlib .patches import Polygon
78from matplotlib .numerix import sqrt , nonzero , equal , asarray , dot , Float
89from matplotlib .numerix .mlab import amin
1112
1213
1314
14- class EditablePolygon ( Polygon ) :
15+ class PolygonInteractor :
1516 """
16- An editable polygon.
17+ An polygon editor .
1718
1819 Key-bindings
1920
@@ -29,105 +30,120 @@ class EditablePolygon(Polygon):
2930
3031 showverts = True
3132 epsilon = 5 # max pixel distance to count as a vertex hit
32- def __init__ (self , * args , ** kwargs ):
33- Polygon .__init__ (self , * args , ** kwargs )
34- self .line = Line2D ([],[],marker = 'o' , markerfacecolor = 'r' )
35- self ._ind = None # the active vert
3633
37- self .xy = list (self .xy ) # make sure it is editable
38-
39- def set_figure (self , fig ):
40- Polygon .set_figure (self , fig )
41- self .line .set_figure (fig )
42- self .figure .canvas .mpl_connect ('button_press_event' , self .button_press_callback )
43- self .figure .canvas .mpl_connect ('key_press_event' , self .key_press_callback )
44- self .figure .canvas .mpl_connect ('button_release_event' , self .button_release_callback )
45- self .figure .canvas .mpl_connect ('motion_notify_event' , self .motion_notify_callback )
46-
34+ def __init__ (self , poly ):
35+ if poly .figure is None :
36+ raise RuntimeError ('You must first add the polygon to a figure or canvas before defining the interactor' )
37+ canvas = poly .figure .canvas
38+ self .poly = poly
39+ self .poly .verts = list (self .poly .verts )
40+ x , y = zip (* self .poly .verts )
41+ self .line = Line2D (x ,y ,marker = 'o' , markerfacecolor = 'r' )
42+ #self._update_line(poly)
4743
48- def set_transform (self , trans ):
49- Polygon .set_transform (self , trans )
50- self .line .set_transform (trans )
51-
52- def set_clip_on (self , b ):
53- Polygon .set_clip_on (self , b )
54- self .line .set_clip_on (b )
55-
56- def set_clip_box (self , b ):
57- Polygon .set_clip_box (self , b )
58- self .line .set_clip_box (b )
44+ cid = self .poly .add_callback (self .poly_changed )
45+ self ._ind = None # the active vert
5946
47+ canvas .mpl_connect ('button_press_event' , self .button_press_callback )
48+ canvas .mpl_connect ('key_press_event' , self .key_press_callback )
49+ canvas .mpl_connect ('button_release_event' , self .button_release_callback )
50+ canvas .mpl_connect ('motion_notify_event' , self .motion_notify_callback )
51+ self .canvas = canvas
52+
6053
61- def draw (self , renderer ):
62- if not self ._visible : return
63- Polygon .draw (self , renderer )
64- if self .showverts :
65- self .line .set_data (zip (* self .xy ))
66- self .line .draw (renderer )
54+ def poly_changed (self , poly ):
55+ 'this method is called whenever the polygon object is called'
56+ # only copy the artist props to the line (except visibility)
57+ vis = self .line .get_visible ()
58+ Artist .update_from (self .line , poly )
59+ self .line .set_visible (vis ) # don't use the poly visibility state
60+
6761
6862 def get_ind_under_point (self , event ):
69- x , y = zip (* self .xy )
63+ 'get the index of the vertex under point if within epsilon tolerance'
64+ x , y = zip (* self .poly .verts )
65+
7066 # display coords
71- xt , yt = self ._transform .numerix_x_y (x , y )
67+ xt , yt = self .poly . get_transform () .numerix_x_y (x , y )
7268 d = sqrt ((xt - event .x )** 2 + (yt - event .y )** 2 )
7369 indseq = nonzero (equal (d , amin (d )))
7470 ind = indseq [0 ]
71+
7572 if d [ind ]>= self .epsilon :
7673 ind = None
7774
7875 return ind
7976
8077 def button_press_callback (self , event ):
78+ 'whenever a mouse button is pressed'
8179 if not self .showverts : return
8280 if event .inaxes == None : return
81+ if event .button != 1 : return
8382 self ._ind = self .get_ind_under_point (event )
84-
8583
8684 def button_release_callback (self , event ):
87- if not self .showverts : return
85+ 'whenever a mouse button is released'
86+ if not self .showverts : return
87+ if event .button != 1 : return
8888 self ._ind = None
8989
9090 def key_press_callback (self , event ):
91+ 'whenever a key is pressed'
9192 if not event .inaxes : return
9293 if event .key == 't' :
9394 self .showverts = not self .showverts
95+ self .line .set_visible (self .showverts )
9496 if not self .showverts : self ._ind = None
9597 elif event .key == 'd' :
9698 ind = self .get_ind_under_point (event )
9799 if ind is not None :
98- self .xy = [tup for i ,tup in enumerate (self .xy ) if i != ind ]
100+ self .poly .verts = [tup for i ,tup in enumerate (self .poly .verts ) if i != ind ]
101+ self .line .set_data (zip (* self .poly .verts ))
99102 elif event .key == 'i' :
100- xys = self ._transform . seq_xy_tups (self .xy )
103+ xys = self .poly . get_transform (). seq_xy_tups (self .poly . verts )
101104 p = event .x , event .y # display coords
102105 for i in range (len (xys )- 1 ):
103106 s0 = xys [i ]
104107 s1 = xys [i + 1 ]
105108 d = dist_point_to_segment (p , s0 , s1 )
106109 if d <= self .epsilon :
107- self .xy .insert (i + 1 , (event .xdata , event .ydata ))
110+ self .poly .verts .insert (i + 1 , (event .xdata , event .ydata ))
111+ self .line .set_data (zip (* self .poly .verts ))
108112 break
109113
110-
111-
112-
113- self .figure .canvas .draw ()
114+
115+ self .canvas .draw ()
114116
115117 def motion_notify_callback (self , event ):
118+ 'on mouse movement'
116119 if not self .showverts : return
117120 if self ._ind is None : return
118121 if event .inaxes is None : return
122+ if event .button != 1 : return
119123 x ,y = event .xdata , event .ydata
120- self .xy [self ._ind ] = x ,y
121- self .figure .canvas .draw_idle ()
124+ self .poly .verts [self ._ind ] = x ,y
125+ self .line .set_data (zip (* self .poly .verts ))
126+ self .canvas .draw_idle ()
122127
123128
124129from pylab import *
125- verts = Circle ((.5 ,.5 ),.5 ).get_verts ()
126- p = EditablePolygon (verts )
130+
131+
132+
133+
127134
128135fig = figure ()
136+ circ = Circle ((.5 ,.5 ),.5 )
137+
138+
139+
140+
129141ax = subplot (111 )
130- ax .add_patch (p )
131- title ('Click and drag a point to move it' )
132- axis ([0 ,1 ,0 ,1 ])
142+ ax .add_patch (circ )
143+ p = PolygonInteractor ( circ )
144+
145+ ax .add_line (p .line )
146+ ax .set_title ('Click and drag a point to move it' )
147+ ax .set_xlim ((0 ,1 ))
148+ ax .set_ylim ((0 ,1 ))
133149show ()
0 commit comments