2 """wxPython interactive shell
4 Copyright (c) 1999 SIA "ANK"
6 this module is free software. it may be used under same terms as Python itself
9 i would like to use command completion (see rlcompleter library module),
10 but i cannot load it because i don't have readline...
13 03-oct-1999 [als] created
14 04-oct-1999 [als] PyShellOutput.intro moved from __init__ parameters
15 to class attributes; html debug disabled
16 04-oct-1999 [als] fixed bug with class attributes
17 input prompts and output styles added to customized demo
19 04-oct-1999 [rpd] Changed to use the new sizers
20 05-oct-1999 [als] changes inspired by code.InteractiveInterpreter()
21 from Python Library. if i knew about this class earlier,
22 i would rather inherit from it.
23 renamed to wxPyShell.py since i've renounced the 8.3 scheme
25 8-10-2001 THIS MODULE IS NOW DEPRECATED. Please see the most excellent
26 PyCrust package instead.
29 __version__
="$Revision$"
32 import sys
, code
, traceback
33 from wxPython
.wx
import *
34 from wxPython
.html
import *
37 class PyShellInput(wxPanel
):
38 """PyShell input window
41 PS1
=" Enter Command:"
43 def __init__(self
, parent
, shell
, id=-1):
44 """Create input window
46 shell must be a PyShell object.
47 it is used for exception handling, eval() namespaces,
48 and shell.output is used for output
49 (print's go to overridden stdout)
51 wxPanel
.__init
__(self
, parent
, id)
53 # make a private copy of class attrs
54 self
.PS1
=PyShellInput
.PS1
55 self
.PS2
=PyShellInput
.PS2
57 self
.label
=wxStaticText(self
, -1, self
.PS1
)
59 self
.entry
=wxTextCtrl(self
, tid
, style
= wxTE_MULTILINE
)
60 EVT_CHAR(self
.entry
, self
.OnChar
)
61 self
.entry
.SetFont(wxFont(9, wxMODERN
, wxNORMAL
, wxNORMAL
, False))
62 sizer
=wxBoxSizer(wxVERTICAL
)
63 sizer
.AddMany([(self
.label
, 0, wxEXPAND
), (self
.entry
, 1, wxEXPAND
)])
65 self
.SetAutoLayout(True)
66 EVT_SET_FOCUS(self
, self
.OnSetFocus
)
67 # when in "continuation" mode,
68 # two consecutive newlines are required
69 # to avoid execution of unfinished block
72 def OnSetFocus(self
, event
):
76 def Clear(self
, event
=None):
77 """reset input state"""
78 self
.label
.SetLabel(self
.PS1
)
80 self
.entry
.SetSelection(0, self
.entry
.GetLastPosition())
82 # self.entry.SetFocus()
84 def OnChar(self
, event
):
85 """called on CHARevent. executes input on newline"""
86 # print "On Char:", event.__dict__.keys()
87 if event
.KeyCode() !=WXK_RETURN
:
91 text
=self
.entry
.GetValue()
93 text
= text
.replace("\r\n", "\n")
94 # see if we've finished
95 if (not (self
.first_line
or text
[-1] =="\n") # in continuation mode
96 or (text
[-1] =="\\") # escaped newline
98 # XXX should escaped newline put myself i "continuation" mode?
101 # ok, we can try to execute this
102 rc
=self
.shell
.TryExec(text
)
104 # code is incomplete; continue input
106 self
.label
.SetLabel(self
.PS2
)
113 class PyShellOutput(wxPanel
):
114 """PyShell output window
116 for now, it is based on simple wxTextCtrl,
117 but i'm looking at HTML classes to provide colorized output
119 # attributes for for different (input, output, exception) display styles:
120 # begin tag, end tag, newline
121 in_style
=(" <font color=\"#000080\"><tt>>>> ",
122 "</tt></font><br>\n", "<br>\n... ")
123 out_style
=("<tt>", "</tt>\n", "<br>\n")
124 exc_style
=("<font color=\"#FF0000\"><tt>",
125 "</tt></font>\n", "<br>\n")
126 intro
="<H3>wxPython Interactive Shell</H3>\n"
129 erefs
=(("&", "&"), (">", ">"), ("<", "<"), (" ", " "))
130 def __init__(self
, parent
, id=-1):
131 wxPanel
.__init
__(self
, parent
, id)
132 # make a private copy of class attrs
133 self
.in_style
=PyShellOutput
.in_style
134 self
.out_style
=PyShellOutput
.out_style
135 self
.exc_style
=PyShellOutput
.exc_style
136 self
.intro
=PyShellOutput
.intro
137 self
.html_debug
=PyShellOutput
.html_debug
140 # this was used in html debugging,
141 # but i don't want to delete it; it's funny
142 splitter
=wxSplitterWindow(self
, -1)
143 self
.view
=wxTextCtrl(splitter
, -1,
144 style
= wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL
)
145 self
.html
=wxHtmlWindow(splitter
)
146 splitter
.SplitVertically(self
.view
, self
.html
)
147 splitter
.SetSashPosition(40)
148 splitter
.SetMinimumPaneSize(3)
149 self
.client
=splitter
152 self
.html
=wxHtmlWindow(self
)
153 self
.client
=self
.html
# used in OnSize()
154 self
.text
=self
.intro
155 self
.html
.SetPage(self
.text
)
156 self
.html
.SetAutoLayout(True)
158 # refreshes are annoying
161 EVT_SIZE(self
, self
.OnSize
)
162 EVT_IDLE(self
, self
.OnIdle
)
164 def OnSize(self
, event
):
165 self
.client
.SetSize(self
.GetClientSize())
167 def OnIdle(self
, event
):
168 """when there's nothing to do, we can update display"""
169 if self
.in_batch
and self
.dirty
: self
.UpdWindow()
171 def BeginBatch(self
):
172 """do not refresh display till EndBatch()"""
176 """end batch; start updating display immediately"""
178 if self
.dirty
: self
.UpdWindow()
181 """sync display with text buffer"""
183 html
.SetPage(self
.text
)
186 (x
,y
) =html
.GetVirtualSize()
189 def AddText(self
, text
, style
=None):
190 """write text to output window"""
191 # a trick needed to defer default from compile-time to execute-time
192 if style
==None: style
=self
.out_style
193 if 0 and __debug__
: sys
.__stdout
__.write(text
)
195 for (symbol
, eref
) in self
.erefs
:
196 text
= text
.replace(symbol
, eref
)
198 text
= text
.replace("\n", style
[2])
200 self
.text
=self
.text
+style
[0] +text
+style
[1]
201 if not self
.in_batch
: self
.UpdWindow()
204 # html debug output needn't to be too large
205 self
.view
.SetValue(self
.text
[-4096:])
207 def write(self
, str, style
=None):
208 """stdout-like interface"""
209 if style
==None: style
=self
.out_style
210 # do not process incomplete lines
212 # hm... what was i supposed to do?
215 self
.line_buffer
=self
.line_buffer
+str
217 self
.AddText(self
.line_buffer
+str, style
)
220 def flush(self
, style
=None):
221 """write out all that was left in line buffer"""
222 if style
==None: style
=self
.out_style
223 self
.AddText(self
.line_buffer
+"\n", style
)
225 def write_in(self
, str, style
=None):
226 """write text in "input" style"""
227 if style
==None: style
=self
.in_style
228 self
.AddText(str, style
)
230 def write_exc(self
, str, style
=None):
231 """write text in "exception" style"""
232 if style
==None: style
=self
.exc_style
233 self
.AddText(str, style
)
235 class PyShell(wxPanel
):
236 """interactive Python shell with wxPython interface
239 def __init__(self
, parent
, globals=globals(), locals={},
240 id=-1, pos
=wxDefaultPosition
, size
=wxDefaultSize
,
241 style
=wxTAB_TRAVERSAL
, name
="shell"):
242 """create PyShell window"""
243 wxPanel
.__init
__(self
, parent
, id, pos
, size
, style
, name
)
244 self
.globals =globals
246 splitter
=wxSplitterWindow(self
, -1)
247 self
.output
=PyShellOutput(splitter
)
248 self
.input =PyShellInput(splitter
, self
)
249 self
.input.SetFocus()
250 splitter
.SplitHorizontally(self
.input, self
.output
)
251 splitter
.SetSashPosition(100)
252 splitter
.SetMinimumPaneSize(20)
253 self
.splitter
=splitter
254 EVT_SET_FOCUS(self
, self
.OnSetFocus
)
255 EVT_SIZE(self
, self
.OnSize
)
257 def OnSetFocus(self
, event
):
258 self
.input.SetFocus()
260 def TryExec(self
, source
, symbol
="single"):
261 """Compile and run some source in the interpreter.
263 borrowed from code.InteractiveInterpreter().runsource()
264 as i said above, i would rather like to inherit from that class
266 returns 1 if more input is required, or 0, otherwise
269 cc
= code
.compile_command(source
, symbol
=symbol
)
270 except (OverflowError, SyntaxError):
271 # [als] hm... never seen anything of that kind
272 self
.ShowSyntaxError()
275 # source is incomplete
277 # source is sucessfully compiled
279 # redirect system stdout to the output window
282 # begin printout batch (html updates are deferred until EndBatch())
286 exec cc
in self
.globals, self
.locals
288 # SystemExit is not handled and has to be re-raised
291 # all other exceptions produce traceback output
293 # switch back to saved stdout
300 def ShowException(self
):
301 """display the traceback for the latest exception"""
302 (etype
, value
, tb
) =sys
.exc_info()
303 # remove myself from traceback
304 tblist
=traceback
.extract_tb(tb
)[1:]
305 msg
= ' '.join(traceback
.format_exception_only(etype
, value
)
306 +traceback
.format_list(tblist
))
307 self
.output
.write_exc(msg
)
309 def ShowSyntaxError(self
):
310 """display message about syntax error (no traceback here)"""
311 (etype
, value
, tb
) =sys
.exc_info()
312 msg
= ' '.join(traceback
.format_exception_only(etype
, value
))
313 self
.output
.write_exc(msg
)
315 def OnSize(self
, event
):
316 self
.splitter
.SetSize(self
.GetClientSize())
318 #----------------------------------------------------------------------
319 if __name__
== '__main__':
320 class MyFrame(wxFrame
):
321 """Very standard Frame class. Nothing special here!"""
322 def __init__(self
, parent
=NULL
, id =-1,
323 title
="wxPython Interactive Shell"):
324 wxFrame
.__init
__(self
, parent
, id, title
)
325 self
.shell
=PyShell(self
)
328 """Demonstrates usage of both default and customized shells"""
332 self
.SetTopWindow(frame
)
333 ## PyShellInput.PS1 =" let's get some work done..."
334 ## PyShellInput.PS2 =" ok, what do you really mean?"
335 ## PyShellOutput.in_style =(
336 ## "<I><font color=\"#008000\"><tt>>>> ",
337 ## "</tt></font></I><br>\n", "<br>\n... ")
338 ## PyShellOutput.out_style =(
339 ## "<font color=\"#000080\"><tt>",
340 ## "</tt></font><br>\n", "<br>\n")
341 ## PyShellOutput.exc_style =("<B><font color=\"#FF0000\"><tt>",
342 ## "</tt></font></B>\n", "<br>\n")
343 ## PyShellOutput.intro ="<I><B>Customized wxPython Shell</B>" \
344 ## "<br><-- move this sash to see html debug output</I><br>\n"
345 ## PyShellOutput.html_debug =1
346 ## frame = MyFrame(title="Customized wxPython Shell")