00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 __facility__ = "Online"
00011 __abstract__ = "Python shell class for the ScriptEngine"
00012 __author__ = "S. Tuvi <stuvi@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online"
00013 __date__ = "2005/07/23 00:08:27"
00014 __updated__ = "$Date: 2005/09/22 00:29:30 $"
00015 __version__ = "$Revision: 1.5 $"
00016 __release__ = "$Name: HEAD $"
00017 __credits__ = "SLAC"
00018
00019 import LICOS.copyright_SLAC
00020
00021 import os, sys
00022 from code import InteractiveInterpreter as Interpreter
00023 from qt import *
00024
00025 class rcPythonShell(QWidget):
00026 """!\brief Python Shell widget.
00027
00028 This widget is based on the QTextEdit widget and allows
00029 the execution of ad-hoc Python commands within the confines
00030 of the script engine.
00031 """
00032 def __init__(self,parent = None,name = None,fl = 0):
00033 """!\brief rcPythonShell constructor.
00034
00035 """
00036 QWidget.__init__(self,parent,name,fl)
00037
00038 if not name:
00039 self.setName("rcPythonShell")
00040
00041 self.__parent = parent
00042
00043 Form2Layout = QGridLayout(self,1,1,0,6,"Form2Layout")
00044
00045 self.__teShell = PythonShellWidget(locals=sys.modules['__main__'].__dict__, log='', parent=self)
00046
00047 Form2Layout.addWidget(self.__teShell,0,0)
00048
00049 self.languageChange()
00050
00051 self.resize(QSize(291,219).expandedTo(self.minimumSizeHint()))
00052
00053 def getParent(self):
00054 """!\brief Retrieve the parent GUI
00055
00056 \return Parent GUI
00057 """
00058 return self.__parent
00059
00060 def getWidget(self):
00061 """!\brief Retrieve the comment panel widget
00062
00063 \return CommentEntryWidget instance
00064 """
00065 return self.__teShell
00066
00067 def languageChange(self):
00068 """!\brief languageChange
00069
00070 """
00071 self.setCaption(self.tr("rcPythonShell"))
00072
00073 class PythonShellWidget(QTextEdit):
00074 def __init__(self, locals=None, log='', parent=None):
00075 """!\brief PythonShellWidget constructor.
00076
00077 \param locals The optional 'locals' argument specifies the dictionary in
00078 which code will be executed; it defaults to a newly created
00079 dictionary with key "__name__" set to "__console__" and key
00080 "__doc__" set to None.
00081 \param log The optional 'log' argument specifies the file in which the
00082 interpreter session is to be logged.
00083 \param parent The optional 'parent' argument specifies the parent widget.
00084 If no parent widget has been specified, it is possible to
00085 exit the interpreter by Ctrl-D.
00086 """
00087
00088 QTextEdit.__init__(self, parent)
00089 self.interpreter = Interpreter(locals)
00090
00091
00092 self.log = log or ''
00093
00094 self.parent = parent
00095
00096
00097 if os.name == 'nt' or os.name == 'dos':
00098 self.eofKey = Qt.Key_Z
00099 else:
00100 self.eofKey = Qt.Key_D
00101
00102
00103 self.line = QString()
00104 self.lines = []
00105
00106 self.point = 0
00107
00108 self.more = 0
00109
00110 self.reading = 0
00111
00112 self.history = []
00113 self.pointer = 0
00114 self.xLast = 0
00115 self.yLast = 0
00116 self.clip = QApplication.clipboard()
00117
00118
00119
00120 self.setTextFormat(QTextEdit.PlainText)
00121 self.setWrapPolicy(QTextEdit.Anywhere)
00122 self.setCaption('Python Shell')
00123
00124 if os.name == 'posix':
00125 font = QFont("Fixed", 12)
00126 elif os.name == 'nt' or os.name == 'dos':
00127 font = QFont("Courier New", 8)
00128 else:
00129 raise SystemExit, "FIXME for 'os2', 'mac', 'ce' or 'riscos'"
00130 font.setFixedPitch(1)
00131 self.setFont(font)
00132
00133
00134 height = 40*QFontMetrics(font).lineSpacing()
00135 request = QSize(600, height)
00136 if parent is not None:
00137 request = request.boundedTo(parent.size())
00138 self.resize(request)
00139
00140
00141 try:
00142 sys.ps1
00143 except AttributeError:
00144 sys.ps1 = ">>> "
00145 try:
00146 sys.ps2
00147 except AttributeError:
00148 sys.ps2 = "... "
00149
00150
00151 self.write('Python shell running Python %s on %s.\n' %
00152 (sys.version, sys.platform))
00153 self.write('Type "copyright", "credits" or "license"'
00154 ' for more information on Python.\n')
00155 self.write(sys.ps1)
00156
00157 self.connect(self, SIGNAL("clicked(int,int)"), self.handleClicked)
00158
00159 def redirectIO(self):
00160 """!\brief Redirect all console IO to this widget.
00161
00162 """
00163
00164 sys.stdout = self
00165 sys.stderr = self
00166 sys.stdin = self
00167
00168 def restoreIO(self):
00169 """!\brief Restore all console IO to their original targets.
00170
00171 """
00172 sys.stdout = sys.__stdout__
00173 sys.stderr = sys.__stderr__
00174 sys.stdin = sys.__stdin__
00175
00176 def flush(self):
00177 """!\brief Simulate stdin, stdout, and stderr.
00178
00179 """
00180 pass
00181
00182 def isatty(self):
00183 """!\brief Simulate stdin, stdout, and stderr.
00184
00185 """
00186 return 1
00187
00188 def readline(self):
00189 """!\brief Simulate stdin, stdout, and stderr.
00190
00191 """
00192 self.reading = 1
00193 self.__clearLine()
00194 self.moveCursor(QTextEdit.MoveEnd, 0)
00195 while self.reading:
00196 qApp.processOneEvent()
00197 if self.line.length() == 0:
00198 return '\n'
00199 else:
00200 return str(self.line)
00201
00202 def write(self, text):
00203 """!\brief Simulate stdin, stdout, and stderr.
00204
00205 """
00206
00207
00208 hack = self.text()
00209 hack.append(text)
00210 self.setText(hack)
00211
00212 self.moveCursor(QTextEdit.MoveEnd, 0)
00213 self.yLast, self.xLast = self.getCursorPosition()
00214
00215 def writelines(self, text):
00216 """!\brief Simulate stdin, stdout, and stderr.
00217
00218 """
00219 map(self.write, text)
00220 print "DO WE EVER GET HERE? IF YES, OPTIMIZATION POSSIBLE"
00221
00222 def fakeUser(self, lines):
00223 """!\brief Simulate a user.
00224
00225 \param lines Sequence of strings, (Python statements).
00226 """
00227 for line in lines:
00228 self.line = QString(line.rstrip())
00229 self.write(self.line)
00230 self.write('\n')
00231 self.__run()
00232
00233 def __run(self):
00234 """!\brief Execute entered Python statement.
00235
00236 Append the last line to the history list, let the interpreter execute
00237 the last line(s), and clean up accounting for the interpreter results:
00238 (1) the interpreter succeeds
00239 (2) the interpreter fails, finds no errors and wants more line(s)
00240 (3) the interpreter fails, finds errors and writes them to sys.stderr
00241 """
00242 self.pointer = 0
00243 self.history.append(QString(self.line))
00244 self.lines.append(str(self.line))
00245 source = '\n'.join(self.lines)
00246 self.redirectIO()
00247 self.more = self.interpreter.runsource(source)
00248 self.restoreIO()
00249 if self.more:
00250 self.write(sys.ps2)
00251 else:
00252 self.write(sys.ps1)
00253 self.lines = []
00254 self.__clearLine()
00255
00256 def __clearLine(self):
00257 """!\brief Clear input line buffer
00258
00259 """
00260 self.line.truncate(0)
00261 self.point = 0
00262
00263 def __insertText(self, text):
00264 """!\brief Insert text at the current cursor position.
00265
00266 """
00267 y, x = self.getCursorPosition()
00268 self.insertAt(text, y, x)
00269 self.line.insert(self.point, text)
00270 self.point += text.length()
00271 self.setCursorPosition(y, x + text.length())
00272
00273 def keyPressEvent(self, e):
00274 """!\brief Handle user input a key at a time.
00275
00276 """
00277 text = e.text()
00278 key = e.key()
00279 ascii = e.ascii()
00280
00281 if text.length() and ascii>=32 and ascii<127:
00282 self.__insertText(text)
00283 return
00284
00285 if e.state() & Qt.ControlButton and key == self.eofKey:
00286 if self.parent is None:
00287 try:
00288 file = open(self.log, "w")
00289 file.write(str(self.text()))
00290 file.close()
00291 except:
00292 pass
00293 sys.exit()
00294 else:
00295 self.restoreIO()
00296 self.parent.getParent().hide()
00297 return
00298
00299 if e.state() & Qt.ControlButton or e.state() & Qt.ShiftButton:
00300 e.ignore()
00301 return
00302
00303 if key == Qt.Key_Backspace:
00304 if self.point:
00305 self.doKeyboardAction(QTextEdit.ActionBackspace)
00306 self.point -= 1
00307 self.line.remove(self.point, 1)
00308 elif key == Qt.Key_Delete:
00309 self.doKeyboardAction(QTextEdit.ActionDelete)
00310 self.line.remove(self.point, 1)
00311 elif key == Qt.Key_Return or key == Qt.Key_Enter:
00312 self.write('\n')
00313 if self.reading:
00314 self.reading = 0
00315 else:
00316 self.__run()
00317 elif key == Qt.Key_Tab:
00318 self.__insertText(text)
00319 elif key == Qt.Key_Left:
00320 if self.point:
00321 self.moveCursor(QTextEdit.MoveBackward, 0)
00322 self.point -= 1
00323 elif key == Qt.Key_Right:
00324 if self.point < self.line.length():
00325 self.moveCursor(QTextEdit.MoveForward, 0)
00326 self.point += 1
00327 elif key == Qt.Key_Home:
00328 self.setCursorPosition(self.yLast, self.xLast)
00329 self.point = 0
00330 elif key == Qt.Key_End:
00331 self.moveCursor(QTextEdit.MoveLineEnd, 0)
00332 self.point = self.line.length()
00333 elif key == Qt.Key_Up:
00334 if len(self.history):
00335 if self.pointer == 0:
00336 self.pointer = len(self.history)
00337 self.pointer -= 1
00338 self.__recall()
00339 elif key == Qt.Key_Down:
00340 if len(self.history):
00341 self.pointer += 1
00342 if self.pointer == len(self.history):
00343 self.pointer = 0
00344 self.__recall()
00345 elif key == Qt.Key_Escape:
00346 self.__deleteLine()
00347 else:
00348 e.ignore()
00349
00350 def __deleteLine(self):
00351 """!\brief Delete a line.
00352
00353 """
00354 self.setCursorPosition(self.yLast, self.xLast)
00355 self.setSelection(self.yLast, self.xLast,
00356 self.yLast, self.paragraphLength(self.yLast))
00357 self.removeSelectedText()
00358 self.__clearLine()
00359
00360 def __recall(self):
00361 """!\brief Display the current item from the command history.
00362
00363 """
00364 self.__deleteLine()
00365 self.__insertText(self.history[self.pointer])
00366
00367
00368 def focusNextPrevChild(self, next):
00369 """!\brief Suppress tabbing to the next window in multi-line commands.
00370
00371 """
00372 if next and self.more:
00373 return 0
00374 return QTextEdit.focusNextPrevChild(self, next)
00375
00376 def handleClicked(self, para, pos):
00377 """!\brief Save the selected text to the clipboard.
00378
00379 """
00380 if self.hasSelectedText():
00381 if len(self.selectedText()) > 0:
00382 self.clip.setText(self.selectedText())
00383 self.moveCursor(QTextEdit.MoveEnd, 0)
00384 return
00385
00386 def contentsContextMenuEvent(self,ev):
00387 """!\brief Paste the contents of the clipboard to the cursor position.
00388
00389 """
00390 self.__insertText(self.clip.text())
00391 ev.accept()
00392 return