source: trunk/python/customgui_base.py@ 2166

Last change on this file since 2166 was 2155, checked in by Kana Sugimoto, 14 years ago

New Development: Yes

JIRA Issue: No (reorganization of modules)

Ready for Test: Yes

Interface Changes: Yes

What Interface Changed: the modules casatoolbar, flagtoolbar, notaionwindow, were reorganized as customgui_base, customgui_tkagg, and customgui_qt4agg, based on backend

Test Programs: List test programs

Put in Release Notes: Yes/No

Module(s): Module Names change impacts.

Description: Describe your changes here...


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