source: trunk/python/customgui_base.py @ 2170

Last change on this file since 2170 was 2170, checked in by Kana Sugimoto, 13 years ago

New Development: No

JIRA Issue: No (minor fixes and improvements)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs: interactive testing

Put in Release Notes: No

Module(s): asapplotter, asaplotbase, flagplotter, sdplot, and sdflag

Description:

Misc bug fixes and minor modifications:
+ fixed bug in customgui_base (scantable class referenced but not imported)
+ minor improvement of toolbar message in customgui_tkagg
+ added a new method _set_window_title(string title) to asaplotgui_qt4 and asaplotgui modules

File size: 41.5 KB
Line 
1import os
2import matplotlib, numpy
3from asap.logging import asaplog, asaplog_post_dec
4from matplotlib.patches import Rectangle
5from asap.parameters import rcParams
6from asap import scantable
7from asap._asap import stmath
8
9######################################
10##    Add CASA custom toolbar       ##
11######################################
12class CustomToolbarCommon:
13    def __init__(self,parent):
14        self.plotter = parent
15        #self.figmgr=self.plotter._plotter.figmgr
16
17    ### select the nearest spectrum in pick radius
18    ###    and display spectral value on the toolbar.
19    def _select_spectrum(self,event):
20        # Do not fire event when in zooming/panning mode
21        mode = self.figmgr.toolbar.mode
22        if not mode == '':
23            return
24            # When selected point is out of panels
25        if event.inaxes == None:
26            return
27        # If not left button
28        if event.button != 1:
29            return
30
31        xclick = event.xdata
32        yclick = event.ydata
33        dist2 = 1000.
34        pickline = None
35        # If the pannel has picable objects
36        pflag = False
37        for lin in event.inaxes.lines:
38            if not lin.pickable():
39                continue
40            pflag = True
41            flag,pind = lin.contains(event)
42            if not flag:
43                continue
44            # Get nearest point
45            inds = pind['ind']
46            xlin = lin.get_xdata()
47            ylin = lin.get_ydata()
48            for i in inds:
49                d2=(xlin[i]-xclick)**2+(ylin[i]-yclick)**2
50                if dist2 >= d2:
51                    dist2 = d2
52                    pickline = lin
53        # No pickcable line in the pannel
54        if not pflag:
55            return
56        # Pickable but too far from mouse position
57        elif pickline is None:
58            picked = 'No line selected.'
59            self.figmgr.toolbar.set_message(picked)
60            return
61        del pind, inds, xlin, ylin
62        # Spectra are Picked
63        theplot = self.plotter._plotter
64        thetoolbar = self.figmgr.toolbar
65        thecanvas = self.figmgr.canvas
66        # Disconnect the default motion notify event
67        # Notice! the other buttons are also diabled!!!
68        thecanvas.mpl_disconnect(thetoolbar._idDrag)
69        # Get picked spectrum
70        xdata = pickline.get_xdata()
71        ydata = pickline.get_ydata()
72        titl = pickline.get_label()
73        titp = event.inaxes.title.get_text()
74        panel0 = event.inaxes
75        picked = "Selected: '"+titl+"' in panel '"+titp+"'."
76        thetoolbar.set_message(picked)
77        # Generate a navigation window
78        #naviwin=Navigationwindow(titp,titl)
79        #------------------------------------------------------#
80        # Show spectrum data at mouse position
81        def spec_data(event):
82            # Getting spectrum data of neiboring point
83            xclick = event.xdata
84            if event.inaxes != panel0:
85                return
86            ipoint = len(xdata)-1
87            for i in range(len(xdata)-1):
88                xl = xclick - xdata[i]
89                xr = xclick - xdata[i+1]
90                if xl*xr <= 0.:
91                    ipoint = i
92                    break
93            # Output spectral value on the navigation window
94            posi = '[ %s, %s ]:  x = %.2f   value = %.2f'\
95                   %(titl,titp,xdata[ipoint],ydata[ipoint])
96            #naviwin.posi.set(posi)
97            thetoolbar.set_message(posi)
98        #------------------------------------------------------#
99        # Disconnect from mouse events
100        def discon(event):
101            #naviwin.window.destroy()
102            theplot.register('motion_notify',None)
103            # Re-activate the default motion_notify_event
104            thetoolbar._idDrag = thecanvas.mpl_connect('motion_notify_event',
105                                                       thetoolbar.mouse_move)
106            theplot.register('button_release',None)
107            return
108        #------------------------------------------------------#
109        # Show data value along with mouse movement
110        theplot.register('motion_notify',spec_data)
111        # Finish events when mouse button is released
112        theplot.register('button_release',discon)
113
114
115    ### Calculate statistics of the selected area.
116    def _single_mask(self,event):
117        # Do not fire event when in zooming/panning mode
118        if not self.figmgr.toolbar.mode == '':
119            return
120        # When selected point is out of panels
121        if event.inaxes == None:
122            return
123        if event.button == 1:
124            baseinv=True
125        elif event.button == 3:
126            baseinv=False
127        else:
128            return
129
130        def _calc_stats():
131            msk=mymask.get_mask()
132            statstr = ['max', 'min', 'mean', 'median', 'sum', 'stddev', 'rms']
133            for stat in statstr:
134                mymask.scan.stats(stat=stat,mask=msk)
135
136        # Interactive mask definition
137        from asap.interactivemask import interactivemask
138        mymask = interactivemask(plotter=self.plotter,scan=self.plotter._data)
139        # Create initial mask
140        mymask.set_basemask(invert=baseinv)
141        # Inherit event
142        mymask.set_startevent(event)
143        # Set callback func
144        mymask.set_callback(_calc_stats)
145        # Selected mask
146        mymask.select_mask(once=True,showmask=False)
147
148    def _mod_note(self,event):
149        # Do not fire event when in zooming/panning mode
150        if not self.figmgr.toolbar.mode == '':
151            return
152        if event.button == 1:
153            self.notewin.load_textwindow(event)
154        elif event.button == 3 and self._note_picked(event):
155            self.notewin.load_modmenu(event)
156        return
157
158    def _note_picked(self,event):
159        # just briefly check if any texts are picked
160        for textobj in self.canvas.figure.texts:
161            if textobj.contains(event)[0]:
162                return True
163        for ax in self.canvas.figure.axes:
164            for textobj in ax.texts:
165                if textobj.contains(event)[0]:
166                    return True
167        #print "No text picked"
168        return False
169
170    ### Page chages
171    ### go to the previous page
172    def prev_page(self):
173        self.figmgr.toolbar.set_message('plotting the previous page')
174        #self._pause_buttons(operation="start",msg='plotting the previous page')
175        self._new_page(goback=True)
176        #self._pause_buttons(operation="end")
177
178    ### go to the next page
179    def next_page(self):
180        self.figmgr.toolbar.set_message('plotting the next page')
181        #self._pause_buttons(operation="start",msg='plotting the next page')
182        self._new_page(goback=False)
183        #self._pause_buttons(operation="end")
184
185    ### actual plotting of the new page
186    def _new_page(self,goback=False):
187        top = None
188        header = self.plotter._headtext
189        reset = False
190        doheader = (isinstance(header['textobj'],list) and \
191                    len(header['textobj']) > 0)
192        if self.plotter._startrow <= 0:
193            msg = "The page counter is reset due to chages of plot settings. "
194            msg += "Plotting from the first page."
195            asaplog.push(msg)
196            asaplog.post('WARN')
197            reset = True
198            goback = False
199            if doheader:
200                extrastr = selstr = ''
201                if header.has_key('extrastr'):
202                    extrastr = header['extrastr']
203                if header.has_key('selstr'):
204                    selstr = header['selstr']
205            self.plotter._reset_header()
206        if doheader:
207            top = self.plotter._plotter.figure.subplotpars.top
208            fontsize = header['textobj'][0].get_fontproperties().get_size()
209
210        self.plotter._plotter.hold()
211        if goback:
212            self._set_prevpage_counter()
213        #self.plotter._plotter.clear()
214        self.plotter._plot(self.plotter._data)
215        self.set_pagecounter(self._get_pagenum())
216        # Plot header information
217        if header['textobj']:
218            if top and top != self.plotter._margins[3]:
219                # work around for sdplot in CASA. complete checking in future?
220                self.plotter._plotter.figure.subplots_adjust(top=top)
221            if reset:
222                self.plotter.print_header(plot=True,fontsize=fontsize,selstr=selstr, extrastr=extrastr)
223            else:
224                self.plotter._header_plot(header['string'],fontsize=fontsize)
225        self.plotter._plotter.release()
226        self.plotter._plotter.tidy()
227        self.plotter._plotter.show(hardrefresh=False)
228        del top
229
230    ### calculate the panel ID and start row to plot the previous page
231    def _set_prevpage_counter(self):
232        # set row and panel counters to those of the 1st panel of previous page
233        maxpanel = 16
234        # the ID of the last panel in current plot
235        lastpanel = self.plotter._ipanel
236        # the number of current subplots
237        currpnum = len(self.plotter._plotter.subplots)
238        # the nuber of previous subplots
239        prevpnum = None
240        if self.plotter._rows and self.plotter._cols:
241            # when user set layout
242            prevpnum = self.plotter._rows*self.plotter._cols
243        else:
244            # no user specification
245            prevpnum = maxpanel
246
247        start_ipanel = max(lastpanel-currpnum-prevpnum+1, 0)
248        # set the pannel ID of the last panel of prev-prev page
249        self.plotter._ipanel = start_ipanel-1
250        if self.plotter._panelling == 'r':
251            self.plotter._startrow = start_ipanel
252        else:
253            # the start row number of the next panel
254            self.plotter._startrow = self.plotter._panelrows[start_ipanel]
255        del lastpanel,currpnum,prevpnum,start_ipanel
256
257    ### refresh the page counter
258    ### refresh the page counter
259    def set_pagecounter(self,page):
260        nwidth = int(numpy.ceil(numpy.log10(max(page,1))))+1
261        nwidth = max(nwidth,4)
262        formatstr = '%'+str(nwidth)+'d'
263        self.show_pagenum(page,formatstr)
264
265    def show_pagenum(self,pagenum,formatstr):
266        # passed to backend dependent class
267        pass       
268
269    def _get_pagenum(self):
270        maxpanel = 16
271        # get the ID of last panel in the current page
272        idlastpanel = self.plotter._ipanel
273        if self.plotter._rows and self.plotter._cols:
274            ppp = self.plotter._rows*self.plotter._cols
275        else:
276            ppp = maxpanel
277        return int(idlastpanel/ppp)+1
278
279    # pause buttons for slow operations. implemented at a backend dependent class
280    def _pause_buttons(self,operation="end",msg=""):
281        pass
282
283
284
285
286######################################
287##    Notation box window           ##
288######################################
289class NotationWindowCommon:
290    """
291    A base class to define the functions that the backend-based
292    GUI notation class must implement to print/modify/delete notes on a canvas.
293
294    The following methods *must* be implemented in the backend-based
295    parent class:
296        _get_note : get text in text box
297        _get_anchval : get anchor value selected
298    """
299    def __init__(self,master=None):
300        #self.parent = master
301        self.canvas = master
302        self.event = None
303        self.note = None
304        self.anchors = ["figure","axes","data"]
305        self.seltext = {}
306        self.numnote = 0
307
308    @asaplog_post_dec
309    def print_text(self):
310        """
311        Print a note on a canvas specified with the Notation window.
312        Called when 'print' button selected on the window.
313        """
314        anchor = self.anchors[self._get_anchval()]
315        notestr = self._get_note().rstrip("\n")
316        if len(notestr.strip()) == 0:
317            #self._clear_textbox()
318            #print "Empty string!"
319            return
320
321        myaxes = None
322        calcpos = True
323        xpos = None
324        ypos = None
325        if self.seltext:
326            # You are modifying a text
327            mycanvas = self.canvas
328            oldanch = self.seltext['anchor']
329            if oldanch != 'figure':
330                myaxes = self.seltext['parent']
331            calcpos = (anchor != oldanch)
332            if not calcpos:
333                # printing text in the same coord.
334                # you don't have to recalc position
335                parent = self.seltext['parent']
336                transform = self.seltext['textobj'].get_transform()
337                (xpos, ypos) = self.seltext['textobj'].get_position()
338            elif anchor == "figure":
339                # converting from "axes"/"data" -> "figure"
340                (x, y) = self.seltext['textobj']._get_xy_display()
341            elif oldanch == "data":
342                # converting from "data" -> "axes".
343                # need xdata & ydata in the axes
344                (x, y) = self.seltext['textobj'].get_position()
345            else:
346                # converting "figure"/"axes" -> "data"
347                # need to calculate xdata & ydata in the axes
348                pixpos = self.seltext['textobj']._get_xy_display()
349                (w,h) = mycanvas.get_width_height()
350                relpos = (pixpos[0]/float(w), pixpos[1]/float(h))
351                if not myaxes:
352                    myaxes = self._get_axes_from_pos(relpos,mycanvas)
353                    if not myaxes:
354                        raise RuntimeError, "Axes resolution failed!"
355                (x, y) = self._convert_pix2dat(relpos,myaxes)
356            self._remove_seltext()
357        elif self.event:
358            mycanvas = self.event.canvas
359            myaxes = self.event.inaxes
360            if myaxes and (anchor != "figure"):
361                x = self.event.xdata
362                y = self.event.ydata
363            else:
364                x = self.event.x
365                y = self.event.y
366        else:
367            raise RuntimeError, "No valid position to print data"
368            return
369
370        # now you know
371        picker = True
372        # alignment of the text: ha (horizontal), va (vertical)
373        ha = 'left'
374        #va = 'center'
375        va = 'top'
376        if not calcpos:
377            # you aready know parent, tansform, xpos and ypos
378            pass
379        elif anchor == "figure":
380            # text instance will be appended to mycanvas.figure.texts
381            parent = mycanvas.figure
382            transform = parent.transFigure
383            (w,h) = mycanvas.get_width_height()
384            xpos = x/float(w)
385            ypos = y/float(h)           
386        elif myaxes:
387            ## text instance will be appended to myaxes.texts
388            parent = myaxes
389            if anchor == "axes":
390                transform = myaxes.transAxes
391                lims = myaxes.get_xlim()
392                xpos = (x-lims[0])/(lims[1]-lims[0])
393                lims = myaxes.get_ylim()
394                ypos = (y-lims[0])/(lims[1]-lims[0])
395            else:
396                # anchored on "data"
397                transform = myaxes.transData
398                xpos = x
399                ypos = y
400        parent.text(xpos,ypos,notestr,transform=transform,
401                    ha=ha,va=va,picker=picker)
402        mycanvas.draw()
403
404        self.numnote += 1
405
406        #self._clear_textbox()
407        msg = "Added note: '"+notestr+"'"
408        msg += " @["+str(xpos)+", "+str(ypos)+"] ("+anchor+"-coord)"
409        msg += "\ntotal number of notes are "+str(self.numnote)
410        asaplog.push( msg )
411
412    def _get_axes_from_pos(self,pos,canvas):
413        """helper function to get axes of a position in a plot (fig-coord)"""
414        if len(pos) != 2:
415            raise ValueError, "pixel position should have 2 elements"
416        for axes in canvas.figure.axes:
417            ##check if pos is in the axes
418            #if axes.contains_point(pos): ### seems not working
419            #    return axes
420            try:
421                axbox = axes.get_position().get_points()
422            except AttributeError: ### WORKAROUND for old matplotlib
423                axbox = self._oldpos2new(axes.get_position())
424            if (axbox[0][0] <= pos[0] <= axbox[1][0]) and \
425               (axbox[0][1] <= pos[1] <= axbox[1][1]):
426                return axes
427        return None
428
429    ### WORKAROUND for old matplotlib
430    def _oldpos2new(self,oldpos=None):
431        return [[oldpos[0],oldpos[1]],[oldpos[0]+oldpos[2],oldpos[1]+oldpos[3]]]
432       
433    def _convert_pix2dat(self,pos,axes):
434        """
435        helper function to convert a position in figure-coord (0-1) to
436        data-coordinate of the axes       
437        """
438        # convert a relative position from lower-left of the canvas
439        # to a data in axes
440        if len(pos) != 2:
441            raise ValueError, "pixel position should have 2 elements"
442        # left-/bottom-pixel, and pixel width & height of the axes
443        bbox = axes.get_position()
444        try:
445            axpos = bbox.get_points()
446        except AttributeError: ### WORKAROUND for old matplotlib
447            axpos = self._oldpos2new(bbox)
448        # check pos value
449        if (pos[0] < axpos[0][0]) or (pos[1] < axpos[0][1]) \
450               or (pos[0] > axpos[1][0]) or (pos[1] > axpos[1][1]):
451            raise ValueError, "The position is out of the axes"
452        xlims = axes.get_xlim()
453        ylims = axes.get_ylim()
454        wdat = xlims[1] - xlims[0]
455        hdat = ylims[1] - ylims[0]
456        xdat = xlims[0] + wdat*(pos[0] - axpos[0][0])/(axpos[1][0] - axpos[0][0])
457        ydat = ylims[0] + hdat*(pos[1] - axpos[0][1])/(axpos[1][1] - axpos[0][1])
458        return (xdat, ydat)
459
460    @asaplog_post_dec
461    def _get_selected_text(self,event):
462        """helper function to return a dictionary of the nearest note to the event."""
463        (w,h) = event.canvas.get_width_height()
464        dist2 = w*w+h*h
465        selected = {}
466        for textobj in self.canvas.figure.texts:
467            if textobj.contains(event)[0]:
468                d2 = self._get_text_dist2(event,textobj)
469                if dist2 >= d2:
470                    dist2 = d2
471                    selected = {'anchor': 'figure', \
472                                'parent': event.canvas.figure, 'textobj': textobj}
473                    msg = "Fig loop: a text, '"+textobj.get_text()+"', at "
474                    msg += str(textobj.get_position())+" detected"
475                    #print msg
476        for ax in self.canvas.figure.axes:
477            for textobj in ax.texts:
478                if textobj.contains(event)[0]:
479                    d2 = self._get_text_dist2(event,textobj)
480                    if dist2 >= d2:
481                        anchor='axes'
482                        if ax.transData == textobj.get_transform():
483                            anchor = 'data'                   
484                        selected = {'anchor': anchor, \
485                                    'parent': ax, 'textobj': textobj}
486                        msg = "Ax loop: a text, '"+textobj.get_text()+"', at "
487                        msg += str(textobj.get_position())+" detected"
488                        #print msg
489
490        if selected:
491            msg = "Selected (modify/delete): '"+selected['textobj'].get_text()
492            msg += "' @"+str(selected['textobj'].get_position())
493            msg += " ("+selected['anchor']+"-coord)"
494            asaplog.push(msg)
495
496        return selected
497
498    def _get_text_dist2(self,event,textobj):
499        """
500        helper function to calculate square of distance between
501        a event position and a text object.
502        """
503        (x,y) = textobj._get_xy_display()
504        return (x-event.x)**2+(y-event.y)**2
505
506    def delete_note(self):
507        """
508        Remove selected note.
509        """
510        #print "You selected 'OK'"
511        self._remove_seltext()
512        self.canvas.draw()
513
514    @asaplog_post_dec
515    def _remove_seltext(self):
516        """helper function to remove the selected note"""
517        if len(self.seltext) < 3:
518            raise ValueError, "Don't under stand selected text obj."
519            return
520        try:
521            self.seltext['textobj'].remove()
522        except NotImplementedError:
523                self.seltext['parent'].texts.pop(self.seltext['parent'].texts.index(self.seltext['textobj']))
524        self.numnote -= 1
525
526        textobj = self.seltext['textobj']
527        msg = "Deleted note: '"+textobj.get_text()+"'"
528        msg += "@"+str(textobj.get_position())\
529               +" ("+self.seltext['anchor']+"-coord)"
530        msg += "\ntotal number of notes are "+str(self.numnote)
531        asaplog.push( msg )
532
533        self.seltext = {}
534
535    @asaplog_post_dec
536    def cancel_delete(self):
537        """
538        Cancel deleting the selected note.
539        Called when 'cancel' button selected on confirmation dialog.
540        """
541        asaplog.push( "Cancel deleting: '"+self.seltext['textobj'].get_text()+"'" )
542        self.seltext = {}
543
544
545
546###########################################
547##    Add CASA custom Flag toolbar       ##
548###########################################
549class CustomFlagToolbarCommon:
550    def __init__(self,parent):
551        self.plotter=parent
552        #self.figmgr=self.plotter._plotter.figmgr
553        self._selregions = {}
554        self._selpanels = []
555        self._polygons = []
556        self._thisregion = None
557        self.xdataold=None
558
559    ### select the nearest spectrum in pick radius
560    ###    and display spectral value on the toolbar.
561    def _select_spectrum(self,event):
562        # Do not fire event when in zooming/panning mode
563        mode = self.figmgr.toolbar.mode
564        if not mode == '':
565            return
566            # When selected point is out of panels
567        if event.inaxes == None:
568            return
569        # If not left button
570        if event.button != 1:
571            return
572
573        xclick = event.xdata
574        yclick = event.ydata
575        dist2 = 1000.
576        pickline = None
577        # If the pannel has picable objects
578        pflag = False
579        for lin in event.inaxes.lines:
580            if not lin.pickable():
581                continue
582            pflag = True
583            flag,pind = lin.contains(event)
584            if not flag:
585                continue
586            # Get nearest point
587            inds = pind['ind']
588            xlin = lin.get_xdata()
589            ylin = lin.get_ydata()
590            for i in inds:
591                d2=(xlin[i]-xclick)**2+(ylin[i]-yclick)**2
592                if dist2 >= d2:
593                    dist2 = d2
594                    pickline = lin
595        # No pickcable line in the pannel
596        if not pflag:
597            return
598        # Pickable but too far from mouse position
599        elif pickline is None:
600            picked = 'No line selected.'
601            self.figmgr.toolbar.set_message(picked)
602            return
603        del pind, inds, xlin, ylin
604        # Spectra are Picked
605        theplot = self.plotter._plotter
606        thetoolbar = self.figmgr.toolbar
607        thecanvas = self.figmgr.canvas
608        # Disconnect the default motion notify event
609        # Notice! the other buttons are also diabled!!!
610        thecanvas.mpl_disconnect(thetoolbar._idDrag)
611        # Get picked spectrum
612        xdata = pickline.get_xdata()
613        ydata = pickline.get_ydata()
614        titl = pickline.get_label()
615        titp = event.inaxes.title.get_text()
616        panel0 = event.inaxes
617        picked = "Selected: '"+titl+"' in panel '"+titp+"'."
618        thetoolbar.set_message(picked)
619        # Generate a navigation window
620        #naviwin=Navigationwindow(titp,titl)
621        #------------------------------------------------------#
622        # Show spectrum data at mouse position
623        def spec_data(event):
624            # Getting spectrum data of neiboring point
625            xclick = event.xdata
626            if event.inaxes != panel0:
627                return
628            ipoint = len(xdata)-1
629            for i in range(len(xdata)-1):
630                xl = xclick-xdata[i]
631                xr = xclick-xdata[i+1]
632                if xl*xr <= 0.:
633                    ipoint = i
634                    break
635            # Output spectral value on the navigation window
636            posi = '[ %s, %s ]:  x = %.2f   value = %.2f'\
637                  %(titl,titp,xdata[ipoint],ydata[ipoint])
638            #naviwin.posi.set(posi)
639            thetoolbar.set_message(posi)
640        #------------------------------------------------------#
641        # Disconnect from mouse events
642        def discon(event):
643            #naviwin.window.destroy()
644            theplot.register('motion_notify',None)
645            # Re-activate the default motion_notify_event
646            thetoolbar._idDrag=thecanvas.mpl_connect('motion_notify_event',
647                                                     thetoolbar.mouse_move)
648            theplot.register('button_release',None)
649            return
650        #------------------------------------------------------#
651        # Show data value along with mouse movement
652        theplot.register('motion_notify',spec_data)
653        # Finish events when mouse button is released
654        theplot.register('button_release',discon)
655
656
657    ### Notation
658    def _mod_note(self,event):
659        # Do not fire event when in zooming/panning mode
660        if not self.figmgr.toolbar.mode == '':
661            return
662        if event.button == 1:
663            self.notewin.load_textwindow(event)
664        elif event.button == 3 and self._note_picked(event):
665            self.notewin.load_modmenu(event)
666        return
667
668    def _note_picked(self,event):
669        # just briefly check if any texts are picked
670        for textobj in self.canvas.figure.texts:
671            if textobj.contains(event)[0]:
672                return True
673        for ax in self.canvas.figure.axes:
674            for textobj in ax.texts:
675                if textobj.contains(event)[0]:
676                    return True
677        return False
678
679    ### Region/Panel selection & oparations
680    ### add regions to selections
681    @asaplog_post_dec
682    def _add_region(self,event):
683        if not self.figmgr.toolbar.mode == '':
684            return
685        if event.button != 1 or event.inaxes == None:
686            return
687        # this row resolution assumes row panelling
688        irow = int(self._getrownum(event.inaxes))
689        if irow in self._selpanels:
690            msg = "The whole spectrum is already selected"
691            asaplog.post()
692            asaplog.push(msg)
693            asaplog.post('WARN')
694            return
695        self._thisregion = {'axes': event.inaxes,'xs': event.x,
696                            'worldx': [event.xdata,event.xdata]}
697        self.plotter._plotter.register('button_press',None)
698        self.plotter._plotter.register('motion_notify', self._xspan_draw)
699        self.plotter._plotter.register('button_press', self._xspan_end)
700
701    def _xspan_draw(self,event):
702        if event.inaxes == self._thisregion['axes']:
703            xnow = event.x
704            self.xdataold = xnow
705        else:
706            xnow = self.xdataold
707        try: self.lastspan
708        except AttributeError: pass
709        else: self._remove_span(self.lastspan)
710
711        #self.lastspan = self._draw_span(self._thisregion['axes'],self._thisregion['xs'],xnow,fill="#555555",stipple="gray50")
712        self.lastspan = self._draw_span(self._thisregion['axes'],self._thisregion['xs'],xnow,fill="")
713        del xnow
714
715    def _draw_span(self,axes,x0,x1,**kwargs):
716        pass
717
718    def _remove_span(self,span):
719        pass
720
721    @asaplog_post_dec
722    def _xspan_end(self,event):
723        if not self.figmgr.toolbar.mode == '':
724            return
725        if event.button != 1:
726            return
727
728        try: self.lastspan
729        except AttributeError: pass
730        else:
731            self._remove_span(self.lastspan)
732            del self.lastspan
733        if event.inaxes == self._thisregion['axes']:
734            xdataend = event.xdata
735        else:
736            xdataend=self.xdataold
737
738        self._thisregion['worldx'][1] = xdataend
739        lregion = self._thisregion['worldx']
740        pregion = self._thisregion['axes'].axvspan(lregion[0],lregion[1],
741                                                   facecolor='0.7')
742        self.plotter._plotter.canvas.draw()
743        self._polygons.append(pregion)
744        srow = self._getrownum(self._thisregion['axes'])
745        irow = int(srow)
746        if not self._selregions.has_key(srow):
747            self._selregions[srow] = []
748        self._selregions[srow].append(lregion)
749        del lregion, pregion, xdataend
750        sout = "selected region: "+str(self._thisregion['worldx'])+\
751              "(@row "+str(self._getrownum(self._thisregion['axes']))+")"
752        asaplog.push(sout)
753
754        # release event
755        self.plotter._plotter.register('button_press',None)
756        self.plotter._plotter.register('motion_notify',None)
757        # Clear up region selection
758        self._thisregion = None
759        self.xdataold = None
760        # finally recover region selection event
761        self.plotter._plotter.register('button_press',self._add_region)
762
763    ### add panels to selections
764    @asaplog_post_dec
765    def _add_panel(self,event):
766        if not self.figmgr.toolbar.mode == '':
767            return
768        if event.button != 1 or event.inaxes == None:
769            return
770        selax = event.inaxes
771        # this row resolution assumes row panelling
772        srow = self._getrownum(selax)
773        irow = int(srow)
774        if srow:
775            self._selpanels.append(irow)
776        shadow = Rectangle((0,0),1,1,facecolor='0.7',transform=selax.transAxes,visible=True)
777        self._polygons.append(selax.add_patch(shadow))
778        #self.plotter._plotter.show(False)
779        self.plotter._plotter.canvas.draw()
780        asaplog.push("row "+str(irow)+" is selected")
781        ## check for region selection of the spectra and overwrite it.
782        ##!!!! currently disabled for consistency with flag tools !!!!
783        #if self._selregions.has_key(srow):
784        #    self._selregions.pop(srow)
785        #    msg = "The whole spectrum is selected for row="+srow+". Region selection will be overwritten."
786        #    asaplog.push(msg)
787
788    def _getrownum(self,axis):
789        ### returns the row number of selected spectrum as a string ###
790        plabel = axis.get_title()
791        if plabel.startswith("row "):
792            return plabel.strip("row ")
793        return None
794
795    def _any_selection(self):
796        ### returns if users have selected any spectrum or region ###
797        if len(self._selpanels) or len(self._selregions):
798            return True
799        return False
800
801    def _plot_selections(self,regions=None,panels=None):
802        ### mark panels/spectra selections in the page
803        if not self._any_selection() and not (regions or panels):
804            return
805        regions = regions or self._selregions.copy() or {}
806        panels = panels or self._selpanels or []
807        if not isinstance(regions,dict):
808            asaplog.post()
809            asaplog.push("Invalid region specification")
810            asaplog.post('ERROR')
811        if not isinstance(panels,list):
812            asaplog.post()
813            asaplog.push("Invalid panel specification")
814            asaplog.post('ERROR')
815        strow = self._getrownum(self.plotter._plotter.subplots[0]['axes'])
816        enrow = self._getrownum(self.plotter._plotter.subplots[-1]['axes'])
817        for irow in range(int(strow),int(enrow)+1):
818            if regions.has_key(str(irow)):
819                ax = self.plotter._plotter.subplots[irow - int(strow)]['axes']
820                mlist = regions.pop(str(irow))
821                for i in range(len(mlist)):
822                    self._polygons.append(ax.axvspan(mlist[i][0],mlist[i][1],
823                                                     facecolor='0.7'))
824                del ax,mlist
825            if irow in panels:
826                ax = self.plotter._plotter.subplots[irow - int(strow)]['axes']
827                shadow = Rectangle((0,0),1,1,facecolor='0.7',
828                                   transform=ax.transAxes,visible=True)
829                self._polygons.append(ax.add_patch(shadow))
830                del ax,shadow
831        self.plotter._plotter.canvas.draw()
832        del regions,panels,strow,enrow
833
834    def _clear_selection_plot(self, refresh=True):
835        ### clear up polygons which mark selected spectra and regions ###
836        if len(self._polygons) > 0:
837            for shadow in self._polygons:
838                shadow.remove()
839            if refresh: self.plotter._plotter.canvas.draw()
840        self._polygons = []
841
842    def _clearup_selections(self, refresh=True):
843        # clear-up selection and polygons
844        self._selpanels = []
845        self._selregions = {}
846        self._clear_selection_plot(refresh=refresh)
847
848    ### clear up selections
849    def cancel_select(self):
850        self.figmgr.toolbar.set_message('selections canceled')
851        # clear-up selection and polygons
852        self._clearup_selections(refresh=True)
853
854    ### flag selected spectra/regions
855    @asaplog_post_dec
856    def flag(self):
857        if not self._any_selection():
858            msg = "No selection to be Flagged"
859            asaplog.post()
860            asaplog.push(msg)
861            asaplog.post('WARN')
862            return
863        self._pause_buttons(operation="start",msg="Flagging data...")
864        self._flag_operation(rows=self._selpanels,
865                             regions=self._selregions,unflag=False)
866        sout = "Flagged:\n"
867        sout += "  rows = "+str(self._selpanels)+"\n"
868        sout += "  regions: "+str(self._selregions)
869        asaplog.push(sout)
870        del sout
871        self.plotter._ismodified = True
872        self._clearup_selections(refresh=False)
873        self._plot_page(pagemode="current")
874        self._pause_buttons(operation="end")
875
876    ### unflag selected spectra/regions
877    @asaplog_post_dec
878    def unflag(self):
879        if not self._any_selection():
880            msg = "No selection to be Flagged"
881            asaplog.push(msg)
882            asaplog.post('WARN')
883            return
884        self._pause_buttons(operation="start",msg="Unflagging data...")
885        self._flag_operation(rows=self._selpanels,
886                             regions=self._selregions,unflag=True)
887        sout = "Unflagged:\n"
888        sout += "  rows = "+str(self._selpanels)+"\n"
889        sout += "  regions: "+str(self._selregions)
890        asaplog.push(sout)
891        del sout
892        self.plotter._ismodified = True
893        self._clearup_selections(refresh=False)
894        self._plot_page(pagemode="current")
895        self._pause_buttons(operation="end")
896
897    ### actual flag operation
898    @asaplog_post_dec
899    def _flag_operation(self,rows=None,regions=None,unflag=False):
900        scan = self.plotter._data
901        if not scan:
902            asaplog.post()
903            asaplog.push("Invalid scantable")
904            asaplog.post("ERROR")
905        if isinstance(rows,list) and len(rows) > 0:
906            scan.flag_row(rows=rows,unflag=unflag)
907        if isinstance(regions,dict) and len(regions) > 0:
908            for srow, masklist in regions.iteritems():
909                if not isinstance(masklist,list) or len(masklist) ==0:
910                    msg = "Ignoring invalid region selection for row = "+srow
911                    asaplog.post()
912                    asaplog.push(msg)
913                    asaplog.post("WARN")
914                    continue
915                irow = int(srow)
916                mask = scan.create_mask(masklist,invert=False,row=irow)
917                scan.flag(row=irow,mask=mask,unflag=unflag)
918                del irow, mask
919            del srow, masklist
920        del scan
921
922    ### show statistics of selected spectra/regions
923    @asaplog_post_dec
924    def stat_cal(self):
925        if not self._any_selection():
926            msg = "No selection to be calculated"
927            asaplog.push(msg)
928            asaplog.post('WARN')
929            return
930        self._selected_stats(rows=self._selpanels,regions=self._selregions)
931        self._clearup_selections(refresh=True)
932
933    @asaplog_post_dec
934    def _selected_stats(self,rows=None,regions=None):
935        scan = self.plotter._data
936        if not scan:
937            asaplog.post()
938            asaplog.push("Invalid scantable")
939            asaplog.post("ERROR")
940        mathobj = stmath( rcParams['insitu'] )
941        statval = {}
942        statstr = ['max', 'min', 'mean', 'median', 'sum', 'stddev', 'rms']
943        if isinstance(rows,list) and len(rows) > 0:
944            for irow in rows:
945                for stat in statstr:
946                    statval[stat] = mathobj._statsrow(scan,[],stat,irow)[0]
947                self._print_stats(scan,irow,statval,statstr=statstr)
948            del irow
949        if isinstance(regions,dict) and len(regions) > 0:
950            for srow, masklist in regions.iteritems():
951                if not isinstance(masklist,list) or len(masklist) ==0:
952                    msg = "Ignoring invalid region selection for row = "+srow
953                    asaplog.post()
954                    asaplog.push(msg)
955                    asaplog.post("WARN")
956                    continue
957                irow = int(srow)
958                mask = scan.create_mask(masklist,invert=False,row=irow)
959                for stat in statstr:
960                    statval[stat] = mathobj._statsrow(scan,mask,stat,irow)[0]
961                self._print_stats(scan,irow,statval,statstr=statstr,mask=masklist)
962                del irow, mask
963            del srow, masklist
964        del scan, statval, mathobj
965
966    @asaplog_post_dec
967    def _print_stats(self,scan,row,stats,statstr=None,mask=None):
968        if not isinstance(scan, scantable):
969            asaplog.post()
970            asaplog.push("Invalid scantable")
971            asaplog.post("ERROR")
972        if row < 0 or row > scan.nrow():
973            asaplog.post()
974            asaplog.push("Invalid row number")
975            asaplog.post("ERROR")
976        if not isinstance(stats,dict) or len(stats) == 0:
977            asaplog.post()
978            asaplog.push("Invalid statistic value")
979            asaplog.post("ERROR")
980        maskstr = "All"
981        if mask:
982            maskstr = str(mask)
983        ssep = "-"*70+"\n"
984        sout = ssep
985        sout += ("Row=%d  Scan=%d  IF=%d  Pol=%d  Time=%s  mask=%s" % \
986                 (row, scan.getscan(row), scan.getif(row), scan.getpol(row), scan.get_time(row),maskstr))
987        sout += "\n"
988        statvals = []
989        if not len(statstr):
990            statstr = stats.keys()
991        for key in statstr:
992            sout += key.ljust(10)
993            statvals.append(stats.pop(key))
994        sout += "\n"
995        sout += ("%f "*len(statstr) % tuple(statvals))
996        sout += "\n"+ssep
997        asaplog.push(sout)
998        del sout, ssep, maskstr, statvals, key, scan, row, stats, statstr, mask
999
1000    ### Page chages
1001    ### go to the previous page
1002    def prev_page(self):
1003        self._pause_buttons(operation="start",msg='plotting the previous page')
1004        self._clear_selection_plot(refresh=False)
1005        self._plot_page(pagemode="prev")
1006        self._plot_selections()
1007        self._pause_buttons(operation="end")
1008
1009    ### go to the next page
1010    def next_page(self):
1011        self._pause_buttons(operation="start",msg='plotting the next page')
1012        self._clear_selection_plot(refresh=False)
1013        self._plot_page(pagemode="next")
1014        self._plot_selections()
1015        self._pause_buttons(operation="end")
1016
1017    ### actual plotting of the new page
1018    def _plot_page(self,pagemode="next"):
1019        if self.plotter._startrow <= 0:
1020            msg = "The page counter is reset due to chages of plot settings. "
1021            msg += "Plotting from the first page."
1022            asaplog.post()
1023            asaplog.push(msg)
1024            asaplog.post('WARN')
1025            goback = False
1026
1027        self.plotter._plotter.hold()
1028        self.plotter._plotter.legend(1)
1029        self._set_plot_counter(pagemode)
1030        self.plotter._plot(self.plotter._data)
1031        self.set_pagecounter(self._get_pagenum())
1032        self.plotter._plotter.release()
1033        self.plotter._plotter.tidy()
1034        self.plotter._plotter.show(hardrefresh=False)
1035
1036    ### calculate the panel ID and start row to plot a page
1037    #def _set_prevpage_counter(self):
1038    def _set_plot_counter(self, pagemode):
1039        ## page operation should be either "previous", "current", or "next"
1040        availpage = ["p","c","n"]
1041        pageop = pagemode[0].lower()
1042        if not (pageop in availpage):
1043            asaplog.post()
1044            asaplog.push("Invalid page operation")
1045            asaplog.post("ERROR")
1046        if pageop == "n":
1047            # nothing necessary to plot the next page
1048            return
1049        # set row and panel counters to those of the 1st panel of previous page
1050        maxpanel = 16
1051        # the ID of the last panel in current plot
1052        lastpanel = self.plotter._ipanel
1053        # the number of current subplots
1054        currpnum = len(self.plotter._plotter.subplots)
1055
1056        # the nuber of previous subplots
1057        start_ipanel = None
1058        if pageop == "c":
1059            start_ipanel = max(lastpanel-currpnum+1, 0)
1060        else:
1061            ## previous page
1062            prevpnum = None
1063            if self.plotter._rows and self.plotter._cols:
1064                # when user set layout
1065                prevpnum = self.plotter._rows*self.plotter._cols
1066            else:
1067                # no user specification
1068                prevpnum = maxpanel
1069            start_ipanel = max(lastpanel-currpnum-prevpnum+1, 0)
1070            del prevpnum
1071
1072        # set the pannel ID of the last panel of the prev(-prev) page
1073        self.plotter._ipanel = start_ipanel-1
1074        if self.plotter._panelling == 'r':
1075            self.plotter._startrow = start_ipanel
1076        else:
1077            # the start row number of the next panel
1078            self.plotter._startrow = self.plotter._panelrows[start_ipanel]
1079        del lastpanel,currpnum,start_ipanel
1080
1081    ### refresh the page counter
1082    def set_pagecounter(self,page):
1083        nwidth = int(numpy.ceil(numpy.log10(max(page,1))))+1
1084        nwidth = max(nwidth,4)
1085        formatstr = '%'+str(nwidth)+'d'
1086        self.show_pagenum(page,formatstr)
1087
1088    def show_pagenum(self,pagenum,formatstr):
1089        # passed to backend dependent class
1090        pass
1091
1092    def _get_pagenum(self):
1093        maxpanel = 16
1094        # get the ID of last panel in the current page
1095        idlastpanel = self.plotter._ipanel
1096        if self.plotter._rows and self.plotter._cols:
1097            ppp = self.plotter._rows*self.plotter._cols
1098        else:
1099            ppp = maxpanel
1100        return int(idlastpanel/ppp)+1
1101
1102    # pause buttons for slow operations. implemented at a backend dependent class
1103    def _pause_buttons(self,operation="end",msg=""):
1104        pass
Note: See TracBrowser for help on using the repository browser.