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