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