]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wxPython/lib/pyshell.py
58dd3601ff3d4875738677bb68a44e79f89c0d64
[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, etc... But
21 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, sys.copyright,
100 self.__class__.__name__))
101 else:
102 self.write("%s\n" % banner)
103
104 # write the initial prompt
105 self.Prompt()
106
107 # Event handlers
108 EVT_KEY_DOWN(self, self.OnKey)
109 EVT_STC_UPDATEUI(self, ID, self.OnUpdateUI)
110 EVT_STC_STYLENEEDED(self, ID, self.OnStyle)
111
112
113 def GetLocals(self): return self.locals
114 def SetLocals(self, locals): self.locals = locals
115
116 def GetProperties(self): return self.props
117 def SetProperties(self, properties):
118 self.props.update(properties)
119 self.UpdateProperties()
120
121
122 def UpdateProperties(self):
123 """
124 Reset the editor and other settings based on the contents of the
125 current properties dictionary.
126 """
127 p = self.props
128
129 self.SetEdgeMode(wxSTC_EDGE_LINE)
130 self.SetEdgeColumn(80)
131
132
133 # set the selection margin and window margin
134 self.SetMarginWidth(1, p['selMargin'])
135 self.SetMargins(p['marginWidth'], p['marginWidth'])
136
137 # styles
138 self.StyleSetSpec(wxSTC_STYLE_DEFAULT, p['default'])
139 self.StyleClearAll()
140 self.StyleSetSpec(_stdout_style, p['stdout'])
141 self.StyleSetSpec(_stderr_style, p['stderr'])
142 self.StyleSetSpec(_trace_style, p['trace'])
143
144 self.StyleSetSpec(wxSTC_STYLE_BRACELIGHT, p['bracegood'])
145 self.StyleSetSpec(wxSTC_STYLE_BRACEBAD, p['bracebad'])
146 self.StyleSetSpec(SCE_P_COMMENTLINE, p['comment'])
147 self.StyleSetSpec(SCE_P_NUMBER, p['number'])
148 self.StyleSetSpec(SCE_P_STRING, p['string'])
149 self.StyleSetSpec(SCE_P_CHARACTER, p['char'])
150 self.StyleSetSpec(SCE_P_WORD, p['keyword'])
151 self.StyleSetSpec(SCE_P_TRIPLE, p['triple'])
152 self.StyleSetSpec(SCE_P_TRIPLEDOUBLE, p['tripledouble'])
153 self.StyleSetSpec(SCE_P_CLASSNAME, p['class'])
154 self.StyleSetSpec(SCE_P_DEFNAME, p['def'])
155 self.StyleSetSpec(SCE_P_OPERATOR, p['operator'])
156 self.StyleSetSpec(SCE_P_COMMENTBLOCK, p['comment'])
157
158
159 # used for writing to stdout, etc.
160 def _write(self, text, style=_stdout_style):
161 self.lastPromptPos = 0
162 pos = self.GetCurrentPos()
163 self.AddText(text)
164 self.StartStyling(pos, 0xFF)
165 self.SetStyleFor(len(text), style)
166 self.EnsureCaretVisible()
167 wxYield()
168
169 write = _write
170
171 def writeTrace(self, text):
172 self._write(text, _trace_style)
173
174
175 def Prompt(self):
176 # is the current line non-empty?
177 text, pos = self.GetCurrentLineText()
178 if pos != 0:
179 self.AddText('\n')
180 self.AddText(self.props['ps1'])
181 self.lastPromptPos = self.GetCurrentPos()
182 self.EnsureCaretVisible()
183 self.ScrollToColumn(0)
184
185
186 def PushLine(self, text):
187 # TODO: Add the text to the line cache, manage the cache so
188 # it doesn't get too big.
189 pass
190
191
192
193 def OnKey(self, evt):
194 key = evt.KeyCode()
195 if key == WXK_RETURN:
196 pos = self.GetCurrentPos()
197 lastPos = self.GetTextLength()
198
199 # if not on the last line, duplicate the current line
200 if self.GetLineCount()-1 != self.GetCurrentLine():
201 text, col = self.GetCurrentLineText()
202 prompt = self.props['ps1']
203 lp = len(prompt)
204 if text[:lp] == prompt:
205 text = text[lp:]
206
207 self.SetSelection(self.lastPromptPos, lastPos)
208 self.ReplaceSelection(text[:-1])
209
210 else: # try to execute the text from the prompt to the end
211 if lastPos == self.lastPromptPos:
212 self.AddText('\n')
213 self.Prompt()
214 return
215
216 text = self.GetTextRange(self.lastPromptPos, lastPos)
217 self.AddText('\n')
218
219 more = self.runsource(text)
220 if not more:
221 self.PushLine(text)
222 self.Prompt()
223
224 # TODO: Add handlers for Alt-P and Alt-N to cycle through entries
225 # in the line cache
226
227 else:
228 evt.Skip()
229
230
231 def OnStyle(self, evt):
232 # Only style from the prompt pos to the end
233 lastPos = self.GetTextLength()
234 if self.lastPromptPos and self.lastPromptPos != lastPos:
235 self.SetLexer(wxSTC_LEX_PYTHON)
236 self.SetKeywords(0, string.join(keyword.kwlist))
237
238 self.Colourise(self.lastPromptPos, lastPos)
239
240 self.SetLexer(0)
241
242
243 def OnUpdateUI(self, evt):
244 # check for matching braces
245 braceAtCaret = -1
246 braceOpposite = -1
247 charBefore = None
248 caretPos = self.GetCurrentPos()
249 if caretPos > 0:
250 charBefore = self.GetCharAt(caretPos - 1)
251 styleBefore = self.GetStyleAt(caretPos - 1)
252
253 # check before
254 if charBefore and charBefore in "[]{}()" and ord(styleBefore) == SCE_P_OPERATOR:
255 braceAtCaret = caretPos - 1
256
257 # check after
258 if braceAtCaret < 0:
259 charAfter = self.GetCharAt(caretPos)
260 styleAfter = self.GetStyleAt(caretPos)
261 if charAfter and charAfter in "[]{}()" and ord(styleAfter) == SCE_P_OPERATOR:
262 braceAtCaret = caretPos
263
264 if braceAtCaret >= 0:
265 braceOpposite = self.BraceMatch(braceAtCaret)
266
267 if braceAtCaret != -1 and braceOpposite == -1:
268 self.BraceBadlight(braceAtCaret)
269 else:
270 self.BraceHighlight(braceAtCaret, braceOpposite)
271
272
273
274 #----------------------------------------------
275 # overloaded methods from InteractiveInterpreter
276 def runsource(self, source):
277 stdout, stderr = sys.stdout, sys.stderr
278 sys.stdout = FauxFile(self, _stdout_style)
279 sys.stderr = FauxFile(self, _stderr_style)
280
281 more = InteractiveInterpreter.runsource(self, source)
282
283 sys.stdout, sys.stderr = stdout, stderr
284 return more
285
286 def showsyntaxerror(self, filename=None):
287 self.write = self.writeTrace
288 InteractiveInterpreter.showsyntaxerror(self, filename)
289 self.write = self._write
290
291 def showtraceback(self):
292 self.write = self.writeTrace
293 InteractiveInterpreter.showtraceback(self)
294 self.write = self._write
295
296 #----------------------------------------------------------------------
297
298 class FauxFile:
299 def __init__(self, psw, style):
300 self.psw = psw
301 self.style = style
302
303 def write(self, text):
304 self.psw.write(text, self.style)
305
306 def writelines(self, lst):
307 map(self.write, lst)
308
309 def flush(self):
310 pass
311
312
313 #----------------------------------------------------------------------
314 # test code
315
316 if __name__ == '__main__':
317 app = wxPyWidgetTester(size = (640, 480))
318 app.SetWidget(PyShellWindow, -1)
319 app.MainLoop()
320
321
322 #----------------------------------------------------------------------
323
324