##// END OF EJS Templates
Fix for Unicode characters when executing processes. Also fix typo in...
Gael Varoquaux -
Show More
@@ -1,433 +1,441 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A Wx widget to act as a console and input commands.
3 A Wx widget to act as a console and input commands.
4
4
5 This widget deals with prompts and provides an edit buffer
5 This widget deals with prompts and provides an edit buffer
6 restricted to after the last prompt.
6 restricted to after the last prompt.
7 """
7 """
8
8
9 __docformat__ = "restructuredtext en"
9 __docformat__ = "restructuredtext en"
10
10
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12 # Copyright (C) 2008 The IPython Development Team
12 # Copyright (C) 2008 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is
14 # Distributed under the terms of the BSD License. The full license is
15 # in the file COPYING, distributed as part of this software.
15 # in the file COPYING, distributed as part of this software.
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 import wx
22 import wx
23 import wx.stc as stc
23 import wx.stc as stc
24
24
25 from wx.py import editwindow
25 from wx.py import editwindow
26 import sys
26 import sys
27 LINESEP = '\n'
27 LINESEP = '\n'
28 if sys.platform == 'win32':
28 if sys.platform == 'win32':
29 LINESEP = '\n\r'
29 LINESEP = '\n\r'
30
30
31 import re
31 import re
32
32
33 # FIXME: Need to provide an API for non user-generated display on the
33 # FIXME: Need to provide an API for non user-generated display on the
34 # screen: this should not be editable by the user.
34 # screen: this should not be editable by the user.
35
35
36 _DEFAULT_SIZE = 10
36 _DEFAULT_SIZE = 10
37
37
38 _DEFAULT_STYLE = {
38 _DEFAULT_STYLE = {
39 'stdout' : 'fore:#0000FF',
39 'stdout' : 'fore:#0000FF',
40 'stderr' : 'fore:#007f00',
40 'stderr' : 'fore:#007f00',
41 'trace' : 'fore:#FF0000',
41 'trace' : 'fore:#FF0000',
42
42
43 'default' : 'size:%d' % _DEFAULT_SIZE,
43 'default' : 'size:%d' % _DEFAULT_SIZE,
44 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
44 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
45 'bracebad' : 'fore:#000000,back:#FF0000,bold',
45 'bracebad' : 'fore:#000000,back:#FF0000,bold',
46
46
47 # properties for the various Python lexer styles
47 # properties for the various Python lexer styles
48 'comment' : 'fore:#007F00',
48 'comment' : 'fore:#007F00',
49 'number' : 'fore:#007F7F',
49 'number' : 'fore:#007F7F',
50 'string' : 'fore:#7F007F,italic',
50 'string' : 'fore:#7F007F,italic',
51 'char' : 'fore:#7F007F,italic',
51 'char' : 'fore:#7F007F,italic',
52 'keyword' : 'fore:#00007F,bold',
52 'keyword' : 'fore:#00007F,bold',
53 'triple' : 'fore:#7F0000',
53 'triple' : 'fore:#7F0000',
54 'tripledouble' : 'fore:#7F0000',
54 'tripledouble' : 'fore:#7F0000',
55 'class' : 'fore:#0000FF,bold,underline',
55 'class' : 'fore:#0000FF,bold,underline',
56 'def' : 'fore:#007F7F,bold',
56 'def' : 'fore:#007F7F,bold',
57 'operator' : 'bold'
57 'operator' : 'bold'
58 }
58 }
59
59
60 # new style numbers
60 # new style numbers
61 _STDOUT_STYLE = 15
61 _STDOUT_STYLE = 15
62 _STDERR_STYLE = 16
62 _STDERR_STYLE = 16
63 _TRACE_STYLE = 17
63 _TRACE_STYLE = 17
64
64
65
65
66 # system colors
66 # system colors
67 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
67 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
68
68
69 #-------------------------------------------------------------------------------
69 #-------------------------------------------------------------------------------
70 # The console widget class
70 # The console widget class
71 #-------------------------------------------------------------------------------
71 #-------------------------------------------------------------------------------
72 class ConsoleWidget(editwindow.EditWindow):
72 class ConsoleWidget(editwindow.EditWindow):
73 """ Specialized styled text control view for console-like workflow.
73 """ Specialized styled text control view for console-like workflow.
74
74
75 This widget is mainly interested in dealing with the prompt and
75 This widget is mainly interested in dealing with the prompt and
76 keeping the cursor inside the editing line.
76 keeping the cursor inside the editing line.
77 """
77 """
78
78
79 title = 'Console'
79 title = 'Console'
80
80
81 style = _DEFAULT_STYLE.copy()
81 style = _DEFAULT_STYLE.copy()
82
82
83 # Translation table from ANSI escape sequences to color. Override
83 # Translation table from ANSI escape sequences to color. Override
84 # this to specify your colors.
84 # this to specify your colors.
85 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
85 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
86 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
86 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
87 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
87 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
88 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
88 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
89 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
89 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
90 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
90 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
91 '1;34': [12, 'LIGHT BLUE'], '1;35':
91 '1;34': [12, 'LIGHT BLUE'], '1;35':
92 [13, 'MEDIUM VIOLET RED'],
92 [13, 'MEDIUM VIOLET RED'],
93 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
93 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
94
94
95 # The color of the carret (call _apply_style() after setting)
95 # The color of the carret (call _apply_style() after setting)
96 carret_color = 'BLACK'
96 carret_color = 'BLACK'
97
97
98 #--------------------------------------------------------------------------
98 #--------------------------------------------------------------------------
99 # Public API
99 # Public API
100 #--------------------------------------------------------------------------
100 #--------------------------------------------------------------------------
101
101
102 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
102 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
103 size=wx.DefaultSize, style=0, ):
103 size=wx.DefaultSize, style=0, ):
104 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
104 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
105 self.configure_scintilla()
105 self.configure_scintilla()
106
106
107 # FIXME: we need to retrieve this from the interpreter.
107 # FIXME: we need to retrieve this from the interpreter.
108 self.prompt = \
108 self.prompt = \
109 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
109 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
110 self.new_prompt(self.prompt % 1)
110 self.new_prompt(self.prompt % 1)
111
111
112 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
112 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
113 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
113 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
114
114
115
115
116 def configure_scintilla(self):
116 def configure_scintilla(self):
117 self.SetEOLMode(stc.STC_EOL_LF)
117 self.SetEOLMode(stc.STC_EOL_LF)
118
118
119 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
119 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
120 # the widget
120 # the widget
121 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
121 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
122 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
122 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
123 # Also allow Ctrl Shift "=" for poor non US keyboard users.
123 # Also allow Ctrl Shift "=" for poor non US keyboard users.
124 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
124 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
125 stc.STC_CMD_ZOOMIN)
125 stc.STC_CMD_ZOOMIN)
126
126
127 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
127 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
128 # stc.STC_CMD_PAGEUP)
128 # stc.STC_CMD_PAGEUP)
129
129
130 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
130 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
131 # stc.STC_CMD_PAGEDOWN)
131 # stc.STC_CMD_PAGEDOWN)
132
132
133 # Keys: we need to clear some of the keys the that don't play
133 # Keys: we need to clear some of the keys the that don't play
134 # well with a console.
134 # well with a console.
135 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
135 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
136 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
136 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
137 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
137 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
138
138
139
139
140 self.SetEOLMode(stc.STC_EOL_CRLF)
140 self.SetEOLMode(stc.STC_EOL_CRLF)
141 self.SetWrapMode(stc.STC_WRAP_CHAR)
141 self.SetWrapMode(stc.STC_WRAP_CHAR)
142 self.SetWrapMode(stc.STC_WRAP_WORD)
142 self.SetWrapMode(stc.STC_WRAP_WORD)
143 self.SetBufferedDraw(True)
143 self.SetBufferedDraw(True)
144 self.SetUseAntiAliasing(True)
144 self.SetUseAntiAliasing(True)
145 self.SetLayoutCache(stc.STC_CACHE_PAGE)
145 self.SetLayoutCache(stc.STC_CACHE_PAGE)
146 self.SetUndoCollection(False)
146 self.SetUndoCollection(False)
147 self.SetUseTabs(True)
147 self.SetUseTabs(True)
148 self.SetIndent(4)
148 self.SetIndent(4)
149 self.SetTabWidth(4)
149 self.SetTabWidth(4)
150
150
151 self.EnsureCaretVisible()
151 self.EnsureCaretVisible()
152 # we don't want scintilla's autocompletion to choose
152 # we don't want scintilla's autocompletion to choose
153 # automaticaly out of a single choice list, as we pop it up
153 # automaticaly out of a single choice list, as we pop it up
154 # automaticaly
154 # automaticaly
155 self.AutoCompSetChooseSingle(False)
155 self.AutoCompSetChooseSingle(False)
156 self.AutoCompSetMaxHeight(10)
156 self.AutoCompSetMaxHeight(10)
157
157
158 self.SetMargins(3, 3) #text is moved away from border with 3px
158 self.SetMargins(3, 3) #text is moved away from border with 3px
159 # Suppressing Scintilla margins
159 # Suppressing Scintilla margins
160 self.SetMarginWidth(0, 0)
160 self.SetMarginWidth(0, 0)
161 self.SetMarginWidth(1, 0)
161 self.SetMarginWidth(1, 0)
162 self.SetMarginWidth(2, 0)
162 self.SetMarginWidth(2, 0)
163
163
164 self._apply_style()
164 self._apply_style()
165
165
166 # Xterm escape sequences
166 # Xterm escape sequences
167 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
167 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
168 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
168 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
169
169
170 #self.SetEdgeMode(stc.STC_EDGE_LINE)
170 #self.SetEdgeMode(stc.STC_EDGE_LINE)
171 #self.SetEdgeColumn(80)
171 #self.SetEdgeColumn(80)
172
172
173 # styles
173 # styles
174 p = self.style
174 p = self.style
175 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
175 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
176 self.StyleClearAll()
176 self.StyleClearAll()
177 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
177 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
178 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
178 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
179 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
179 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
180
180
181 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
181 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
182 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
182 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
183 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
183 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
184 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
184 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
185 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
185 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
186 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
186 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
187 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
187 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
188 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
188 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
189 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
189 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
190 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
190 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
191 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
191 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
192 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
192 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
193 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
193 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
194 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
194 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
195
195
196
196
197 def write(self, text, refresh=True):
197 def write(self, text, refresh=True):
198 """ Write given text to buffer, while translating the ansi escape
198 """ Write given text to buffer, while translating the ansi escape
199 sequences.
199 sequences.
200 """
200 """
201 # XXX: do not put print statements to sys.stdout/sys.stderr in
201 # XXX: do not put print statements to sys.stdout/sys.stderr in
202 # this method, the print statements will call this method, as
202 # this method, the print statements will call this method, as
203 # you will end up with an infinit loop
203 # you will end up with an infinit loop
204 if self.debug:
204 if self.debug:
205 print >>sys.__stderr__, text
205 print >>sys.__stderr__, text
206 title = self.title_pat.split(text)
206 title = self.title_pat.split(text)
207 if len(title)>1:
207 if len(title)>1:
208 self.title = title[-2]
208 self.title = title[-2]
209
209
210 text = self.title_pat.sub('', text)
210 text = self.title_pat.sub('', text)
211 segments = self.color_pat.split(text)
211 segments = self.color_pat.split(text)
212 segment = segments.pop(0)
212 segment = segments.pop(0)
213 self.GotoPos(self.GetLength())
213 self.GotoPos(self.GetLength())
214 self.StartStyling(self.GetLength(), 0xFF)
214 self.StartStyling(self.GetLength(), 0xFF)
215 self.AppendText(segment)
215 try:
216 self.AppendText(segment)
217 except UnicodeDecodeError:
218 # XXX: Do I really want to skip the exception?
219 pass
216
220
217 if segments:
221 if segments:
218 for ansi_tag, text in zip(segments[::2], segments[1::2]):
222 for ansi_tag, text in zip(segments[::2], segments[1::2]):
219 self.StartStyling(self.GetLength(), 0xFF)
223 self.StartStyling(self.GetLength(), 0xFF)
220 self.AppendText(text)
224 try:
225 self.AppendText(text)
226 except UnicodeDecodeError:
227 # XXX: Do I really want to skip the exception?
228 pass
221
229
222 if ansi_tag not in self.ANSI_STYLES:
230 if ansi_tag not in self.ANSI_STYLES:
223 style = 0
231 style = 0
224 else:
232 else:
225 style = self.ANSI_STYLES[ansi_tag][0]
233 style = self.ANSI_STYLES[ansi_tag][0]
226
234
227 self.SetStyling(len(text), style)
235 self.SetStyling(len(text), style)
228
236
229 self.GotoPos(self.GetLength())
237 self.GotoPos(self.GetLength())
230 if refresh:
238 if refresh:
231 wx.Yield()
239 wx.Yield()
232
240
233
241
234 def new_prompt(self, prompt):
242 def new_prompt(self, prompt):
235 """ Prints a prompt at start of line, and move the start of the
243 """ Prints a prompt at start of line, and move the start of the
236 current block there.
244 current block there.
237
245
238 The prompt can be give with ascii escape sequences.
246 The prompt can be give with ascii escape sequences.
239 """
247 """
240 self.write(prompt)
248 self.write(prompt)
241 # now we update our cursor giving end of prompt
249 # now we update our cursor giving end of prompt
242 self.current_prompt_pos = self.GetLength()
250 self.current_prompt_pos = self.GetLength()
243 self.current_prompt_line = self.GetCurrentLine()
251 self.current_prompt_line = self.GetCurrentLine()
244 wx.Yield()
252 wx.Yield()
245 self.EnsureCaretVisible()
253 self.EnsureCaretVisible()
246
254
247
255
248 def replace_current_edit_buffer(self, text):
256 def replace_current_edit_buffer(self, text):
249 """ Replace currently entered command line with given text.
257 """ Replace currently entered command line with given text.
250 """
258 """
251 self.SetSelection(self.current_prompt_pos, self.GetLength())
259 self.SetSelection(self.current_prompt_pos, self.GetLength())
252 self.ReplaceSelection(text)
260 self.ReplaceSelection(text)
253 self.GotoPos(self.GetLength())
261 self.GotoPos(self.GetLength())
254
262
255
263
256 def get_current_edit_buffer(self):
264 def get_current_edit_buffer(self):
257 """ Returns the text in current edit buffer.
265 """ Returns the text in current edit buffer.
258 """
266 """
259 current_edit_buffer = self.GetTextRange(self.current_prompt_pos,
267 current_edit_buffer = self.GetTextRange(self.current_prompt_pos,
260 self.GetLength())
268 self.GetLength())
261 current_edit_buffer = current_edit_buffer.replace(LINESEP, '\n')
269 current_edit_buffer = current_edit_buffer.replace(LINESEP, '\n')
262 return current_edit_buffer
270 return current_edit_buffer
263
271
264
272
265 #--------------------------------------------------------------------------
273 #--------------------------------------------------------------------------
266 # Private API
274 # Private API
267 #--------------------------------------------------------------------------
275 #--------------------------------------------------------------------------
268
276
269 def _apply_style(self):
277 def _apply_style(self):
270 """ Applies the colors for the different text elements and the
278 """ Applies the colors for the different text elements and the
271 carret.
279 carret.
272 """
280 """
273 self.SetCaretForeground(self.carret_color)
281 self.SetCaretForeground(self.carret_color)
274
282
275 #self.StyleClearAll()
283 #self.StyleClearAll()
276 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
284 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
277 "fore:#FF0000,back:#0000FF,bold")
285 "fore:#FF0000,back:#0000FF,bold")
278 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
286 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
279 "fore:#000000,back:#FF0000,bold")
287 "fore:#000000,back:#FF0000,bold")
280
288
281 for style in self.ANSI_STYLES.values():
289 for style in self.ANSI_STYLES.values():
282 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
290 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
283
291
284
292
285 def write_completion(self, possibilities):
293 def write_completion(self, possibilities):
286 # FIXME: This is non Wx specific and needs to be moved into
294 # FIXME: This is non Wx specific and needs to be moved into
287 # the base class.
295 # the base class.
288 current_buffer = self.get_current_edit_buffer()
296 current_buffer = self.get_current_edit_buffer()
289
297
290 self.write('\n')
298 self.write('\n')
291 max_len = len(max(possibilities, key=len)) + 1
299 max_len = len(max(possibilities, key=len)) + 1
292
300
293 #now we check how much symbol we can put on a line...
301 #now we check how much symbol we can put on a line...
294 chars_per_line = self.GetSize()[0]/self.GetCharWidth()
302 chars_per_line = self.GetSize()[0]/self.GetCharWidth()
295 symbols_per_line = max(1, chars_per_line/max_len)
303 symbols_per_line = max(1, chars_per_line/max_len)
296
304
297 pos = 1
305 pos = 1
298 buf = []
306 buf = []
299 for symbol in possibilities:
307 for symbol in possibilities:
300 if pos < symbols_per_line:
308 if pos < symbols_per_line:
301 buf.append(symbol.ljust(max_len))
309 buf.append(symbol.ljust(max_len))
302 pos += 1
310 pos += 1
303 else:
311 else:
304 buf.append(symbol.rstrip() + '\n')
312 buf.append(symbol.rstrip() + '\n')
305 pos = 1
313 pos = 1
306 self.write(''.join(buf))
314 self.write(''.join(buf))
307 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
315 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
308 self.replace_current_edit_buffer(current_buffer)
316 self.replace_current_edit_buffer(current_buffer)
309
317
310
318
311 def pop_completion(self, possibilities, offset=0):
319 def pop_completion(self, possibilities, offset=0):
312 """ Pops up an autocompletion menu. Offset is the offset
320 """ Pops up an autocompletion menu. Offset is the offset
313 in characters of the position at which the menu should
321 in characters of the position at which the menu should
314 appear, relativ to the cursor.
322 appear, relativ to the cursor.
315 """
323 """
316 self.AutoCompSetIgnoreCase(False)
324 self.AutoCompSetIgnoreCase(False)
317 self.AutoCompSetAutoHide(False)
325 self.AutoCompSetAutoHide(False)
318 self.AutoCompSetMaxHeight(len(possibilities))
326 self.AutoCompSetMaxHeight(len(possibilities))
319 self.AutoCompShow(offset, " ".join(possibilities))
327 self.AutoCompShow(offset, " ".join(possibilities))
320
328
321
329
322 def scroll_to_bottom(self):
330 def scroll_to_bottom(self):
323 maxrange = self.GetScrollRange(wx.VERTICAL)
331 maxrange = self.GetScrollRange(wx.VERTICAL)
324 self.ScrollLines(maxrange)
332 self.ScrollLines(maxrange)
325
333
326
334
327 def _on_key_down(self, event, skip=True):
335 def _on_key_down(self, event, skip=True):
328 """ Key press callback used for correcting behavior for
336 """ Key press callback used for correcting behavior for
329 console-like interfaces: the cursor is constraint to be after
337 console-like interfaces: the cursor is constraint to be after
330 the last prompt.
338 the last prompt.
331
339
332 Return True if event as been catched.
340 Return True if event as been catched.
333 """
341 """
334 catched = True
342 catched = True
335 # Intercept some specific keys.
343 # Intercept some specific keys.
336 if event.KeyCode == ord('L') and event.ControlDown() :
344 if event.KeyCode == ord('L') and event.ControlDown() :
337 self.scroll_to_bottom()
345 self.scroll_to_bottom()
338 elif event.KeyCode == ord('K') and event.ControlDown() :
346 elif event.KeyCode == ord('K') and event.ControlDown() :
339 self.replace_current_edit_buffer('')
347 self.replace_current_edit_buffer('')
340 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
348 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
341 self.ScrollPages(-1)
349 self.ScrollPages(-1)
342 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
350 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
343 self.ScrollPages(1)
351 self.ScrollPages(1)
344 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
352 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
345 self.ScrollLines(-1)
353 self.ScrollLines(-1)
346 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
354 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
347 self.ScrollLines(1)
355 self.ScrollLines(1)
348 else:
356 else:
349 catched = False
357 catched = False
350
358
351 if self.AutoCompActive():
359 if self.AutoCompActive():
352 event.Skip()
360 event.Skip()
353 else:
361 else:
354 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
362 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
355 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
363 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
356 catched = True
364 catched = True
357 self.CallTipCancel()
365 self.CallTipCancel()
358 self.write('\n')
366 self.write('\n')
359 # Under windows scintilla seems to be doing funny stuff to the
367 # Under windows scintilla seems to be doing funny stuff to the
360 # line returns here, but get_current_edit_buffer filters this
368 # line returns here, but get_current_edit_buffer filters this
361 # out.
369 # out.
362 if sys.platform == 'win32':
370 if sys.platform == 'win32':
363 self.replace_current_edit_buffer(
371 self.replace_current_edit_buffer(
364 self.get_current_edit_buffer())
372 self.get_current_edit_buffer())
365 self._on_enter()
373 self._on_enter()
366
374
367 elif event.KeyCode == wx.WXK_HOME:
375 elif event.KeyCode == wx.WXK_HOME:
368 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
376 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
369 self.GotoPos(self.current_prompt_pos)
377 self.GotoPos(self.current_prompt_pos)
370 catched = True
378 catched = True
371
379
372 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
380 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
373 # FIXME: This behavior is not ideal: if the selection
381 # FIXME: This behavior is not ideal: if the selection
374 # is already started, it will jump.
382 # is already started, it will jump.
375 self.SetSelectionStart(self.current_prompt_pos)
383 self.SetSelectionStart(self.current_prompt_pos)
376 self.SetSelectionEnd(self.GetCurrentPos())
384 self.SetSelectionEnd(self.GetCurrentPos())
377 catched = True
385 catched = True
378
386
379 elif event.KeyCode == wx.WXK_UP:
387 elif event.KeyCode == wx.WXK_UP:
380 if self.GetCurrentLine() > self.current_prompt_line:
388 if self.GetCurrentLine() > self.current_prompt_line:
381 if self.GetCurrentLine() == self.current_prompt_line + 1 \
389 if self.GetCurrentLine() == self.current_prompt_line + 1 \
382 and self.GetColumn(self.GetCurrentPos()) < \
390 and self.GetColumn(self.GetCurrentPos()) < \
383 self.GetColumn(self.current_prompt_pos):
391 self.GetColumn(self.current_prompt_pos):
384 self.GotoPos(self.current_prompt_pos)
392 self.GotoPos(self.current_prompt_pos)
385 else:
393 else:
386 event.Skip()
394 event.Skip()
387 catched = True
395 catched = True
388
396
389 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
397 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
390 if self.GetCurrentPos() > self.current_prompt_pos:
398 if self.GetCurrentPos() > self.current_prompt_pos:
391 event.Skip()
399 event.Skip()
392 catched = True
400 catched = True
393
401
394 if skip and not catched:
402 if skip and not catched:
395 # Put the cursor back in the edit region
403 # Put the cursor back in the edit region
396 if self.GetCurrentPos() < self.current_prompt_pos:
404 if self.GetCurrentPos() < self.current_prompt_pos:
397 self.GotoPos(self.current_prompt_pos)
405 self.GotoPos(self.current_prompt_pos)
398 else:
406 else:
399 event.Skip()
407 event.Skip()
400
408
401 return catched
409 return catched
402
410
403
411
404 def _on_key_up(self, event, skip=True):
412 def _on_key_up(self, event, skip=True):
405 """ If cursor is outside the editing region, put it back.
413 """ If cursor is outside the editing region, put it back.
406 """
414 """
407 event.Skip()
415 event.Skip()
408 if self.GetCurrentPos() < self.current_prompt_pos:
416 if self.GetCurrentPos() < self.current_prompt_pos:
409 self.GotoPos(self.current_prompt_pos)
417 self.GotoPos(self.current_prompt_pos)
410
418
411
419
412
420
413
421
414 if __name__ == '__main__':
422 if __name__ == '__main__':
415 # Some simple code to test the console widget.
423 # Some simple code to test the console widget.
416 class MainWindow(wx.Frame):
424 class MainWindow(wx.Frame):
417 def __init__(self, parent, id, title):
425 def __init__(self, parent, id, title):
418 wx.Frame.__init__(self, parent, id, title, size=(300,250))
426 wx.Frame.__init__(self, parent, id, title, size=(300,250))
419 self._sizer = wx.BoxSizer(wx.VERTICAL)
427 self._sizer = wx.BoxSizer(wx.VERTICAL)
420 self.console_widget = ConsoleWidget(self)
428 self.console_widget = ConsoleWidget(self)
421 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
429 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
422 self.SetSizer(self._sizer)
430 self.SetSizer(self._sizer)
423 self.SetAutoLayout(1)
431 self.SetAutoLayout(1)
424 self.Show(True)
432 self.Show(True)
425
433
426 app = wx.PySimpleApp()
434 app = wx.PySimpleApp()
427 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
435 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
428 w.SetSize((780, 460))
436 w.SetSize((780, 460))
429 w.Show()
437 w.Show()
430
438
431 app.MainLoop()
439 app.MainLoop()
432
440
433
441
@@ -1,407 +1,408 b''
1 # encoding: utf-8 -*- test-case-name:
1 # encoding: utf-8 -*- test-case-name:
2 # FIXME: Need to add tests.
2 # FIXME: Need to add tests.
3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
4
4
5 """Classes to provide a Wx frontend to the
5 """Classes to provide a Wx frontend to the
6 IPython.kernel.core.interpreter.
6 IPython.kernel.core.interpreter.
7
7
8 """
8 """
9
9
10 __docformat__ = "restructuredtext en"
10 __docformat__ = "restructuredtext en"
11
11
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13 # Copyright (C) 2008 The IPython Development Team
13 # Copyright (C) 2008 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22
22
23
23
24 import wx
24 import wx
25 import re
25 import re
26 from wx import stc
26 from wx import stc
27 from console_widget import ConsoleWidget
27 from console_widget import ConsoleWidget
28 import __builtin__
28 import __builtin__
29 from time import sleep
29 from time import sleep
30 import sys
30 import sys
31 import signal
31 import signal
32
32
33 from threading import Lock
33 from threading import Lock
34
34
35 from IPython.frontend.piped_process import PipedProcess
35 from IPython.frontend.piped_process import PipedProcess
36 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
36 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
37
37
38 #_COMMAND_BG = '#FAFAF1' # Nice green
38 #_COMMAND_BG = '#FAFAF1' # Nice green
39 _RUNNING_BUFFER_BG = '#FDFFD3' # Nice yellow
39 _RUNNING_BUFFER_BG = '#FDFFD3' # Nice yellow
40 _ERROR_BG = '#FFF1F1' # Nice red
40 _ERROR_BG = '#FFF1F1' # Nice red
41
41
42 _RUNNING_BUFFER_MARKER = 31
42 _RUNNING_BUFFER_MARKER = 31
43 _ERROR_MARKER = 30
43 _ERROR_MARKER = 30
44
44
45 #-------------------------------------------------------------------------------
45 #-------------------------------------------------------------------------------
46 # Classes to implement the Wx frontend
46 # Classes to implement the Wx frontend
47 #-------------------------------------------------------------------------------
47 #-------------------------------------------------------------------------------
48 class WxController(PrefilterFrontEnd, ConsoleWidget):
48 class WxController(PrefilterFrontEnd, ConsoleWidget):
49
49
50 output_prompt = \
50 output_prompt = \
51 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
51 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
52
52
53 # Print debug info on what is happening to the console.
53 # Print debug info on what is happening to the console.
54 debug = True
54 debug = True
55
55
56 # The title of the terminal, as captured through the ANSI escape
56 # The title of the terminal, as captured through the ANSI escape
57 # sequences.
57 # sequences.
58
58
59 def _set_title(self, title):
59 def _set_title(self, title):
60 return self.Parent.SetTitle(title)
60 return self.Parent.SetTitle(title)
61
61
62 def _get_title(self):
62 def _get_title(self):
63 return self.Parent.GetTitle()
63 return self.Parent.GetTitle()
64
64
65 title = property(_get_title, _set_title)
65 title = property(_get_title, _set_title)
66
66
67 #--------------------------------------------------------------------------
67 #--------------------------------------------------------------------------
68 # Private Attributes
68 # Private Attributes
69 #--------------------------------------------------------------------------
69 #--------------------------------------------------------------------------
70
70
71 # A flag governing the behavior of the input. Can be:
71 # A flag governing the behavior of the input. Can be:
72 #
72 #
73 # 'readline' for readline-like behavior with a prompt
73 # 'readline' for readline-like behavior with a prompt
74 # and an edit buffer.
74 # and an edit buffer.
75 # 'subprocess' for sending the raw input directly to a
75 # 'subprocess' for sending the raw input directly to a
76 # subprocess.
76 # subprocess.
77 # 'buffering' for buffering of the input, that will be used
77 # 'buffering' for buffering of the input, that will be used
78 # when the input state switches back to another state.
78 # when the input state switches back to another state.
79 _input_state = 'readline'
79 _input_state = 'readline'
80
80
81 # Attribute to store reference to the pipes of a subprocess, if we
81 # Attribute to store reference to the pipes of a subprocess, if we
82 # are running any.
82 # are running any.
83 _running_process = False
83 _running_process = False
84
84
85 # A queue for writing fast streams to the screen without flooding the
85 # A queue for writing fast streams to the screen without flooding the
86 # event loop
86 # event loop
87 _out_buffer = []
87 _out_buffer = []
88
88
89 # A lock to lock the _out_buffer to make sure we don't empty it
89 # A lock to lock the _out_buffer to make sure we don't empty it
90 # while it is being swapped
90 # while it is being swapped
91 _out_buffer_lock = Lock()
91 _out_buffer_lock = Lock()
92
92
93 #--------------------------------------------------------------------------
93 #--------------------------------------------------------------------------
94 # Public API
94 # Public API
95 #--------------------------------------------------------------------------
95 #--------------------------------------------------------------------------
96
96
97 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
97 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
98 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
98 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
99 *args, **kwds):
99 *args, **kwds):
100 """ Create Shell instance.
100 """ Create Shell instance.
101 """
101 """
102 ConsoleWidget.__init__(self, parent, id, pos, size, style)
102 ConsoleWidget.__init__(self, parent, id, pos, size, style)
103 PrefilterFrontEnd.__init__(self)
103 PrefilterFrontEnd.__init__(self)
104
104
105 # Marker for running buffer.
105 # Marker for running buffer.
106 self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
106 self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
107 background=_RUNNING_BUFFER_BG)
107 background=_RUNNING_BUFFER_BG)
108 # Marker for tracebacks.
108 # Marker for tracebacks.
109 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
109 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
110 background=_ERROR_BG)
110 background=_ERROR_BG)
111
111
112 # A time for flushing the write buffer
112 # A time for flushing the write buffer
113 BUFFER_FLUSH_TIMER_ID = 100
113 BUFFER_FLUSH_TIMER_ID = 100
114 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
114 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
115 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
115 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
116
116
117 def do_completion(self):
117 def do_completion(self):
118 """ Do code completion.
118 """ Do code completion.
119 """
119 """
120 if self.debug:
120 if self.debug:
121 print >>sys.__stdout__, "do_completion",
121 print >>sys.__stdout__, "do_completion",
122 line = self.get_current_edit_buffer()
122 line = self.get_current_edit_buffer()
123 new_line, completions = self.complete(line)
123 new_line, completions = self.complete(line)
124 if len(completions)>1:
124 if len(completions)>1:
125 self.write_completion(completions)
125 self.write_completion(completions)
126 self.replace_current_edit_buffer(new_line)
126 self.replace_current_edit_buffer(new_line)
127 if self.debug:
127 if self.debug:
128 print >>sys.__stdout__, completions
128 print >>sys.__stdout__, completions
129
129
130
130
131 def do_calltip(self):
131 def do_calltip(self):
132 if self.debug:
132 if self.debug:
133 print >>sys.__stdout__, "do_calltip"
133 print >>sys.__stdout__, "do_calltip"
134 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
134 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
135 symbol = self.get_current_edit_buffer()
135 symbol = self.get_current_edit_buffer()
136 symbol_string = separators.split(symbol)[-1]
136 symbol_string = separators.split(symbol)[-1]
137 base_symbol_string = symbol_string.split('.')[0]
137 base_symbol_string = symbol_string.split('.')[0]
138 if base_symbol_string in self.shell.user_ns:
138 if base_symbol_string in self.shell.user_ns:
139 symbol = self.shell.user_ns[base_symbol_string]
139 symbol = self.shell.user_ns[base_symbol_string]
140 elif base_symbol_string in self.shell.user_global_ns:
140 elif base_symbol_string in self.shell.user_global_ns:
141 symbol = self.shell.user_global_ns[base_symbol_string]
141 symbol = self.shell.user_global_ns[base_symbol_string]
142 elif base_symbol_string in __builtin__.__dict__:
142 elif base_symbol_string in __builtin__.__dict__:
143 symbol = __builtin__.__dict__[base_symbol_string]
143 symbol = __builtin__.__dict__[base_symbol_string]
144 else:
144 else:
145 return False
145 return False
146 for name in symbol_string.split('.')[1:] + ['__doc__']:
146 for name in symbol_string.split('.')[1:] + ['__doc__']:
147 symbol = getattr(symbol, name)
147 symbol = getattr(symbol, name)
148 try:
148 try:
149 self.AutoCompCancel()
149 self.AutoCompCancel()
150 wx.Yield()
150 wx.Yield()
151 self.CallTipShow(self.GetCurrentPos(), symbol)
151 self.CallTipShow(self.GetCurrentPos(), symbol)
152 except:
152 except:
153 # The retrieve symbol couldn't be converted to a string
153 # The retrieve symbol couldn't be converted to a string
154 pass
154 pass
155
155
156
156
157 def popup_completion(self, create=False):
157 def popup_completion(self, create=False):
158 """ Updates the popup completion menu if it exists. If create is
158 """ Updates the popup completion menu if it exists. If create is
159 true, open the menu.
159 true, open the menu.
160 """
160 """
161 if self.debug:
161 if self.debug:
162 print >>sys.__stdout__, "popup_completion",
162 print >>sys.__stdout__, "popup_completion",
163 line = self.get_current_edit_buffer()
163 line = self.get_current_edit_buffer()
164 if (self.AutoCompActive() and not line[-1] == '.') \
164 if (self.AutoCompActive() and not line[-1] == '.') \
165 or create==True:
165 or create==True:
166 suggestion, completions = self.complete(line)
166 suggestion, completions = self.complete(line)
167 offset=0
167 offset=0
168 if completions:
168 if completions:
169 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
169 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
170 residual = complete_sep.split(line)[-1]
170 residual = complete_sep.split(line)[-1]
171 offset = len(residual)
171 offset = len(residual)
172 self.pop_completion(completions, offset=offset)
172 self.pop_completion(completions, offset=offset)
173 if self.debug:
173 if self.debug:
174 print >>sys.__stdout__, completions
174 print >>sys.__stdout__, completions
175
175
176
176
177 def new_prompt(self, prompt):
177 def new_prompt(self, prompt):
178 self._input_state = 'readline'
178 self._input_state = 'readline'
179 ConsoleWidget.new_prompt(self, prompt)
179 ConsoleWidget.new_prompt(self, prompt)
180
180
181
181
182 def raw_input(self, prompt):
182 def raw_input(self, prompt):
183 """ A replacement from python's raw_input.
183 """ A replacement from python's raw_input.
184 """
184 """
185 self.new_prompt(prompt)
185 self.new_prompt(prompt)
186 self.waiting = True
186 self.waiting = True
187 self.__old_on_enter = self._on_enter
187 self.__old_on_enter = self._on_enter
188 def my_on_enter():
188 def my_on_enter():
189 self.waiting = False
189 self.waiting = False
190 self._on_enter = my_on_enter
190 self._on_enter = my_on_enter
191 # XXX: Busy waiting, ugly.
191 # XXX: Busy waiting, ugly.
192 while self.waiting:
192 while self.waiting:
193 wx.Yield()
193 wx.Yield()
194 sleep(0.1)
194 sleep(0.1)
195 self._on_enter = self.__old_on_enter
195 self._on_enter = self.__old_on_enter
196 self._input_state = 'buffering'
196 self._input_state = 'buffering'
197 return self.get_current_edit_buffer().rstrip('\n')
197 return self.get_current_edit_buffer().rstrip('\n')
198
198
199
199
200 def execute(self, python_string, raw_string=None):
200 def execute(self, python_string, raw_string=None):
201 self._input_state = 'buffering'
201 self._input_state = 'buffering'
202 self.CallTipCancel()
202 self.CallTipCancel()
203 self._cursor = wx.BusyCursor()
203 self._cursor = wx.BusyCursor()
204 if raw_string is None:
204 if raw_string is None:
205 raw_string = python_string
205 raw_string = python_string
206 end_line = self.current_prompt_line \
206 end_line = self.current_prompt_line \
207 + max(1, len(raw_string.split('\n'))-1)
207 + max(1, len(raw_string.split('\n'))-1)
208 for i in range(self.current_prompt_line, end_line):
208 for i in range(self.current_prompt_line, end_line):
209 self.MarkerAdd(i, _RUNNING_BUFFER_MARKER)
209 self.MarkerAdd(i, _RUNNING_BUFFER_MARKER)
210 # Update the display:
210 # Update the display:
211 wx.Yield()
211 wx.Yield()
212 self.GotoPos(self.GetLength())
212 self.GotoPos(self.GetLength())
213 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
213 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
214
214
215
215
216 def capture_output(self):
216 def capture_output(self):
217 self.__old_raw_input = __builtin__.raw_input
217 self.__old_raw_input = __builtin__.raw_input
218 __builtin__.raw_input = self.raw_input
218 __builtin__.raw_input = self.raw_input
219 PrefilterFrontEnd.capture_output(self)
219 PrefilterFrontEnd.capture_output(self)
220
220
221
221
222 def release_output(self):
222 def release_output(self):
223 __builtin__.raw_input = self.__old_raw_input
223 __builtin__.raw_input = self.__old_raw_input
224 PrefilterFrontEnd.capture_output(self)
224 PrefilterFrontEnd.capture_output(self)
225
225
226
226
227 def after_execute(self):
227 def after_execute(self):
228 PrefilterFrontEnd.after_execute(self)
228 PrefilterFrontEnd.after_execute(self)
229 if hasattr(self, '_cursor'):
229 if hasattr(self, '_cursor'):
230 del self._cursor
230 del self._cursor
231
231
232
232
233 def system_call(self, command_string):
233 def system_call(self, command_string):
234 self._input_state = 'subprocess'
234 self._input_state = 'subprocess'
235 self._running_process = PipedProcess(command_string,
235 self._running_process = PipedProcess(command_string,
236 out_callback=self.buffered_write,
236 out_callback=self.buffered_write,
237 end_callback = self._end_system_call)
237 end_callback = self._end_system_call)
238 self._running_process.start()
238 self._running_process.start()
239 # XXX: another one of these polling loops to have a blocking
239 # XXX: another one of these polling loops to have a blocking
240 # call
240 # call
241 wx.Yield()
241 wx.Yield()
242 while self._running_process:
242 while self._running_process:
243 wx.Yield()
243 wx.Yield()
244 sleep(0.1)
244 sleep(0.1)
245 # Be sure to flush the buffer.
245 # Be sure to flush the buffer.
246 self._buffer_flush(event=None)
246 self._buffer_flush(event=None)
247
247
248
248
249 def buffered_write(self, text):
249 def buffered_write(self, text):
250 """ A write method for streams, that caches the stream in order
250 """ A write method for streams, that caches the stream in order
251 to avoid flooding the event loop.
251 to avoid flooding the event loop.
252
252
253 This can be called outside of the main loop, in separate
253 This can be called outside of the main loop, in separate
254 threads.
254 threads.
255 """
255 """
256 self._out_buffer_lock.acquire()
256 self._out_buffer_lock.acquire()
257 self._out_buffer.append(text)
257 self._out_buffer.append(text)
258 self._out_buffer_lock.release()
258 self._out_buffer_lock.release()
259 if not self._buffer_flush_timer.IsRunning():
259 if not self._buffer_flush_timer.IsRunning():
260 wx.CallAfter(self._buffer_flush_timer.Start, 100) # milliseconds
260 wx.CallAfter(self._buffer_flush_timer.Start, 100) # milliseconds
261
261
262
262
263 def show_traceback(self):
263 def show_traceback(self):
264 start_line = self.GetCurrentLine()
264 start_line = self.GetCurrentLine()
265 PrefilterFrontEnd.show_traceback(self)
265 PrefilterFrontEnd.show_traceback(self)
266 wx.Yield()
266 wx.Yield()
267 for i in range(start_line, self.GetCurrentLine()):
267 for i in range(start_line, self.GetCurrentLine()):
268 self.MarkerAdd(i, _ERROR_MARKER)
268 self.MarkerAdd(i, _ERROR_MARKER)
269
269
270
270
271 #--------------------------------------------------------------------------
271 #--------------------------------------------------------------------------
272 # Private API
272 # Private API
273 #--------------------------------------------------------------------------
273 #--------------------------------------------------------------------------
274
274
275 def _on_key_down(self, event, skip=True):
275 def _on_key_down(self, event, skip=True):
276 """ Capture the character events, let the parent
276 """ Capture the character events, let the parent
277 widget handle them, and put our logic afterward.
277 widget handle them, and put our logic afterward.
278 """
278 """
279 # FIXME: This method needs to be broken down in smaller ones.
279 current_line_number = self.GetCurrentLine()
280 current_line_number = self.GetCurrentLine()
280 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
281 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
281 # Capture Control-C
282 # Capture Control-C
282 if self._input_state == 'subprocess':
283 if self._input_state == 'subprocess':
283 if self.debug:
284 if self.debug:
284 print >>sys.__stderr__, 'Killing running process'
285 print >>sys.__stderr__, 'Killing running process'
285 self._running_process.process.kill()
286 self._running_process.process.kill()
286 elif self._input_state == 'buffering':
287 elif self._input_state == 'buffering':
287 if self.debug:
288 if self.debug:
288 print >>sys.__stderr__, 'Raising KeyboardException'
289 print >>sys.__stderr__, 'Raising KeyboardException'
289 raise KeyboardException
290 raise KeyboardException
290 # XXX: We need to make really sure we
291 # XXX: We need to make really sure we
291 # get back to a prompt.
292 # get back to a prompt.
292 elif self._input_state == 'subprocess' and (
293 elif self._input_state == 'subprocess' and (
293 ( event.KeyCode<256 and
294 ( event.KeyCode<256 and
294 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN, wx.MOD_SHIFT))
295 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN, wx.MOD_SHIFT))
295 or
296 or
296 ( event.KeyCode in (ord('d'), ord('D')) and
297 ( event.KeyCode in (ord('d'), ord('D')) and
297 event.ControlDown())):
298 event.ControlDown())):
298 # We are running a process, we redirect keys.
299 # We are running a process, we redirect keys.
299 ConsoleWidget._on_key_down(self, event, skip=skip)
300 ConsoleWidget._on_key_down(self, event, skip=skip)
300 char = chr(event.KeyCode)
301 char = chr(event.KeyCode)
301 # Deal with some inconsistency in wx keycodes:
302 # Deal with some inconsistency in wx keycodes:
302 if char == '\r':
303 if char == '\r':
303 char = '\n'
304 char = '\n'
304 elif not event.ShiftDown():
305 elif not event.ShiftDown():
305 char = char.lower()
306 char = char.lower()
306 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
307 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
307 char = '\04'
308 char = '\04'
308 self._running_process.process.stdin.write(char)
309 self._running_process.process.stdin.write(char)
309 self._running_process.process.stdin.flush()
310 self._running_process.process.stdin.flush()
310 elif event.KeyCode in (ord('('), 57):
311 elif event.KeyCode in (ord('('), 57):
311 # Calltips
312 # Calltips
312 event.Skip()
313 event.Skip()
313 self.do_calltip()
314 self.do_calltip()
314 elif self.AutoCompActive():
315 elif self.AutoCompActive():
315 event.Skip()
316 event.Skip()
316 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
317 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
317 wx.CallAfter(self.popup_completion, create=True)
318 wx.CallAfter(self.popup_completion, create=True)
318 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
319 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
319 wx.WXK_RIGHT):
320 wx.WXK_RIGHT):
320 wx.CallAfter(self.popup_completion)
321 wx.CallAfter(self.popup_completion)
321 else:
322 else:
322 # Up history
323 # Up history
323 if event.KeyCode == wx.WXK_UP and (
324 if event.KeyCode == wx.WXK_UP and (
324 ( current_line_number == self.current_prompt_line and
325 ( current_line_number == self.current_prompt_line and
325 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
326 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
326 or event.ControlDown() ):
327 or event.ControlDown() ):
327 new_buffer = self.get_history_previous(
328 new_buffer = self.get_history_previous(
328 self.get_current_edit_buffer())
329 self.get_current_edit_buffer())
329 if new_buffer is not None:
330 if new_buffer is not None:
330 self.replace_current_edit_buffer(new_buffer)
331 self.replace_current_edit_buffer(new_buffer)
331 if self.GetCurrentLine() > self.current_prompt_line:
332 if self.GetCurrentLine() > self.current_prompt_line:
332 # Go to first line, for seemless history up.
333 # Go to first line, for seemless history up.
333 self.GotoPos(self.current_prompt_pos)
334 self.GotoPos(self.current_prompt_pos)
334 # Down history
335 # Down history
335 elif event.KeyCode == wx.WXK_DOWN and (
336 elif event.KeyCode == wx.WXK_DOWN and (
336 ( current_line_number == self.LineCount -1 and
337 ( current_line_number == self.LineCount -1 and
337 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
338 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
338 or event.ControlDown() ):
339 or event.ControlDown() ):
339 new_buffer = self.get_history_next()
340 new_buffer = self.get_history_next()
340 if new_buffer is not None:
341 if new_buffer is not None:
341 self.replace_current_edit_buffer(new_buffer)
342 self.replace_current_edit_buffer(new_buffer)
342 # Tab-completion
343 # Tab-completion
343 elif event.KeyCode == ord('\t'):
344 elif event.KeyCode == ord('\t'):
344 last_line = self.get_current_edit_buffer().split('\n')[-1]
345 last_line = self.get_current_edit_buffer().split('\n')[-1]
345 if not re.match(r'^\s*$', last_line):
346 if not re.match(r'^\s*$', last_line):
346 self.do_completion()
347 self.do_completion()
347 else:
348 else:
348 event.Skip()
349 event.Skip()
349 else:
350 else:
350 ConsoleWidget._on_key_down(self, event, skip=skip)
351 ConsoleWidget._on_key_down(self, event, skip=skip)
351
352
352
353
353 def _on_key_up(self, event, skip=True):
354 def _on_key_up(self, event, skip=True):
354 if event.KeyCode in (59, ord('.')):
355 if event.KeyCode in (59, ord('.')):
355 # Intercepting '.'
356 # Intercepting '.'
356 event.Skip()
357 event.Skip()
357 self.popup_completion(create=True)
358 self.popup_completion(create=True)
358 else:
359 else:
359 ConsoleWidget._on_key_up(self, event, skip=skip)
360 ConsoleWidget._on_key_up(self, event, skip=skip)
360
361
361
362
362 def _on_enter(self):
363 def _on_enter(self):
363 if self.debug:
364 if self.debug:
364 print >>sys.__stdout__, repr(self.get_current_edit_buffer())
365 print >>sys.__stdout__, repr(self.get_current_edit_buffer())
365 PrefilterFrontEnd._on_enter(self)
366 PrefilterFrontEnd._on_enter(self)
366
367
367
368
368 def _end_system_call(self):
369 def _end_system_call(self):
369 """ Called at the end of a system call.
370 """ Called at the end of a system call.
370 """
371 """
371 print >>sys.__stderr__, 'End of system call'
372 print >>sys.__stderr__, 'End of system call'
372 self._input_state = 'buffering'
373 self._input_state = 'buffering'
373 self._running_process = False
374 self._running_process = False
374
375
375
376
376 def _buffer_flush(self, event):
377 def _buffer_flush(self, event):
377 """ Called by the timer to flush the write buffer.
378 """ Called by the timer to flush the write buffer.
378
379
379 This is always called in the mainloop, by the wx timer.
380 This is always called in the mainloop, by the wx timer.
380 """
381 """
381 self._out_buffer_lock.acquire()
382 self._out_buffer_lock.acquire()
382 _out_buffer = self._out_buffer
383 _out_buffer = self._out_buffer
383 self._out_buffer = []
384 self._out_buffer = []
384 self._out_buffer_lock.release()
385 self._out_buffer_lock.release()
385 self.write(''.join(_out_buffer), refresh=False)
386 self.write(''.join(_out_buffer), refresh=False)
386 self._buffer_flush_timer.Stop()
387 self._buffer_flush_timer.Stop()
387
388
388
389
389 if __name__ == '__main__':
390 if __name__ == '__main__':
390 class MainWindow(wx.Frame):
391 class MainWindow(wx.Frame):
391 def __init__(self, parent, id, title):
392 def __init__(self, parent, id, title):
392 wx.Frame.__init__(self, parent, id, title, size=(300,250))
393 wx.Frame.__init__(self, parent, id, title, size=(300,250))
393 self._sizer = wx.BoxSizer(wx.VERTICAL)
394 self._sizer = wx.BoxSizer(wx.VERTICAL)
394 self.shell = WxController(self)
395 self.shell = WxController(self)
395 self._sizer.Add(self.shell, 1, wx.EXPAND)
396 self._sizer.Add(self.shell, 1, wx.EXPAND)
396 self.SetSizer(self._sizer)
397 self.SetSizer(self._sizer)
397 self.SetAutoLayout(1)
398 self.SetAutoLayout(1)
398 self.Show(True)
399 self.Show(True)
399
400
400 app = wx.PySimpleApp()
401 app = wx.PySimpleApp()
401 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
402 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
402 frame.shell.SetFocus()
403 frame.shell.SetFocus()
403 frame.SetSize((680, 460))
404 frame.SetSize((680, 460))
404 self = frame.shell
405 self = frame.shell
405
406
406 app.MainLoop()
407 app.MainLoop()
407
408
@@ -1,41 +1,41 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """Object to manage sys.excepthook().
3 """Object to manage sys.excepthook().
4
4
5 Synchronous version: prints errors when called.
5 Synchronous version: prints errors when called.
6 """
6 """
7
7
8 __docformat__ = "restructuredtext en"
8 __docformat__ = "restructuredtext en"
9
9
10 #-------------------------------------------------------------------------------
10 #-------------------------------------------------------------------------------
11 # Copyright (C) 2008 The IPython Development Team
11 # Copyright (C) 2008 The IPython Development Team
12 #
12 #
13 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16
16
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18 # Imports
18 # Imports
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20 from traceback_trap import TracebackTrap
20 from traceback_trap import TracebackTrap
21 from IPython.ultraTB import ColorTB
21 from IPython.ultraTB import ColorTB
22
22
23 class SyncTracebackTrap(TracebackTrap):
23 class SyncTracebackTrap(TracebackTrap):
24
24
25 def __init__(self, sync_formatter=None, formatters=None):
25 def __init__(self, sync_formatter=None, formatters=None):
26 TracebackTrap.__init__(self, formatters=formatters)
26 TracebackTrap.__init__(self, formatters=formatters)
27 if sync_formatter is None:
27 if sync_formatter is None:
28 sync_formatter = ColorTB(color_scheme='LightBG')
28 sync_formatter = ColorTB(color_scheme='LightBG')
29 self.sync_formatter = sync_formatter
29 self.sync_formatter = sync_formatter
30
30
31
31
32 def hook(self, *args):
32 def hook(self, *args):
33 """ This method actually implements the hook.
33 """ This method actually implements the hook.
34 """
34 """
35 self.args = args
35 self.args = args
36
36
37 print self.sync_formatters(*self.args)
37 print self.sync_formatter(*self.args)
38
38
39
39
40
40
41
41
General Comments 0
You need to be logged in to leave comments. Login now