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