X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8b9a4190f70909de9568f45389e7aa3ecbc66b8a..b887061dd1d7b3a49a111513309b1bb5c308537c:/wxPython/wx/lib/shell.py diff --git a/wxPython/wx/lib/shell.py b/wxPython/wx/lib/shell.py index f3f7d272e4..428a2cbbef 100644 --- a/wxPython/wx/lib/shell.py +++ b/wxPython/wx/lib/shell.py @@ -1,8 +1,377 @@ +# shell.py +#---------------------------------------------------------------------- +# 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.""" +"""wxPython interactive shell + +Copyright (c) 1999 SIA "ANK" + +this module is free software. it may be used under same terms as Python itself + +Notes: +i would like to use command completion (see rlcompleter library module), +but i cannot load it because i don't have readline... + +History: +03-oct-1999 [als] created +04-oct-1999 [als] PyShellOutput.intro moved from __init__ parameters + to class attributes; html debug disabled +04-oct-1999 [als] fixed bug with class attributes + input prompts and output styles added to customized demo + some html cleanups +04-oct-1999 [rpd] Changed to use the new sizers +05-oct-1999 [als] changes inspired by code.InteractiveInterpreter() + from Python Library. if i knew about this class earlier, + i would rather inherit from it. + renamed to wxPyShell.py since i've renounced the 8.3 scheme + +8-10-2001 THIS MODULE IS NOW DEPRECATED. Please see the most excellent + PyCrust package instead. + +""" +__version__ ="$Revision$" +# $RCSfile$ + +import code +import sys +import traceback +import warnings + +import wx +import wx.html + +warningmsg = r"""\ + +########################################\ +# THIS MODULE IS NOW DEPRECATED | +# | +# Please see the most excellent PyCrust | +# package instead. | +########################################/ + +""" + +warnings.warn(warningmsg, DeprecationWarning, stacklevel=2) + +#---------------------------------------------------------------------- + +class PyShellInput(wx.Panel): + """PyShell input window + + """ + PS1 =" Enter Command:" + PS2 ="... continue:" + def __init__(self, parent, shell, id=-1): + """Create input window + + shell must be a PyShell object. + it is used for exception handling, eval() namespaces, + and shell.output is used for output + (print's go to overridden stdout) + """ + wx.Panel.__init__(self, parent, id) + self.shell =shell + # make a private copy of class attrs + self.PS1 =PyShellInput.PS1 + self.PS2 =PyShellInput.PS2 + # create controls + self.label =wx.StaticText(self, -1, self.PS1) + tid =wx.NewId() + self.entry =wx.TextCtrl(self, tid, style = wx.TE_MULTILINE) + self.entry.Bind(wx.EVT_CHAR, self.OnChar) + self.entry.SetFont(wx.Font(9, wx.MODERN, wx.NORMAL, wx.NORMAL, False)) + sizer =wx.BoxSizer(wx.VERTICAL) + sizer.AddMany([(self.label, 0, wx.EXPAND), (self.entry, 1, wx.EXPAND)]) + self.SetSizer(sizer) + self.SetAutoLayout(True) + self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) + # when in "continuation" mode, + # two consecutive newlines are required + # to avoid execution of unfinished block + self.first_line =1 + + def OnSetFocus(self, event): + self.entry.SetFocus() + + + def Clear(self, event=None): + """reset input state""" + self.label.SetLabel(self.PS1) + self.label.Refresh() + self.entry.SetSelection(0, self.entry.GetLastPosition()) + self.first_line =1 + # self.entry.SetFocus() + + def OnChar(self, event): + """called on CHARevent. executes input on newline""" + # print "On Char:", event.__dict__.keys() + if event.KeyCode() !=wx.WXK_RETURN: + # not of our business + event.Skip() + return + text =self.entry.GetValue() + # weird CRLF thingy + text = text.replace("\r\n", "\n") + # see if we've finished + if (not (self.first_line or text[-1] =="\n") # in continuation mode + or (text[-1] =="\\") # escaped newline + ): + # XXX should escaped newline put myself i "continuation" mode? + event.Skip() + return + # ok, we can try to execute this + rc =self.shell.TryExec(text) + if rc: + # code is incomplete; continue input + if self.first_line: + self.label.SetLabel(self.PS2) + self.label.Refresh() + self.first_line =0 + event.Skip() + else: + self.Clear() + +class PyShellOutput(wx.Panel): + """PyShell output window + + for now, it is based on simple wxTextCtrl, + but i'm looking at HTML classes to provide colorized output + """ + # attributes for for different (input, output, exception) display styles: + # begin tag, end tag, newline + in_style =(" >>> ", + "
\n", "
\n... ") + out_style =("", "\n", "
\n") + exc_style =("", + "\n", "
\n") + intro ="

wxPython Interactive Shell

\n" + html_debug =0 + # entity references + erefs =(("&", "&"), (">", ">"), ("<", "<"), (" ", "  ")) + def __init__(self, parent, id=-1): + wx.Panel.__init__(self, parent, id) + # make a private copy of class attrs + self.in_style =PyShellOutput.in_style + self.out_style =PyShellOutput.out_style + self.exc_style =PyShellOutput.exc_style + self.intro =PyShellOutput.intro + self.html_debug =PyShellOutput.html_debug + # create windows + if self.html_debug: + # this was used in html debugging, + # but i don't want to delete it; it's funny + splitter =wx.SplitterWindow(self, -1) + self.view =wx.TextCtrl(splitter, -1, + style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL) + self.html =wx.html.HtmlWindow(splitter) + splitter.SplitVertically(self.view, self.html) + splitter.SetSashPosition(40) + splitter.SetMinimumPaneSize(3) + self.client =splitter + else: + self.view =None + self.html =wx.html.HtmlWindow(self) + self.client =self.html # used in OnSize() + self.text =self.intro + self.html.SetPage(self.text) + self.html.SetAutoLayout(True) + self.line_buffer ="" + # refreshes are annoying + self.in_batch =0 + self.dirty =0 + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(wx.EVT_IDLE, self.OnIdle) + + def OnSize(self, event): + self.client.SetSize(self.GetClientSize()) + + def OnIdle(self, event): + """when there's nothing to do, we can update display""" + if self.in_batch and self.dirty: self.UpdWindow() + + def BeginBatch(self): + """do not refresh display till EndBatch()""" + self.in_batch =1 + + def EndBatch(self): + """end batch; start updating display immediately""" + self.in_batch =0 + if self.dirty: self.UpdWindow() + + def UpdWindow(self): + """sync display with text buffer""" + html =self.html + html.SetPage(self.text) + self.dirty =0 + # scroll to the end + (x,y) =html.GetVirtualSize() + html.Scroll(0, y) + + def AddText(self, text, style=None): + """write text to output window""" + # a trick needed to defer default from compile-time to execute-time + if style ==None: style =self.out_style + if 0 and __debug__: sys.__stdout__.write(text) + # handle entities + for (symbol, eref) in self.erefs: + text = text.replace(symbol, eref) + # replace newlines + text = text.replace("\n", style[2]) + # add to contents + self.text =self.text +style[0] +text +style[1] + if not self.in_batch: self.UpdWindow() + else: self.dirty =1 + if self.html_debug: + # html debug output needn't to be too large + self.view.SetValue(self.text[-4096:]) + + def write(self, str, style=None): + """stdout-like interface""" + if style ==None: style =self.out_style + # do not process incomplete lines + if len(str) <1: + # hm... what was i supposed to do? + return + elif str[-1] !="\n": + self.line_buffer =self.line_buffer +str + else: + self.AddText(self.line_buffer +str, style) + self.line_buffer ="" + + def flush(self, style=None): + """write out all that was left in line buffer""" + if style ==None: style =self.out_style + self.AddText(self.line_buffer +"\n", style) + + def write_in(self, str, style=None): + """write text in "input" style""" + if style ==None: style =self.in_style + self.AddText(str, style) + + def write_exc(self, str, style=None): + """write text in "exception" style""" + if style ==None: style =self.exc_style + self.AddText(str, style) + +class PyShell(wx.Panel): + """interactive Python shell with wxPython interface + + """ + def __init__(self, parent, globals=globals(), locals={}, + id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, + style=wx.TAB_TRAVERSAL, name="shell"): + """create PyShell window""" + wx.Panel.__init__(self, parent, id, pos, size, style, name) + self.globals =globals + self.locals =locals + splitter =wx.SplitterWindow(self, -1) + self.output =PyShellOutput(splitter) + self.input =PyShellInput(splitter, self) + self.input.SetFocus() + splitter.SplitHorizontally(self.input, self.output) + splitter.SetSashPosition(100) + splitter.SetMinimumPaneSize(20) + self.splitter =splitter + self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) + self.Bind(wx.EVT_SIZE, self.OnSize) + + def OnSetFocus(self, event): + self.input.SetFocus() + + def TryExec(self, source, symbol="single"): + """Compile and run some source in the interpreter. + + borrowed from code.InteractiveInterpreter().runsource() + as i said above, i would rather like to inherit from that class + + returns 1 if more input is required, or 0, otherwise + """ + try: + cc = code.compile_command(source, symbol=symbol) + except (OverflowError, SyntaxError): + # [als] hm... never seen anything of that kind + self.ShowSyntaxError() + return 0 + if cc is None: + # source is incomplete + return 1 + # source is sucessfully compiled + out =self.output + # redirect system stdout to the output window + prev_out =sys.stdout + sys.stdout =out + # begin printout batch (html updates are deferred until EndBatch()) + out.BeginBatch() + out.write_in(source) + try: + exec cc in self.globals, self.locals + except SystemExit: + # SystemExit is not handled and has to be re-raised + raise + except: + # all other exceptions produce traceback output + self.ShowException() + # switch back to saved stdout + sys.stdout =prev_out + # commit printout + out.flush() + out.EndBatch() + return 0 + + def ShowException(self): + """display the traceback for the latest exception""" + (etype, value, tb) =sys.exc_info() + # remove myself from traceback + tblist =traceback.extract_tb(tb)[1:] + msg = ' '.join(traceback.format_exception_only(etype, value) + +traceback.format_list(tblist)) + self.output.write_exc(msg) + + def ShowSyntaxError(self): + """display message about syntax error (no traceback here)""" + (etype, value, tb) =sys.exc_info() + msg = ' '.join(traceback.format_exception_only(etype, value)) + self.output.write_exc(msg) + + def OnSize(self, event): + self.splitter.SetSize(self.GetClientSize()) + +#---------------------------------------------------------------------- +if __name__ == '__main__': + class MyFrame(wx.Frame): + """Very standard Frame class. Nothing special here!""" + def __init__(self, parent=None, id =-1, + title="wxPython Interactive Shell"): + wx.Frame.__init__(self, parent, id, title) + self.shell =PyShell(self) + + class MyApp(wx.App): + """Demonstrates usage of both default and customized shells""" + def OnInit(self): + frame = MyFrame() + frame.Show(True) + self.SetTopWindow(frame) +## PyShellInput.PS1 =" let's get some work done..." +## PyShellInput.PS2 =" ok, what do you really mean?" +## PyShellOutput.in_style =( +## ">>> ", +## "
\n", "
\n... ") +## PyShellOutput.out_style =( +## "", +## "
\n", "
\n") +## PyShellOutput.exc_style =("", +## "\n", "
\n") +## PyShellOutput.intro ="Customized wxPython Shell" \ +## "
<-- move this sash to see html debug output

\n" +## PyShellOutput.html_debug =1 +## frame = MyFrame(title="Customized wxPython Shell") +## frame.Show(True) + return True + + app = MyApp(0) + app.MainLoop() -from wx import _rename -from wxPython.lib import shell -_rename(globals(), shell.__dict__, modulename='lib.shell') -del shell -del _rename