##// END OF EJS Templates
Fix traceback on exit.
Gael Varoquaux -
Show More
@@ -1,492 +1,493 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.wx.tests.test_wx_frontend -*-
3 # ipython1.frontend.wx.tests.test_wx_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 This class inherits from ConsoleWidget, that provides a console-like
8 This class inherits from ConsoleWidget, that provides a console-like
9 widget to provide a text-rendering widget suitable for a terminal.
9 widget to provide a text-rendering widget suitable for a terminal.
10 """
10 """
11
11
12 __docformat__ = "restructuredtext en"
12 __docformat__ = "restructuredtext en"
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Copyright (C) 2008 The IPython Development Team
15 # Copyright (C) 2008 The IPython Development Team
16 #
16 #
17 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20
20
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-------------------------------------------------------------------------------
23 #-------------------------------------------------------------------------------
24
24
25 # Major library imports
25 # Major library imports
26 import re
26 import re
27 import __builtin__
27 import __builtin__
28 from time import sleep
28 from time import sleep
29 import sys
29 import sys
30 from threading import Lock
30 from threading import Lock
31 import string
31 import string
32
32
33 import wx
33 import wx
34 from wx import stc
34 from wx import stc
35
35
36 # Ipython-specific imports.
36 # Ipython-specific imports.
37 from IPython.frontend._process import PipedProcess
37 from IPython.frontend._process import PipedProcess
38 from console_widget import ConsoleWidget
38 from console_widget import ConsoleWidget
39 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
39 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
40
40
41 #-------------------------------------------------------------------------------
41 #-------------------------------------------------------------------------------
42 # Constants
42 # Constants
43 #-------------------------------------------------------------------------------
43 #-------------------------------------------------------------------------------
44
44
45 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
45 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
46 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
46 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
47 _ERROR_BG = '#FFF1F1' # Nice red
47 _ERROR_BG = '#FFF1F1' # Nice red
48
48
49 _COMPLETE_BUFFER_MARKER = 31
49 _COMPLETE_BUFFER_MARKER = 31
50 _ERROR_MARKER = 30
50 _ERROR_MARKER = 30
51 _INPUT_MARKER = 29
51 _INPUT_MARKER = 29
52
52
53 prompt_in1 = \
53 prompt_in1 = \
54 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
54 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
55
55
56 prompt_out = \
56 prompt_out = \
57 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
57 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
58
58
59 #-------------------------------------------------------------------------------
59 #-------------------------------------------------------------------------------
60 # Classes to implement the Wx frontend
60 # Classes to implement the Wx frontend
61 #-------------------------------------------------------------------------------
61 #-------------------------------------------------------------------------------
62 class WxController(ConsoleWidget, PrefilterFrontEnd):
62 class WxController(ConsoleWidget, PrefilterFrontEnd):
63 """Classes to provide a Wx frontend to the
63 """Classes to provide a Wx frontend to the
64 IPython.kernel.core.interpreter.
64 IPython.kernel.core.interpreter.
65
65
66 This class inherits from ConsoleWidget, that provides a console-like
66 This class inherits from ConsoleWidget, that provides a console-like
67 widget to provide a text-rendering widget suitable for a terminal.
67 widget to provide a text-rendering widget suitable for a terminal.
68 """
68 """
69
69
70 output_prompt_template = string.Template(prompt_out)
70 output_prompt_template = string.Template(prompt_out)
71
71
72 input_prompt_template = string.Template(prompt_in1)
72 input_prompt_template = string.Template(prompt_in1)
73
73
74 # Print debug info on what is happening to the console.
74 # Print debug info on what is happening to the console.
75 debug = False
75 debug = False
76
76
77 # The title of the terminal, as captured through the ANSI escape
77 # The title of the terminal, as captured through the ANSI escape
78 # sequences.
78 # sequences.
79 def _set_title(self, title):
79 def _set_title(self, title):
80 return self.Parent.SetTitle(title)
80 return self.Parent.SetTitle(title)
81
81
82 def _get_title(self):
82 def _get_title(self):
83 return self.Parent.GetTitle()
83 return self.Parent.GetTitle()
84
84
85 title = property(_get_title, _set_title)
85 title = property(_get_title, _set_title)
86
86
87
87
88 # The buffer being edited.
88 # The buffer being edited.
89 # We are duplicating the definition here because of multiple
89 # We are duplicating the definition here because of multiple
90 # inheritence
90 # inheritence
91 def _set_input_buffer(self, string):
91 def _set_input_buffer(self, string):
92 ConsoleWidget._set_input_buffer(self, string)
92 ConsoleWidget._set_input_buffer(self, string)
93 self._colorize_input_buffer()
93 self._colorize_input_buffer()
94
94
95 def _get_input_buffer(self):
95 def _get_input_buffer(self):
96 """ Returns the text in current edit buffer.
96 """ Returns the text in current edit buffer.
97 """
97 """
98 return ConsoleWidget._get_input_buffer(self)
98 return ConsoleWidget._get_input_buffer(self)
99
99
100 input_buffer = property(_get_input_buffer, _set_input_buffer)
100 input_buffer = property(_get_input_buffer, _set_input_buffer)
101
101
102
102
103 #--------------------------------------------------------------------------
103 #--------------------------------------------------------------------------
104 # Private Attributes
104 # Private Attributes
105 #--------------------------------------------------------------------------
105 #--------------------------------------------------------------------------
106
106
107 # A flag governing the behavior of the input. Can be:
107 # A flag governing the behavior of the input. Can be:
108 #
108 #
109 # 'readline' for readline-like behavior with a prompt
109 # 'readline' for readline-like behavior with a prompt
110 # and an edit buffer.
110 # and an edit buffer.
111 # 'raw_input' similar to readline, but triggered by a raw-input
111 # 'raw_input' similar to readline, but triggered by a raw-input
112 # call. Can be used by subclasses to act differently.
112 # call. Can be used by subclasses to act differently.
113 # 'subprocess' for sending the raw input directly to a
113 # 'subprocess' for sending the raw input directly to a
114 # subprocess.
114 # subprocess.
115 # 'buffering' for buffering of the input, that will be used
115 # 'buffering' for buffering of the input, that will be used
116 # when the input state switches back to another state.
116 # when the input state switches back to another state.
117 _input_state = 'readline'
117 _input_state = 'readline'
118
118
119 # Attribute to store reference to the pipes of a subprocess, if we
119 # Attribute to store reference to the pipes of a subprocess, if we
120 # are running any.
120 # are running any.
121 _running_process = False
121 _running_process = False
122
122
123 # A queue for writing fast streams to the screen without flooding the
123 # A queue for writing fast streams to the screen without flooding the
124 # event loop
124 # event loop
125 _out_buffer = []
125 _out_buffer = []
126
126
127 # A lock to lock the _out_buffer to make sure we don't empty it
127 # A lock to lock the _out_buffer to make sure we don't empty it
128 # while it is being swapped
128 # while it is being swapped
129 _out_buffer_lock = Lock()
129 _out_buffer_lock = Lock()
130
130
131 _markers = dict()
131 _markers = dict()
132
132
133 #--------------------------------------------------------------------------
133 #--------------------------------------------------------------------------
134 # Public API
134 # Public API
135 #--------------------------------------------------------------------------
135 #--------------------------------------------------------------------------
136
136
137 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
137 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
138 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
138 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
139 *args, **kwds):
139 *args, **kwds):
140 """ Create Shell instance.
140 """ Create Shell instance.
141 """
141 """
142 ConsoleWidget.__init__(self, parent, id, pos, size, style)
142 ConsoleWidget.__init__(self, parent, id, pos, size, style)
143 PrefilterFrontEnd.__init__(self, **kwds)
143 PrefilterFrontEnd.__init__(self, **kwds)
144
144
145 # Marker for complete buffer.
145 # Marker for complete buffer.
146 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
146 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
147 background=_COMPLETE_BUFFER_BG)
147 background=_COMPLETE_BUFFER_BG)
148 # Marker for current input buffer.
148 # Marker for current input buffer.
149 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
149 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
150 background=_INPUT_BUFFER_BG)
150 background=_INPUT_BUFFER_BG)
151 # Marker for tracebacks.
151 # Marker for tracebacks.
152 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
152 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
153 background=_ERROR_BG)
153 background=_ERROR_BG)
154
154
155 # A time for flushing the write buffer
155 # A time for flushing the write buffer
156 BUFFER_FLUSH_TIMER_ID = 100
156 BUFFER_FLUSH_TIMER_ID = 100
157 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
157 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
158 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
158 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
159
159
160 if 'debug' in kwds:
160 if 'debug' in kwds:
161 self.debug = kwds['debug']
161 self.debug = kwds['debug']
162 kwds.pop('debug')
162 kwds.pop('debug')
163
163
164 # Inject self in namespace, for debug
164 # Inject self in namespace, for debug
165 if self.debug:
165 if self.debug:
166 self.shell.user_ns['self'] = self
166 self.shell.user_ns['self'] = self
167
167
168
168
169 def raw_input(self, prompt):
169 def raw_input(self, prompt):
170 """ A replacement from python's raw_input.
170 """ A replacement from python's raw_input.
171 """
171 """
172 self.new_prompt(prompt)
172 self.new_prompt(prompt)
173 self._input_state = 'raw_input'
173 self._input_state = 'raw_input'
174 del self._cursor
174 if hasattr(self, '_cursor'):
175 del self._cursor
175 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
176 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
176 self.waiting = True
177 self.waiting = True
177 self.__old_on_enter = self._on_enter
178 self.__old_on_enter = self._on_enter
178 def my_on_enter():
179 def my_on_enter():
179 self.waiting = False
180 self.waiting = False
180 self._on_enter = my_on_enter
181 self._on_enter = my_on_enter
181 # XXX: Busy waiting, ugly.
182 # XXX: Busy waiting, ugly.
182 while self.waiting:
183 while self.waiting:
183 wx.Yield()
184 wx.Yield()
184 sleep(0.1)
185 sleep(0.1)
185 self._on_enter = self.__old_on_enter
186 self._on_enter = self.__old_on_enter
186 self._input_state = 'buffering'
187 self._input_state = 'buffering'
187 self._cursor = wx.BusyCursor()
188 self._cursor = wx.BusyCursor()
188 return self.input_buffer.rstrip('\n')
189 return self.input_buffer.rstrip('\n')
189
190
190
191
191 def system_call(self, command_string):
192 def system_call(self, command_string):
192 self._input_state = 'subprocess'
193 self._input_state = 'subprocess'
193 self._running_process = PipedProcess(command_string,
194 self._running_process = PipedProcess(command_string,
194 out_callback=self.buffered_write,
195 out_callback=self.buffered_write,
195 end_callback = self._end_system_call)
196 end_callback = self._end_system_call)
196 self._running_process.start()
197 self._running_process.start()
197 # XXX: another one of these polling loops to have a blocking
198 # XXX: another one of these polling loops to have a blocking
198 # call
199 # call
199 wx.Yield()
200 wx.Yield()
200 while self._running_process:
201 while self._running_process:
201 wx.Yield()
202 wx.Yield()
202 sleep(0.1)
203 sleep(0.1)
203 # Be sure to flush the buffer.
204 # Be sure to flush the buffer.
204 self._buffer_flush(event=None)
205 self._buffer_flush(event=None)
205
206
206
207
207 def do_calltip(self):
208 def do_calltip(self):
208 """ Analyse current and displays useful calltip for it.
209 """ Analyse current and displays useful calltip for it.
209 """
210 """
210 if self.debug:
211 if self.debug:
211 print >>sys.__stdout__, "do_calltip"
212 print >>sys.__stdout__, "do_calltip"
212 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
213 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
213 symbol = self.input_buffer
214 symbol = self.input_buffer
214 symbol_string = separators.split(symbol)[-1]
215 symbol_string = separators.split(symbol)[-1]
215 base_symbol_string = symbol_string.split('.')[0]
216 base_symbol_string = symbol_string.split('.')[0]
216 if base_symbol_string in self.shell.user_ns:
217 if base_symbol_string in self.shell.user_ns:
217 symbol = self.shell.user_ns[base_symbol_string]
218 symbol = self.shell.user_ns[base_symbol_string]
218 elif base_symbol_string in self.shell.user_global_ns:
219 elif base_symbol_string in self.shell.user_global_ns:
219 symbol = self.shell.user_global_ns[base_symbol_string]
220 symbol = self.shell.user_global_ns[base_symbol_string]
220 elif base_symbol_string in __builtin__.__dict__:
221 elif base_symbol_string in __builtin__.__dict__:
221 symbol = __builtin__.__dict__[base_symbol_string]
222 symbol = __builtin__.__dict__[base_symbol_string]
222 else:
223 else:
223 return False
224 return False
224 try:
225 try:
225 for name in symbol_string.split('.')[1:] + ['__doc__']:
226 for name in symbol_string.split('.')[1:] + ['__doc__']:
226 symbol = getattr(symbol, name)
227 symbol = getattr(symbol, name)
227 self.AutoCompCancel()
228 self.AutoCompCancel()
228 wx.Yield()
229 wx.Yield()
229 self.CallTipShow(self.GetCurrentPos(), symbol)
230 self.CallTipShow(self.GetCurrentPos(), symbol)
230 except:
231 except:
231 # The retrieve symbol couldn't be converted to a string
232 # The retrieve symbol couldn't be converted to a string
232 pass
233 pass
233
234
234
235
235 def _popup_completion(self, create=False):
236 def _popup_completion(self, create=False):
236 """ Updates the popup completion menu if it exists. If create is
237 """ Updates the popup completion menu if it exists. If create is
237 true, open the menu.
238 true, open the menu.
238 """
239 """
239 if self.debug:
240 if self.debug:
240 print >>sys.__stdout__, "_popup_completion",
241 print >>sys.__stdout__, "_popup_completion",
241 line = self.input_buffer
242 line = self.input_buffer
242 if (self.AutoCompActive() and not line[-1] == '.') \
243 if (self.AutoCompActive() and not line[-1] == '.') \
243 or create==True:
244 or create==True:
244 suggestion, completions = self.complete(line)
245 suggestion, completions = self.complete(line)
245 offset=0
246 offset=0
246 if completions:
247 if completions:
247 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
248 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
248 residual = complete_sep.split(line)[-1]
249 residual = complete_sep.split(line)[-1]
249 offset = len(residual)
250 offset = len(residual)
250 self.pop_completion(completions, offset=offset)
251 self.pop_completion(completions, offset=offset)
251 if self.debug:
252 if self.debug:
252 print >>sys.__stdout__, completions
253 print >>sys.__stdout__, completions
253
254
254
255
255 def buffered_write(self, text):
256 def buffered_write(self, text):
256 """ A write method for streams, that caches the stream in order
257 """ A write method for streams, that caches the stream in order
257 to avoid flooding the event loop.
258 to avoid flooding the event loop.
258
259
259 This can be called outside of the main loop, in separate
260 This can be called outside of the main loop, in separate
260 threads.
261 threads.
261 """
262 """
262 self._out_buffer_lock.acquire()
263 self._out_buffer_lock.acquire()
263 self._out_buffer.append(text)
264 self._out_buffer.append(text)
264 self._out_buffer_lock.release()
265 self._out_buffer_lock.release()
265 if not self._buffer_flush_timer.IsRunning():
266 if not self._buffer_flush_timer.IsRunning():
266 wx.CallAfter(self._buffer_flush_timer.Start, 100) # milliseconds
267 wx.CallAfter(self._buffer_flush_timer.Start, 100) # milliseconds
267
268
268
269
269 #--------------------------------------------------------------------------
270 #--------------------------------------------------------------------------
270 # LineFrontEnd interface
271 # LineFrontEnd interface
271 #--------------------------------------------------------------------------
272 #--------------------------------------------------------------------------
272
273
273 def execute(self, python_string, raw_string=None):
274 def execute(self, python_string, raw_string=None):
274 self._input_state = 'buffering'
275 self._input_state = 'buffering'
275 self.CallTipCancel()
276 self.CallTipCancel()
276 self._cursor = wx.BusyCursor()
277 self._cursor = wx.BusyCursor()
277 if raw_string is None:
278 if raw_string is None:
278 raw_string = python_string
279 raw_string = python_string
279 end_line = self.current_prompt_line \
280 end_line = self.current_prompt_line \
280 + max(1, len(raw_string.split('\n'))-1)
281 + max(1, len(raw_string.split('\n'))-1)
281 for i in range(self.current_prompt_line, end_line):
282 for i in range(self.current_prompt_line, end_line):
282 if i in self._markers:
283 if i in self._markers:
283 self.MarkerDeleteHandle(self._markers[i])
284 self.MarkerDeleteHandle(self._markers[i])
284 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
285 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
285 # Update the display:
286 # Update the display:
286 wx.Yield()
287 wx.Yield()
287 self.GotoPos(self.GetLength())
288 self.GotoPos(self.GetLength())
288 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
289 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
289
290
290 def save_output_hooks(self):
291 def save_output_hooks(self):
291 self.__old_raw_input = __builtin__.raw_input
292 self.__old_raw_input = __builtin__.raw_input
292 PrefilterFrontEnd.save_output_hooks(self)
293 PrefilterFrontEnd.save_output_hooks(self)
293
294
294 def capture_output(self):
295 def capture_output(self):
295 __builtin__.raw_input = self.raw_input
296 __builtin__.raw_input = self.raw_input
296 PrefilterFrontEnd.capture_output(self)
297 PrefilterFrontEnd.capture_output(self)
297
298
298
299
299 def release_output(self):
300 def release_output(self):
300 __builtin__.raw_input = self.__old_raw_input
301 __builtin__.raw_input = self.__old_raw_input
301 PrefilterFrontEnd.release_output(self)
302 PrefilterFrontEnd.release_output(self)
302
303
303
304
304 def after_execute(self):
305 def after_execute(self):
305 PrefilterFrontEnd.after_execute(self)
306 PrefilterFrontEnd.after_execute(self)
306 # Clear the wait cursor
307 # Clear the wait cursor
307 if hasattr(self, '_cursor'):
308 if hasattr(self, '_cursor'):
308 del self._cursor
309 del self._cursor
309 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
310 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
310
311
311
312
312 def show_traceback(self):
313 def show_traceback(self):
313 start_line = self.GetCurrentLine()
314 start_line = self.GetCurrentLine()
314 PrefilterFrontEnd.show_traceback(self)
315 PrefilterFrontEnd.show_traceback(self)
315 wx.Yield()
316 wx.Yield()
316 for i in range(start_line, self.GetCurrentLine()):
317 for i in range(start_line, self.GetCurrentLine()):
317 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
318 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
318
319
319
320
320 #--------------------------------------------------------------------------
321 #--------------------------------------------------------------------------
321 # ConsoleWidget interface
322 # ConsoleWidget interface
322 #--------------------------------------------------------------------------
323 #--------------------------------------------------------------------------
323
324
324 def new_prompt(self, prompt):
325 def new_prompt(self, prompt):
325 """ Display a new prompt, and start a new input buffer.
326 """ Display a new prompt, and start a new input buffer.
326 """
327 """
327 self._input_state = 'readline'
328 self._input_state = 'readline'
328 ConsoleWidget.new_prompt(self, prompt)
329 ConsoleWidget.new_prompt(self, prompt)
329 i = self.current_prompt_line
330 i = self.current_prompt_line
330 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
331 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
331
332
332
333
333 def write(self, *args, **kwargs):
334 def write(self, *args, **kwargs):
334 # Avoid multiple inheritence, be explicit about which
335 # Avoid multiple inheritence, be explicit about which
335 # parent method class gets called
336 # parent method class gets called
336 ConsoleWidget.write(self, *args, **kwargs)
337 ConsoleWidget.write(self, *args, **kwargs)
337
338
338
339
339 def _on_key_down(self, event, skip=True):
340 def _on_key_down(self, event, skip=True):
340 """ Capture the character events, let the parent
341 """ Capture the character events, let the parent
341 widget handle them, and put our logic afterward.
342 widget handle them, and put our logic afterward.
342 """
343 """
343 # FIXME: This method needs to be broken down in smaller ones.
344 # FIXME: This method needs to be broken down in smaller ones.
344 current_line_number = self.GetCurrentLine()
345 current_line_number = self.GetCurrentLine()
345 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
346 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
346 # Capture Control-C
347 # Capture Control-C
347 if self._input_state == 'subprocess':
348 if self._input_state == 'subprocess':
348 if self.debug:
349 if self.debug:
349 print >>sys.__stderr__, 'Killing running process'
350 print >>sys.__stderr__, 'Killing running process'
350 self._running_process.process.kill()
351 self._running_process.process.kill()
351 elif self._input_state == 'buffering':
352 elif self._input_state == 'buffering':
352 if self.debug:
353 if self.debug:
353 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
354 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
354 raise KeyboardInterrupt
355 raise KeyboardInterrupt
355 # XXX: We need to make really sure we
356 # XXX: We need to make really sure we
356 # get back to a prompt.
357 # get back to a prompt.
357 elif self._input_state == 'subprocess' and (
358 elif self._input_state == 'subprocess' and (
358 ( event.KeyCode<256 and
359 ( event.KeyCode<256 and
359 not event.ControlDown() )
360 not event.ControlDown() )
360 or
361 or
361 ( event.KeyCode in (ord('d'), ord('D')) and
362 ( event.KeyCode in (ord('d'), ord('D')) and
362 event.ControlDown())):
363 event.ControlDown())):
363 # We are running a process, we redirect keys.
364 # We are running a process, we redirect keys.
364 ConsoleWidget._on_key_down(self, event, skip=skip)
365 ConsoleWidget._on_key_down(self, event, skip=skip)
365 char = chr(event.KeyCode)
366 char = chr(event.KeyCode)
366 # Deal with some inconsistency in wx keycodes:
367 # Deal with some inconsistency in wx keycodes:
367 if char == '\r':
368 if char == '\r':
368 char = '\n'
369 char = '\n'
369 elif not event.ShiftDown():
370 elif not event.ShiftDown():
370 char = char.lower()
371 char = char.lower()
371 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
372 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
372 char = '\04'
373 char = '\04'
373 self._running_process.process.stdin.write(char)
374 self._running_process.process.stdin.write(char)
374 self._running_process.process.stdin.flush()
375 self._running_process.process.stdin.flush()
375 elif event.KeyCode in (ord('('), 57):
376 elif event.KeyCode in (ord('('), 57):
376 # Calltips
377 # Calltips
377 event.Skip()
378 event.Skip()
378 self.do_calltip()
379 self.do_calltip()
379 elif self.AutoCompActive():
380 elif self.AutoCompActive():
380 event.Skip()
381 event.Skip()
381 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
382 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
382 wx.CallAfter(self._popup_completion, create=True)
383 wx.CallAfter(self._popup_completion, create=True)
383 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
384 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
384 wx.WXK_RIGHT):
385 wx.WXK_RIGHT):
385 wx.CallAfter(self._popup_completion)
386 wx.CallAfter(self._popup_completion)
386 else:
387 else:
387 # Up history
388 # Up history
388 if event.KeyCode == wx.WXK_UP and (
389 if event.KeyCode == wx.WXK_UP and (
389 ( current_line_number == self.current_prompt_line and
390 ( current_line_number == self.current_prompt_line and
390 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
391 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
391 or event.ControlDown() ):
392 or event.ControlDown() ):
392 new_buffer = self.get_history_previous(
393 new_buffer = self.get_history_previous(
393 self.input_buffer)
394 self.input_buffer)
394 if new_buffer is not None:
395 if new_buffer is not None:
395 self.input_buffer = new_buffer
396 self.input_buffer = new_buffer
396 if self.GetCurrentLine() > self.current_prompt_line:
397 if self.GetCurrentLine() > self.current_prompt_line:
397 # Go to first line, for seemless history up.
398 # Go to first line, for seemless history up.
398 self.GotoPos(self.current_prompt_pos)
399 self.GotoPos(self.current_prompt_pos)
399 # Down history
400 # Down history
400 elif event.KeyCode == wx.WXK_DOWN and (
401 elif event.KeyCode == wx.WXK_DOWN and (
401 ( current_line_number == self.LineCount -1 and
402 ( current_line_number == self.LineCount -1 and
402 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
403 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
403 or event.ControlDown() ):
404 or event.ControlDown() ):
404 new_buffer = self.get_history_next()
405 new_buffer = self.get_history_next()
405 if new_buffer is not None:
406 if new_buffer is not None:
406 self.input_buffer = new_buffer
407 self.input_buffer = new_buffer
407 # Tab-completion
408 # Tab-completion
408 elif event.KeyCode == ord('\t'):
409 elif event.KeyCode == ord('\t'):
409 last_line = self.input_buffer.split('\n')[-1]
410 last_line = self.input_buffer.split('\n')[-1]
410 if not re.match(r'^\s*$', last_line):
411 if not re.match(r'^\s*$', last_line):
411 self.complete_current_input()
412 self.complete_current_input()
412 else:
413 else:
413 event.Skip()
414 event.Skip()
414 else:
415 else:
415 ConsoleWidget._on_key_down(self, event, skip=skip)
416 ConsoleWidget._on_key_down(self, event, skip=skip)
416
417
417
418
418 def _on_key_up(self, event, skip=True):
419 def _on_key_up(self, event, skip=True):
419 """ Called when any key is released.
420 """ Called when any key is released.
420 """
421 """
421 if event.KeyCode in (59, ord('.')):
422 if event.KeyCode in (59, ord('.')):
422 # Intercepting '.'
423 # Intercepting '.'
423 event.Skip()
424 event.Skip()
424 self._popup_completion(create=True)
425 self._popup_completion(create=True)
425 else:
426 else:
426 ConsoleWidget._on_key_up(self, event, skip=skip)
427 ConsoleWidget._on_key_up(self, event, skip=skip)
427
428
428
429
429 def _on_enter(self):
430 def _on_enter(self):
430 """ Called on return key down, in readline input_state.
431 """ Called on return key down, in readline input_state.
431 """
432 """
432 if self.debug:
433 if self.debug:
433 print >>sys.__stdout__, repr(self.input_buffer)
434 print >>sys.__stdout__, repr(self.input_buffer)
434 PrefilterFrontEnd._on_enter(self)
435 PrefilterFrontEnd._on_enter(self)
435
436
436
437
437 #--------------------------------------------------------------------------
438 #--------------------------------------------------------------------------
438 # Private API
439 # Private API
439 #--------------------------------------------------------------------------
440 #--------------------------------------------------------------------------
440
441
441 def _end_system_call(self):
442 def _end_system_call(self):
442 """ Called at the end of a system call.
443 """ Called at the end of a system call.
443 """
444 """
444 self._input_state = 'buffering'
445 self._input_state = 'buffering'
445 self._running_process = False
446 self._running_process = False
446
447
447
448
448 def _buffer_flush(self, event):
449 def _buffer_flush(self, event):
449 """ Called by the timer to flush the write buffer.
450 """ Called by the timer to flush the write buffer.
450
451
451 This is always called in the mainloop, by the wx timer.
452 This is always called in the mainloop, by the wx timer.
452 """
453 """
453 self._out_buffer_lock.acquire()
454 self._out_buffer_lock.acquire()
454 _out_buffer = self._out_buffer
455 _out_buffer = self._out_buffer
455 self._out_buffer = []
456 self._out_buffer = []
456 self._out_buffer_lock.release()
457 self._out_buffer_lock.release()
457 self.write(''.join(_out_buffer), refresh=False)
458 self.write(''.join(_out_buffer), refresh=False)
458 self._buffer_flush_timer.Stop()
459 self._buffer_flush_timer.Stop()
459
460
460 def _colorize_input_buffer(self):
461 def _colorize_input_buffer(self):
461 """ Keep the input buffer lines at a bright color.
462 """ Keep the input buffer lines at a bright color.
462 """
463 """
463 if not self._input_state in ('readline', 'raw_input'):
464 if not self._input_state in ('readline', 'raw_input'):
464 return
465 return
465 end_line = self.GetCurrentLine()
466 end_line = self.GetCurrentLine()
466 if not sys.platform == 'win32':
467 if not sys.platform == 'win32':
467 end_line += 1
468 end_line += 1
468 for i in range(self.current_prompt_line, end_line):
469 for i in range(self.current_prompt_line, end_line):
469 if i in self._markers:
470 if i in self._markers:
470 self.MarkerDeleteHandle(self._markers[i])
471 self.MarkerDeleteHandle(self._markers[i])
471 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
472 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
472
473
473
474
474 if __name__ == '__main__':
475 if __name__ == '__main__':
475 class MainWindow(wx.Frame):
476 class MainWindow(wx.Frame):
476 def __init__(self, parent, id, title):
477 def __init__(self, parent, id, title):
477 wx.Frame.__init__(self, parent, id, title, size=(300,250))
478 wx.Frame.__init__(self, parent, id, title, size=(300,250))
478 self._sizer = wx.BoxSizer(wx.VERTICAL)
479 self._sizer = wx.BoxSizer(wx.VERTICAL)
479 self.shell = WxController(self)
480 self.shell = WxController(self)
480 self._sizer.Add(self.shell, 1, wx.EXPAND)
481 self._sizer.Add(self.shell, 1, wx.EXPAND)
481 self.SetSizer(self._sizer)
482 self.SetSizer(self._sizer)
482 self.SetAutoLayout(1)
483 self.SetAutoLayout(1)
483 self.Show(True)
484 self.Show(True)
484
485
485 app = wx.PySimpleApp()
486 app = wx.PySimpleApp()
486 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
487 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
487 frame.shell.SetFocus()
488 frame.shell.SetFocus()
488 frame.SetSize((680, 460))
489 frame.SetSize((680, 460))
489 self = frame.shell
490 self = frame.shell
490
491
491 app.MainLoop()
492 app.MainLoop()
492
493
General Comments 0
You need to be logged in to leave comments. Login now