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