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

Last change on this file since 1630 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.