]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wx/lib/shell.py
Today's changes
[wxWidgets.git] / wxPython / wx / lib / shell.py
CommitLineData
d14a1e28 1# shell.py
b881fc78
RD
2#----------------------------------------------------------------------
3# 12/10/2003 - Jeff Grimmett (grimmtooth@softhome.net)
4#
5# o 2.5 compatability update.
6# o Added deprecation warning.
7#
8
d14a1e28 9"""wxPython interactive shell
1fded56b 10
d14a1e28
RD
11Copyright (c) 1999 SIA "ANK"
12
13this module is free software. it may be used under same terms as Python itself
14
15Notes:
16i would like to use command completion (see rlcompleter library module),
17but i cannot load it because i don't have readline...
18
19History:
2003-oct-1999 [als] created
2104-oct-1999 [als] PyShellOutput.intro moved from __init__ parameters
22 to class attributes; html debug disabled
2304-oct-1999 [als] fixed bug with class attributes
24 input prompts and output styles added to customized demo
25 some html cleanups
2604-oct-1999 [rpd] Changed to use the new sizers
2705-oct-1999 [als] changes inspired by code.InteractiveInterpreter()
28 from Python Library. if i knew about this class earlier,
29 i would rather inherit from it.
30 renamed to wxPyShell.py since i've renounced the 8.3 scheme
31
328-10-2001 THIS MODULE IS NOW DEPRECATED. Please see the most excellent
33 PyCrust package instead.
34
35"""
36__version__ ="$Revision$"
37# $RCSfile$
38
b881fc78
RD
39import code
40import sys
41import traceback
42import warnings
43
44import wx
45import wx.html
46
47warningmsg = r"""\
48
49########################################\
50# THIS MODULE IS NOW DEPRECATED |
51# |
52# Please see the most excellent PyCrust |
53# package instead. |
54########################################/
d14a1e28 55
b881fc78
RD
56"""
57
58warnings.warn(warningmsg, DeprecationWarning, stacklevel=2)
59
60#----------------------------------------------------------------------
d14a1e28 61
b881fc78 62class PyShellInput(wx.Panel):
d14a1e28
RD
63 """PyShell input window
64
65 """
66 PS1 =" Enter Command:"
67 PS2 ="... continue:"
68 def __init__(self, parent, shell, id=-1):
69 """Create input window
70
71 shell must be a PyShell object.
72 it is used for exception handling, eval() namespaces,
73 and shell.output is used for output
74 (print's go to overridden stdout)
75 """
b881fc78 76 wx.Panel.__init__(self, parent, id)
d14a1e28
RD
77 self.shell =shell
78 # make a private copy of class attrs
79 self.PS1 =PyShellInput.PS1
80 self.PS2 =PyShellInput.PS2
81 # create controls
b881fc78
RD
82 self.label =wx.StaticText(self, -1, self.PS1)
83 tid =wx.NewId()
84 self.entry =wx.TextCtrl(self, tid, style = wx.TE_MULTILINE)
85 self.entry.Bind(wx.EVT_CHAR, self.OnChar)
86 self.entry.SetFont(wx.Font(9, wx.MODERN, wx.NORMAL, wx.NORMAL, False))
87 sizer =wx.BoxSizer(wx.VERTICAL)
88 sizer.AddMany([(self.label, 0, wx.EXPAND), (self.entry, 1, wx.EXPAND)])
d14a1e28
RD
89 self.SetSizer(sizer)
90 self.SetAutoLayout(True)
b881fc78 91 self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
d14a1e28
RD
92 # when in "continuation" mode,
93 # two consecutive newlines are required
94 # to avoid execution of unfinished block
95 self.first_line =1
96
97 def OnSetFocus(self, event):
98 self.entry.SetFocus()
99
100
101 def Clear(self, event=None):
102 """reset input state"""
103 self.label.SetLabel(self.PS1)
104 self.label.Refresh()
105 self.entry.SetSelection(0, self.entry.GetLastPosition())
106 self.first_line =1
107 # self.entry.SetFocus()
108
109 def OnChar(self, event):
110 """called on CHARevent. executes input on newline"""
111 # print "On Char:", event.__dict__.keys()
b881fc78 112 if event.KeyCode() !=wx.WXK_RETURN:
d14a1e28
RD
113 # not of our business
114 event.Skip()
115 return
116 text =self.entry.GetValue()
117 # weird CRLF thingy
118 text = text.replace("\r\n", "\n")
119 # see if we've finished
120 if (not (self.first_line or text[-1] =="\n") # in continuation mode
121 or (text[-1] =="\\") # escaped newline
122 ):
123 # XXX should escaped newline put myself i "continuation" mode?
124 event.Skip()
125 return
126 # ok, we can try to execute this
127 rc =self.shell.TryExec(text)
128 if rc:
129 # code is incomplete; continue input
130 if self.first_line:
131 self.label.SetLabel(self.PS2)
132 self.label.Refresh()
133 self.first_line =0
134 event.Skip()
135 else:
136 self.Clear()
137
b881fc78 138class PyShellOutput(wx.Panel):
d14a1e28
RD
139 """PyShell output window
140
141 for now, it is based on simple wxTextCtrl,
142 but i'm looking at HTML classes to provide colorized output
143 """
144 # attributes for for different (input, output, exception) display styles:
145 # begin tag, end tag, newline
146 in_style =(" <font color=\"#000080\"><tt>&gt;&gt;&gt;&nbsp;",
147 "</tt></font><br>\n", "<br>\n...&nbsp;")
148 out_style =("<tt>", "</tt>\n", "<br>\n")
149 exc_style =("<font color=\"#FF0000\"><tt>",
150 "</tt></font>\n", "<br>\n")
151 intro ="<H3>wxPython Interactive Shell</H3>\n"
152 html_debug =0
153 # entity references
154 erefs =(("&", "&amp;"), (">", "&gt;"), ("<", "&lt;"), (" ", "&nbsp; "))
155 def __init__(self, parent, id=-1):
b881fc78 156 wx.Panel.__init__(self, parent, id)
d14a1e28
RD
157 # make a private copy of class attrs
158 self.in_style =PyShellOutput.in_style
159 self.out_style =PyShellOutput.out_style
160 self.exc_style =PyShellOutput.exc_style
161 self.intro =PyShellOutput.intro
162 self.html_debug =PyShellOutput.html_debug
163 # create windows
164 if self.html_debug:
165 # this was used in html debugging,
166 # but i don't want to delete it; it's funny
b881fc78
RD
167 splitter =wx.SplitterWindow(self, -1)
168 self.view =wx.TextCtrl(splitter, -1,
169 style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
170 self.html =wx.html.HtmlWindow(splitter)
d14a1e28
RD
171 splitter.SplitVertically(self.view, self.html)
172 splitter.SetSashPosition(40)
173 splitter.SetMinimumPaneSize(3)
174 self.client =splitter
175 else:
176 self.view =None
b881fc78 177 self.html =wx.html.HtmlWindow(self)
d14a1e28
RD
178 self.client =self.html # used in OnSize()
179 self.text =self.intro
180 self.html.SetPage(self.text)
181 self.html.SetAutoLayout(True)
182 self.line_buffer =""
183 # refreshes are annoying
184 self.in_batch =0
185 self.dirty =0
b881fc78
RD
186 self.Bind(wx.EVT_SIZE, self.OnSize)
187 self.Bind(wx.EVT_IDLE, self.OnIdle)
d14a1e28
RD
188
189 def OnSize(self, event):
190 self.client.SetSize(self.GetClientSize())
191
192 def OnIdle(self, event):
193 """when there's nothing to do, we can update display"""
194 if self.in_batch and self.dirty: self.UpdWindow()
195
196 def BeginBatch(self):
197 """do not refresh display till EndBatch()"""
198 self.in_batch =1
199
200 def EndBatch(self):
201 """end batch; start updating display immediately"""
202 self.in_batch =0
203 if self.dirty: self.UpdWindow()
204
205 def UpdWindow(self):
206 """sync display with text buffer"""
207 html =self.html
208 html.SetPage(self.text)
209 self.dirty =0
210 # scroll to the end
211 (x,y) =html.GetVirtualSize()
212 html.Scroll(0, y)
213
214 def AddText(self, text, style=None):
215 """write text to output window"""
216 # a trick needed to defer default from compile-time to execute-time
217 if style ==None: style =self.out_style
218 if 0 and __debug__: sys.__stdout__.write(text)
219 # handle entities
220 for (symbol, eref) in self.erefs:
221 text = text.replace(symbol, eref)
222 # replace newlines
223 text = text.replace("\n", style[2])
224 # add to contents
225 self.text =self.text +style[0] +text +style[1]
226 if not self.in_batch: self.UpdWindow()
227 else: self.dirty =1
228 if self.html_debug:
229 # html debug output needn't to be too large
230 self.view.SetValue(self.text[-4096:])
231
232 def write(self, str, style=None):
233 """stdout-like interface"""
234 if style ==None: style =self.out_style
235 # do not process incomplete lines
236 if len(str) <1:
237 # hm... what was i supposed to do?
238 return
239 elif str[-1] !="\n":
240 self.line_buffer =self.line_buffer +str
241 else:
242 self.AddText(self.line_buffer +str, style)
243 self.line_buffer =""
244
245 def flush(self, style=None):
246 """write out all that was left in line buffer"""
247 if style ==None: style =self.out_style
248 self.AddText(self.line_buffer +"\n", style)
249
250 def write_in(self, str, style=None):
251 """write text in "input" style"""
252 if style ==None: style =self.in_style
253 self.AddText(str, style)
254
255 def write_exc(self, str, style=None):
256 """write text in "exception" style"""
257 if style ==None: style =self.exc_style
258 self.AddText(str, style)
259
b881fc78 260class PyShell(wx.Panel):
d14a1e28
RD
261 """interactive Python shell with wxPython interface
262
263 """
264 def __init__(self, parent, globals=globals(), locals={},
b881fc78
RD
265 id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize,
266 style=wx.TAB_TRAVERSAL, name="shell"):
d14a1e28 267 """create PyShell window"""
b881fc78 268 wx.Panel.__init__(self, parent, id, pos, size, style, name)
d14a1e28
RD
269 self.globals =globals
270 self.locals =locals
b881fc78 271 splitter =wx.SplitterWindow(self, -1)
d14a1e28
RD
272 self.output =PyShellOutput(splitter)
273 self.input =PyShellInput(splitter, self)
274 self.input.SetFocus()
275 splitter.SplitHorizontally(self.input, self.output)
276 splitter.SetSashPosition(100)
277 splitter.SetMinimumPaneSize(20)
278 self.splitter =splitter
b881fc78
RD
279 self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
280 self.Bind(wx.EVT_SIZE, self.OnSize)
d14a1e28
RD
281
282 def OnSetFocus(self, event):
283 self.input.SetFocus()
284
285 def TryExec(self, source, symbol="single"):
286 """Compile and run some source in the interpreter.
287
288 borrowed from code.InteractiveInterpreter().runsource()
289 as i said above, i would rather like to inherit from that class
290
291 returns 1 if more input is required, or 0, otherwise
292 """
293 try:
294 cc = code.compile_command(source, symbol=symbol)
295 except (OverflowError, SyntaxError):
296 # [als] hm... never seen anything of that kind
297 self.ShowSyntaxError()
298 return 0
299 if cc is None:
300 # source is incomplete
301 return 1
302 # source is sucessfully compiled
303 out =self.output
304 # redirect system stdout to the output window
305 prev_out =sys.stdout
306 sys.stdout =out
307 # begin printout batch (html updates are deferred until EndBatch())
308 out.BeginBatch()
309 out.write_in(source)
310 try:
311 exec cc in self.globals, self.locals
312 except SystemExit:
313 # SystemExit is not handled and has to be re-raised
314 raise
315 except:
316 # all other exceptions produce traceback output
317 self.ShowException()
318 # switch back to saved stdout
319 sys.stdout =prev_out
320 # commit printout
321 out.flush()
322 out.EndBatch()
323 return 0
324
325 def ShowException(self):
326 """display the traceback for the latest exception"""
327 (etype, value, tb) =sys.exc_info()
328 # remove myself from traceback
329 tblist =traceback.extract_tb(tb)[1:]
330 msg = ' '.join(traceback.format_exception_only(etype, value)
331 +traceback.format_list(tblist))
332 self.output.write_exc(msg)
333
334 def ShowSyntaxError(self):
335 """display message about syntax error (no traceback here)"""
336 (etype, value, tb) =sys.exc_info()
337 msg = ' '.join(traceback.format_exception_only(etype, value))
338 self.output.write_exc(msg)
339
340 def OnSize(self, event):
341 self.splitter.SetSize(self.GetClientSize())
342
343#----------------------------------------------------------------------
344if __name__ == '__main__':
b881fc78 345 class MyFrame(wx.Frame):
d14a1e28 346 """Very standard Frame class. Nothing special here!"""
b881fc78 347 def __init__(self, parent=None, id =-1,
d14a1e28 348 title="wxPython Interactive Shell"):
b881fc78 349 wx.Frame.__init__(self, parent, id, title)
d14a1e28
RD
350 self.shell =PyShell(self)
351
b881fc78 352 class MyApp(wx.App):
d14a1e28
RD
353 """Demonstrates usage of both default and customized shells"""
354 def OnInit(self):
355 frame = MyFrame()
356 frame.Show(True)
357 self.SetTopWindow(frame)
358## PyShellInput.PS1 =" let's get some work done..."
359## PyShellInput.PS2 =" ok, what do you really mean?"
360## PyShellOutput.in_style =(
361## "<I><font color=\"#008000\"><tt>&gt;&gt;&gt;&nbsp;",
362## "</tt></font></I><br>\n", "<br>\n...&nbsp;")
363## PyShellOutput.out_style =(
364## "<font color=\"#000080\"><tt>",
365## "</tt></font><br>\n", "<br>\n")
366## PyShellOutput.exc_style =("<B><font color=\"#FF0000\"><tt>",
367## "</tt></font></B>\n", "<br>\n")
368## PyShellOutput.intro ="<I><B>Customized wxPython Shell</B>" \
369## "<br>&lt;-- move this sash to see html debug output</I><br>\n"
370## PyShellOutput.html_debug =1
371## frame = MyFrame(title="Customized wxPython Shell")
372## frame.Show(True)
373 return True
374
375 app = MyApp(0)
376 app.MainLoop()
1fded56b 377