source: branches/alma/python/interactivemask.py@ 1653

Last change on this file since 1653 was 1630, checked in by Kana Sugimoto, 15 years ago

New Development: No

JIRA Issue: No

Ready to Release: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s):

Description: A bug fix. (Removed an output for debugging.)


File size: 11.3 KB
Line 
1from asap import rcParams
2from asap import _n_bools, mask_and, mask_or
3from asap.scantable import scantable
4
5class interactivemask:
6 """
7 The class for interactive mask selection.
8
9 Example:
10 my_mask=interactivemask(plotter,scan)
11 my_mask.set_basemask(masklist=[[0,10],[90,100]],invert=False)
12 # Do interactive mask selection
13 my_mask.select_mask()
14 finish=raw_input('Press return to finish selection.\n')
15 my_mask.finish_selection(callback=func)
16 mask=my_mask.get_mask()
17
18 Modify mask region by selecting a region on a plot with mouse.
19 """
20
21 def __init__(self,plotter=None, scan=None):
22 """
23 Create a interactive masking object.
24 Either or both 'plotter' or/and 'scan' should be defined.
25
26 Parameters:
27 plotter: an ASAP plotter object for interactive selection
28 scan: a scantable to create a mask interactively
29 """
30 # Return if GUI is not active
31 if not rcParams['plotter.gui']:
32 print 'GUI plotter is disabled.\n'
33 print 'Exit interactive mode.'
34 return
35 # Verify input parameters
36 if scan is None and plotter is None:
37 msg = "Either scantable or plotter should be defined."
38 raise TypeError(msg)
39
40 self.scan=None
41 self.p=None
42 self.newplot=False
43 if scan and isinstance(scan, scantable):
44 self.scan=scan
45 from asap.asapplotter import asapplotter
46 if plotter and isinstance(plotter,asapplotter):
47 self.p = plotter
48 if self.scan is None and isinstance(self.p._data,scantable):
49 self.scan=self.p._data
50 if self.scan is None:
51 msg = "Invalid scantable."
52 raise TypeError(msg)
53
54 self.mask=_n_bools(self.scan.nchan(),True)
55 self.callback=None
56 self.event=None
57 self.once=False
58 self.showmask=True
59 self.rect={}
60 self.xold=None
61 self.yold=None
62 self.xdataold=None
63 self.ydataold=None
64 self._polygons=[]
65
66
67 def set_basemask(self,masklist=[],invert=False):
68 """
69 Set initial channel mask.
70
71 Parameters:
72 masklist: [[min, max], [min2, max2], ...]
73 A list of pairs of start/end points (inclusive)
74 specifying the regions to be masked
75 invert: optional argument. If specified as True,
76 return an inverted mask, i.e. the regions
77 specified are excluded
78 You can reset the mask selection by running this method with
79 the default parameters.
80 """
81 # Verify input parameters
82 if not (isinstance(masklist, list) or isinstance(masklist, tuple)) \
83 or not isinstance(invert, bool):
84 msg = 'Invalid mask definition'
85 raise TypeError(msg)
86
87 # Create base mask
88 if ( len(masklist) > 0 ):
89 self.mask=self.scan.create_mask(masklist,invert=invert)
90 elif invert==True:
91 self.mask=_n_bools(self.scan.nchan(),False)
92 else:
93 self.mask=_n_bools(self.scan.nchan(),True)
94
95
96 def set_startevent(self,event):
97 """
98 Inherit an event from the parent function.
99
100 Parameters:
101 event: 'button_press_event' object to be inherited to
102 start interactive region selection .
103 """
104 from matplotlib.backend_bases import MouseEvent
105 if isinstance(event,MouseEvent) and event.name=='button_press_event':
106 self.event=event
107 else:
108 msg="Invalid event."
109 raise TypeError(msg)
110
111 def set_callback(self,callback):
112 """
113 Set callback function to run when finish_selection() is executed.
114 callback: The post processing function to run after
115 the mask selections are completed.
116 This will be overwritten if callback is defined in
117 finish_selection(callback=func)
118 """
119 self.callback=callback
120
121 def select_mask(self,once=False,showmask=True):
122 """
123 Do interactive mask selection.
124 Modify masks interactively by adding/deleting regions with
125 mouse drawing.(left-button: mask; right-button: UNmask)
126 Note that the interactive region selection is available only
127 when GUI plotter is active.
128
129 Parameters:
130 once: If specified as True, you can modify masks only
131 once. Else if False, you can modify them repeatedly.
132 showmask: If specified as True, the masked regions are plotted
133 on the plotter.
134 Note this parameter is valid only when once=True.
135 Otherwise, maskes are forced to be plotted for reference.
136 """
137 # Return if GUI is not active
138 if not rcParams['plotter.gui']:
139 print 'GUI plotter is disabled.\n'
140 print 'Exit interactive mode.'
141 return
142
143 self.once = once
144 if self.once:
145 self.showmask=showmask
146 else:
147 if not showmask: print 'Warning: showmask spcification is ignored. Mask regions are plotted anyway.'
148 self.showmask=True
149
150 #if not self.p._plotter or self.p._plotter.is_dead:
151 if not self.p or self.p._plotter.is_dead:
152 print 'A new ASAP plotter will be loaded'
153 from asap.asapplotter import asapplotter
154 self.p=asapplotter()
155 self.newplot=True
156
157 # Plot selected spectra if needed
158 if self.scan != self.p._data:
159 # Need replot
160 self.p.plot(self.scan)
161 for panel in self.p._plotter.subplots:
162 xmin, xmax = panel['axes'].get_xlim()
163 marg = 0.05*(xmax-xmin)
164 panel['axes'].set_xlim(xmin-marg, xmax+marg)
165 self.p._plotter.show()
166
167 # Plot initial mask region
168 #if self.showmask or not self.once:
169 if self.showmask:
170 self._plot_mask()
171 print ''
172 print 'Selected regions are shaded with yellow. (gray: projections)'
173 print 'Now you can modify the selection.'
174 print 'Draw rectangles with Left-mouse to add the regions,'
175 print 'or with Right-mouse to exclude the regions.'
176
177
178 if self.event != None:
179 self._region_start(self.event)
180 else:
181 self.p._plotter.register('button_press',None)
182 self.p._plotter.register('button_press',self._region_start)
183
184
185 def _region_start(self,event):
186 # Do not fire event when in zooming/panning mode
187 mode = self.p._plotter.figmgr.toolbar.mode
188 if not mode =='':
189 return
190 # Return if selected point is out of panel
191 if event.inaxes == None: return
192 # Select mask/unmask region with mask
193 self.rect = {'button': event.button, 'axes': event.inaxes,
194 'x': event.x, 'y': event.y,
195 'world': [event.xdata, event.ydata,
196 event.xdata, event.ydata],
197 'pixel': [event.x, event.y,
198 event.x, event.y]}
199 ### Start mod: 2009/08/17 kana ###
200 #self._default_motion('stop')
201 ### End mod ######################
202 self.p._plotter.register('motion_notify', self._region_draw)
203 self.p._plotter.register('button_release', self._region_end)
204
205 def _region_draw(self,event):
206 sameaxes=(event.inaxes == self.rect['axes'])
207 if sameaxes:
208 xnow=event.x
209 ynow=event.y
210 self.xold=xnow
211 self.yold=ynow
212 self.xdataold=event.xdata
213 self.ydataold=event.ydata
214 else:
215 xnow=self.xold
216 ynow=self.yold
217
218 self.p._plotter.figmgr.toolbar.draw_rubberband(event, xnow, ynow, self.rect['x'], self.rect['y'])
219 ### Start mod: 2009/08/17 kana ###
220 #self.p._plotter.figmgr.toolbar.mouse_move(event)
221 ### End mod ######################
222
223 def _region_end(self,event):
224 self.p._plotter.register('motion_notify', None)
225 ### Start mod: 2009/08/17 kana ###
226 #self._default_motion('start')
227 ### End mod ######################
228 self.p._plotter.register('button_release', None)
229
230 self.p._plotter.figmgr.toolbar.release(event)
231
232 if event.inaxes == self.rect['axes']:
233 xend=event.x
234 yend=event.y
235 xdataend=event.xdata
236 ydataend=event.ydata
237 else:
238 xend=self.xold
239 yend=self.yold
240 xdataend=self.xdataold
241 ydataend=self.ydataold
242
243 self.rect['world'][2:4] = [xdataend, ydataend]
244 self.rect['pixel'][2:4] = [xend, yend]
245 self._update_mask()
246 # Clear up region selection
247 self.rect={}
248 self.xold=None
249 self.yold=None
250 self.xdataold=None
251 self.ydataold=None
252 if self.once: self.finish_selection(callback=self.callback)
253
254 def _update_mask(self):
255 # Min and Max for new mask
256 xstart=self.rect['world'][0]
257 xend=self.rect['world'][2]
258 if xstart <= xend: newlist=[xstart,xend]
259 else: newlist=[xend,xstart]
260 # Mask or unmask
261 invmask=None
262 if self.rect['button'] == 1:
263 invmask=False
264 mflg='Mask'
265 elif self.rect['button'] == 3:
266 invmask=True
267 mflg='UNmask'
268 print mflg+': ',newlist
269 newmask=self.scan.create_mask(newlist,invert=invmask)
270 # Logic operation to update mask
271 if invmask:
272 self.mask=mask_and(self.mask,newmask)
273 else:
274 self.mask=mask_or(self.mask,newmask)
275 # Plot masked regions
276 #if self.showmask or not self.once: self._plot_mask()
277 if self.showmask: self._plot_mask()
278
279 # Plot masked regions
280 def _plot_mask(self):
281 msks = []
282 msks = self.scan.get_masklist(self.mask,row=0)
283 # Get projection masks for multi-IF
284 ifs=self.scan.getifnos()
285 projs = []
286 if len(ifs) > 1:
287 row0if=self.scan.getif(0)
288 for ifno in ifs:
289 if ifno == row0if: continue
290 for row in xrange(self.scan.nrow()):
291 if self.scan.getif(row) == ifno:
292 projs.append(self.scan.get_masklist(self.mask,row=row))
293 break
294 if len(self._polygons)>0:
295 # Remove old polygons
296 for polygon in self._polygons: polygon.remove()
297 self._polygons=[]
298 # Plot new polygons
299 if len(msks) > 0:
300 npanel=len(self.p._plotter.subplots)
301 j=-1
302 for iloop in range(len(msks)*npanel):
303 i = iloop % len(msks)
304 if i == 0 : j += 1
305 if len(ifs) > 1:
306 for k in xrange(len(ifs)-1):
307 self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(projs[k][i][0],projs[k][i][1],facecolor='#aaaaaa'))
308 self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(msks[i][0],msks[i][1],facecolor='yellow'))
309 self.p._plotter.canvas.draw()
310
311 ### Start mod: 2009/08/17 kana ###
312 def _default_motion(self,action):
313 if not isinstance(action,str):
314 print "WARN: Either 'stop' or 'start' is valid."
315 return
316
317 canvas=self.p._plotter.canvas
318 toolbar=self.p._plotter.figmgr.toolbar
319 if action == 'stop':
320 if toolbar._idDrag:
321 print "WARN: No default event. Nothing to be done."
322 return
323 canvas.mpl_disconnect(toolbar._idDrag)
324 print "Disconnecting default motion event ", toolbar._idDrag
325 elif action == 'start':
326 if toolbar._idDrag:
327 print "WARN: Default event already exists. Disconnecting the event."
328 canvas.mpl_disconnect(toolbar._idDrag)
329 toolbar._idDrag=canvas.mpl_connect('motion_notify_event',toolbar.mouse_move)
330 print "Connecting default motion event ", toolbar._idDrag
331 else:
332 print "WARN: Either 'stop' or 'start' is valid."
333
334 ### End mod ######################
335
336 def finish_selection(self, callback=None):
337 """
338 Execute callback function, reset or close plotter window as
339 necessary.
340
341 Parameters:
342 callback: The post processing function to run after
343 the mask selections are completed.
344 Specifying the callback function here will overwrite
345 the one set by set_callback(func)
346
347 Note this function is automatically called at the end of
348 select_mask() if once=True.
349 """
350 if callback: self.callback=callback
351 if self.callback: self.callback()
352 if not self.event: self.p._plotter.register('button_press',None)
353 # Finish the plot
354 if not self.newplot:
355 self.clear_polygon()
356 else:
357 self.p._plotter.unmap()
358 self.p._plotter = None
359 del self.p
360 self.p=None
361 self._polygons=[]
362
363
364 def clear_polygon(self):
365 """
366 Erase masks plots from the plotter.
367 """
368 if len(self._polygons)>0:
369 # Remove old polygons
370 for polygon in self._polygons: polygon.remove()
371 self.p._plotter.show()
372 self._polygons=[]
373
374
375 def get_mask(self):
376 """
377 Get the interactively selected channel mask.
378 Returns:
379 A list of channel mask.
380 """
381 return self.mask
382
383
Note: See TracBrowser for help on using the repository browser.