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

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

New Development: Yes

JIRA Issue: Yes (CAS-1801)

Ready to Release: Yes

Interface Changes: Yes

What Interface Changed: two classes, CustomToolbarTkAgg and CustomToolbarCommon,

are created to a new module casatoolbar

Test Programs: run sdplot with plottype='spectra' and 'totalpower'

Put in Release Notes: Yes/No

Module(s): CASA task sdplot, sdbaseline, sdstat, sdfit

Description:

Two new classes, CustomToolbarTkAgg and CustomToolbarCommon, are
created to a new module casatoolbar in order to remove backend dependency
of sdplot(). The other code are modified accordingly.
The additional toolbar, casabar, is now default in ASAP plotter
(asapplotter.plot and asapplotter.plottp).


File size: 11.4 KB
RevLine 
[1460]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:
[1621]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()
[1460]17
18 Modify mask region by selecting a region on a plot with mouse.
19 """
20
[1621]21 def __init__(self,plotter=None, scan=None):
[1460]22 """
[1621]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
[1460]29 """
[1621]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
[1460]40 self.scan=None
[1621]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
[1460]59 self.rect={}
60 self.xold=None
61 self.yold=None
62 self.xdataold=None
63 self.ydataold=None
64 self._polygons=[]
65
[1621]66
67 def set_basemask(self,masklist=[],invert=False):
[1460]68 """
[1621]69 Set initial channel mask.
70
[1460]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
[1621]78 You can reset the mask selection by running this method with
79 the default parameters.
[1460]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
[1621]87 # Create base mask
[1460]88 if ( len(masklist) > 0 ):
89 self.mask=self.scan.create_mask(masklist,invert=invert)
[1621]90 elif invert==True:
91 self.mask=_n_bools(self.scan.nchan(),False)
92 else:
[1460]93 self.mask=_n_bools(self.scan.nchan(),True)
94
[1621]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 """
[1460]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
[1621]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'
[1460]153 from asap.asapplotter import asapplotter
[1621]154 self.p=asapplotter()
155 self.newplot=True
[1460]156
[1621]157 # Plot selected spectra if needed
158 if self.scan != self.p._data:
159 # Need replot
160 self.p.plot(self.scan)
[1724]161 # disable casa toolbar
162 if self.p._plotter.figmgr.casabar: self.p._plotter.figmgr.casabar.disable_button()
[1621]163 for panel in self.p._plotter.subplots:
164 xmin, xmax = panel['axes'].get_xlim()
[1667]165 marg = 0.05*abs(xmax-xmin)
[1621]166 panel['axes'].set_xlim(xmin-marg, xmax+marg)
[1667]167 if rcParams['plotter.ganged']: break
[1621]168 self.p._plotter.show()
[1460]169
[1621]170 # Plot initial mask region
171 #if self.showmask or not self.once:
172 if self.showmask:
173 self._plot_mask()
174 print ''
175 print 'Selected regions are shaded with yellow. (gray: projections)'
176 print 'Now you can modify the selection.'
177 print 'Draw rectangles with Left-mouse to add the regions,'
178 print 'or with Right-mouse to exclude the regions.'
[1460]179
[1621]180
181 if self.event != None:
182 self._region_start(self.event)
183 else:
184 self.p._plotter.register('button_press',None)
185 self.p._plotter.register('button_press',self._region_start)
186
187
[1460]188 def _region_start(self,event):
189 # Do not fire event when in zooming/panning mode
[1621]190 mode = self.p._plotter.figmgr.toolbar.mode
[1460]191 if not mode =='':
192 return
193 # Return if selected point is out of panel
194 if event.inaxes == None: return
195 # Select mask/unmask region with mask
196 self.rect = {'button': event.button, 'axes': event.inaxes,
[1621]197 'x': event.x, 'y': event.y,
[1460]198 'world': [event.xdata, event.ydata,
199 event.xdata, event.ydata],
[1621]200 'pixel': [event.x, event.y,
201 event.x, event.y]}
[1623]202 ### Start mod: 2009/08/17 kana ###
203 #self._default_motion('stop')
204 ### End mod ######################
[1621]205 self.p._plotter.register('motion_notify', self._region_draw)
206 self.p._plotter.register('button_release', self._region_end)
[1460]207
208 def _region_draw(self,event):
209 sameaxes=(event.inaxes == self.rect['axes'])
210 if sameaxes:
211 xnow=event.x
212 ynow=event.y
213 self.xold=xnow
214 self.yold=ynow
215 self.xdataold=event.xdata
216 self.ydataold=event.ydata
217 else:
218 xnow=self.xold
219 ynow=self.yold
220
[1621]221 self.p._plotter.figmgr.toolbar.draw_rubberband(event, xnow, ynow, self.rect['x'], self.rect['y'])
[1623]222 ### Start mod: 2009/08/17 kana ###
223 #self.p._plotter.figmgr.toolbar.mouse_move(event)
224 ### End mod ######################
[1621]225
[1460]226 def _region_end(self,event):
[1621]227 self.p._plotter.register('motion_notify', None)
[1623]228 ### Start mod: 2009/08/17 kana ###
229 #self._default_motion('start')
230 ### End mod ######################
[1621]231 self.p._plotter.register('button_release', None)
232
233 self.p._plotter.figmgr.toolbar.release(event)
[1460]234
235 if event.inaxes == self.rect['axes']:
236 xend=event.x
237 yend=event.y
238 xdataend=event.xdata
239 ydataend=event.ydata
240 else:
241 xend=self.xold
242 yend=self.yold
243 xdataend=self.xdataold
244 ydataend=self.ydataold
245
246 self.rect['world'][2:4] = [xdataend, ydataend]
[1621]247 self.rect['pixel'][2:4] = [xend, yend]
[1460]248 self._update_mask()
[1623]249 # Clear up region selection
250 self.rect={}
251 self.xold=None
252 self.yold=None
253 self.xdataold=None
254 self.ydataold=None
[1621]255 if self.once: self.finish_selection(callback=self.callback)
[1460]256
257 def _update_mask(self):
258 # Min and Max for new mask
259 xstart=self.rect['world'][0]
260 xend=self.rect['world'][2]
261 if xstart <= xend: newlist=[xstart,xend]
262 else: newlist=[xend,xstart]
263 # Mask or unmask
264 invmask=None
265 if self.rect['button'] == 1:
266 invmask=False
267 mflg='Mask'
268 elif self.rect['button'] == 3:
269 invmask=True
270 mflg='UNmask'
271 print mflg+': ',newlist
272 newmask=self.scan.create_mask(newlist,invert=invmask)
273 # Logic operation to update mask
274 if invmask:
275 self.mask=mask_and(self.mask,newmask)
276 else:
277 self.mask=mask_or(self.mask,newmask)
278 # Plot masked regions
[1621]279 #if self.showmask or not self.once: self._plot_mask()
280 if self.showmask: self._plot_mask()
[1460]281
282 # Plot masked regions
283 def _plot_mask(self):
284 msks = []
285 msks = self.scan.get_masklist(self.mask,row=0)
286 # Get projection masks for multi-IF
287 ifs=self.scan.getifnos()
288 projs = []
289 if len(ifs) > 1:
290 row0if=self.scan.getif(0)
291 for ifno in ifs:
292 if ifno == row0if: continue
293 for row in xrange(self.scan.nrow()):
294 if self.scan.getif(row) == ifno:
295 projs.append(self.scan.get_masklist(self.mask,row=row))
296 break
297 if len(self._polygons)>0:
298 # Remove old polygons
299 for polygon in self._polygons: polygon.remove()
300 self._polygons=[]
301 # Plot new polygons
[1498]302 if len(msks) > 0:
[1621]303 npanel=len(self.p._plotter.subplots)
[1498]304 j=-1
305 for iloop in range(len(msks)*npanel):
306 i = iloop % len(msks)
307 if i == 0 : j += 1
308 if len(ifs) > 1:
309 for k in xrange(len(ifs)-1):
[1621]310 self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(projs[k][i][0],projs[k][i][1],facecolor='#aaaaaa'))
311 self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(msks[i][0],msks[i][1],facecolor='yellow'))
312 self.p._plotter.canvas.draw()
[1460]313
[1623]314 ### Start mod: 2009/08/17 kana ###
315 def _default_motion(self,action):
316 if not isinstance(action,str):
317 print "WARN: Either 'stop' or 'start' is valid."
318 return
319
320 canvas=self.p._plotter.canvas
321 toolbar=self.p._plotter.figmgr.toolbar
322 if action == 'stop':
323 if toolbar._idDrag:
324 print "WARN: No default event. Nothing to be done."
325 return
326 canvas.mpl_disconnect(toolbar._idDrag)
327 print "Disconnecting default motion event ", toolbar._idDrag
328 elif action == 'start':
329 if toolbar._idDrag:
330 print "WARN: Default event already exists. Disconnecting the event."
331 canvas.mpl_disconnect(toolbar._idDrag)
332 toolbar._idDrag=canvas.mpl_connect('motion_notify_event',toolbar.mouse_move)
333 print "Connecting default motion event ", toolbar._idDrag
334 else:
335 print "WARN: Either 'stop' or 'start' is valid."
336
337 ### End mod ######################
338
[1621]339 def finish_selection(self, callback=None):
[1623]340 """
341 Execute callback function, reset or close plotter window as
342 necessary.
343
344 Parameters:
345 callback: The post processing function to run after
346 the mask selections are completed.
347 Specifying the callback function here will overwrite
348 the one set by set_callback(func)
349
350 Note this function is automatically called at the end of
351 select_mask() if once=True.
352 """
[1621]353 if callback: self.callback=callback
354 if self.callback: self.callback()
[1622]355 if not self.event: self.p._plotter.register('button_press',None)
[1621]356 # Finish the plot
357 if not self.newplot:
358 self.clear_polygon()
359 else:
360 self.p._plotter.unmap()
361 self.p._plotter = None
362 del self.p
363 self.p=None
[1623]364 self._polygons=[]
[1621]365
366
367 def clear_polygon(self):
368 """
369 Erase masks plots from the plotter.
370 """
371 if len(self._polygons)>0:
372 # Remove old polygons
373 for polygon in self._polygons: polygon.remove()
374 self.p._plotter.show()
375 self._polygons=[]
376
377
[1460]378 def get_mask(self):
379 """
380 Get the interactively selected channel mask.
381 Returns:
382 A list of channel mask.
383 """
384 return self.mask
385
[1621]386
Note: See TracBrowser for help on using the repository browser.