source: trunk/python/interactivemask.py@ 2950

Last change on this file since 2950 was 2538, checked in by Kana Sugimoto, 12 years ago

New Development: No

JIRA Issue: Yes (related to CAS-3749)

Ready for Test: Yes

Interface Changes: Yes

What Interface Changed: added a new method, _alive(), in asaplotbase class

Test Programs: Interactive tests

Put in Release Notes: No

Module(s):

Description:

Added a new method, _alive(), in asaplotbase class. The method return True
if asaplot instance is alive. More complete check compared to 'is_dead' parameter.
asapplotter._assert_plotter method is simplified by calling this function.

Fixed misc bugs found in interactive tests.

  1. set back linewidth = 1 in plots invoked by modules, interactivemask, asapmath, and asapfitter.
  2. interactivemask module: plotter in properly quited at the end.
  3. interactivemask module: avoid error when user close interacive plot window before calling the finalization method, finish_selection().
  4. added definition of a dummy function, quit(), in asaplot class.


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