From a75b761a1d5a6ef50a7a0fcbfc26b2fd8709cc66 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Fri, 14 Jul 2000 03:43:23 +0000 Subject: [PATCH] Added PyShellWindow and a demo. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_2_BRANCH@7739 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- wxPython/CHANGES.txt | 1 + wxPython/demo/Main.py | 6 +- wxPython/demo/PyShellWindow.py | 15 ++ wxPython/wxPython/lib/pyshell.py | 238 +++++++++++++++++++++++++++++++ 4 files changed, 257 insertions(+), 3 deletions(-) create mode 100644 wxPython/demo/PyShellWindow.py create mode 100644 wxPython/wxPython/lib/pyshell.py diff --git a/wxPython/CHANGES.txt b/wxPython/CHANGES.txt index e49519f938..83608f35c0 100644 --- a/wxPython/CHANGES.txt +++ b/wxPython/CHANGES.txt @@ -9,6 +9,7 @@ Added wxLog and friends. Added wxFrame.ShowFullScreen for MSW. +Added PyShellWindow to the wxPython.lib package. diff --git a/wxPython/demo/Main.py b/wxPython/demo/Main.py index 989d7e448f..10ed61edd0 100644 --- a/wxPython/demo/Main.py +++ b/wxPython/demo/Main.py @@ -20,7 +20,7 @@ from wxPython.html import wxHtmlWindow _treeList = [ - ('New since last release', ['wxDragImage', 'wxCalendarCtrl', 'wxSpinCtrl', + ('New since last release', ['PyShellWindow', ]), ('Managed Windows', ['wxFrame', 'wxDialog', 'wxMiniFrame']), @@ -40,7 +40,7 @@ _treeList = [ 'wxComboBox', 'wxGauge', 'wxListBox', 'wxListCtrl', 'wxTextCtrl', 'wxTreeCtrl', 'wxSpinButton', 'wxSpinCtrl', 'wxStaticText', 'wxStaticBitmap', 'wxRadioBox', 'wxSlider', 'wxToolBar', - #'wxToggleButton' + 'wxCalendarCtrl', ]), ('Window Layout', ['wxLayoutConstraints', 'Sizers', 'OldSizers']), @@ -50,7 +50,7 @@ _treeList = [ 'wxImage', 'wxMask', 'PrintFramework', 'wxOGL', 'PythonEvents', 'Threads', 'ActiveXWrapper_Acrobat', 'ActiveXWrapper_IE', - 'wxDragImage', 'wxCalendarCtrl', + 'wxDragImage', 'PyShellWindow', ]), ('wxPython Library', ['Layoutf', 'wxScrolledMessageDialog', diff --git a/wxPython/demo/PyShellWindow.py b/wxPython/demo/PyShellWindow.py new file mode 100644 index 0000000000..1f68ed8be6 --- /dev/null +++ b/wxPython/demo/PyShellWindow.py @@ -0,0 +1,15 @@ + + +from wxPython.lib.pyshell import PyShellWindow + +#---------------------------------------------------------------------- + +def runTest(frame, nb, log): + win = PyShellWindow(nb, -1) + return win + +#---------------------------------------------------------------------- + +import wxPython.lib.pyshell + +overview = wxPython.lib.pyshell.__doc__ diff --git a/wxPython/wxPython/lib/pyshell.py b/wxPython/wxPython/lib/pyshell.py new file mode 100644 index 0000000000..f1ecb73351 --- /dev/null +++ b/wxPython/wxPython/lib/pyshell.py @@ -0,0 +1,238 @@ +#---------------------------------------------------------------------- +# 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, colourizing the python code, autoindent, etc. but it's +a good start. + +""" + + +from wxPython.wx import * +from wxPython.stc import * + +import sys, string, keyword +from code import InteractiveInterpreter + +#---------------------------------------------------------------------- +# default styles, etc. to use for the STC + +_default_properties = { + 'marginWidth' : 5, + 'ps1' : '>>> ', + 'stdout' : 'fore:#0000FF', + 'stderr' : 'fore:#007f00', + 'trace' : 'fore:#FF0000', + + # TODO: Add properties for the various Python lexer styles + + } + + +# 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) + + # 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) + + + + 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.SetLexer(wxSTC_LEX_PYTHON) + self.SetKeywords(0, string.join(keyword.kwlist)) + + # set the selection margin + self.SetMarginWidth(1, p['marginWidth']) + + # styles + self.StyleSetSpec(_stdout_style, p['stdout']) + self.StyleSetSpec(_stderr_style, p['stderr']) + self.StyleSetSpec(_trace_style, p['trace']) + + + + # used for writing to stdout, etc. + def _write(self, text, style=_stdout_style): + pos = self.GetCurrentPos() + self.AddText(text) + self.StartStyling(pos, 0xFF) + self.SetStyleFor(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.GetCurrentLineText() + 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.GetCurrentLineText() + 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() + + + # 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() + + +#---------------------------------------------------------------------- + +