Package client :: Module Bridge
[hide private]
[frames] | no frames]

Source Code for Module client.Bridge

  1  """ 
  2  """ 
  3   
  4  from PyQt4.QtGui  import * 
  5  from PyQt4.QtCore import * 
  6   
  7  import Constants 
  8  import sys 
  9  import re 
 10   
11 -class Bridge (QObject):
12 """ 13 """ 14
15 - def __init__ (self, app, win):
16 """ 17 Overview 18 ======== 19 The bridge controls many "global" functionality, and in some cases may want to alter 20 something in the main window or application. 21 22 Also, and probably most importantly, the bridge serves as a universal interconnection 23 between any class you want. It makes it incredibly easy for the open file menu to send a 24 command to the ipython shell which is going to perform a command and then update the 25 region and variable viewers. 26 27 @param app: This gives the bridge a reference to the underlying application, allowing 28 it to change its fundamental properties if necessary. 29 @type app: QApplication 30 31 @param win: This gives the bridge a reference to the underlying main window, allowing 32 it to change its fundamental properties if necessary. 33 @type win: QMainWindow 34 """ 35 36 QObject.__init__ (self) 37 self._app = app 38 self._mainWindow = win 39 40 self._connected = False 41 self._parallel = False 42 self._drugItem = None 43 44 self._host = None 45 self._port = None 46 self._user = None 47 self._nodes = [] 48 self._numNodes = 0 49 50 # Imported libraries (get set in IPythonView.Init ()) 51 self._importPylab = False 52 self._importNumpy = False 53 self._importScipy = False 54 55 try: 56 import visit 57 self._importVisIt = True 58 except (ImportError): 59 self._importVisIt = False 60 61 62 # Defaults, before general.conf gets loaded 63 self._inPlace = False 64 self._autoGuess = True 65 self._limitOut = True 66 self._dataPath = None 67 68 self._pythonVersion = sys.version_info 69 70 self._visitModuleStarted = False 71 72 # attempt to refresh ever 3 seconds 73 self._lastUpdated = 0 74 self.startTimer (3000) 75 76 self.loadSettings ()
77
78 - def loadSettings (self):
79 """ 80 Overview 81 ======== 82 This method will load the contents of the configuration file: ~/.basin/client/general.conf 83 84 These correspond to the general preferences found within the BASIN_preferences.Preferences class. 85 86 This method is called upon the instantiation of the bridge as well as whenever the general.conf 87 is overwritten by the preferences menu. 88 """ 89 90 file = QFile (str (QDir.home ().path ()) + "/.basin/client/general.conf") 91 if file.exists (): 92 file.open (QIODevice.ReadOnly | QIODevice.Text) 93 stream = QTextStream (file) 94 reInPlace = re.compile ("\s*INPLACE\s*:\s*.*\s*") 95 reAutoGuess = re.compile ("\s*AUTOGUESS\s*:\s*.*\s*") 96 reLimitOut = re.compile ("\s*LIMITOUT\s*:\s*.*\s*") 97 reDataPath = re.compile ("\s*DATAPATH\s*:\s*.*\s*") 98 while not stream.atEnd (): 99 line = str (stream.readLine ()) 100 val = line[line.find (':') + 1:] 101 if val == "False": 102 val = False 103 elif val == "True": 104 val = True 105 elif val == "None": 106 val = None 107 108 if reInPlace.match (line): 109 self._inPlace = val 110 elif reAutoGuess.match (line): 111 self._autoGuess = val 112 elif reLimitOut.match (line): 113 self._limitOut= val 114 elif reDataPath.match (line): 115 self._dataPath = val 116 file.close ()
117
118 - def setMenuBar (self, menuBar):
119 """ 120 Overview 121 ======== 122 This gives the bridge a reference to the menubar. This isn't done during initialization 123 since the bridge is created before the menu bar exists. 124 125 @param menuBar: This is the reference to the menu bar that the Bridge requires. 126 @type menuBar: MenuBar 127 """ 128 129 self._menuBar = menuBar
130
131 - def setToolBar (self, toolBar):
132 """ 133 Overview 134 ======== 135 This gives the bridge a reference to the menubar. This isn't done during initialization 136 since the bridge is created before the tool bar exists. 137 138 @param toolBar: This is the reference to the tool bar that the Bridge requires. 139 @type toolBar: ToolBar 140 """ 141 142 self._toolBar = toolBar
143
144 - def setActions (self, actions):
145 """ 146 Overview 147 ======== 148 This gives the bridge a reference to the actions. This isn't done during initialization 149 since the bridge is created before the actions exists. 150 151 @param actions: 152 @type actions: 153 """ 154 155 self._actions = actions
156
157 - def setRegionViewer (self, regionViewer):
158 """ 159 Overview 160 ======== 161 This gives the bridge a reference to the region viewer. This isn't done during initialization 162 since the bridge is created before the region viewer exists. 163 164 @param regionViewer: 165 @type regionViewer: 166 """ 167 168 self._regionViewer = regionViewer 169 QObject.connect (self, SIGNAL ("updateBasin (PyQt_PyObject)"), self._regionViewer.Update)
170
171 - def setVariableViewer (self, varViewer):
172 """ 173 Overview 174 ======== 175 This gives the bridge a reference to the variable viewer. This isn't done during initialization 176 since the bridge is created before the variable viewer exists. 177 """ 178 179 self._varViewer = varViewer 180 QObject.connect (self, SIGNAL ("updateWorld (PyQt_PyObject)"), self._varViewer.Update)
181
182 - def setDock (self, dock):
183 """ 184 Overview 185 ======== 186 This gives the bridge a reference to the dock. This isn't done during initialization since 187 the bridge is created before the dock exists. 188 189 @param dock: 190 @type dock: Dock 191 """ 192 193 self._dock = dock
194
195 - def setStatusBar (self, statusBar):
196 """ 197 Overview 198 ======== 199 This gives the bridge a reference to the status bar. This isn't done during initialization 200 since the bridge is created before the status bar exists. 201 """ 202 203 self._statusBar = statusBar
204
205 - def setIPyShell (self, ipyShell):
206 """ 207 Overview 208 ======== 209 This gives the bridge a reference to the IPython Shell. This isn't done during initialization 210 since the bridge is created before the IPython Shell exists. 211 """ 212 213 self._ipyShell = ipyShell 214 self.pythonStatus ()
215
216 - def CursorNorm (self):
217 """ 218 Overview 219 ======== 220 This method returns the cursor to its former state. This means that you shouldn't change 221 a cursor until it has been returned to its original state, because returning it twice has no effect. 222 """ 223 224 self._app.restoreOverrideCursor ()
225
226 - def CursorBusy (self):
227 """ 228 Bridge.CursorBusy (self) 229 230 This method changes the cursor to a busy cursor. This 231 should really only be called if the cursor is in a 232 nromal state to begin with. 233 """ 234 self._app.setOverrideCursor (QCursor (Qt.WaitCursor))
235
236 - def CursorOpenHand (self):
237 """ 238 Bridge.CursorBusy (self) 239 240 This method changes the cursor to an open hand cursor. 241 This should really only be called if the cursor is in a 242 nromal state to begin with. 243 """ 244 self._app.setOverrideCursor (QCursor (Qt.OpenHandCursor))
245
246 - def CursorClosedHand (self):
247 """ 248 Bridge.CursorBusy (self) 249 250 This method changes the cursor to a closed hand cursor. 251 This should really only be called if the cursor is in a 252 nromal state to begin with. 253 """ 254 self._app.setOverrideCursor (QCursor (Qt.ClosedHandCursor))
255 256
257 - def Update (self):
258 """ 259 Bridge.Update (self) 260 261 This is the updating function that should be used to perform 262 an update across the entie basin_client. While the individual 263 region viewer and free variable viewer have update methods, 264 this should be the only point at which they are called. 265 266 That being said, the roder in which they are called is of 267 great importance. The Region Viewer must be updated first, 268 then the free variable viewer. This is because the variable 269 viewer has to check for aliases after it sets its cotnents up, 270 and if the region viewer is not already updated, then it 271 would be providing possibly false, and certainly old, results. 272 273 As this operation can take some noticeable time, the cursor 274 is changed to busy during this oepration. 275 276 After the update is complete, the _lastUpdated member is also 277 updated to reflect the ipython line number at which the client 278 is updated to. This gets used because every few seconds a 279 timer checks if an update is needed, by comparing this _lastUpdated 280 member to the current line. 281 """ 282 if (not self._connected) and (not self._parallel): 283 return 284 self.CursorBusy () 285 self.emit (SIGNAL ("updateBasin (PyQt_PyObject)"), self._ipyShell.getTopLevelRegions ()) 286 self.emit (SIGNAL ("updateWorld (PyQt_PyObject)"), self._ipyShell.getFreeVariables ()) 287 discrete, discreteWindows = self._ipyShell.getVisItExposeDiscrete () 288 continuous, continuousWindows = self._ipyShell.getVisItExposeContinuous () 289 self.emit (SIGNAL ("updateVisIt (PyQt_PyObject, PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)"), discrete, discreteWindows, continuous, continuousWindows) 290 self.CursorNorm () 291 self._ipyShell._commandLine._update == False 292 result = self._ipyShell.SilentCommand ("print \"\\n\" + BASIN_UPDATE").split ("\n") 293 lineNumber = result[1][result[1].find ("[", 1) + \ 294 1:result[1].find ("]", result[1].find ("[", 1))] 295 self._lastUpdated = int (lineNumber)
296
297 - def PopDrugItem (self):
298 """ 299 Bridge.PopDrugItem (self) 300 301 This will retrieve an item tacked onto the bridge from a drag 302 operation. On top of that, it also erases from the bridge in 303 a traditional "pop" fashion. 304 """ 305 temp = self._drugItem 306 self._drugItem = None 307 return temp
308
309 - def SaveIPythonSession (self):
310 """ 311 Bridge.SaveIPythonSession (self) 312 313 This method will create a File Saving dialog designed to save 314 the contents of the IPython Shell's History to a log file. If 315 the dialog is successful in getting a file name, it will save 316 the entire contents of the history in clear text format, dis- 317 regarding any formating that is actually seen in the IPython 318 History. 319 """ 320 dialog = QFileDialog (None, QString ("Save IPython Session Log"), 321 QString (str (QDir.home ().path ())), 322 QString ("Text (*.log)")) 323 dialog.setWindowIcon (QIcon (QString (ICON_DIR + "/file/python.png"))) 324 dialog.setViewMode (QFileDialog.Detail) 325 dialog.setFileMode (QFileDialog.AnyFile) 326 dialog.setAcceptMode (QFileDialog.AcceptSave) 327 dialog.exec_ () 328 329 files = dialog.selectedFiles () 330 if not files.isEmpty (): 331 fileName = str (files.takeFirst ()) 332 file = QFile () 333 file.setFileName (fileName) 334 file.setPermissions (QFile.WriteOwner | QFile.ReadOwner | QFile.ReadUser \ 335 | QFile.WriteUser | QFile.ReadGroup | QFile.WriteGroup | QFile.ReadOther) 336 if dialog.selectedFilter () == "Text (*.log)": 337 file.open (QIODevice.WriteOnly | QIODevice.Text) 338 file.writeData (str (self._ipyShell._commandHistory.toPlainText ())) 339 file.close ()
340
341 - def Connect (self):
342 """ 343 Bridge.Connect (self) 344 345 This method should be called whenever the user makes a 346 successful connection. This is done automatically through 347 the Connection module. It will enable the command line 348 for ipython, make everything aware that it is connected, and 349 set the menu actions to reflect connectivity. 350 351 The only real way to connect is via the Connection module, 352 so there is little need for this method to be called 353 anywhere else. 354 """ 355 self._ipyShell._commandLine.setReadOnly (False) 356 self._ipyShell._commandStatus.toggleButton.setEnabled (True) 357 self._connected = True 358 self.pythonStatus () 359 self._actions.menuConnect ()
360
361 - def Disconnect (self):
362 """ 363 Bridge.Disconnect (self) 364 365 This method should be called whenever the user disconnects 366 from the basin session. This can either happen manually by 367 the user, or if the bridge's method Error deems it necessary 368 to automatically kill the session. 369 370 Generally speaking it looks for specific types of connectivity 371 errors to decide if it should pull the plug on the session 372 or if it will be ok. Manually killing the basin_start.sh 373 session, along with segfaults within the C-code will 374 result in this type of disconnection. 375 376 This method really only disables the ipython command line 377 along with clearing the region/variable viewers, and changing 378 menu's to reflect a disconnected status. 379 """ 380 self._ipyShell._commandLine.setReadOnly (True) 381 self._ipyShell._initialized = False 382 self._ipyShell._scriptPath = "" 383 self._ipyShell._commandStatus.toggleButton.setEnabled (False) 384 self._regionViewer.clear () 385 self._varViewer.clear () 386 self._connected = False 387 self.pythonStatus () 388 self._actions.menuDisconnect ()
389
390 - def parallelOn (self):
391 """ 392 Bridge.parallelOn (self) 393 394 This method simply makes the data viewers available as well 395 as making the application aware that it is in parallel mode. 396 397 This is not meant to change any behaviour, but rather reflect 398 that state of the program. 399 """ 400 self._regionViewer.setEnabled (True) 401 self._varViewer.setEnabled (True) 402 self._parallel = True 403 self.pythonStatus ()
404
405 - def parallelOff (self):
406 """ 407 Bridge.parallelOff (self) 408 409 This method simply makes the data viewers disabled as well 410 as informing the application as to its non-parallel status. 411 412 This method will not affect any real behaviour, but rather 413 reflects the state of the program. 414 """ 415 self._regionViewer.setEnabled (False) 416 self._varViewer.setEnabled (False) 417 self._parallel = False 418 self.pythonStatus ()
419
420 - def pythonStatus (self):
421 """ 422 Bridge.pythonStatus (self) 423 424 This method will update the IPython status, which is displayed 425 in between the command history and command lines of the shell. 426 427 The status string is dependant on the status of basin_client's 428 connectivity as well as its parallel status. Assuming it is 429 both conencted and parallel, it will also inform the user of the 430 number of nodes that it is currently working with in the session. 431 """ 432 if self._parallel and self._connected: 433 self._ipyShell.showStatus ("Connected, Parallel Mode: On, " + 434 str (len (self._nodes)) + " nodes") 435 elif self._parallel and not self._connected: 436 self._ipyShell.showStatus ("Disconnected") 437 elif not self._parallel and self._connected: 438 self._ipyShell.showStatus ("Connected, Parallel Mode: Off") 439 elif not self._parallel and not self._connected: 440 self._ipyShell.showStatus ("Disconnected")
441
442 - def AddToDock (self, item):
443 """ 444 Bridge.AddToDock (self, item) 445 446 This method is generally called from the region or 447 free variable viwer. It will send an item that it 448 wants to send to the dock here first, since the viewers 449 don't directly know which widget is currently docked 450 (even though the user can obviously tell this). 451 452 It will determine the proper widget in the dock to send 453 the item to. This does not imply that the dock can 454 actually do anythung with the item, that is up to the 455 dock widget to decide. 456 """ 457 if item != None: 458 currentWidget = self._dock.GetCurrentWidget () 459 if currentWidget != None: 460 currentWidget.DoubleClicked (item)
461
462 - def makeParallel (self):
463 """ 464 Bridge.makeParallel (self) 465 466 This is just a method from the bridge to force the client into 467 a parallel mode. If it already is parallel, it does nothing. 468 """ 469 if self._parallel == False: 470 self._ipyShell.toggleSilentAutopx ()
471
472 - def makeLocal (self):
473 """ 474 Bridge.makeParallel (self) 475 476 This is just a method from the bridge to force the client into 477 a non-parallel mode. If it isn't parallel currently, it does nothing. 478 """ 479 if self._parallel == True: 480 self._ipyShell.toggleSilentAutopx ()
481
482 - def timerEvent (self, ev):
483 """ 484 Bridge.timerEvent (self, ev) 485 486 In the Bridge's initialization it sets a timer event, currently 487 for every 3 seconds. Every 3 seconds, this method is called. 488 489 If the client is connected, parallel, and the user has nothing 490 currently typed on the command line, it will go ahead and see 491 if it needs to bother updating. 492 493 In the bridge, there is a variable keeping track of the last 494 updated line in the ipython shell. It will check that against 495 a variable that keeps track of the latest relevant update line 496 in ipython. This assumes that code in the ipython shell knows 497 when it is and isn't relevant to updates. 498 499 If it finds that its update line is less than the ipython's 500 last relevant update line, then it will go ahead and perform 501 an Update, as well as setting its newly updated lastUpdated 502 variable. 503 """ 504 505 # Only attempt auto-updates if you are connected and parallel 506 if self._parallel and self._connected and self._ipyShell.isClear () \ 507 and self._ipyShell._commandLine._singleLine: 508 # Retrieve the last update-worthy line number 509 result = self._ipyShell.SilentCommand ("print \"\\n\" + BASIN_UPDATE") 510 if result.find ("rror") != -1: 511 self.Error ("BASIN_bridge.py", "Bridge", "timerEvent", result) 512 513 # A connection error, most likely, so disconnect 514 self.makeLocal () 515 self.Disconnect () 516 return 517 val = result.split ("\n")[3] 518 # And see if it is greater than your last updated line number 519 if self._lastUpdated < int (val): 520 # If they are, update your last updated number, and actually update 521 self._lastUpdated = int (val) 522 self.Update ()
523
524 - def Error (self, _file, _class, _function, _message = "", silent = False):
525 """ 526 Bridge.Error (self, _file, _class, _function, _message = "", silent) 527 528 This error method should be called whenever ipython makes a boo-boo. 529 It is up the the coder to determine if an error has occurred, I lazily 530 do this by doing a quick search through the return string for error. 531 532 All of the arguments are for the purpose of reporting where in this 533 basin_client code that the error occurred in. Of course the error 534 may have nothing to do with the client code, however, it is possible 535 that the error occurred from a mal-constructed ipython command being 536 sent to the ipython shell, in which case it is important. _file refers 537 to the actual scrip file, _class to the python class it occurred in, 538 _function for the method in the class it occurred. _message should be 539 a copy of the ipython error received. Silent hasn't been implemented 540 yet, but was intended to be a way of suppressing error messages to the 541 user, while still logging them in ~/.basin/client/error.log 542 """ 543 self.CursorNorm () 544 545 # Retrieve Date & Time 546 _date = QDate ().currentDate ().toString ("MM/dd/yyyy") 547 _time = QTime ().currentTime ().toString ("H:mm:ss") 548 549 # Log error 550 path = QDir.home () 551 if not path.exists (".basin"): 552 path.mkdir (".basin") 553 path.cd (".basin") 554 if not path.exists ("client"): 555 path.mkdir ("client") 556 path.cd ("client") 557 logPath = path.path () + "/error.log" 558 file = open (logPath, "a") 559 file.write ("Date: " + str (_date) + "\n") 560 file.write ("Time: " + str (_time) + "\n") 561 file.write ("File: " + str (_file) + "\n") 562 file.write ("Class: " + str (_class) + "\n") 563 file.write ("Function: " + str (_function) + "\n\n") 564 file.write ("***************************************************************************\n") 565 file.write ("*******************************Error Message*******************************\n") 566 file.write ("***************************************************************************\n") 567 file.write (_message + "\n") 568 file.write ("***************************************************************************\n\n\n\n\n") 569 file.close () 570 571 if silent: 572 return 573 574 # Inform user of error 575 dialog = QDialog (self._mainWindow) 576 dialog.setWindowTitle ("Error!") 577 layout = QGridLayout () 578 dialog.setLayout (layout) 579 acceptButton = QPushButton ("Ok") 580 QObject.connect (acceptButton, SIGNAL ("clicked ()"), dialog.accept) 581 layout.addWidget (QLabel (QString ("IPython Error!")), 0, 0, 1, 2, Qt.AlignHCenter) 582 layout.addWidget (QLabel (QString ("Date:")), 1, 0, 2, 1, Qt.AlignRight) 583 layout.addWidget (QLabel (QString (str (_date))), 1, 1, 2, 1, Qt.AlignLeft) 584 layout.addWidget (QLabel (QString ("Time:")), 3, 0, 2, 1, Qt.AlignRight) 585 layout.addWidget (QLabel (QString (str (_time))), 3, 1, 2, 1, Qt.AlignLeft) 586 layout.addWidget (QLabel (QString ("File:")), 5, 0, Qt.AlignRight) 587 layout.addWidget (QLabel (QString (str (_file))), 5, 1, Qt.AlignLeft) 588 layout.addWidget (QLabel (QString ("Class:")), 6, 0, Qt.AlignRight) 589 layout.addWidget (QLabel (QString (str (_class))), 6, 1, Qt.AlignLeft) 590 layout.addWidget (QLabel (QString ("Function:")), 7, 0, Qt.AlignRight) 591 layout.addWidget (QLabel (QString (str (_function))), 7, 1, Qt.AlignLeft) 592 layout.addWidget (QLabel (QString ("Log available in " + logPath)), \ 593 8, 0, 1, 2, Qt.AlignHCenter) 594 layout.addWidget (acceptButton, 9, 0, 1, 2, Qt.AlignHCenter) 595 dialog.exec_ ()
596