Package reflectometry :: Package model1d :: Package profileview :: Module baseInteractor

Source Code for Module reflectometry.model1d.profileview.baseInteractor

  1  """ 
  2  Basic interactor for Reflectometry profile. 
  3  """ 
  4   
  5  from reflutils       import active_color 
  6  from matplotlib      import transforms 
  7  from matplotlib.axes import Subplot 
  8  from binder import pixel_to_data 
  9  if hasattr(transforms,'blended_transform_factory'): 
 10      # matplotlib 0.98 
 11      blend_xy = transforms.blended_transform_factory 
 12  else: 
 13      # CRUFT matplotlib 0.91 
 14      blend_xy = transforms.blend_xy_sep_transform 
 15   
 16  # ---------------------- Base interactors ---------------------- 
 17  # GUI starts here 
 18  # Other interactor will inherit this 
 19  # -------------------------------------------------------------- 
 20   
21 -class BaseInteractor:
22 """ 23 Abstract base class for someone who use interactor 24 25 Share some functions between the interface interactor and various layer 26 interactors. 27 28 Individual interactors need the following functions: 29 30 save(ev) - save the current state for later restore 31 restore() - restore the old state 32 move(x,y,ev) - move the interactor to position x,y 33 moveend(ev) - end the drag event 34 update() - draw the interactors 35 36 The following are provided by the base class: 37 38 connect_markers(markers) - register callbacks for all markers 39 clear_markers() - remove all items in self.markers 40 onHilite(ev) - enter/leave event processing 41 onLeave(ev) - enter/leave event processing 42 onClick(ev) - mouse click: calls save() 43 onRelease(ev) - mouse click ends: calls moveend() 44 onDrag(ev) - mouse move: calls move() or restore() 45 onKey(ev) - keyboard move: calls move() or restore() 46 47 Interactor attributes: 48 49 base - model we are operating on 50 axes - axes holding the interactor 51 color - color of the interactor in non-active state 52 markers - list of handles for the interactor 53 """
54 - def __init__(self, 55 base, 56 axes, 57 color='black' 58 ):
59 self.base = base 60 self.axes = axes 61 self.xcoords = blend_xy(axes.transData, axes.transAxes) 62 self.color = color 63 self.infopanel = base.parent.infopanel 64 self.model = base.model 65 self._save_n = 0 66 self._save_depth_n = 0 67 self.click_flag = 0
68 69
70 - def clear_markers(self):
71 """Clear old markers and interfaces.""" 72 for h in self.markers: 73 h.remove() 74 if self.markers: 75 self.base.connect.clear(*self.markers) 76 self.markers = []
77 78 79 #======================================
80 - def save(self, ev):
81 pass
82
83 - def restore(self, ev):
84 pass
85
86 - def move(self, x, y, ev):
87 pass
88
89 - def moveend(self, ev):
90 pass
91
92 - def updateValue(self, event):
93 pass
94
95 - def setValue(self, event):
96 pass
97
98 - def Artist2Name(self, event):
99 pass
100 #===================================== 101 102
103 - def BestDepthLayerNum(self, x):
104 n = self.model.find( x ) 105 if abs(self.model.offset[n+1]-x)<3: 106 ret = n 107 else: 108 ret = n-1 109 if ret < 0 : return 0 110 else: return ret
111 112
113 - def connect_markers(self,markers):
114 """ 115 Connect markers to callbacks 116 """ 117 for h in markers: 118 connect = self.base.connect 119 connect('enter', h, self.onHilite) 120 connect('leave', h, self.onLeave) 121 connect('click', h, self.onClick) 122 connect('release', h, self.onRelease) 123 connect('drag', h, self.onDrag) 124 connect('key', h, self.onKey)
125 126
127 - def onHilite(self, event):
128 """ 129 Hilite the artist reporting the event, indicating that it is 130 ready to receive a click. 131 """ 132 event.artist.set_color(active_color) 133 self.base.draw() 134 self.click_flag = 0 135 136 return True
137 138
139 - def onLeave(self, event):
140 """ 141 Restore the artist to the original colour when the cursor leaves. 142 """ 143 event.artist.set_color(self.color) 144 self.base.draw() 145 return True
146 147
148 - def onClick(self, event):
149 """ 150 Prepare to move the artist. Calls save() to preserve the state for 151 later restore(). 152 """ 153 # set the click_flag 154 self.click_flag = 1 155 156 157 # Make sure the x,y data use the coordinate system of the 158 # artist rather than the default axes coordinates. 159 transform = event.artist.get_transform() 160 xy = pixel_to_data(transform, event.x, event.y) 161 event.xdata, event.ydata = xy 162 163 # save current position and event 164 self.clickx = event.xdata 165 self.clicky = event.ydata 166 self.save(event) 167 168 # save the layer number for current mouse position 169 self._save_n = self.base.model.find( event.xdata ) 170 171 # save best layer index for current mouse position 172 self._save_depth_n = self.BestDepthLayerNum(event.xdata) 173 174 # Here we just show the parameter. Change nothing to model 175 self.showValue(event) 176 177 178 return True
179 180 181
182 - def onRelease(self, event):
183 """ 184 Release the mouse 185 """ 186 self.moveend(event) 187 188 # Release the click flag for next click operation 189 self.click_flag = 0 190 191 return True
192 193
194 - def onDrag(self, event):
195 """ 196 Move the artist. Calls move() to update the state, or restore() if 197 the mouse leaves the window. 198 """ 199 200 inside,prop = self.axes.contains(event) 201 202 if inside: 203 self.clickx = event.xdata 204 self.clicky = event.ydata 205 206 # In case of no click and just drag 207 if self.click_flag ==0: 208 self._save_depth_n = self.BestDepthLayerNum(event.xdata) 209 210 # Why? 211 self.click_flag = 1 212 213 self.move(event.xdata, event.ydata, event) 214 215 # set the parameter 216 self.setValue(event) 217 218 else: 219 self.restore() 220 221 #update model 222 self.base.update() 223 224 return True
225 226 227
228 - def onKey(self, event):
229 """ 230 Respond to keyboard events. Arrow keys move the widget. Escape 231 restores it to the position before the last click. 232 233 Calls move() to update the state. Calls restore() on escape. 234 """ 235 if event.key == 'escape': 236 #restore the state 237 self.restore() 238 239 elif event.key in ['up', 'down', 'right', 'left']: 240 241 #inside,prop = self.axes.contains(event) 242 if hasattr(self, 'clickx') and hasattr(self, 'clicky'): 243 244 dx,dy=self.dpixel(self.clickx,self.clicky,nudge=event.control) 245 246 if event.key == 'up': self.clicky += dy 247 elif event.key == 'down': self.clicky -= dy 248 elif event.key == 'right': self.clickx += dx 249 else: self.clickx -= dx 250 251 252 # update the state 253 self.move(self.clickx, self.clicky, event) 254 255 # set the parameter 256 # Make sure that update the xdata and ydata in event 257 event.xdata = self.clickx 258 event.ydata = self.clicky 259 260 self.setValue(event) 261 262 else: 263 return False 264 265 self.base.update() 266 267 return True
268 269 270
271 - def dpixel(self,x,y,nudge=False):
272 """ 273 Return the step size in data coordinates for a small 274 step in screen coordinates. 275 If nudge is False (default) the step size is one pixel. 276 If nudge is True, the step size is 0.2 pixels. 277 """ 278 ax = self.axes 279 if isinstance(ax, Subplot): 280 step = 0.01 281 else: 282 step = 0.5 283 284 px,py = ax.transData.inverted().transform( (x,y) ) 285 if nudge: 286 nx,ny = ax.transData.xy_tup( (px+0.2, py+0.02) ) 287 else: 288 nx,ny = ax.transData.xy_tup( (px+1, py+step) ) 289 dx = nx-x 290 dy = ny-y 291 292 return dx,dy
293