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

Last change on this file since 1724 was 1724, checked in by Kana Sugimoto, 14 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
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                        # disable casa toolbar
162                        if self.p._plotter.figmgr.casabar:  self.p._plotter.figmgr.casabar.disable_button()
163                        for panel in self.p._plotter.subplots:
164                                xmin, xmax = panel['axes'].get_xlim()
165                                marg = 0.05*abs(xmax-xmin)
166                                panel['axes'].set_xlim(xmin-marg, xmax+marg)
167                                if rcParams['plotter.ganged']: break
168                        self.p._plotter.show()
169
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.'
179
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
188        def _region_start(self,event):
189                # Do not fire event when in zooming/panning mode
190                mode = self.p._plotter.figmgr.toolbar.mode
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,
197                             'x': event.x, 'y': event.y,
198                             'world': [event.xdata, event.ydata,
199                                       event.xdata, event.ydata],
200                             'pixel': [event.x, event.y,
201                                       event.x, event.y]}
202                ### Start mod: 2009/08/17 kana ###
203                #self._default_motion('stop')
204                ### End mod ######################
205                self.p._plotter.register('motion_notify', self._region_draw)
206                self.p._plotter.register('button_release', self._region_end)
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
221                self.p._plotter.figmgr.toolbar.draw_rubberband(event, xnow, ynow, self.rect['x'], self.rect['y'])
222                ### Start mod: 2009/08/17 kana ###
223                #self.p._plotter.figmgr.toolbar.mouse_move(event)
224                ### End mod ######################
225
226        def _region_end(self,event):
227                self.p._plotter.register('motion_notify', None)
228                ### Start mod: 2009/08/17 kana ###
229                #self._default_motion('start')
230                ### End mod ######################
231                self.p._plotter.register('button_release', None)
232
233                self.p._plotter.figmgr.toolbar.release(event)
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]
247                self.rect['pixel'][2:4] = [xend, yend]
248                self._update_mask()
249                # Clear up region selection
250                self.rect={}
251                self.xold=None
252                self.yold=None
253                self.xdataold=None
254                self.ydataold=None
255                if self.once: self.finish_selection(callback=self.callback)
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
279                #if self.showmask or not self.once: self._plot_mask()
280                if self.showmask: self._plot_mask()
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
302                if len(msks) > 0:
303                        npanel=len(self.p._plotter.subplots)
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):
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()
313
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               
339        def finish_selection(self, callback=None):
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                """
353                if callback: self.callback=callback
354                if self.callback: self.callback()
355                if not self.event: self.p._plotter.register('button_press',None)
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
364                        self._polygons=[]
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
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
386
Note: See TracBrowser for help on using the repository browser.