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