source: trunk/python/customgui_base.py@ 2170

Last change on this file since 2170 was 2170, checked in by Kana Sugimoto, 14 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.