]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wx/lib/pyshell.py
Added a set of button classes to wx.lib.buttons from David Hughes that
[wxWidgets.git] / wxPython / wx / lib / pyshell.py
CommitLineData
d14a1e28
RD
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#----------------------------------------------------------------------
b881fc78
RD
13# 12/10/2003 - Jeff Grimmett (grimmtooth@softhome.net)
14#
15# o 2.5 compatability update.
16# o Added deprecation warning.
17#
d14a1e28
RD
18
19"""
20PyShellWindow is a class that provides an Interactive Interpreter running
21inside a wxStyledTextCtrl, similar to the Python shell windows found in
22IDLE and PythonWin.
23
24There is still much to be done to improve this class, such as line
25buffering/recall, autoindent, calltips, autocomplete, fixing the colourizer,
26etc... But it's a good start.
27
28
298-10-2001 THIS MODULE IS NOW DEPRECATED. Please see the most excellent
30 PyCrust package instead.
31
32"""
33
b881fc78
RD
34import keyword
35import sys
36import warnings
d14a1e28 37
d14a1e28
RD
38from code import InteractiveInterpreter
39
b881fc78
RD
40import wx
41import wx.stc as stc
42
43warningmsg = r"""\
44
45########################################\
46# THIS MODULE IS NOW DEPRECATED |
47# |
48# Please see the most excellent PyCrust |
49# package instead. |
50########################################/
51
52"""
53
54warnings.warn(warningmsg, DeprecationWarning, stacklevel=2)
55
d14a1e28
RD
56#----------------------------------------------------------------------
57# default styles, etc. to use for the STC
58
b881fc78 59if wx.Platform == '__WXMSW__':
d14a1e28
RD
60 _defaultSize = 8
61else:
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
b881fc78
RD
100class PyShellWindow(stc.StyledTextCtrl, InteractiveInterpreter):
101 def __init__(self, parent, ID, pos=wx.DefaultPosition,
102 size=wx.DefaultSize, style=0,
d14a1e28 103 locals=None, properties=None, banner=None):
b881fc78 104 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
d14a1e28
RD
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
b881fc78
RD
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)
d14a1e28
RD
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
b881fc78 154 #self.SetEdgeMode(stc.STC_EDGE_LINE)
d14a1e28
RD
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
b881fc78 163 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
d14a1e28
RD
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
b881fc78
RD
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'])
d14a1e28
RD
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()
b881fc78 192 wx.Yield()
d14a1e28
RD
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):
a3cee65e 219 key = evt.GetKeyCode()
b881fc78 220 if key == wx.WXK_RETURN:
d14a1e28
RD
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:
b881fc78 260 self.SetLexer(stc.STC_LEX_PYTHON)
d14a1e28
RD
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
b881fc78 279 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
d14a1e28
RD
280 braceAtCaret = caretPos - 1
281
282 # check after
283 if braceAtCaret < 0:
284 charAfter = self.GetCharAt(caretPos)
285 styleAfter = self.GetStyleAt(caretPos)
b881fc78 286 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
d14a1e28
RD
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
323class 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
341if __name__ == '__main__':
b881fc78 342 app = wx.PyWidgetTester(size = (640, 480))
d14a1e28
RD
343 app.SetWidget(PyShellWindow, -1)
344 app.MainLoop()
345
346
347#----------------------------------------------------------------------
1fded56b 348
1fded56b 349