+# 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 =(" <font color=\"#000080\"><tt>>>> ",
+ "</tt></font><br>\n", "<br>\n... ")
+ out_style =("<tt>", "</tt>\n", "<br>\n")
+ exc_style =("<font color=\"#FF0000\"><tt>",
+ "</tt></font>\n", "<br>\n")
+ intro ="<H3>wxPython Interactive Shell</H3>\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 =(
+## "<I><font color=\"#008000\"><tt>>>> ",
+## "</tt></font></I><br>\n", "<br>\n... ")
+## PyShellOutput.out_style =(
+## "<font color=\"#000080\"><tt>",
+## "</tt></font><br>\n", "<br>\n")
+## PyShellOutput.exc_style =("<B><font color=\"#FF0000\"><tt>",
+## "</tt></font></B>\n", "<br>\n")
+## PyShellOutput.intro ="<I><B>Customized wxPython Shell</B>" \
+## "<br><-- move this sash to see html debug output</I><br>\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