00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 __facility__ = "Online"
00011 __abstract__ = "GLAST LAT GUI for Online Script Engine Main Window"
00012 __author__ = """A. Kavelaars <AliciaK@SLAC.Stanford.edu>
00013 SLAC - GLAST LAT I&T/Online"""
00014 __date__ = "2005/07/23 00:08:27"
00015 __updated__ = "$Date: 2006/04/28 01:41:21 $"
00016 __version__ = "$Revision: 1.10 $"
00017 __release__ = "$Name: HEAD $"
00018 __credits__ = "SLAC"
00019
00020 import LICOS.copyright_SLAC
00021
00022
00023 import sys
00024 import logging as log
00025 from qt import *
00026
00027 from ScriptEngineMainGUI import ScriptEngineMainGUI
00028 from rcStatusMonitor import *
00029 from rcCommentPanel import *
00030 from rcPythonShell import *
00031 from rcParamVerifier import rcParamVerifier
00032
00033 class ScriptEngineMainGUIImpl(ScriptEngineMainGUI):
00034 """!\brief Main GUI for the Script Engine.
00035
00036 """
00037
00038 class ListBoxToolTip(QToolTip):
00039 """!\brief Tool tip for a QListBox.
00040
00041 """
00042 def __init__(self, listbox):
00043 """!\brief ListBoxToolTip constructor.
00044
00045 \param listbox QListBox instance
00046 """
00047 QToolTip.__init__(self, listbox.viewport())
00048 self.__listbox = listbox
00049
00050 def maybeTip(self, pos):
00051 """!\brief Tooltip handler method.
00052
00053 \param pos The position of the listbox item for which a tooltip is needed.
00054 """
00055 item = self.__listbox.itemAt(pos)
00056 tipString = item.text()
00057 cr = self.__listbox.itemRect(item)
00058 cr.moveTopLeft(self.__listbox.contentsToViewport(cr.topLeft()))
00059 QToolTip(self).tip(cr, tipString)
00060
00061 class DockableTab(QDockWindow):
00062 """!\brief A dockable/undockable tab.
00063
00064 """
00065 def __init__(self, parent, name, panel, tabPage, flags = 0):
00066 """!\brief DockableTab constructor.
00067
00068 \param parent Parent GUI
00069 \param name GUI name
00070 \param panel Container widget for the panel
00071 \param tabPage Tab page
00072 \param flags GUI flags
00073 """
00074 QDockWindow.__init__(self, parent, name, flags)
00075 self.__parent = parent
00076 self.__panel = panel
00077 self.__tabPage = tabPage
00078 self.__name = name
00079 self.setCaption(name)
00080 self.connect(self,SIGNAL("visibilityChanged(bool)"), self.visibilityChanged)
00081
00082 def visibilityChanged(self, visible):
00083 """!\brief Slot for handling the visibility changes.
00084
00085 \param visible Boolean indicating whether the dock window is visible or not
00086 """
00087 if not visible:
00088 self.__parent.showPage(self.__tabPage)
00089 self.__panel.reparent(self.__tabPage, 0, QPoint(0,0), 1)
00090 self.__tabPage.layout().addWidget(self.__panel,0,1)
00091 self.disconnect(self,SIGNAL("visibilityChanged(bool)"), self.visibilityChanged)
00092
00093 def __init__(self, common, parent = None, name = None, fl = 0):
00094 """!\brief ScriptEngineMainGUIImpl constructor.
00095
00096 \param common ScriptEngineCommon instance
00097 \param parent Parent GUI
00098 \param name GUI name
00099 \param fl GUI flags
00100 """
00101 ScriptEngineMainGUI.__init__(self, parent, name, fl)
00102
00103 self.connect(self.AppOpenButton,SIGNAL("clicked()"),self.loadScript)
00104 self.connect(self.OpenAppl,SIGNAL("activated(const QString &)"),self.initApp)
00105 self.OpenApplTip = ScriptEngineMainGUIImpl.ListBoxToolTip(self.OpenAppl.listBox())
00106
00107 self.connect(self.ResetButton,SIGNAL("clicked()"),self.ResetClicked)
00108 self.connect(self.RunButton,SIGNAL("clicked()"),self.RunClicked)
00109 self.connect(self.PauseButton,SIGNAL("clicked()"),self.PauseClicked)
00110 self.connect(self.StopButton,SIGNAL("clicked()"),self.StopClicked)
00111
00112 self.connect(self.fileOpen_ApplicationAction,SIGNAL("activated()"),self.loadScript)
00113 self.connect(self.fileConnectAction,SIGNAL("activated()"),self.connectClicked)
00114 self.connect(self.fileExitAction,SIGNAL("activated()"),self.fileExit)
00115 self.connect(self.fileLogoutAction,SIGNAL("activated()"),self.fileLogout)
00116 self.connect(self.controlRunAction,SIGNAL("activated()"),self.RunClicked)
00117 self.connect(self.editPreferencesAction,SIGNAL("activated()"),self.PreferencesActivated)
00118
00119 self.connect(self.helpVerifyAction,SIGNAL("activated()"),self.verifySoftware)
00120 self.connect(self.helpELogAction,SIGNAL("activated()"),self.elogWebsite)
00121 self.connect(self.helpAboutAction,SIGNAL("activated()"),self.aboutScriptEngine)
00122 self.connect(self.viewStatusPanel,SIGNAL("activated()"),self.showHideStatusPanel)
00123 self.connect(self.viewComments,SIGNAL("activated()"),self.showHideComments)
00124
00125 self.connect(self.viewPythonShell,SIGNAL("activated()"),self.showHidePythonShell)
00126 self.connect(self.cmbOperator,SIGNAL("activated(const QString&)"),self.setOperator)
00127 self.connect(self.comboParticleType, SIGNAL("activated(const QString&)"), self.setParticleType)
00128 self.connect(self.comboOrientation, SIGNAL("activated(const QString&)"), self.setOrientation)
00129 self.connect(self.ScriptEngineTab, SIGNAL("currentChanged(QWidget*)"), self.activateTab)
00130
00131
00132 self.__dwStatusPanel = QDockWindow()
00133 self.__dwStatusPanel.setResizeEnabled(1)
00134 self.__dwStatusPanel.setCloseMode(QDockWindow.Always)
00135 self.__statusPanel = rcStatusPanel(self.__dwStatusPanel)
00136 self.__dwStatusPanel.setWidget(self.__statusPanel)
00137 self.__dwStatusPanel.setCaption('Status Panel')
00138 self.statusView = self.__statusPanel.statusView
00139 self.statusView.setSorting(-1)
00140 self.statusView.setColumnWidth(1, 200)
00141 self.setDockEnabled(self.__dwStatusPanel, Qt.DockTop, 0)
00142 self.setDockEnabled(self.__dwStatusPanel, Qt.DockBottom, 0)
00143 self.setDockEnabled(self.__dwStatusPanel, Qt.DockLeft, 0)
00144 self.addDockWindow(self.__dwStatusPanel, Qt.DockRight)
00145 self.__dwStatusPanel.setFixedExtentWidth(321)
00146
00147 self.connect(self.__dwStatusPanel,SIGNAL("visibilityChanged(bool)"),self.statusPanelVisChanged)
00148
00149 self.__dwComments = QDockWindow()
00150 self.__dwComments.setResizeEnabled(1)
00151 self.__dwComments.setCloseMode(QDockWindow.Always)
00152 self.__comments = rcCommentPanel(self.__dwComments)
00153 self.__dwComments.setWidget(self.__comments)
00154 self.__dwComments.setCaption('Comments')
00155 self.__teComments = self.__comments.getWidget()
00156 self.setDockEnabled(self.__dwComments, Qt.DockTop, 0)
00157 self.setDockEnabled(self.__dwComments, Qt.DockBottom, 1)
00158 self.setDockEnabled(self.__dwComments, Qt.DockLeft, 0)
00159 self.addDockWindow(self.__dwComments, Qt.DockBottom)
00160 self.__dwComments.setFixedExtentWidth(0)
00161 self.__dwComments.setFixedExtentHeight(50)
00162 self.connect(self.__dwComments,SIGNAL("visibilityChanged(bool)"),self.viewComments, SLOT("setOn(bool)"))
00163 self.__dwComments.hide()
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185 self.__dwPythonShell = QDockWindow()
00186 self.__dwPythonShell.setResizeEnabled(1)
00187 self.__dwPythonShell.setVerticallyStretchable(0)
00188 self.__dwPythonShell.setCloseMode(QDockWindow.Always)
00189 self.__pythonShell = rcPythonShell(self.__dwPythonShell)
00190 self.__dwPythonShell.setWidget(self.__pythonShell)
00191 self.__dwPythonShell.setCaption('Python Shell')
00192 self.__tePythonShell = self.__pythonShell.getWidget()
00193 self.setDockEnabled(self.__dwPythonShell, Qt.DockTop, 0)
00194 self.setDockEnabled(self.__dwPythonShell, Qt.DockBottom, 1)
00195 self.setDockEnabled(self.__dwPythonShell, Qt.DockLeft, 0)
00196 self.setDockEnabled(self.__dwPythonShell, Qt.DockRight, 1)
00197 self.addDockWindow(self.__dwPythonShell, Qt.DockRight)
00198 self.__dwPythonShell.undock()
00199
00200 self.__dwPythonShell.setFixedExtentHeight(50)
00201 self.connect(self.__dwPythonShell,SIGNAL("visibilityChanged(bool)"),self.viewPythonShell, SLOT("setOn(bool)"))
00202 self.hidePythonShell()
00203
00204 self.__common = common
00205 self.__prefs = common.prefMan().preferences()
00206 self.__common.setGUI(self)
00207
00208 if self.__prefs.has_key('particle type'):
00209 for (item, val) in self.__prefs['particle type'].items():
00210 self.comboParticleType.insertItem(item)
00211 if self.common().options().securedir is None:
00212 self.comboParticleType.insertItem('NOT-DEFINED')
00213 if self.__prefs.has_key('lastparticletype'):
00214 self.comboParticleType.setCurrentText(self.__prefs['lastparticletype'])
00215 else:
00216 self.__prefs["lastparticletype"]= str(self.comboParticleType.currentText())
00217
00218
00219
00220
00221
00222
00223
00224
00225 if self.__prefs.has_key('orientation'):
00226 keyList = self.__prefs['orientation'].keys()
00227 keyList.sort()
00228 for item in keyList:
00229 self.comboOrientation.insertItem(item)
00230 if self.common().options().securedir is None:
00231 self.comboOrientation.insertItem('NOT-DEFINED')
00232 if self.__prefs.has_key('lastorientation'):
00233 self.comboOrientation.setCurrentText(self.__prefs['lastorientation'])
00234 else:
00235 self.__prefs["lastorientation"] = str(self.comboOrientation.currentText())
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245 self.__currentAppDir = self.__prefs['appdir']
00246 self.__currentApp = ""
00247 self.__statMon = rcStatusMonitor(1000, self)
00248 self.__statMon.addWatchItem('Operator', self.getOperator)
00249 self.__statMon.addWatchItem('Operator Id', self.__prefs, 'userid')
00250
00251
00252 self.__statMon.addWatchItem('Data Dir', self.__prefs, 'datadir')
00253 self.__statMon.addWatchItem('Log Dir', self.__prefs, 'logdir')
00254 self.__statMon.addWatchItem('App Dir', self.__prefs, 'appdir')
00255 self.__statMon.addWatchItem('Config Base Dir', self.__prefs, 'reposdir')
00256 self.__statMon.addWatchItem('Report Dir', self.__prefs, 'reportdir')
00257
00258 self.__statMon.addWatchItem('Export Dir', self.__prefs, 'exportdir')
00259 self.__statMon.addWatchItem('Message Log Enabled', self.__prefs, 'lognbl')
00260 self.__statMon.addWatchItem('Message Log Level', self.__prefs, 'loglevel')
00261 self.__statMon.addWatchItem('Message Log File', self.__prefs, 'logfilename')
00262 self.__statMon.addWatchItem('Archive On', self.__prefs, 'datasave',alarmExpr='==0')
00263 self.__statMon.addWatchItem('Export On', self.__prefs, 'dataexport',alarmExpr='==0')
00264
00265 self.__paramVerifier = rcParamVerifier(self.common(), self)
00266
00267 self.refreshUsers()
00268
00269 self.__panels = {}
00270
00271 self.ScriptEngineTab.mouseDoubleClickEvent = self.mouseDblClick
00272
00273 for i in range(self.ScriptEngineTab.count()):
00274 tabPage = self.ScriptEngineTab.page(i)
00275 label = str(self.ScriptEngineTab.tabLabel(tabPage))
00276 if label == 'Global':
00277 try:
00278 import GlobalPanelImpl
00279 except Exception, e:
00280 log.exception(e)
00281 else:
00282 GlobalPanelLayout = QGridLayout(tabPage,1,1,0,6,"GlobalPanelLayout")
00283 panel = GlobalPanelImpl.GlobalPanelImpl(self, self.common(), tabPage)
00284 self.__panels[label] = panel
00285 GlobalPanelLayout.addWidget(panel,0,1)
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296 elif label == 'ACD':
00297 try:
00298 import AcdPanelImpl
00299 except ImportError:
00300 pass
00301 except Exception, e:
00302 log.exception(e)
00303 log.warning("*** Unable to load ACD sub-system panel...bypassing ***")
00304 else:
00305 AcdPanelLayout = QGridLayout(tabPage,1,1,0,6,"AcdPanelLayout")
00306 panel = AcdPanelImpl.AcdPanelImpl(self, self.common(), tabPage)
00307 self.__panels[label] = panel
00308 AcdPanelLayout.addWidget(panel,0,1)
00309 elif label == 'CAL':
00310 try:
00311 import CalPanelImpl
00312 except ImportError, e:
00313 pass
00314 except:
00315 log.exception(e)
00316 log.warning("*** Unable to load CAL sub-system panel...bypassing ***")
00317 else:
00318 CalPanelLayout = QGridLayout(tabPage,1,1,0,6,"CalPanelLayout")
00319 panel = CalPanelImpl.CalPanelImpl(self, self.common(), tabPage)
00320 self.__panels[label] = panel
00321 CalPanelLayout.addWidget(panel,0,1)
00322 elif label == 'TKR':
00323 try:
00324 import TkrPanelImpl
00325 except ImportError, e:
00326 pass
00327 except:
00328 log.exception(e)
00329 log.warning("*** Unable to load TKR sub-system panel...bypassing ***")
00330 else:
00331 TkrPanelLayout = QGridLayout(tabPage,1,1,0,6,"TkrPanelLayout")
00332 panel = TkrPanelImpl.TkrPanelImpl(self, self.common(), tabPage)
00333 self.__panels[label] = panel
00334 TkrPanelLayout.addWidget(panel,0,1)
00335
00336 def refreshUsers(self, operator=None):
00337 """!\brief Refresh the list of users under the user combobox.
00338
00339 \param operator Current operator
00340 """
00341 self.cmbOperator.clear()
00342 userList = self.__prefs['users'].getIdNameDict().values()
00343 userList.sort()
00344 for userName in userList:
00345 self.cmbOperator.insertItem(userName)
00346 if self.common().options().securedir is None:
00347 self.cmbOperator.insertItem('NOT-DEFINED')
00348
00349 if operator is not None:
00350 self.cmbOperator.setCurrentText(operator.getName())
00351
00352 def mouseDblClick(self, event):
00353 """!\brief Trap mouse double click.
00354
00355 \param event A QEvent object
00356 """
00357 tabPage = self.ScriptEngineTab.currentPage()
00358 label = str(self.ScriptEngineTab.tabLabel(tabPage))
00359 if label in self.__panels:
00360 panel = self.__panels[label]
00361 dwPanel = self.DockableTab(self.ScriptEngineTab, label, panel, tabPage)
00362 dwPanel.setResizeEnabled(1)
00363 dwPanel.setVerticallyStretchable(0)
00364 dwPanel.setCloseMode(QDockWindow.Always)
00365 panel.reparent(dwPanel, 0, QPoint(0, 0), 1)
00366 dwPanel.setWidget(panel)
00367 dwPanel.undock()
00368 dwPanel.show()
00369
00370 def activateTab(self, tabPage):
00371 """!\brief Slot which gets called when a tab is activated.
00372
00373 If any of the panels have an 'activate' method it gets called.
00374
00375 \param tabPage The tab page which is being activated.
00376 """
00377 label = str(self.ScriptEngineTab.tabLabel(tabPage))
00378 if label in self.__panels:
00379 panel = self.__panels[label]
00380 if "activate" in dir(panel):
00381 panel.activate()
00382
00383 def panels(self):
00384 """!\brief Return a list of script engine panels.
00385
00386 \return A list of script engine panels
00387 """
00388 return self.__panels
00389
00390 def common(self):
00391 """!\brief Retrieve the common object.
00392
00393 \return ScriptEngineCommon instance
00394 """
00395 return self.__common
00396
00397 def preferences(self):
00398 """!\brief Retrieve the preferences.
00399
00400 \return Preferences instance
00401 """
00402 return self.__prefs
00403
00404 def statusMonitor(self):
00405 """!\brief Retrieve the status monitor.
00406
00407 \return rcStatusMonitor instance
00408 """
00409 return self.__statMon
00410
00411 def parameterVerifier(self):
00412 """!\brief Retrieve the parameter verifier.
00413
00414 \return rcParamVerifier instance
00415 """
00416 return self.__paramVerifier
00417
00418 def getOperator(self):
00419 """!\brief Retrieve the current operator name.
00420
00421 \return Current operator name
00422 """
00423 return self.__prefs['operator']
00424
00425 def getCommentWidget(self):
00426 """!\brief Retrieve the comment panel widget.
00427
00428 \return CommentEntryWidget instance
00429 """
00430 return self.__comments.getWidget()
00431
00432 def getPythonShellWidget(self):
00433 """!\brief Retrieve the Python shell widget.
00434
00435 \return PythonShellWidget instance
00436 """
00437 return self.__pythonShell.getWidget()
00438
00439 def statusPanelVisChanged(self, visible):
00440 """!\brief Status panel visibility changed.
00441
00442 \param visible Boolean indicating whether the panel is visible or not
00443 """
00444 self.viewStatusPanel.setOn(visible)
00445 if visible:
00446 self.__statMon.startWatch()
00447 else:
00448 self.__statMon.stopWatch()
00449
00450 def showHideStatusPanel(self):
00451 """!\brief Toggle the status panel.
00452
00453 """
00454 if self.viewStatusPanel.isOn():
00455 self.__dwStatusPanel.show()
00456 else:
00457 self.__dwStatusPanel.hide()
00458
00459 def showHideComments(self):
00460 """!\brief Toggle the comment panel.
00461
00462 """
00463 if self.viewComments.isOn():
00464 self.__dwComments.show()
00465 else:
00466 self.__dwComments.hide()
00467
00468 def showComments(self):
00469 """!\brief Show the comment panel.
00470
00471 """
00472 self.__dwComments.show()
00473
00474 def hideComments(self):
00475 """!\brief Hide the comment panel.
00476
00477 """
00478 self.__dwComments.hide()
00479
00480
00481
00482
00483
00484
00485
00486 def showHidePythonShell(self):
00487 """!\brief Toggle the Python shell panel.
00488
00489 """
00490 pass
00491
00492 def showPythonShell(self):
00493 """!\brief Show the Python shell panel.
00494
00495 """
00496 self.__dwPythonShell.show()
00497 self.__tePythonShell.setFocus()
00498
00499 def hidePythonShell(self):
00500 """!\brief Hide the Python shell panel.
00501
00502 """
00503 self.__dwPythonShell.hide()
00504
00505 def isPythonShellHidden(self):
00506 """!\brief Retrieve the visiblity of the Python shell.
00507
00508 \return Boolean indicating whether the Python shell is visible or not
00509 """
00510 return not self.viewPythonShell.isOn()
00511
00512 def PreferencesActivated(self):
00513 """!\brief Handle the preferences GUI activation.
00514
00515 Display the preferences GUI and based on the changes
00516 update various session variables.
00517 """
00518 prefGUI = self.__common.prefGUI()
00519 prefGUI.loadConfig()
00520 status = prefGUI.exec_loop()
00521 if status == prefGUI.Accepted:
00522 prefs = self.__common.prefMan().preferences()
00523 self.__common.EBFdistribution()
00524 self.__common.startLogger(startup=False)
00525 operator = prefs["operatorobj"]
00526 self.refreshUsers(operator)
00527 if self.__currentAppDir != prefs['appdir']:
00528 self.__currentAppDir = prefs['appdir']
00529 self.__currentApp = ""
00530
00531 def ResetClicked(self):
00532 """!\brief ResetClicked.
00533
00534 """
00535 pass
00536
00537 def StopClicked(self):
00538 """!\brief StopClicked.
00539
00540 """
00541 pass
00542
00543 def RunClicked(self):
00544 """!\brief RunClicked.
00545
00546 """
00547 pass
00548
00549 def PauseClicked(self):
00550 """!\brief PauseClicked.
00551
00552 """
00553 pass
00554
00555 def loadScript(self):
00556 """!\brief Load a Python script.
00557
00558 """
00559 currentApp = os.path.join(self.__currentAppDir, self.__currentApp)
00560 rcAppSel = rcFileSelector(currentApp, "(*.py)", self, "Select an application")
00561 applName = rcAppSel.getValue()
00562
00563 rcAppSel.deleteLater()
00564 if applName is None: return
00565 self.__currentAppDir, self.__currentApp = os.path.split(applName)
00566 self.OpenAppl.clearEdit()
00567 self.OpenAppl.setCurrentText(applName)
00568 self.initApp()
00569
00570 def fileExit(self):
00571 """!\brief Exit the script engine.
00572
00573 """
00574
00575 self.close()
00576
00577 def connectClicked(self):
00578 """!\brief Default connectClicked method.
00579
00580 """
00581 log.warning("ScriptEngineMainGUIImpl.connectClicked(): Not implemented yet")
00582
00583 def fileLogout(self):
00584 """!\brief Default fileLogout method.
00585
00586 """
00587 log.warning("ScriptEngineGUIImpl.fileLogout(): Not implemented yet")
00588
00589 def closeEvent(self, e):
00590 """!\brief Default closeEvent handler.
00591
00592 """
00593 e.accept()
00594
00595 def filePrint(self):
00596 """!\brief Default filePrint method.
00597
00598 """
00599 log.warning("ScriptEngineMainGUIImpl.filePrint(): Not implemented yet")
00600
00601 def setOperator(self, operator):
00602 """!\brief Default setOperator method.
00603
00604 \param operator Operator
00605 """
00606 pass
00607
00608 def setParticleType(self, particleType):
00609 """!\brief Default setParticleType method.
00610
00611 \param particleType Particle type
00612 """
00613 pass
00614
00615 def setInstrumentType(self, instrumentType):
00616 """!\brief Default setInstrumentType method.
00617
00618 \param instrumentType Instrument type
00619 """
00620 pass
00621
00622 def setOrientation(self, orientation):
00623 """!\brief Default setOrientation method.
00624
00625 \param orientation Orientation
00626 """
00627 pass
00628
00629 def setPhase(self, phase):
00630 """!\brief Default setPhase method.
00631
00632 \param phase Phase
00633 """
00634 pass
00635
00636 def verifySoftware(self):
00637 """!\brief Default verifySoftware method.
00638
00639 """
00640 pass
00641
00642 def elogWebsite(self):
00643 """!\brief Default elogWebsite method.
00644
00645 """
00646 pass
00647
00648 def aboutScriptEngine(self):
00649 """!\brief Default aboutScriptEngine method.
00650
00651 """
00652 pass
00653
00654 class rcFileSelector(QFileDialog):
00655 """!\brief A customized file open dialog class.
00656
00657 """
00658 def __init__(self, startwith, filter, parent, name):
00659 """!\brief rcFileSelector constructor.
00660
00661 \param startwith File path to start with
00662 \param filter File extension filter
00663 \param parent Parent GUI
00664 \param name GUI name
00665 """
00666 QFileDialog.__init__(self, parent, name, 0)
00667 self.setMode(QFileDialog.ExistingFile)
00668 self.setFilter(filter)
00669 self.setDir(os.path.dirname(startwith))
00670 self.setCaption(name)
00671 self.setViewMode( QFileDialog.Detail)
00672 self.setSelection(startwith)
00673
00674 def getValue(self):
00675 """!\brief Retrieve the file name selected.
00676
00677 \return Full path of the file selected or
00678 None if the user cancelled file selection.
00679 """
00680 if self.exec_loop() == QDialog.Accepted:
00681 return str(self.selectedFile())
00682 else:
00683 return None