]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/wx/lib/pyshell.py
reduce the number of objects for a quicker startup time
[wxWidgets.git] / wxPython / wx / lib / pyshell.py
index cdfca52db3faf382ec1a908ea8caef7a04409010..1dca90da9ef7f74f19ad8caf743cb5033a7ac584 100644 (file)
+#----------------------------------------------------------------------
+# 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.
+#
 
-"""Renamer stub: provides a way to drop the wx prefix from wxPython objects."""
+"""
+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.GetKeyCode()
+        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()
+
+
+#----------------------------------------------------------------------
 
-__cvsid__ = "$Id$"
-__revision__ = "$Revision$"[11:-2]
 
-from wx import _rename
-from wxPython.lib import pyshell
-_rename(globals(), pyshell.__dict__, modulename='lib.pyshell')
-del pyshell
-del _rename