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