+#----------------------------------------------------------------------
+# 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
+#----------------------------------------------------------------------
+# 12/10/2003 - Jeff Grimmett (grimmtooth@softhome.net)
+#
+# o 2.5 compatability update.
+# o Added deprecation warning.
+#
+
+"""
+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.
+
+"""
+
+import keyword
+import sys
+import warnings
+
+from code import InteractiveInterpreter
+
+import wx
+import wx.stc as stc
+
+warningmsg = r"""\
+
+########################################\
+# THIS MODULE IS NOW DEPRECATED |
+# |
+# Please see the most excellent PyCrust |
+# package instead. |
+########################################/
+
+"""
+
+warnings.warn(warningmsg, DeprecationWarning, stacklevel=2)
+
+#----------------------------------------------------------------------
+# default styles, etc. to use for the STC
+
+if wx.Platform == '__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(stc.StyledTextCtrl, InteractiveInterpreter):
+ def __init__(self, parent, ID, pos=wx.DefaultPosition,
+ size=wx.DefaultSize, style=0,
+ locals=None, properties=None, banner=None):
+ stc.StyledTextCtrl.__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
+ self.Bind(wx.EVT_KEY_DOWN, self.OnKey)
+ self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI, id=ID)
+ #self.Bind(stc.EVT_STC_STYLENEEDED, self.OnStyle, id=ID)
+
+
+ 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(stc.STC_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(stc.STC_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(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
+ self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
+ self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
+ self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
+ self.StyleSetSpec(stc.STC_P_STRING, p['string'])
+ self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
+ self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
+ self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
+ self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
+ self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
+ self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
+ self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
+ self.StyleSetSpec(stc.STC_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()
+ wx.Yield()
+
+ 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 == wx.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(stc.STC_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 == stc.STC_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 == stc.STC_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 = wx.PyWidgetTester(size = (640, 480))
+ app.SetWidget(PyShellWindow, -1)
+ app.MainLoop()
+
+
+#----------------------------------------------------------------------