wxWidgets/wxPython/wx/lib/pyshell.py
2003-11-12 21:34:20 +00:00

330 lines
9.9 KiB
Python

#----------------------------------------------------------------------
# Name: wxPython.lib.pyshell
# Purpose: A Python Interactive Interpreter running in a wxStyledTextCtrl
# window.
#
# Author: Robin Dunn
#
# Created: 7-July-2000
# RCS-ID: $Id$
# Copyright: (c) 2000 by Total Control Software
# Licence: wxWindows license
#----------------------------------------------------------------------
"""
PyShellWindow is a class that provides an Interactive Interpreter running
inside a wxStyledTextCtrl, similar to the Python shell windows found in
IDLE and PythonWin.
There is still much to be done to improve this class, such as line
buffering/recall, autoindent, calltips, autocomplete, fixing the colourizer,
etc... But it's a good start.
8-10-2001 THIS MODULE IS NOW DEPRECATED. Please see the most excellent
PyCrust package instead.
"""
from wxPython.wx import *
from wxPython.stc import *
import sys, keyword
from code import InteractiveInterpreter
#----------------------------------------------------------------------
# default styles, etc. to use for the STC
if wxPlatform == '__WXMSW__':
_defaultSize = 8
else:
_defaultSize = 10
_default_properties = {
'selMargin' : 0,
'marginWidth' : 1,
'ps1' : '>>> ',
'stdout' : 'fore:#0000FF',
'stderr' : 'fore:#007f00',
'trace' : 'fore:#FF0000',
'default' : 'size:%d' % _defaultSize,
'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
'bracebad' : 'fore:#000000,back:#FF0000,bold',
# properties for the various Python lexer styles
'comment' : 'fore:#007F00',
'number' : 'fore:#007F7F',
'string' : 'fore:#7F007F,italic',
'char' : 'fore:#7F007F,italic',
'keyword' : 'fore:#00007F,bold',
'triple' : 'fore:#7F0000',
'tripledouble': 'fore:#7F0000',
'class' : 'fore:#0000FF,bold,underline',
'def' : 'fore:#007F7F,bold',
'operator' : 'bold',
}
# new style numbers
_stdout_style = 15
_stderr_style = 16
_trace_style = 17
#----------------------------------------------------------------------
class PyShellWindow(wxStyledTextCtrl, InteractiveInterpreter):
def __init__(self, parent, ID, pos=wxDefaultPosition,
size=wxDefaultSize, style=0,
locals=None, properties=None, banner=None):
wxStyledTextCtrl.__init__(self, parent, ID, pos, size, style)
InteractiveInterpreter.__init__(self, locals)
self.lastPromptPos = 0
# the line cache is used to cycle through previous commands
self.lines = []
self.lastUsedLine = self.curLine = 0
# set defaults and then deal with any user defined properties
self.props = {}
self.props.update(_default_properties)
if properties:
self.props.update(properties)
self.UpdateProperties()
# copyright/banner message
if banner is None:
self.write("Python %s on %s\n" % #%s\n(%s)\n" %
(sys.version, sys.platform,
#sys.copyright, self.__class__.__name__
))
else:
self.write("%s\n" % banner)
# write the initial prompt
self.Prompt()
# Event handlers
EVT_KEY_DOWN(self, self.OnKey)
EVT_STC_UPDATEUI(self, ID, self.OnUpdateUI)
#EVT_STC_STYLENEEDED(self, ID, self.OnStyle)
def GetLocals(self): return self.locals
def SetLocals(self, locals): self.locals = locals
def GetProperties(self): return self.props
def SetProperties(self, properties):
self.props.update(properties)
self.UpdateProperties()
def UpdateProperties(self):
"""
Reset the editor and other settings based on the contents of the
current properties dictionary.
"""
p = self.props
#self.SetEdgeMode(wxSTC_EDGE_LINE)
#self.SetEdgeColumn(80)
# set the selection margin and window margin
self.SetMarginWidth(1, p['selMargin'])
self.SetMargins(p['marginWidth'], p['marginWidth'])
# styles
self.StyleSetSpec(wxSTC_STYLE_DEFAULT, p['default'])
self.StyleClearAll()
self.StyleSetSpec(_stdout_style, p['stdout'])
self.StyleSetSpec(_stderr_style, p['stderr'])
self.StyleSetSpec(_trace_style, p['trace'])
self.StyleSetSpec(wxSTC_STYLE_BRACELIGHT, p['bracegood'])
self.StyleSetSpec(wxSTC_STYLE_BRACEBAD, p['bracebad'])
self.StyleSetSpec(wxSTC_P_COMMENTLINE, p['comment'])
self.StyleSetSpec(wxSTC_P_NUMBER, p['number'])
self.StyleSetSpec(wxSTC_P_STRING, p['string'])
self.StyleSetSpec(wxSTC_P_CHARACTER, p['char'])
self.StyleSetSpec(wxSTC_P_WORD, p['keyword'])
self.StyleSetSpec(wxSTC_P_TRIPLE, p['triple'])
self.StyleSetSpec(wxSTC_P_TRIPLEDOUBLE, p['tripledouble'])
self.StyleSetSpec(wxSTC_P_CLASSNAME, p['class'])
self.StyleSetSpec(wxSTC_P_DEFNAME, p['def'])
self.StyleSetSpec(wxSTC_P_OPERATOR, p['operator'])
self.StyleSetSpec(wxSTC_P_COMMENTBLOCK, p['comment'])
# used for writing to stdout, etc.
def _write(self, text, style=_stdout_style):
self.lastPromptPos = 0
pos = self.GetCurrentPos()
self.AddText(text)
self.StartStyling(pos, 0xFF)
self.SetStyling(len(text), style)
self.EnsureCaretVisible()
wxYield()
write = _write
def writeTrace(self, text):
self._write(text, _trace_style)
def Prompt(self):
# is the current line non-empty?
text, pos = self.GetCurLine()
if pos != 0:
self.AddText('\n')
self.AddText(self.props['ps1'])
self.lastPromptPos = self.GetCurrentPos()
self.EnsureCaretVisible()
self.ScrollToColumn(0)
def PushLine(self, text):
# TODO: Add the text to the line cache, manage the cache so
# it doesn't get too big.
pass
def OnKey(self, evt):
key = evt.KeyCode()
if key == WXK_RETURN:
pos = self.GetCurrentPos()
lastPos = self.GetTextLength()
# if not on the last line, duplicate the current line
if self.GetLineCount()-1 != self.GetCurrentLine():
text, col = self.GetCurLine()
prompt = self.props['ps1']
lp = len(prompt)
if text[:lp] == prompt:
text = text[lp:]
self.SetSelection(self.lastPromptPos, lastPos)
self.ReplaceSelection(text[:-1])
else: # try to execute the text from the prompt to the end
if lastPos == self.lastPromptPos:
self.AddText('\n')
self.Prompt()
return
text = self.GetTextRange(self.lastPromptPos, lastPos)
self.AddText('\n')
more = self.runsource(text)
if not more:
self.PushLine(text)
self.Prompt()
# TODO: Add handlers for Alt-P and Alt-N to cycle through entries
# in the line cache
else:
evt.Skip()
def OnStyle(self, evt):
# Only style from the prompt pos to the end
lastPos = self.GetTextLength()
if self.lastPromptPos and self.lastPromptPos != lastPos:
self.SetLexer(wxSTC_LEX_PYTHON)
self.SetKeywords(0, ' '.join(keyword.kwlist))
self.Colourise(self.lastPromptPos, lastPos)
self.SetLexer(0)
def OnUpdateUI(self, evt):
# check for matching braces
braceAtCaret = -1
braceOpposite = -1
charBefore = None
caretPos = self.GetCurrentPos()
if caretPos > 0:
charBefore = self.GetCharAt(caretPos - 1)
styleBefore = self.GetStyleAt(caretPos - 1)
# check before
if charBefore and chr(charBefore) in "[]{}()" and styleBefore == wxSTC_P_OPERATOR:
braceAtCaret = caretPos - 1
# check after
if braceAtCaret < 0:
charAfter = self.GetCharAt(caretPos)
styleAfter = self.GetStyleAt(caretPos)
if charAfter and chr(charAfter) in "[]{}()" and styleAfter == wxSTC_P_OPERATOR:
braceAtCaret = caretPos
if braceAtCaret >= 0:
braceOpposite = self.BraceMatch(braceAtCaret)
if braceAtCaret != -1 and braceOpposite == -1:
self.BraceBadlight(braceAtCaret)
else:
self.BraceHighlight(braceAtCaret, braceOpposite)
#----------------------------------------------
# overloaded methods from InteractiveInterpreter
def runsource(self, source):
stdout, stderr = sys.stdout, sys.stderr
sys.stdout = FauxFile(self, _stdout_style)
sys.stderr = FauxFile(self, _stderr_style)
more = InteractiveInterpreter.runsource(self, source)
sys.stdout, sys.stderr = stdout, stderr
return more
def showsyntaxerror(self, filename=None):
self.write = self.writeTrace
InteractiveInterpreter.showsyntaxerror(self, filename)
self.write = self._write
def showtraceback(self):
self.write = self.writeTrace
InteractiveInterpreter.showtraceback(self)
self.write = self._write
#----------------------------------------------------------------------
class FauxFile:
def __init__(self, psw, style):
self.psw = psw
self.style = style
def write(self, text):
self.psw.write(text, self.style)
def writelines(self, lst):
map(self.write, lst)
def flush(self):
pass
#----------------------------------------------------------------------
# test code
if __name__ == '__main__':
app = wxPyWidgetTester(size = (640, 480))
app.SetWidget(PyShellWindow, -1)
app.MainLoop()
#----------------------------------------------------------------------