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-1990 [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
26 __version__
="$Revision$"
29 import sys
, string
, code
, traceback
30 from wxPython
.wx
import *
31 from wxPython
.html
import *
34 class PyShellInput(wxPanel
):
35 """PyShell input window
38 PS1
=" Enter Command:"
40 def __init__(self
, parent
, shell
, id=-1):
41 """Create input window
43 shell must be a PyShell object.
44 it is used for exception handling, eval() namespaces,
45 and shell.output is used for output
46 (print's go to overridden stdout)
48 wxPanel
.__init
__(self
, parent
, id)
50 # make a private copy of class attrs
51 self
.PS1
=PyShellInput
.PS1
52 self
.PS2
=PyShellInput
.PS2
54 self
.label
=wxStaticText(self
, -1, self
.PS1
)
56 self
.entry
=wxTextCtrl(self
, tid
, style
= wxTE_MULTILINE
)
57 EVT_CHAR(self
.entry
, self
.OnChar
)
58 self
.entry
.SetFont(wxFont(9, wxMODERN
, wxNORMAL
, wxNORMAL
, false
))
59 sizer
=wxBoxSizer(wxVERTICAL
)
60 sizer
.AddMany([(self
.label
, 0, wxEXPAND
), (self
.entry
, 1, wxEXPAND
)])
62 self
.SetAutoLayout(true
)
63 EVT_SET_FOCUS(self
, self
.OnSetFocus
)
64 # when in "continuation" mode,
65 # two consecutive newlines are required
66 # to avoid execution of unfinished block
69 def OnSetFocus(self
, event
):
73 def Clear(self
, event
=None):
74 """reset input state"""
75 self
.label
.SetLabel(self
.PS1
)
77 self
.entry
.SetSelection(0, self
.entry
.GetLastPosition())
79 # self.entry.SetFocus()
81 def OnChar(self
, event
):
82 """called on CHARevent. executes input on newline"""
83 # print "On Char:", event.__dict__.keys()
84 if event
.KeyCode() !=WXK_RETURN
:
88 text
=self
.entry
.GetValue()
90 text
=string
.replace(text
, "\r\n", "\n")
91 # see if we've finished
92 if (not (self
.first_line
or text
[-1] =="\n") # in continuation mode
93 or (text
[-1] =="\\") # escaped newline
95 # XXX should escaped newline put myself i "continuation" mode?
98 # ok, we can try to execute this
99 rc
=self
.shell
.TryExec(text
)
101 # code is incomplete; continue input
103 self
.label
.SetLabel(self
.PS2
)
110 class PyShellOutput(wxPanel
):
111 """PyShell output window
113 for now, it is based on simple wxTextCtrl,
114 but i'm looking at HTML classes to provide colorized output
116 # attributes for for different (input, output, exception) display styles:
117 # begin tag, end tag, newline
118 in_style
=(" <font color=\"#000080\"><tt>>>> ",
119 "</tt></font><br>\n", "<br>\n... ")
120 out_style
=("<tt>", "</tt>\n", "<br>\n")
121 exc_style
=("<font color=\"#FF0000\"><tt>",
122 "</tt></font>\n", "<br>\n")
123 intro
="<H3>wxPython Interactive Shell</H3>\n"
126 erefs
=(("&", "&"), (">", ">"), ("<", "<"), (" ", " "))
127 def __init__(self
, parent
, id=-1):
128 wxPanel
.__init
__(self
, parent
, id)
129 # make a private copy of class attrs
130 self
.in_style
=PyShellOutput
.in_style
131 self
.out_style
=PyShellOutput
.out_style
132 self
.exc_style
=PyShellOutput
.exc_style
133 self
.intro
=PyShellOutput
.intro
134 self
.html_debug
=PyShellOutput
.html_debug
137 # this was used in html debugging,
138 # but i don't want to delete it; it's funny
139 splitter
=wxSplitterWindow(self
, -1)
140 self
.view
=wxTextCtrl(splitter
, -1,
141 style
= wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL
)
142 self
.html
=wxHtmlWindow(splitter
)
143 splitter
.SplitVertically(self
.view
, self
.html
)
144 splitter
.SetSashPosition(40)
145 splitter
.SetMinimumPaneSize(3)
146 self
.client
=splitter
149 self
.html
=wxHtmlWindow(self
)
150 self
.client
=self
.html
# used in OnSize()
151 self
.text
=self
.intro
152 self
.html
.SetPage(self
.text
)
153 self
.html
.SetAutoLayout(TRUE
)
155 # refreshes are annoying
158 EVT_SIZE(self
, self
.OnSize
)
159 EVT_IDLE(self
, self
.OnIdle
)
161 def OnSize(self
, event
):
162 self
.client
.SetSize(self
.GetClientSize())
164 def OnIdle(self
, event
):
165 """when there's nothing to do, we can update display"""
166 if self
.in_batch
and self
.dirty
: self
.UpdWindow()
168 def BeginBatch(self
):
169 """do not refresh display till EndBatch()"""
173 """end batch; start updating display immediately"""
175 if self
.dirty
: self
.UpdWindow()
178 """sync display with text buffer"""
180 html
.SetPage(self
.text
)
183 (x
,y
) =html
.GetVirtualSize()
186 def AddText(self
, text
, style
=None):
187 """write text to output window"""
188 # a trick needed to defer default from compile-time to execute-time
189 if style
==None: style
=self
.out_style
190 if 0 and __debug__
: sys
.__stdout
__.write(text
)
192 for (symbol
, eref
) in self
.erefs
:
193 text
=string
.replace(text
, symbol
, eref
)
195 text
=string
.replace(text
, "\n", style
[2])
197 self
.text
=self
.text
+style
[0] +text
+style
[1]
198 if not self
.in_batch
: self
.UpdWindow()
201 # html debug output needn't to be too large
202 self
.view
.SetValue(self
.text
[-4096:])
204 def write(self
, str, style
=None):
205 """stdout-like interface"""
206 if style
==None: style
=self
.out_style
207 # do not process incomplete lines
209 # hm... what was i supposed to do?
212 self
.line_buffer
=self
.line_buffer
+str
214 self
.AddText(self
.line_buffer
+str, style
)
217 def flush(self
, style
=None):
218 """write out all that was left in line buffer"""
219 if style
==None: style
=self
.out_style
220 self
.AddText(self
.line_buffer
+"\n", style
)
222 def write_in(self
, str, style
=None):
223 """write text in "input" style"""
224 if style
==None: style
=self
.in_style
225 self
.AddText(str, style
)
227 def write_exc(self
, str, style
=None):
228 """write text in "exception" style"""
229 if style
==None: style
=self
.exc_style
230 self
.AddText(str, style
)
232 class PyShell(wxPanel
):
233 """interactive Python shell with wxPython interface
236 def __init__(self
, parent
, globals=globals(), locals={},
237 id=-1, pos
=wxDefaultPosition
, size
=wxDefaultSize
,
238 style
=wxTAB_TRAVERSAL
, name
="shell"):
239 """create PyShell window"""
240 wxPanel
.__init
__(self
, parent
, id, pos
, size
, style
, name
)
241 self
.globals =globals
243 splitter
=wxSplitterWindow(self
, -1)
244 self
.output
=PyShellOutput(splitter
)
245 self
.input =PyShellInput(splitter
, self
)
246 self
.input.SetFocus()
247 splitter
.SplitHorizontally(self
.input, self
.output
)
248 splitter
.SetSashPosition(100)
249 splitter
.SetMinimumPaneSize(20)
250 self
.splitter
=splitter
251 EVT_SET_FOCUS(self
, self
.OnSetFocus
)
252 EVT_SIZE(self
, self
.OnSize
)
254 def OnSetFocus(self
, event
):
255 self
.input.SetFocus()
257 def TryExec(self
, source
, symbol
="single"):
258 """Compile and run some source in the interpreter.
260 borrowed from code.InteractiveInterpreter().runsource()
261 as i said above, i would rather like to inherit from that class
263 returns 1 if more input is required, or 0, otherwise
266 cc
= code
.compile_command(source
, symbol
=symbol
)
267 except (OverflowError, SyntaxError):
268 # [als] hm... never seen anything of that kind
269 self
.ShowSyntaxError()
272 # source is incomplete
274 # source is sucessfully compiled
276 # redirect system stdout to the output window
279 # begin printout batch (html updates are deferred until EndBatch())
283 exec cc
in self
.globals, self
.locals
285 # SystemExit is not handled and has to be re-raised
288 # all other exceptions produce traceback output
290 # switch back to saved stdout
297 def ShowException(self
):
298 """display the traceback for the latest exception"""
299 (etype
, value
, tb
) =sys
.exc_info()
300 # remove myself from traceback
301 tblist
=traceback
.extract_tb(tb
)[1:]
302 msg
=string
.join(traceback
.format_exception_only(etype
, value
)
303 +traceback
.format_list(tblist
))
304 self
.output
.write_exc(msg
)
306 def ShowSyntaxError(self
):
307 """display message about syntax error (no traceback here)"""
308 (etype
, value
, tb
) =sys
.exc_info()
309 msg
=string
.join(traceback
.format_exception_only(etype
, value
))
310 self
.output
.write_exc(msg
)
312 def OnSize(self
, event
):
313 self
.splitter
.SetSize(self
.GetClientSize())
315 #----------------------------------------------------------------------
316 if __name__
== '__main__':
317 class MyFrame(wxFrame
):
318 """Very standard Frame class. Nothing special here!"""
319 def __init__(self
, parent
=NULL
, id =-1,
320 title
="wxPython Interactive Shell"):
321 wxFrame
.__init
__(self
, parent
, id, title
)
322 self
.shell
=PyShell(self
)
325 """Demonstrates usage of both default and customized shells"""
329 self
.SetTopWindow(frame
)
330 ## PyShellInput.PS1 =" let's get some work done..."
331 ## PyShellInput.PS2 =" ok, what do you really mean?"
332 ## PyShellOutput.in_style =(
333 ## "<I><font color=\"#008000\"><tt>>>> ",
334 ## "</tt></font></I><br>\n", "<br>\n... ")
335 ## PyShellOutput.out_style =(
336 ## "<font color=\"#000080\"><tt>",
337 ## "</tt></font><br>\n", "<br>\n")
338 ## PyShellOutput.exc_style =("<B><font color=\"#FF0000\"><tt>",
339 ## "</tt></font></B>\n", "<br>\n")
340 ## PyShellOutput.intro ="<I><B>Customized wxPython Shell</B>" \
341 ## "<br><-- move this sash to see html debug output</I><br>\n"
342 ## PyShellOutput.html_debug =1
343 ## frame = MyFrame(title="Customized wxPython Shell")