]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/pyshell.py
Patch from Will Sadkin:
[wxWidgets.git] / wxPython / wx / lib / pyshell.py
1 #----------------------------------------------------------------------
2 # Name: wxPython.lib.pyshell
3 # Purpose: A Python Interactive Interpreter running in a wxStyledTextCtrl
4 # window.
5 #
6 # Author: Robin Dunn
7 #
8 # Created: 7-July-2000
9 # RCS-ID: $Id$
10 # Copyright: (c) 2000 by Total Control Software
11 # Licence: wxWindows license
12 #----------------------------------------------------------------------
13 # 12/10/2003 - Jeff Grimmett (grimmtooth@softhome.net)
14 #
15 # o 2.5 compatability update.
16 # o Added deprecation warning.
17 #
18
19 """
20 PyShellWindow is a class that provides an Interactive Interpreter running
21 inside a wxStyledTextCtrl, similar to the Python shell windows found in
22 IDLE and PythonWin.
23
24 There is still much to be done to improve this class, such as line
25 buffering/recall, autoindent, calltips, autocomplete, fixing the colourizer,
26 etc... But it's a good start.
27
28
29 8-10-2001 THIS MODULE IS NOW DEPRECATED. Please see the most excellent
30 PyCrust package instead.
31
32 """
33
34 import keyword
35 import sys
36 import warnings
37
38 from code import InteractiveInterpreter
39
40 import wx
41 import wx.stc as stc
42
43 warningmsg = r"""\
44
45 ########################################\
46 # THIS MODULE IS NOW DEPRECATED |
47 # |
48 # Please see the most excellent PyCrust |
49 # package instead. |
50 ########################################/
51
52 """
53
54 warnings.warn(warningmsg, DeprecationWarning, stacklevel=2)
55
56 #----------------------------------------------------------------------
57 # default styles, etc. to use for the STC
58
59 if wx.Platform == '__WXMSW__':
60 _defaultSize = 8
61 else:
62 _defaultSize = 10
63
64
65 _default_properties = {
66 'selMargin' : 0,
67 'marginWidth' : 1,
68 'ps1' : '>>> ',
69 'stdout' : 'fore:#0000FF',
70 'stderr' : 'fore:#007f00',
71 'trace' : 'fore:#FF0000',
72
73 'default' : 'size:%d' % _defaultSize,
74 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
75 'bracebad' : 'fore:#000000,back:#FF0000,bold',
76
77 # properties for the various Python lexer styles
78 'comment' : 'fore:#007F00',
79 'number' : 'fore:#007F7F',
80 'string' : 'fore:#7F007F,italic',
81 'char' : 'fore:#7F007F,italic',
82 'keyword' : 'fore:#00007F,bold',
83 'triple' : 'fore:#7F0000',
84 'tripledouble': 'fore:#7F0000',
85 'class' : 'fore:#0000FF,bold,underline',
86 'def' : 'fore:#007F7F,bold',
87 'operator' : 'bold',
88
89 }
90
91
92 # new style numbers
93 _stdout_style = 15
94 _stderr_style = 16
95 _trace_style = 17
96
97
98 #----------------------------------------------------------------------
99
100 class PyShellWindow(stc.StyledTextCtrl, InteractiveInterpreter):
101 def __init__(self, parent, ID, pos=wx.DefaultPosition,
102 size=wx.DefaultSize, style=0,
103 locals=None, properties=None, banner=None):
104 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
105 InteractiveInterpreter.__init__(self, locals)
106
107 self.lastPromptPos = 0
108
109 # the line cache is used to cycle through previous commands
110 self.lines = []
111 self.lastUsedLine = self.curLine = 0
112
113 # set defaults and then deal with any user defined properties
114 self.props = {}
115 self.props.update(_default_properties)
116 if properties:
117 self.props.update(properties)
118 self.UpdateProperties()
119
120 # copyright/banner message
121 if banner is None:
122 self.write("Python %s on %s\n" % #%s\n(%s)\n" %
123 (sys.version, sys.platform,
124 #sys.copyright, self.__class__.__name__
125 ))
126 else:
127 self.write("%s\n" % banner)
128
129 # write the initial prompt
130 self.Prompt()
131
132 # Event handlers
133 self.Bind(wx.EVT_KEY_DOWN, self.OnKey)
134 self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI, id=ID)
135 #self.Bind(stc.EVT_STC_STYLENEEDED, self.OnStyle, id=ID)
136
137
138 def GetLocals(self): return self.locals
139 def SetLocals(self, locals): self.locals = locals
140
141 def GetProperties(self): return self.props
142 def SetProperties(self, properties):
143 self.props.update(properties)
144 self.UpdateProperties()
145
146
147 def UpdateProperties(self):
148 """
149 Reset the editor and other settings based on the contents of the
150 current properties dictionary.
151 """
152 p = self.props
153
154 #self.SetEdgeMode(stc.STC_EDGE_LINE)
155 #self.SetEdgeColumn(80)
156
157
158 # set the selection margin and window margin
159 self.SetMarginWidth(1, p['selMargin'])
160 self.SetMargins(p['marginWidth'], p['marginWidth'])
161
162 # styles
163 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
164 self.StyleClearAll()
165 self.StyleSetSpec(_stdout_style, p['stdout'])
166 self.StyleSetSpec(_stderr_style, p['stderr'])
167 self.StyleSetSpec(_trace_style, p['trace'])
168
169 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
170 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
171 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
172 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
173 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
174 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
175 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
176 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
177 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
178 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
179 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
180 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
181 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
182
183
184 # used for writing to stdout, etc.
185 def _write(self, text, style=_stdout_style):
186 self.lastPromptPos = 0
187 pos = self.GetCurrentPos()
188 self.AddText(text)
189 self.StartStyling(pos, 0xFF)
190 self.SetStyling(len(text), style)
191 self.EnsureCaretVisible()
192 wx.Yield()
193
194 write = _write
195
196 def writeTrace(self, text):
197 self._write(text, _trace_style)
198
199
200 def Prompt(self):
201 # is the current line non-empty?
202 text, pos = self.GetCurLine()
203 if pos != 0:
204 self.AddText('\n')
205 self.AddText(self.props['ps1'])
206 self.lastPromptPos = self.GetCurrentPos()
207 self.EnsureCaretVisible()
208 self.ScrollToColumn(0)
209
210
211 def PushLine(self, text):
212 # TODO: Add the text to the line cache, manage the cache so
213 # it doesn't get too big.
214 pass
215
216
217
218 def OnKey(self, evt):
219 key = evt.KeyCode()
220 if key == wx.WXK_RETURN:
221 pos = self.GetCurrentPos()
222 lastPos = self.GetTextLength()
223
224 # if not on the last line, duplicate the current line
225 if self.GetLineCount()-1 != self.GetCurrentLine():
226 text, col = self.GetCurLine()
227 prompt = self.props['ps1']
228 lp = len(prompt)
229 if text[:lp] == prompt:
230 text = text[lp:]
231
232 self.SetSelection(self.lastPromptPos, lastPos)
233 self.ReplaceSelection(text[:-1])
234
235 else: # try to execute the text from the prompt to the end
236 if lastPos == self.lastPromptPos:
237 self.AddText('\n')
238 self.Prompt()
239 return
240
241 text = self.GetTextRange(self.lastPromptPos, lastPos)
242 self.AddText('\n')
243
244 more = self.runsource(text)
245 if not more:
246 self.PushLine(text)
247 self.Prompt()
248
249 # TODO: Add handlers for Alt-P and Alt-N to cycle through entries
250 # in the line cache
251
252 else:
253 evt.Skip()
254
255
256 def OnStyle(self, evt):
257 # Only style from the prompt pos to the end
258 lastPos = self.GetTextLength()
259 if self.lastPromptPos and self.lastPromptPos != lastPos:
260 self.SetLexer(stc.STC_LEX_PYTHON)
261 self.SetKeywords(0, ' '.join(keyword.kwlist))
262
263 self.Colourise(self.lastPromptPos, lastPos)
264
265 self.SetLexer(0)
266
267
268 def OnUpdateUI(self, evt):
269 # check for matching braces
270 braceAtCaret = -1
271 braceOpposite = -1
272 charBefore = None
273 caretPos = self.GetCurrentPos()
274 if caretPos > 0:
275 charBefore = self.GetCharAt(caretPos - 1)
276 styleBefore = self.GetStyleAt(caretPos - 1)
277
278 # check before
279 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
280 braceAtCaret = caretPos - 1
281
282 # check after
283 if braceAtCaret < 0:
284 charAfter = self.GetCharAt(caretPos)
285 styleAfter = self.GetStyleAt(caretPos)
286 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
287 braceAtCaret = caretPos
288
289 if braceAtCaret >= 0:
290 braceOpposite = self.BraceMatch(braceAtCaret)
291
292 if braceAtCaret != -1 and braceOpposite == -1:
293 self.BraceBadlight(braceAtCaret)
294 else:
295 self.BraceHighlight(braceAtCaret, braceOpposite)
296
297
298
299 #----------------------------------------------
300 # overloaded methods from InteractiveInterpreter
301 def runsource(self, source):
302 stdout, stderr = sys.stdout, sys.stderr
303 sys.stdout = FauxFile(self, _stdout_style)
304 sys.stderr = FauxFile(self, _stderr_style)
305
306 more = InteractiveInterpreter.runsource(self, source)
307
308 sys.stdout, sys.stderr = stdout, stderr
309 return more
310
311 def showsyntaxerror(self, filename=None):
312 self.write = self.writeTrace
313 InteractiveInterpreter.showsyntaxerror(self, filename)
314 self.write = self._write
315
316 def showtraceback(self):
317 self.write = self.writeTrace
318 InteractiveInterpreter.showtraceback(self)
319 self.write = self._write
320
321 #----------------------------------------------------------------------
322
323 class FauxFile:
324 def __init__(self, psw, style):
325 self.psw = psw
326 self.style = style
327
328 def write(self, text):
329 self.psw.write(text, self.style)
330
331 def writelines(self, lst):
332 map(self.write, lst)
333
334 def flush(self):
335 pass
336
337
338 #----------------------------------------------------------------------
339 # test code
340
341 if __name__ == '__main__':
342 app = wx.PyWidgetTester(size = (640, 480))
343 app.SetWidget(PyShellWindow, -1)
344 app.MainLoop()
345
346
347 #----------------------------------------------------------------------
348
349