source: trunk/python/customgui_qt4agg.py@ 2168

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

New Development: Yes

JIRA Issue: No

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs: Interactive test with PyQt4 backend

Put in Release Notes: Yes

Module(s): asapplotter, asaplotbase, and sdplot

Description: Enabled additional toolbar, casabar, in asapplotter for Qt4Agg bacend


File size: 18.4 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
7from asap.customgui_base import *
8
9import PyQt4 as qt
10
11######################################
12## Add CASA custom toolbar ##
13######################################
14class CustomToolbarQT4Agg(CustomToolbarCommon, qt.QtGui.QToolBar):
15 def __init__(self,parent):
16 from asap.asapplotter import asapplotter
17 if not isinstance(parent,asapplotter):
18 return False
19 if not parent._plotter:
20 return False
21 self._p = parent._plotter
22 self.figmgr = self._p.figmgr
23 self.canvas = self.figmgr.canvas
24 self.mode = ''
25 self.button = True
26 self.pagecount = None
27 CustomToolbarCommon.__init__(self,parent)
28 self.notewin = NotationWindowQT4Agg(master=self.canvas)
29 self._add_custom_toolbar()
30
31 def _add_custom_toolbar(self):
32 qt.QtGui.QToolBar.__init__(self,parent=self.figmgr.window)
33 self.figmgr.window.addToolBar(qt.QtCore.Qt.BottomToolBarArea,self)
34 self.bNote = self._NewButton(master=self,
35 text='notation',
36 command=self.modify_note,
37 balloon="Add note")
38 self.bNote.setCheckable(True)
39
40 self.bStat = self._NewButton(master=self,
41 text='statistics',
42 command=self.stat_cal,
43 balloon="Calculate statistics")
44 self.bStat.setCheckable(True)
45
46 # page change oparations
47 frPage = qt.QtGui.QWidget(parent=self,flags=qt.QtCore.Qt.Tool)
48 loPage = qt.QtGui.QHBoxLayout(self)
49 loPage.addStretch(1)
50 self.lPagetitle = qt.QtGui.QLabel('Page:',parent=frPage)
51 self.lPagetitle.setMargin(5)
52 loPage.addWidget(self.lPagetitle)
53 self.pagecount = qt.QtGui.QLabel(parent=frPage)
54 self.pagecount.setStyleSheet("background-color: white")
55 self.pagecount.setMargin(3)
56 self.pagecount.setText(' 1')
57 loPage.addWidget(self.pagecount)
58
59 self.bNext = self._NewButton(master=frPage,
60 text=' + ',
61 command=self.next_page,
62 addit=False)
63 loPage.addWidget(self.bNext)
64 self.bPrev = self._NewButton(master=frPage,
65 text=' - ',
66 command=self.prev_page,addit=False)
67 loPage.addWidget(self.bPrev)
68 frPage.setLayout(loPage)
69 self.addWidget(frPage)
70
71 self.bQuit = self._NewButton(master=self,
72 text='Quit',
73 command=self.quit,
74 balloon="Close window")
75
76 self.pagecount.setText(' '*4)
77
78 self.disable_button()
79 return
80
81 def _NewButton(self, master, text, command, balloon=None,addit=True):
82 b = qt.QtGui.QPushButton(text,parent=master)
83 if balloon: b.setToolTip(balloon)
84 if addit: master.addWidget(b)
85 master.connect(b,qt.QtCore.SIGNAL('clicked()'),command)
86 return b
87
88 def show_pagenum(self,pagenum,formatstr):
89 self.pagecount.setText(formatstr % (pagenum))
90
91 def spec_show(self):
92 if not self.figmgr.toolbar.mode == '' or not self.button: return
93 self.figmgr.toolbar.set_message('spec value: drag on a spec')
94 if self.mode == 'spec': return
95 self.mode = 'spec'
96 self.notewin.close_widgets()
97 self.__disconnect_event()
98 self._p.register('button_press',self._select_spectrum)
99
100 def stat_cal(self):
101 if not self.figmgr.toolbar.mode == '' or not self.button:
102 # Get back button status BEFORE clicked
103 self.bStat.setChecked(not self.bStat.isChecked())
104 return
105 if self.mode == 'stat':
106 # go back to spec mode
107 self.bStat.setChecked(False)
108 self.bStat.setToolTip("Calculate statistics")
109 self.spec_show()
110 return
111 self.figmgr.toolbar.set_message('statistics: select a region')
112 self.bStat.setChecked(True)
113 self.bStat.setToolTip("Back to spec value mode")
114 self.bNote.setChecked(False)
115 self.mode = 'stat'
116 self.notewin.close_widgets()
117 self.__disconnect_event()
118 self._p.register('button_press',self._single_mask)
119
120 def modify_note(self):
121 if not self.figmgr.toolbar.mode == '':
122 # Get back button status BEFORE clicked
123 self.bNote.setChecked(not self.bNote.isChecked())
124 return
125 self.figmgr.toolbar.set_message('text: select a position/text')
126 if self.mode == 'note':
127 self.bNote.setChecked(False)
128 self.bNote.setToolTip("Add note")
129 self.mode = 'none'
130 self.spec_show()
131 return
132 self.bStat.setChecked(False)
133 self.bNote.setChecked(True)
134 self.bNote.setToolTip("Back to spec value mode")
135 self.mode = 'note'
136 self.__disconnect_event()
137 self._p.register('button_press',self._mod_note)
138
139 def quit(self):
140 self.__disconnect_event()
141 self.disable_button()
142 self._p.quit()
143
144 def enable_button(self):
145 if self.button: return
146 self.bStat.setEnabled(True)
147 self.button = True
148 self.spec_show()
149
150 def disable_button(self):
151 if not self.button: return
152 self.bStat.setChecked(False)
153 self.bStat.setDisabled(True)
154 self.button = False
155 self.mode = ''
156 self.__disconnect_event()
157
158 def enable_next(self):
159 self.bNext.setEnabled(True)
160
161 def disable_next(self):
162 self.bNext.setDisabled(True)
163
164 def enable_prev(self):
165 self.bPrev.setEnabled(True)
166
167 def disable_prev(self):
168 self.bPrev.setDisabled(True)
169
170 # pause buttons for slow operations
171 def _pause_buttons(self,operation="end",msg=""):
172 buttons = ["bStat","bNote","bQuit"]
173 if operation == "start":
174 enable = False
175 else:
176 enable = True
177 for btn in buttons:
178 getattr(self,btn).setEnabled(enable)
179 self.figmgr.toolbar.set_message(msg)
180
181 def delete_bar(self):
182 self.__disconnect_event()
183 self.destroy()
184
185 def __disconnect_event(self):
186 self._p.register('button_press',None)
187 self._p.register('button_release',None)
188
189
190
191
192######################################
193## Notation box window ##
194######################################
195class NotationWindowQT4Agg(NotationWindowCommon):
196 """
197 Backend based class to create widgets to add, modify, or delete
198 note on the plot.
199
200 Note:
201 Press LEFT-mouse button on the plot to ADD a note on the canvas.
202 A notation window will be loaded for specifying note string and
203 anchor. The note will be anchored on a position in whether figure-
204 (0-1 relative in a figure), panel- (0-1 relative in a plot axes),
205 or data-coordinate (data value in a plot axes).
206 Press RIGHT-mouse button on a note to MODIFY/DELETE it. A cascade
207 menu will be displayed and you can select an operation.
208 """
209 def __init__(self,master=None):
210 self.parent = master
211 NotationWindowCommon.__init__(self,master=master)
212 self.anchval = None
213 self.textwin = self._create_textwindow(master=None)
214 self.menu = self._create_modmenu(master=self.parent)
215
216 ### Notation window widget
217 def _create_textwindow(self,master=None):
218 """Create notation window widget and iconfy it"""
219 #twin = qt.QtGui.QWidget(parent=master, flags=qt.QtCore.Qt.Popup)
220 twin = qt.QtGui.QWidget(parent=master, flags=qt.QtCore.Qt.Dialog)
221 twin.setWindowTitle("Notation")
222 self.textbox = self._NotationBox(parent=twin)
223 radiobox = self._AnchorRadio(parent=twin)
224 self.actionbs = self._ActionButtons(parent=twin)
225 vbox = qt.QtGui.QVBoxLayout(twin)
226 vbox.addWidget(self.textbox)
227 vbox.addWidget(radiobox)
228 vbox.addLayout(self.actionbs)
229 twin.setLayout(vbox)
230 #twin.setCentralWidget(self.textbox)
231 twin.hide()
232 return twin
233
234 def _NotationBox(self,parent=None):
235 textbox = qt.QtGui.QPlainTextEdit(parent=parent)
236 textbox.setStyleSheet("background-color: white")
237 fmetric = qt.QtGui.QFontMetrics(textbox.currentCharFormat().font())
238 textbox.resize(fmetric.width("A")*20+fmetric.leading()*2,
239 fmetric.height()*2+fmetric.ascent()+fmetric.descent())
240 del fmetric
241 textbox.setMinimumSize(textbox.size())
242 textbox.setUndoRedoEnabled(True)
243 textbox.setMidLineWidth(3)
244 textbox.setFrameShadow(qt.QtGui.QFrame.Sunken)
245 textbox.setCursor(qt.QtCore.Qt.IBeamCursor)
246 textbox.setFocus()
247 return textbox
248
249 def _AnchorRadio(self,parent=None):
250 # Returns a QGoupBox object which includes radio butons to
251 # select an anchor
252 anchbox = qt.QtGui.QGroupBox("anchor",parent=parent)
253 self.radio = qt.QtGui.QButtonGroup(parent=anchbox)
254 self.rFig = self._NewRadioButton(anchbox,"figure",\
255 bgr=self.radio,value=0,\
256 balloon="a fixed position in figure")
257 self.rAxis = self._NewRadioButton(anchbox,"panel",\
258 bgr=self.radio,value=1,\
259 balloon="a fixed realtive position in subplot")
260 self.rData = self._NewRadioButton(anchbox,"data",\
261 bgr=self.radio,value=2,\
262 balloon="a fixed data position in subplot")
263 hbox = qt.QtGui.QHBoxLayout(anchbox)
264 hbox.addWidget(self.rFig)
265 hbox.addWidget(self.rAxis)
266 hbox.addWidget(self.rData)
267 anchbox.setLayout(hbox)
268 # set initial selection "figure"
269 self.rFig.setChecked(True)
270 self.radio.setExclusive(True)
271 self.anchval = self.radio.checkedId()
272 return anchbox
273
274 def _NewRadioButton(self,parent,text,balloon=None,bgr=None,value=None):
275 rb= qt.QtGui.QRadioButton(text,parent=parent)
276 if bgr:
277 if value is not None:
278 bgr.addButton(rb,value)
279 else:
280 bgr.addButton(rb)
281 if balloon: rb.setToolTip(balloon)
282 return rb
283
284 def _enable_radio(self):
285 """Enable 'panel' and 'data' radio button"""
286 self.rAxis.setEnabled(True)
287 self.rData.setEnabled(True)
288 # select Figure as the default value
289 self.rFig.setChecked(True)
290 self.anchval = self.radio.checkedId()
291
292 def _reset_radio(self):
293 """Disable 'panel' and 'data' radio button"""
294 self.rAxis.setDisabled(True)
295 self.rData.setDisabled(True)
296 self.rFig.setEnabled(True)
297 # select Figure as the default value
298 self.rFig.setChecked(True)
299 self.anchval = self.radio.checkedId()
300
301 def _select_radio(self,selection):
302 """Select a specified radio button"""
303 if not selection in self.anchors:
304 return
305 if selection == "data":
306 self.rData.setChecked(True)
307 elif selection == "axes":
308 self.rAxis.setChecked(True)
309 else:
310 self.rFig.setChecked(True)
311 self.anchval = self.radio.checkedId()
312
313 def _get_anchval(self):
314 """Returns a integer of a selected radio button"""
315 self.anchval = self.radio.checkedId()
316 return self.anchval
317
318 def _get_note(self):
319 """Returns a note string specified in the text box"""
320 return str(self.textbox.toPlainText())
321
322 def _clear_textbox(self):
323 """Clear the text box"""
324 self.textbox.clear()
325
326 def _set_note(self,note=None):
327 """Set a note string to the text box"""
328 self._clear_textbox()
329 if len(note) >0:
330 self.textbox.setPlainText(note)
331
332 def _ActionButtons(self,parent=None):
333 # Returns a layout object which includes "cancel" and "print" buttons
334 actbuts = qt.QtGui.QHBoxLayout()
335 bCancel = self._NewButton(parent,"cancel",self._cancel_text,\
336 addit=False,\
337 balloon="cancel printing/modifying")
338 bPrint = self._NewButton(parent,"print", self._print_text,\
339 addit=False,\
340 balloon="print text on plot")
341 actbuts.addWidget(bCancel)
342 actbuts.addWidget(bPrint)
343 return actbuts
344
345 def _NewButton(self, parent, text, command, balloon=None, addit=True):
346 b = qt.QtGui.QPushButton(text,parent=parent)
347 if balloon: b.setToolTip(balloon)
348 if addit: parent.addWidget(b)
349 parent.connect(b,qt.QtCore.SIGNAL('clicked()'),command)
350 return b
351
352 def _cancel_text(self):
353 """
354 Cancel adding/modifying a note and close notaion window.
355 called when 'cancel' is selected.
356 """
357 self.close_textwindow()
358
359 def _print_text(self):
360 """
361 Add/Modify a note. Called when 'print' is selected on the
362 notation window.
363 """
364 self.print_text()
365 self.close_textwindow()
366
367 def load_textwindow(self,event):
368 """
369 Load text window at a event position to add a note on a plot.
370 Parameter:
371 event: an even object to specify the position to load
372 text window.
373 """
374 self.close_modmenu()
375 if event.canvas != self.parent:
376 raise RuntimeError, "Got invalid event!"
377
378 self.event = event
379 is_ax = (event.inaxes != None)
380 (xpix, ypix) = self._disppix2screen(event.x, event.y)
381 offset = 5
382 self.show_textwindow(xpix+offset,ypix+offset,enableaxes=is_ax)
383
384 def show_textwindow(self,xpix,ypix,basetext=None,enableaxes=False):
385 """
386 Load text window at a position of screen to add a note on a plot.
387 Parameters:
388 xpix, ypix: a pixel position from Upper-left corner
389 of the screen.
390 basetext: None (default) or any string.
391 A string to be printed on text box when loaded.
392 enableaxes: False (default) or True.
393 If True, 'panel' & 'data' radio button is enabled.
394 """
395 if not self.textwin: return
396 self._reset_radio()
397 if enableaxes:
398 self._enable_radio()
399 self.textwin.activateWindow()
400 h = self.textwin.minimumHeight()
401 w = self.textwin.minimumWidth()
402 self.textwin.resize(w,h)
403 self.textwin.move(xpix,ypix)
404 self.textbox.setFocus()
405 self.textwin.raise_()
406 self.textwin.show()
407 if w*h <= 1: # Initial load
408 self.textwin.setMinimumSize(self.textwin.size())
409
410 def close_textwindow(self):
411 """Close text window."""
412 self.seltext = {}
413 self._reset_radio()
414 self._clear_textbox()
415 self.textwin.hide()
416
417
418 ### Modify/Delete menu widget
419 def _create_modmenu(self,master=None):
420 """Create modify/delete menu widget"""
421 if master:
422 self.parent = master
423 if not self.parent:
424 return False
425 menu = qt.QtGui.QMenu(parent=self.parent)
426 menu.setTearOffEnabled(False)
427 menu.addAction("Modify",self._modify_note)
428 menu.addAction("Delete",self._delnote_dialog)
429 return menu
430
431 def load_modmenu(self,event):
432 """
433 Load cascade menu at a event position to modify or delete
434 selected text.
435 Parameter:
436 event: an even object to specify the position to load
437 text window.
438 """
439 self.close_textwindow()
440 self.seltext = self._get_selected_text(event)
441 if len(self.seltext) == 3:
442 canvas = event.canvas
443 corig = canvas.mapToGlobal(qt.QtCore.QPoint(0,0))
444 xpixs = corig.x() + int(event.x)
445 ypixs = corig.y() + canvas.height() - int(event.y)
446 self.menu.activateWindow()
447 self.menu.move(xpixs,ypixs)
448 self.menu.show()
449
450 def close_modmenu(self):
451 """Close cascade menu."""
452 self.seltext = {}
453 self.menu.hide()
454
455 ### load text window for modification
456 def _modify_note(self):
457 """helper function to load text window to modify selected note"""
458 textobj = self.seltext['textobj']
459 (xtx, ytx) = textobj._get_xy_display()
460 is_ax = (self.seltext['anchor'] != 'figure')
461 if not is_ax:
462 # previous anchor is figure
463 pos = textobj.get_position()
464 is_ax = (self._get_axes_from_pos(pos,self.canvas) != None)
465
466 (xpix, ypix) = self._disppix2screen(xtx,ytx)
467 offset = int(textobj.get_size())*2
468 self.show_textwindow(xpix,ypix+offset,basetext=textobj.get_text(),\
469 enableaxes=is_ax)
470 self._select_radio(self.seltext['anchor'])
471 self._set_note(textobj.get_text())
472
473 ### close all widgets
474 def close_widgets(self):
475 """Close note window and menu"""
476 self.close_textwindow()
477 self.close_modmenu()
478
479 ### dialog to confirm deleting note
480 def _delnote_dialog(self):
481 """Load dialog to confirm deletion of the text"""
482 remind = "Delete text?\n '"+self.seltext['textobj'].get_text()+"'"
483 from PyQt4.QtGui import QMessageBox as mbox
484 answer = mbox.question(self.parent,"Delete?",remind,
485 buttons = mbox.Ok | mbox.Cancel,
486 defaultButton=mbox.Cancel)
487 if answer == mbox.Ok:
488 self.delete_note()
489 else:
490 self.cancel_delete()
491
492 ### helper functions
493 def _disppix2screen(self,xpixd,ypixd):
494 """
495 helper function to calculate a pixel position form Upper-left
496 corner of the SCREEN from a pixel position (xpixd, ypixd)
497 from Lower-left of the CANVAS (which, e.g., event.x/y returns)
498
499 Returns:
500 (x, y): pixel position from Upper-left corner of the SCREEN.
501 """
502 corig = self.parent.mapToGlobal(qt.QtCore.QPoint(0,0))
503 xpixs = corig.x() + xpixd
504 ypixs = corig.y() + self.parent.height() - ypixd
505 return (int(xpixs), int(ypixs))
506
507
508
509
510
511
512###########################################
513## Add CASA custom Flag toolbar ##
514###########################################
Note: See TracBrowser for help on using the repository browser.