source: branches/casa-prerelease/pre-asap/python/casatoolbar.py @ 2119

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

merged modifications in trunk (r2118)

File size: 16.6 KB
Line 
1import os
2import matplotlib, numpy
3from asap.logging import asaplog, asaplog_post_dec
4
5######################################
6##    Add CASA custom toolbar       ##
7######################################
8class CustomToolbarCommon:
9    def __init__(self,parent):
10        self.plotter = parent
11        #self.figmgr=self.plotter._plotter.figmgr
12
13    ### select the nearest spectrum in pick radius
14    ###    and display spectral value on the toolbar.
15    def _select_spectrum(self,event):
16        # Do not fire event when in zooming/panning mode
17        mode = self.figmgr.toolbar.mode
18        if not mode == '':
19            return
20            # When selected point is out of panels
21        if event.inaxes == None:
22            return
23        # If not left button
24        if event.button != 1:
25            return
26
27        xclick = event.xdata
28        yclick = event.ydata
29        dist2 = 1000.
30        pickline = None
31        # If the pannel has picable objects
32        pflag = False
33        for lin in event.inaxes.lines:
34            if not lin.pickable():
35                continue
36            pflag = True
37            flag,pind = lin.contains(event)
38            if not flag:
39                continue
40            # Get nearest point
41            inds = pind['ind']
42            xlin = lin.get_xdata()
43            ylin = lin.get_ydata()
44            for i in inds:
45                d2=(xlin[i]-xclick)**2+(ylin[i]-yclick)**2
46                if dist2 >= d2:
47                    dist2 = d2
48                    pickline = lin
49        # No pickcable line in the pannel
50        if not pflag:
51            return
52        # Pickable but too far from mouse position
53        elif pickline is None:
54            picked = 'No line selected.'
55            self.figmgr.toolbar.set_message(picked)
56            return
57        del pind, inds, xlin, ylin
58        # Spectra are Picked
59        theplot = self.plotter._plotter
60        thetoolbar = self.figmgr.toolbar
61        thecanvas = self.figmgr.canvas
62        # Disconnect the default motion notify event
63        # Notice! the other buttons are also diabled!!!
64        thecanvas.mpl_disconnect(thetoolbar._idDrag)
65        # Get picked spectrum
66        xdata = pickline.get_xdata()
67        ydata = pickline.get_ydata()
68        titl = pickline.get_label()
69        titp = event.inaxes.title.get_text()
70        panel0 = event.inaxes
71        picked = "Selected: '"+titl+"' in panel '"+titp+"'."
72        thetoolbar.set_message(picked)
73        # Generate a navigation window
74        #naviwin=Navigationwindow(titp,titl)
75        #------------------------------------------------------#
76        # Show spectrum data at mouse position
77        def spec_data(event):
78            # Getting spectrum data of neiboring point
79            xclick = event.xdata
80            if event.inaxes != panel0:
81                return
82            ipoint = len(xdata)-1
83            for i in range(len(xdata)-1):
84                xl = xclick - xdata[i]
85                xr = xclick - xdata[i+1]
86                if xl*xr <= 0.:
87                    ipoint = i
88                    break
89            # Output spectral value on the navigation window
90            posi = '[ %s, %s ]:  x = %.2f   value = %.2f'\
91                   %(titl,titp,xdata[ipoint],ydata[ipoint])
92            #naviwin.posi.set(posi)
93            thetoolbar.set_message(posi)
94        #------------------------------------------------------#
95        # Disconnect from mouse events
96        def discon(event):
97            #naviwin.window.destroy()
98            theplot.register('motion_notify',None)
99            # Re-activate the default motion_notify_event
100            thetoolbar._idDrag = thecanvas.mpl_connect('motion_notify_event',
101                                                       thetoolbar.mouse_move)
102            theplot.register('button_release',None)
103            return
104        #------------------------------------------------------#
105        # Show data value along with mouse movement
106        theplot.register('motion_notify',spec_data)
107        # Finish events when mouse button is released
108        theplot.register('button_release',discon)
109
110
111    ### Calculate statistics of the selected area.
112    def _single_mask(self,event):
113        # Do not fire event when in zooming/panning mode
114        if not self.figmgr.toolbar.mode == '':
115            return
116        # When selected point is out of panels
117        if event.inaxes == None:
118            return
119        if event.button == 1:
120            baseinv=True
121        elif event.button == 3:
122            baseinv=False
123        else:
124            return
125
126        def _calc_stats():
127            msk=mymask.get_mask()
128            statstr = ['max', 'min', 'mean', 'median', 'sum', 'stddev', 'rms']
129            for stat in statstr:
130                mymask.scan.stats(stat=stat,mask=msk)
131
132        # Interactive mask definition
133        from asap.interactivemask import interactivemask
134        mymask = interactivemask(plotter=self.plotter,scan=self.plotter._data)
135        # Create initial mask
136        mymask.set_basemask(invert=baseinv)
137        # Inherit event
138        mymask.set_startevent(event)
139        # Set callback func
140        mymask.set_callback(_calc_stats)
141        # Selected mask
142        mymask.select_mask(once=True,showmask=False)
143
144    def _mod_note(self,event):
145        # Do not fire event when in zooming/panning mode
146        if not self.figmgr.toolbar.mode == '':
147            return
148        if event.button == 1:
149            self.notewin.load_textwindow(event)
150        elif event.button == 3 and self._note_picked(event):
151            self.notewin.load_modmenu(event)
152        return
153
154    def _note_picked(self,event):
155        # just briefly check if any texts are picked
156        for textobj in self.canvas.figure.texts:
157            if textobj.contains(event)[0]:
158                return True
159        for ax in self.canvas.figure.axes:
160            for textobj in ax.texts:
161                if textobj.contains(event)[0]:
162                    return True
163        #print "No text picked"
164        return False
165
166    ### Page chages
167    ### go to the previous page
168    def prev_page(self):
169        self.figmgr.toolbar.set_message('plotting the previous page')
170        #self._pause_buttons(operation="start",msg='plotting the previous page')
171        self._new_page(goback=True)
172        #self._pause_buttons(operation="end")
173
174    ### go to the next page
175    def next_page(self):
176        self.figmgr.toolbar.set_message('plotting the next page')
177        #self._pause_buttons(operation="start",msg='plotting the next page')
178        self._new_page(goback=False)
179        #self._pause_buttons(operation="end")
180
181    ### actual plotting of the new page
182    def _new_page(self,goback=False):
183        top = None
184        header = self.plotter._headtext
185        reset = False
186        doheader = (isinstance(header['textobj'],list) and \
187                    len(header['textobj']) > 0)
188        if self.plotter._startrow <= 0:
189            msg = "The page counter is reset due to chages of plot settings. "
190            msg += "Plotting from the first page."
191            asaplog.push(msg)
192            asaplog.post('WARN')
193            reset = True
194            goback = False
195            if doheader:
196                extrastr = selstr = ''
197                if header.has_key('extrastr'):
198                    extrastr = header['extrastr']
199                if header.has_key('selstr'):
200                    selstr = header['selstr']
201            self.plotter._reset_header()
202        if doheader:
203            top = self.plotter._plotter.figure.subplotpars.top
204            fontsize = header['textobj'][0].get_fontproperties().get_size()
205
206        self.plotter._plotter.hold()
207        if goback:
208            self._set_prevpage_counter()
209        #self.plotter._plotter.clear()
210        self.plotter._plot(self.plotter._data)
211        self.set_pagecounter(self._get_pagenum())
212        # Plot header information
213        if header['textobj']:
214            if top and top != self.plotter._margins[3]:
215                # work around for sdplot in CASA. complete checking in future?
216                self.plotter._plotter.figure.subplots_adjust(top=top)
217            if reset:
218                self.plotter.print_header(plot=True,fontsize=fontsize,selstr=selstr, extrastr=extrastr)
219            else:
220                self.plotter._header_plot(header['string'],fontsize=fontsize)
221        self.plotter._plotter.release()
222        self.plotter._plotter.tidy()
223        self.plotter._plotter.show(hardrefresh=False)
224        del top
225
226    ### calculate the panel ID and start row to plot the previous page
227    def _set_prevpage_counter(self):
228        # set row and panel counters to those of the 1st panel of previous page
229        maxpanel = 16
230        # the ID of the last panel in current plot
231        lastpanel = self.plotter._ipanel
232        # the number of current subplots
233        currpnum = len(self.plotter._plotter.subplots)
234        # the nuber of previous subplots
235        prevpnum = None
236        if self.plotter._rows and self.plotter._cols:
237            # when user set layout
238            prevpnum = self.plotter._rows*self.plotter._cols
239        else:
240            # no user specification
241            prevpnum = maxpanel
242
243        start_ipanel = max(lastpanel-currpnum-prevpnum+1, 0)
244        # set the pannel ID of the last panel of prev-prev page
245        self.plotter._ipanel = start_ipanel-1
246        if self.plotter._panelling == 'r':
247            self.plotter._startrow = start_ipanel
248        else:
249            # the start row number of the next panel
250            self.plotter._startrow = self.plotter._panelrows[start_ipanel]
251        del lastpanel,currpnum,prevpnum,start_ipanel
252
253    ### refresh the page counter
254    ### refresh the page counter
255    def set_pagecounter(self,page):
256        nwidth = int(numpy.ceil(numpy.log10(max(page,1))))+1
257        nwidth = max(nwidth,4)
258        formatstr = '%'+str(nwidth)+'d'
259        self.show_pagenum(page,formatstr)
260
261    def show_pagenum(self,pagenum,formatstr):
262        # passed to backend dependent class
263        pass       
264
265    def _get_pagenum(self):
266        maxpanel = 16
267        # get the ID of last panel in the current page
268        idlastpanel = self.plotter._ipanel
269        if self.plotter._rows and self.plotter._cols:
270            ppp = self.plotter._rows*self.plotter._cols
271        else:
272            ppp = maxpanel
273        return int(idlastpanel/ppp)+1
274
275    # pause buttons for slow operations. implemented at a backend dependent class
276    def _pause_buttons(self,operation="end",msg=""):
277        pass
278
279
280#####################################
281##    Backend dependent Classes    ##
282#####################################
283### TkAgg
284if matplotlib.get_backend() == 'TkAgg':
285    import Tkinter as Tk
286    from notationwindow import NotationWindowTkAgg
287
288class CustomToolbarTkAgg(CustomToolbarCommon, Tk.Frame):
289    def __init__(self,parent):
290        from asap.asapplotter import asapplotter
291        if not isinstance(parent,asapplotter):
292            return False
293        if not parent._plotter:
294            return False
295        self._p = parent._plotter
296        self.figmgr = self._p.figmgr
297        self.canvas = self.figmgr.canvas
298        self.mode = ''
299        self.button = True
300        self.pagecount = None
301        CustomToolbarCommon.__init__(self,parent)
302        self.notewin = NotationWindowTkAgg(master=self.canvas)
303        self._add_custom_toolbar()
304
305    def _add_custom_toolbar(self):
306        Tk.Frame.__init__(self,master=self.figmgr.window)
307        #self.bSpec = self._NewButton(master=self,
308        #                             text='spec value',
309        #                             command=self.spec_show)
310        self.bNote = self._NewButton(master=self,
311                                     text='notation',
312                                     command=self.modify_note)
313
314        self.bStat = self._NewButton(master=self,
315                                     text='statistics',
316                                     command=self.stat_cal)
317        self.bQuit = self._NewButton(master=self,
318                                     text='Quit',
319                                     command=self.quit,
320                                     side=Tk.RIGHT)
321
322        # page change oparations
323        frPage = Tk.Frame(master=self,borderwidth=2,relief=Tk.GROOVE)
324        frPage.pack(ipadx=2,padx=10,side=Tk.RIGHT)
325        self.lPagetitle = Tk.Label(master=frPage,text='Page:',padx=5)
326        self.lPagetitle.pack(side=Tk.LEFT)
327        self.pagecount = Tk.StringVar(master=frPage)
328        self.lPagecount = Tk.Label(master=frPage,
329                                   textvariable=self.pagecount,
330                                   padx=5,bg='white')
331        self.lPagecount.pack(side=Tk.LEFT,padx=3)
332       
333        self.bNext = self._NewButton(master=frPage,
334                                     text=' + ',
335                                     command=self.next_page)
336        self.bPrev = self._NewButton(master=frPage,
337                                     text=' - ',
338                                     command=self.prev_page)
339
340        if os.uname()[0] != 'Darwin':
341            self.bPrev.config(padx=5)
342            self.bNext.config(padx=5)
343
344        self.pack(side=Tk.BOTTOM,fill=Tk.BOTH)
345        self.pagecount.set(' '*4)
346
347        self.disable_button()
348        return #self
349
350    def _NewButton(self, master, text, command, side=Tk.LEFT):
351        if os.uname()[0] == 'Darwin':
352            b = Tk.Button(master=master, text=text, command=command)
353        else:
354            b = Tk.Button(master=master, text=text, padx=2, pady=2,
355                          command=command)
356        b.pack(side=side)
357        return b
358
359    def show_pagenum(self,pagenum,formatstr):
360        self.pagecount.set(formatstr % (pagenum))
361
362    def spec_show(self):
363        if not self.figmgr.toolbar.mode == '' or not self.button: return
364        self.figmgr.toolbar.set_message('spec value: drag on a spec')
365        if self.mode == 'spec': return
366        #self.bStat.config(relief='raised')
367        #self.bSpec.config(relief='sunken')
368        #self.bNote.config(relief='raised')
369        self.mode = 'spec'
370        self.notewin.close_widgets()
371        self.__disconnect_event()
372        self._p.register('button_press',self._select_spectrum)
373
374    def stat_cal(self):
375        if not self.figmgr.toolbar.mode == '' or not self.button: return
376        self.figmgr.toolbar.set_message('statistics: select a region')
377        if self.mode == 'stat':
378            # go back to spec mode
379            self.bStat.config(relief='raised')
380            self.spec_show()
381            return
382        #self.bSpec.config(relief='raised')
383        self.bStat.config(relief='sunken')
384        self.bNote.config(relief='raised')
385        self.mode = 'stat'
386        self.notewin.close_widgets()
387        self.__disconnect_event()
388        self._p.register('button_press',self._single_mask)
389
390    def modify_note(self):
391        if not self.figmgr.toolbar.mode == '': return
392        self.figmgr.toolbar.set_message('text: select a position/text')
393        if self.mode == 'note':
394            self.bNote.config(relief='raised')
395            self.mode = 'none'
396            self.spec_show()
397            return
398        #self.bSpec.config(relief='raised')
399        self.bStat.config(relief='raised')
400        self.bNote.config(relief='sunken')
401        self.mode = 'note'
402        self.__disconnect_event()
403        self._p.register('button_press',self._mod_note)
404
405    def quit(self):
406        self.__disconnect_event()
407        #self.delete_bar()
408        self.disable_button()
409        self.figmgr.window.wm_withdraw()
410
411    def enable_button(self):
412        if self.button: return
413        #self.bSpec.config(state=Tk.NORMAL)
414        self.bStat.config(state=Tk.NORMAL)
415        self.button = True
416        self.spec_show()
417
418    def disable_button(self):
419        if not self.button: return
420        #self.bSpec.config(relief='raised', state=Tk.DISABLED)
421        self.bStat.config(relief='raised', state=Tk.DISABLED)
422        #self.bNext.config(state=Tk.DISABLED)
423        #self.bPrev.config(state=Tk.DISABLED)
424        self.button = False
425        self.mode = ''
426        self.__disconnect_event()
427
428    def enable_next(self):
429        self.bNext.config(state=Tk.NORMAL)
430
431    def disable_next(self):
432        self.bNext.config(state=Tk.DISABLED)
433
434    def enable_prev(self):
435        self.bPrev.config(state=Tk.NORMAL)
436
437    def disable_prev(self):
438        self.bPrev.config(state=Tk.DISABLED)
439
440    # pause buttons for slow operations
441    def _pause_buttons(self,operation="end",msg=""):
442        buttons = ["bStat","bNote","bQuit"]
443        if operation == "start":
444            state = Tk.DISABLED
445        else:
446            state = Tk.NORMAL
447        for btn in buttons:
448            getattr(self,btn).config(state=state)
449        self.figmgr.toolbar.set_message(msg)
450
451    def delete_bar(self):
452        self.__disconnect_event()
453        self.destroy()
454
455    def __disconnect_event(self):
456        self._p.register('button_press',None)
457        self._p.register('button_release',None)
Note: See TracBrowser for help on using the repository browser.