rcPythonShell.py

Go to the documentation of this file.
00001 #!/usr/local/bin/python
00002 #
00003 #                               Copyright 2005
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 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     # session log
00092     self.log = log or ''
00093 
00094     self.parent = parent
00095 
00096     # to exit the main interpreter by a Ctrl-D if PyCute has no parent
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     # last line + last incomplete lines
00103     self.line    = QString()
00104     self.lines   = []
00105     # the cursor position in the last line
00106     self.point   = 0
00107     # flag: the interpreter needs more input to run the last lines.
00108     self.more    = 0
00109     # flag: readline() is being used for e.g. raw_input() and input()
00110     self.reading = 0
00111     # history
00112     self.history = []
00113     self.pointer = 0
00114     self.xLast   = 0
00115     self.yLast   = 0
00116     self.clip = QApplication.clipboard()
00117 
00118 
00119     # user interface setup
00120     self.setTextFormat(QTextEdit.PlainText)
00121     self.setWrapPolicy(QTextEdit.Anywhere)
00122     self.setCaption('Python Shell')
00123     # font
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     # geometry
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     # interpreter prompt.
00141     try:
00142       sys.ps1
00143     except AttributeError:
00144       sys.ps1 = ">>> "
00145     try:
00146       sys.ps2
00147     except AttributeError:
00148       sys.ps2 = "... "
00149 
00150     # interpreter banner
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     # capture all interactive input/output
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     # The output of self.append(text) contains to many newline characters,
00207     # so work around QTextEdit's policy for handling newline characters.
00208     hack = self.text()
00209     hack.append(text)
00210     self.setText(hack)
00211     #self.setText(self.text().append(text)) # segmentation fault
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

Generated on Thu Apr 27 20:52:43 2006 for LICOS L02-01-00 by doxygen 1.4.6-NO