##// END OF EJS Templates
Apply the same hack used for raw_input to system calls.
gvaroquaux -
Show More
@@ -1,530 +1,525 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 # The different line markers used to higlight the prompts.
131 # The different line markers used to higlight the prompts.
132 _markers = dict()
132 _markers = dict()
133
133
134 #--------------------------------------------------------------------------
134 #--------------------------------------------------------------------------
135 # Public API
135 # Public API
136 #--------------------------------------------------------------------------
136 #--------------------------------------------------------------------------
137
137
138 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
138 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
139 size=wx.DefaultSize,
139 size=wx.DefaultSize,
140 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
140 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
141 *args, **kwds):
141 *args, **kwds):
142 """ Create Shell instance.
142 """ Create Shell instance.
143 """
143 """
144 ConsoleWidget.__init__(self, parent, id, pos, size, style)
144 ConsoleWidget.__init__(self, parent, id, pos, size, style)
145 PrefilterFrontEnd.__init__(self, **kwds)
145 PrefilterFrontEnd.__init__(self, **kwds)
146
146
147 # Stick in our own raw_input:
147 # Stick in our own raw_input:
148 self.ipython0.raw_input = self.raw_input
148 self.ipython0.raw_input = self.raw_input
149
149
150 # Marker for complete buffer.
150 # Marker for complete buffer.
151 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
151 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
152 background=_COMPLETE_BUFFER_BG)
152 background=_COMPLETE_BUFFER_BG)
153 # Marker for current input buffer.
153 # Marker for current input buffer.
154 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
154 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
155 background=_INPUT_BUFFER_BG)
155 background=_INPUT_BUFFER_BG)
156 # Marker for tracebacks.
156 # Marker for tracebacks.
157 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
157 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
158 background=_ERROR_BG)
158 background=_ERROR_BG)
159
159
160 # A time for flushing the write buffer
160 # A time for flushing the write buffer
161 BUFFER_FLUSH_TIMER_ID = 100
161 BUFFER_FLUSH_TIMER_ID = 100
162 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
162 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
163 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
163 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
164
164
165 if 'debug' in kwds:
165 if 'debug' in kwds:
166 self.debug = kwds['debug']
166 self.debug = kwds['debug']
167 kwds.pop('debug')
167 kwds.pop('debug')
168
168
169 # Inject self in namespace, for debug
169 # Inject self in namespace, for debug
170 if self.debug:
170 if self.debug:
171 self.shell.user_ns['self'] = self
171 self.shell.user_ns['self'] = self
172 # Inject our own raw_input in namespace
172 # Inject our own raw_input in namespace
173 self.shell.user_ns['raw_input'] = self.raw_input
173 self.shell.user_ns['raw_input'] = self.raw_input
174
174
175
175
176 def raw_input(self, prompt):
176 def raw_input(self, prompt):
177 """ A replacement from python's raw_input.
177 """ A replacement from python's raw_input.
178 """
178 """
179 self.new_prompt(prompt)
179 self.new_prompt(prompt)
180 self._input_state = 'raw_input'
180 self._input_state = 'raw_input'
181 if hasattr(self, '_cursor'):
181 if hasattr(self, '_cursor'):
182 del self._cursor
182 del self._cursor
183 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
183 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
184 self.__old_on_enter = self._on_enter
184 self.__old_on_enter = self._on_enter
185 event_loop = wx.EventLoop()
185 event_loop = wx.EventLoop()
186 def my_on_enter():
186 def my_on_enter():
187 event_loop.Exit()
187 event_loop.Exit()
188 self._on_enter = my_on_enter
188 self._on_enter = my_on_enter
189 # XXX: Running a separate event_loop. Ugly.
189 # XXX: Running a separate event_loop. Ugly.
190 event_loop.Run()
190 event_loop.Run()
191 self._on_enter = self.__old_on_enter
191 self._on_enter = self.__old_on_enter
192 self._input_state = 'buffering'
192 self._input_state = 'buffering'
193 self._cursor = wx.BusyCursor()
193 self._cursor = wx.BusyCursor()
194 return self.input_buffer.rstrip('\n')
194 return self.input_buffer.rstrip('\n')
195
195
196
196
197 def system_call(self, command_string):
197 def system_call(self, command_string):
198 self._input_state = 'subprocess'
198 self._input_state = 'subprocess'
199 event_loop = wx.EventLoop()
200 def _end_system_call():
201 self._input_state = 'buffering'
202 self._running_process = False
203 event_loop.Exit()
204
199 self._running_process = PipedProcess(command_string,
205 self._running_process = PipedProcess(command_string,
200 out_callback=self.buffered_write,
206 out_callback=self.buffered_write,
201 end_callback = self._end_system_call)
207 end_callback = _end_system_call)
202 self._running_process.start()
208 self._running_process.start()
203 # XXX: another one of these polling loops to have a blocking
209 # XXX: Running a separate event_loop. Ugly.
204 # call
210 event_loop.Run()
205 wx.Yield()
206 while self._running_process:
207 wx.Yield()
208 sleep(0.1)
209 # Be sure to flush the buffer.
211 # Be sure to flush the buffer.
210 self._buffer_flush(event=None)
212 self._buffer_flush(event=None)
211
213
212
214
213 def do_calltip(self):
215 def do_calltip(self):
214 """ Analyse current and displays useful calltip for it.
216 """ Analyse current and displays useful calltip for it.
215 """
217 """
216 if self.debug:
218 if self.debug:
217 print >>sys.__stdout__, "do_calltip"
219 print >>sys.__stdout__, "do_calltip"
218 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
220 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
219 symbol = self.input_buffer
221 symbol = self.input_buffer
220 symbol_string = separators.split(symbol)[-1]
222 symbol_string = separators.split(symbol)[-1]
221 base_symbol_string = symbol_string.split('.')[0]
223 base_symbol_string = symbol_string.split('.')[0]
222 if base_symbol_string in self.shell.user_ns:
224 if base_symbol_string in self.shell.user_ns:
223 symbol = self.shell.user_ns[base_symbol_string]
225 symbol = self.shell.user_ns[base_symbol_string]
224 elif base_symbol_string in self.shell.user_global_ns:
226 elif base_symbol_string in self.shell.user_global_ns:
225 symbol = self.shell.user_global_ns[base_symbol_string]
227 symbol = self.shell.user_global_ns[base_symbol_string]
226 elif base_symbol_string in __builtin__.__dict__:
228 elif base_symbol_string in __builtin__.__dict__:
227 symbol = __builtin__.__dict__[base_symbol_string]
229 symbol = __builtin__.__dict__[base_symbol_string]
228 else:
230 else:
229 return False
231 return False
230 try:
232 try:
231 for name in symbol_string.split('.')[1:] + ['__doc__']:
233 for name in symbol_string.split('.')[1:] + ['__doc__']:
232 symbol = getattr(symbol, name)
234 symbol = getattr(symbol, name)
233 self.AutoCompCancel()
235 self.AutoCompCancel()
234 wx.Yield()
236 wx.Yield()
235 self.CallTipShow(self.GetCurrentPos(), symbol)
237 self.CallTipShow(self.GetCurrentPos(), symbol)
236 except:
238 except:
237 # The retrieve symbol couldn't be converted to a string
239 # The retrieve symbol couldn't be converted to a string
238 pass
240 pass
239
241
240
242
241 def _popup_completion(self, create=False):
243 def _popup_completion(self, create=False):
242 """ Updates the popup completion menu if it exists. If create is
244 """ Updates the popup completion menu if it exists. If create is
243 true, open the menu.
245 true, open the menu.
244 """
246 """
245 if self.debug:
247 if self.debug:
246 print >>sys.__stdout__, "_popup_completion"
248 print >>sys.__stdout__, "_popup_completion"
247 line = self.input_buffer
249 line = self.input_buffer
248 if (self.AutoCompActive() and line and not line[-1] == '.') \
250 if (self.AutoCompActive() and line and not line[-1] == '.') \
249 or create==True:
251 or create==True:
250 suggestion, completions = self.complete(line)
252 suggestion, completions = self.complete(line)
251 offset=0
253 offset=0
252 if completions:
254 if completions:
253 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
255 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
254 residual = complete_sep.split(line)[-1]
256 residual = complete_sep.split(line)[-1]
255 offset = len(residual)
257 offset = len(residual)
256 self.pop_completion(completions, offset=offset)
258 self.pop_completion(completions, offset=offset)
257 if self.debug:
259 if self.debug:
258 print >>sys.__stdout__, completions
260 print >>sys.__stdout__, completions
259
261
260
262
261 def buffered_write(self, text):
263 def buffered_write(self, text):
262 """ A write method for streams, that caches the stream in order
264 """ A write method for streams, that caches the stream in order
263 to avoid flooding the event loop.
265 to avoid flooding the event loop.
264
266
265 This can be called outside of the main loop, in separate
267 This can be called outside of the main loop, in separate
266 threads.
268 threads.
267 """
269 """
268 self._out_buffer_lock.acquire()
270 self._out_buffer_lock.acquire()
269 self._out_buffer.append(text)
271 self._out_buffer.append(text)
270 self._out_buffer_lock.release()
272 self._out_buffer_lock.release()
271 if not self._buffer_flush_timer.IsRunning():
273 if not self._buffer_flush_timer.IsRunning():
272 wx.CallAfter(self._buffer_flush_timer.Start,
274 wx.CallAfter(self._buffer_flush_timer.Start,
273 milliseconds=100, oneShot=True)
275 milliseconds=100, oneShot=True)
274
276
275
277
276 #--------------------------------------------------------------------------
278 #--------------------------------------------------------------------------
277 # LineFrontEnd interface
279 # LineFrontEnd interface
278 #--------------------------------------------------------------------------
280 #--------------------------------------------------------------------------
279
281
280 def execute(self, python_string, raw_string=None):
282 def execute(self, python_string, raw_string=None):
281 self._input_state = 'buffering'
283 self._input_state = 'buffering'
282 self.CallTipCancel()
284 self.CallTipCancel()
283 self._cursor = wx.BusyCursor()
285 self._cursor = wx.BusyCursor()
284 if raw_string is None:
286 if raw_string is None:
285 raw_string = python_string
287 raw_string = python_string
286 end_line = self.current_prompt_line \
288 end_line = self.current_prompt_line \
287 + max(1, len(raw_string.split('\n'))-1)
289 + max(1, len(raw_string.split('\n'))-1)
288 for i in range(self.current_prompt_line, end_line):
290 for i in range(self.current_prompt_line, end_line):
289 if i in self._markers:
291 if i in self._markers:
290 self.MarkerDeleteHandle(self._markers[i])
292 self.MarkerDeleteHandle(self._markers[i])
291 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
293 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
292 # Use a callafter to update the display robustly under windows
294 # Use a callafter to update the display robustly under windows
293 def callback():
295 def callback():
294 self.GotoPos(self.GetLength())
296 self.GotoPos(self.GetLength())
295 PrefilterFrontEnd.execute(self, python_string,
297 PrefilterFrontEnd.execute(self, python_string,
296 raw_string=raw_string)
298 raw_string=raw_string)
297 wx.CallAfter(callback)
299 wx.CallAfter(callback)
298
300
299 def save_output_hooks(self):
301 def save_output_hooks(self):
300 self.__old_raw_input = __builtin__.raw_input
302 self.__old_raw_input = __builtin__.raw_input
301 PrefilterFrontEnd.save_output_hooks(self)
303 PrefilterFrontEnd.save_output_hooks(self)
302
304
303 def capture_output(self):
305 def capture_output(self):
304 self.SetLexer(stc.STC_LEX_NULL)
306 self.SetLexer(stc.STC_LEX_NULL)
305 PrefilterFrontEnd.capture_output(self)
307 PrefilterFrontEnd.capture_output(self)
306 __builtin__.raw_input = self.raw_input
308 __builtin__.raw_input = self.raw_input
307
309
308
310
309 def release_output(self):
311 def release_output(self):
310 __builtin__.raw_input = self.__old_raw_input
312 __builtin__.raw_input = self.__old_raw_input
311 PrefilterFrontEnd.release_output(self)
313 PrefilterFrontEnd.release_output(self)
312 self.SetLexer(stc.STC_LEX_PYTHON)
314 self.SetLexer(stc.STC_LEX_PYTHON)
313
315
314
316
315 def after_execute(self):
317 def after_execute(self):
316 PrefilterFrontEnd.after_execute(self)
318 PrefilterFrontEnd.after_execute(self)
317 # Clear the wait cursor
319 # Clear the wait cursor
318 if hasattr(self, '_cursor'):
320 if hasattr(self, '_cursor'):
319 del self._cursor
321 del self._cursor
320 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
322 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
321
323
322
324
323 def show_traceback(self):
325 def show_traceback(self):
324 start_line = self.GetCurrentLine()
326 start_line = self.GetCurrentLine()
325 PrefilterFrontEnd.show_traceback(self)
327 PrefilterFrontEnd.show_traceback(self)
326 self.ProcessEvent(wx.PaintEvent())
328 self.ProcessEvent(wx.PaintEvent())
327 #wx.Yield()
329 #wx.Yield()
328 for i in range(start_line, self.GetCurrentLine()):
330 for i in range(start_line, self.GetCurrentLine()):
329 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
331 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
330
332
331
333
332 #--------------------------------------------------------------------------
334 #--------------------------------------------------------------------------
333 # FrontEndBase interface
335 # FrontEndBase interface
334 #--------------------------------------------------------------------------
336 #--------------------------------------------------------------------------
335
337
336 def render_error(self, e):
338 def render_error(self, e):
337 start_line = self.GetCurrentLine()
339 start_line = self.GetCurrentLine()
338 self.write('\n' + e + '\n')
340 self.write('\n' + e + '\n')
339 for i in range(start_line, self.GetCurrentLine()):
341 for i in range(start_line, self.GetCurrentLine()):
340 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
342 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
341
343
342
344
343 #--------------------------------------------------------------------------
345 #--------------------------------------------------------------------------
344 # ConsoleWidget interface
346 # ConsoleWidget interface
345 #--------------------------------------------------------------------------
347 #--------------------------------------------------------------------------
346
348
347 def new_prompt(self, prompt):
349 def new_prompt(self, prompt):
348 """ Display a new prompt, and start a new input buffer.
350 """ Display a new prompt, and start a new input buffer.
349 """
351 """
350 self._input_state = 'readline'
352 self._input_state = 'readline'
351 ConsoleWidget.new_prompt(self, prompt)
353 ConsoleWidget.new_prompt(self, prompt)
352 i = self.current_prompt_line
354 i = self.current_prompt_line
353 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
355 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
354
356
355
357
356 def write(self, *args, **kwargs):
358 def write(self, *args, **kwargs):
357 # Avoid multiple inheritence, be explicit about which
359 # Avoid multiple inheritence, be explicit about which
358 # parent method class gets called
360 # parent method class gets called
359 ConsoleWidget.write(self, *args, **kwargs)
361 ConsoleWidget.write(self, *args, **kwargs)
360
362
361
363
362 def _on_key_down(self, event, skip=True):
364 def _on_key_down(self, event, skip=True):
363 """ Capture the character events, let the parent
365 """ Capture the character events, let the parent
364 widget handle them, and put our logic afterward.
366 widget handle them, and put our logic afterward.
365 """
367 """
366 # FIXME: This method needs to be broken down in smaller ones.
368 # FIXME: This method needs to be broken down in smaller ones.
367 current_line_number = self.GetCurrentLine()
369 current_line_number = self.GetCurrentLine()
368 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
370 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
369 # Capture Control-C
371 # Capture Control-C
370 if self._input_state == 'subprocess':
372 if self._input_state == 'subprocess':
371 if self.debug:
373 if self.debug:
372 print >>sys.__stderr__, 'Killing running process'
374 print >>sys.__stderr__, 'Killing running process'
373 if hasattr(self._running_process, 'process'):
375 if hasattr(self._running_process, 'process'):
374 self._running_process.process.kill()
376 self._running_process.process.kill()
375 elif self._input_state == 'buffering':
377 elif self._input_state == 'buffering':
376 if self.debug:
378 if self.debug:
377 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
379 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
378 raise KeyboardInterrupt
380 raise KeyboardInterrupt
379 # XXX: We need to make really sure we
381 # XXX: We need to make really sure we
380 # get back to a prompt.
382 # get back to a prompt.
381 elif self._input_state == 'subprocess' and (
383 elif self._input_state == 'subprocess' and (
382 ( event.KeyCode<256 and
384 ( event.KeyCode<256 and
383 not event.ControlDown() )
385 not event.ControlDown() )
384 or
386 or
385 ( event.KeyCode in (ord('d'), ord('D')) and
387 ( event.KeyCode in (ord('d'), ord('D')) and
386 event.ControlDown())):
388 event.ControlDown())):
387 # We are running a process, we redirect keys.
389 # We are running a process, we redirect keys.
388 ConsoleWidget._on_key_down(self, event, skip=skip)
390 ConsoleWidget._on_key_down(self, event, skip=skip)
389 char = chr(event.KeyCode)
391 char = chr(event.KeyCode)
390 # Deal with some inconsistency in wx keycodes:
392 # Deal with some inconsistency in wx keycodes:
391 if char == '\r':
393 if char == '\r':
392 char = '\n'
394 char = '\n'
393 elif not event.ShiftDown():
395 elif not event.ShiftDown():
394 char = char.lower()
396 char = char.lower()
395 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
397 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
396 char = '\04'
398 char = '\04'
397 self._running_process.process.stdin.write(char)
399 self._running_process.process.stdin.write(char)
398 self._running_process.process.stdin.flush()
400 self._running_process.process.stdin.flush()
399 elif event.KeyCode in (ord('('), 57):
401 elif event.KeyCode in (ord('('), 57):
400 # Calltips
402 # Calltips
401 event.Skip()
403 event.Skip()
402 self.do_calltip()
404 self.do_calltip()
403 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
405 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
404 event.Skip()
406 event.Skip()
405 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
407 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
406 wx.CallAfter(self._popup_completion, create=True)
408 wx.CallAfter(self._popup_completion, create=True)
407 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
409 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
408 wx.WXK_RIGHT, wx.WXK_ESCAPE):
410 wx.WXK_RIGHT, wx.WXK_ESCAPE):
409 wx.CallAfter(self._popup_completion)
411 wx.CallAfter(self._popup_completion)
410 else:
412 else:
411 # Up history
413 # Up history
412 if event.KeyCode == wx.WXK_UP and (
414 if event.KeyCode == wx.WXK_UP and (
413 ( current_line_number == self.current_prompt_line and
415 ( current_line_number == self.current_prompt_line and
414 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
416 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
415 or event.ControlDown() ):
417 or event.ControlDown() ):
416 new_buffer = self.get_history_previous(
418 new_buffer = self.get_history_previous(
417 self.input_buffer)
419 self.input_buffer)
418 if new_buffer is not None:
420 if new_buffer is not None:
419 self.input_buffer = new_buffer
421 self.input_buffer = new_buffer
420 if self.GetCurrentLine() > self.current_prompt_line:
422 if self.GetCurrentLine() > self.current_prompt_line:
421 # Go to first line, for seemless history up.
423 # Go to first line, for seemless history up.
422 self.GotoPos(self.current_prompt_pos)
424 self.GotoPos(self.current_prompt_pos)
423 # Down history
425 # Down history
424 elif event.KeyCode == wx.WXK_DOWN and (
426 elif event.KeyCode == wx.WXK_DOWN and (
425 ( current_line_number == self.LineCount -1 and
427 ( current_line_number == self.LineCount -1 and
426 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
428 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
427 or event.ControlDown() ):
429 or event.ControlDown() ):
428 new_buffer = self.get_history_next()
430 new_buffer = self.get_history_next()
429 if new_buffer is not None:
431 if new_buffer is not None:
430 self.input_buffer = new_buffer
432 self.input_buffer = new_buffer
431 # Tab-completion
433 # Tab-completion
432 elif event.KeyCode == ord('\t'):
434 elif event.KeyCode == ord('\t'):
433 current_line, current_line_number = self.CurLine
435 current_line, current_line_number = self.CurLine
434 if not re.match(r'^\s*$', current_line):
436 if not re.match(r'^\s*$', current_line):
435 self.complete_current_input()
437 self.complete_current_input()
436 if self.AutoCompActive():
438 if self.AutoCompActive():
437 wx.CallAfter(self._popup_completion, create=True)
439 wx.CallAfter(self._popup_completion, create=True)
438 else:
440 else:
439 event.Skip()
441 event.Skip()
440 else:
442 else:
441 ConsoleWidget._on_key_down(self, event, skip=skip)
443 ConsoleWidget._on_key_down(self, event, skip=skip)
442
444
443
445
444 def _on_key_up(self, event, skip=True):
446 def _on_key_up(self, event, skip=True):
445 """ Called when any key is released.
447 """ Called when any key is released.
446 """
448 """
447 if event.KeyCode in (59, ord('.')):
449 if event.KeyCode in (59, ord('.')):
448 # Intercepting '.'
450 # Intercepting '.'
449 event.Skip()
451 event.Skip()
450 wx.CallAfter(self._popup_completion, create=True)
452 wx.CallAfter(self._popup_completion, create=True)
451 else:
453 else:
452 ConsoleWidget._on_key_up(self, event, skip=skip)
454 ConsoleWidget._on_key_up(self, event, skip=skip)
453
455
454
456
455 def _on_enter(self):
457 def _on_enter(self):
456 """ Called on return key down, in readline input_state.
458 """ Called on return key down, in readline input_state.
457 """
459 """
458 if self.debug:
460 if self.debug:
459 print >>sys.__stdout__, repr(self.input_buffer)
461 print >>sys.__stdout__, repr(self.input_buffer)
460 PrefilterFrontEnd._on_enter(self)
462 PrefilterFrontEnd._on_enter(self)
461
463
462
464
463 #--------------------------------------------------------------------------
465 #--------------------------------------------------------------------------
464 # EditWindow API
466 # EditWindow API
465 #--------------------------------------------------------------------------
467 #--------------------------------------------------------------------------
466
468
467 def OnUpdateUI(self, event):
469 def OnUpdateUI(self, event):
468 """ Override the OnUpdateUI of the EditWindow class, to prevent
470 """ Override the OnUpdateUI of the EditWindow class, to prevent
469 syntax highlighting both for faster redraw, and for more
471 syntax highlighting both for faster redraw, and for more
470 consistent look and feel.
472 consistent look and feel.
471 """
473 """
472 if not self._input_state == 'readline':
474 if not self._input_state == 'readline':
473 ConsoleWidget.OnUpdateUI(self, event)
475 ConsoleWidget.OnUpdateUI(self, event)
474
476
475 #--------------------------------------------------------------------------
477 #--------------------------------------------------------------------------
476 # Private API
478 # Private API
477 #--------------------------------------------------------------------------
479 #--------------------------------------------------------------------------
478
480
479 def _end_system_call(self):
480 """ Called at the end of a system call.
481 """
482 self._input_state = 'buffering'
483 self._running_process = False
484
485
486 def _buffer_flush(self, event):
481 def _buffer_flush(self, event):
487 """ Called by the timer to flush the write buffer.
482 """ Called by the timer to flush the write buffer.
488
483
489 This is always called in the mainloop, by the wx timer.
484 This is always called in the mainloop, by the wx timer.
490 """
485 """
491 self._out_buffer_lock.acquire()
486 self._out_buffer_lock.acquire()
492 _out_buffer = self._out_buffer
487 _out_buffer = self._out_buffer
493 self._out_buffer = []
488 self._out_buffer = []
494 self._out_buffer_lock.release()
489 self._out_buffer_lock.release()
495 self.write(''.join(_out_buffer), refresh=False)
490 self.write(''.join(_out_buffer), refresh=False)
496
491
497
492
498 def _colorize_input_buffer(self):
493 def _colorize_input_buffer(self):
499 """ Keep the input buffer lines at a bright color.
494 """ Keep the input buffer lines at a bright color.
500 """
495 """
501 if not self._input_state in ('readline', 'raw_input'):
496 if not self._input_state in ('readline', 'raw_input'):
502 return
497 return
503 end_line = self.GetCurrentLine()
498 end_line = self.GetCurrentLine()
504 if not sys.platform == 'win32':
499 if not sys.platform == 'win32':
505 end_line += 1
500 end_line += 1
506 for i in range(self.current_prompt_line, end_line):
501 for i in range(self.current_prompt_line, end_line):
507 if i in self._markers:
502 if i in self._markers:
508 self.MarkerDeleteHandle(self._markers[i])
503 self.MarkerDeleteHandle(self._markers[i])
509 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
504 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
510
505
511
506
512 if __name__ == '__main__':
507 if __name__ == '__main__':
513 class MainWindow(wx.Frame):
508 class MainWindow(wx.Frame):
514 def __init__(self, parent, id, title):
509 def __init__(self, parent, id, title):
515 wx.Frame.__init__(self, parent, id, title, size=(300,250))
510 wx.Frame.__init__(self, parent, id, title, size=(300,250))
516 self._sizer = wx.BoxSizer(wx.VERTICAL)
511 self._sizer = wx.BoxSizer(wx.VERTICAL)
517 self.shell = WxController(self)
512 self.shell = WxController(self)
518 self._sizer.Add(self.shell, 1, wx.EXPAND)
513 self._sizer.Add(self.shell, 1, wx.EXPAND)
519 self.SetSizer(self._sizer)
514 self.SetSizer(self._sizer)
520 self.SetAutoLayout(1)
515 self.SetAutoLayout(1)
521 self.Show(True)
516 self.Show(True)
522
517
523 app = wx.PySimpleApp()
518 app = wx.PySimpleApp()
524 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
519 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
525 frame.shell.SetFocus()
520 frame.shell.SetFocus()
526 frame.SetSize((680, 460))
521 frame.SetSize((680, 460))
527 self = frame.shell
522 self = frame.shell
528
523
529 app.MainLoop()
524 app.MainLoop()
530
525
General Comments 0
You need to be logged in to leave comments. Login now