wx_frontend.py
532 lines
| 19.6 KiB
| text/x-python
|
PythonLexer
Gael Varoquaux
|
r1349 | # encoding: utf-8 -*- test-case-name: | ||
# FIXME: Need to add tests. | ||||
gvaroquaux
|
r1460 | # ipython1.frontend.wx.tests.test_wx_frontend -*- | ||
Gael Varoquaux
|
r1349 | |||
"""Classes to provide a Wx frontend to the | ||||
Gael Varoquaux
|
r1360 | IPython.kernel.core.interpreter. | ||
Gael Varoquaux
|
r1349 | |||
gvaroquaux
|
r1455 | This class inherits from ConsoleWidget, that provides a console-like | ||
widget to provide a text-rendering widget suitable for a terminal. | ||||
Gael Varoquaux
|
r1349 | """ | ||
__docformat__ = "restructuredtext en" | ||||
#------------------------------------------------------------------------------- | ||||
# Copyright (C) 2008 The IPython Development Team | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#------------------------------------------------------------------------------- | ||||
#------------------------------------------------------------------------------- | ||||
# Imports | ||||
#------------------------------------------------------------------------------- | ||||
gvaroquaux
|
r1455 | # Major library imports | ||
Gael Varoquaux
|
r1371 | import re | ||
Gael Varoquaux
|
r1375 | import __builtin__ | ||
Gael Varoquaux
|
r1388 | from time import sleep | ||
Gael Varoquaux
|
r1437 | import sys | ||
from threading import Lock | ||||
Gael Varoquaux
|
r1458 | import string | ||
Gael Varoquaux
|
r1437 | |||
gvaroquaux
|
r1455 | import wx | ||
from wx import stc | ||||
# Ipython-specific imports. | ||||
from IPython.frontend._process import PipedProcess | ||||
from console_widget import ConsoleWidget | ||||
Gael Varoquaux
|
r1360 | from IPython.frontend.prefilterfrontend import PrefilterFrontEnd | ||
Gael Varoquaux
|
r1349 | |||
gvaroquaux
|
r1455 | #------------------------------------------------------------------------------- | ||
# Constants | ||||
#------------------------------------------------------------------------------- | ||||
Gael Varoquaux
|
r1472 | _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green | ||
_INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow | ||||
Gael Varoquaux
|
r1384 | _ERROR_BG = '#FFF1F1' # Nice red | ||
Gael Varoquaux
|
r1374 | |||
Gael Varoquaux
|
r1472 | _COMPLETE_BUFFER_MARKER = 31 | ||
Gael Varoquaux
|
r1384 | _ERROR_MARKER = 30 | ||
Gael Varoquaux
|
r1472 | _INPUT_MARKER = 29 | ||
Gael Varoquaux
|
r1374 | |||
Gael Varoquaux
|
r1458 | prompt_in1 = \ | ||
'\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02' | ||||
prompt_out = \ | ||||
'\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02' | ||||
Gael Varoquaux
|
r1349 | #------------------------------------------------------------------------------- | ||
# Classes to implement the Wx frontend | ||||
#------------------------------------------------------------------------------- | ||||
gvaroquaux
|
r1462 | class WxController(ConsoleWidget, PrefilterFrontEnd): | ||
gvaroquaux
|
r1455 | """Classes to provide a Wx frontend to the | ||
IPython.kernel.core.interpreter. | ||||
This class inherits from ConsoleWidget, that provides a console-like | ||||
widget to provide a text-rendering widget suitable for a terminal. | ||||
""" | ||||
Gael Varoquaux
|
r1349 | |||
Gael Varoquaux
|
r1458 | output_prompt_template = string.Template(prompt_out) | ||
input_prompt_template = string.Template(prompt_in1) | ||||
gvaroquaux
|
r1436 | |||
gvaroquaux
|
r1447 | # Print debug info on what is happening to the console. | ||
gvaroquaux
|
r1475 | debug = False | ||
Gael Varoquaux
|
r1437 | |||
gvaroquaux
|
r1447 | # The title of the terminal, as captured through the ANSI escape | ||
# sequences. | ||||
def _set_title(self, title): | ||||
return self.Parent.SetTitle(title) | ||||
def _get_title(self): | ||||
return self.Parent.GetTitle() | ||||
title = property(_get_title, _set_title) | ||||
gvaroquaux
|
r1462 | |||
# The buffer being edited. | ||||
Gael Varoquaux
|
r1473 | # We are duplicating the definition here because of multiple | ||
gvaroquaux
|
r1462 | # inheritence | ||
def _set_input_buffer(self, string): | ||||
Gael Varoquaux
|
r1473 | ConsoleWidget._set_input_buffer(self, string) | ||
self._colorize_input_buffer() | ||||
gvaroquaux
|
r1462 | |||
def _get_input_buffer(self): | ||||
""" Returns the text in current edit buffer. | ||||
""" | ||||
return ConsoleWidget._get_input_buffer(self) | ||||
input_buffer = property(_get_input_buffer, _set_input_buffer) | ||||
gvaroquaux
|
r1447 | #-------------------------------------------------------------------------- | ||
# Private Attributes | ||||
#-------------------------------------------------------------------------- | ||||
# A flag governing the behavior of the input. Can be: | ||||
# | ||||
# 'readline' for readline-like behavior with a prompt | ||||
# and an edit buffer. | ||||
gvaroquaux
|
r1480 | # 'raw_input' similar to readline, but triggered by a raw-input | ||
# call. Can be used by subclasses to act differently. | ||||
gvaroquaux
|
r1447 | # 'subprocess' for sending the raw input directly to a | ||
# subprocess. | ||||
# 'buffering' for buffering of the input, that will be used | ||||
# when the input state switches back to another state. | ||||
_input_state = 'readline' | ||||
Gael Varoquaux
|
r1437 | # Attribute to store reference to the pipes of a subprocess, if we | ||
# are running any. | ||||
gvaroquaux
|
r1447 | _running_process = False | ||
Gael Varoquaux
|
r1437 | |||
# A queue for writing fast streams to the screen without flooding the | ||||
# event loop | ||||
gvaroquaux
|
r1447 | _out_buffer = [] | ||
Gael Varoquaux
|
r1437 | |||
gvaroquaux
|
r1447 | # A lock to lock the _out_buffer to make sure we don't empty it | ||
Gael Varoquaux
|
r1437 | # while it is being swapped | ||
gvaroquaux
|
r1447 | _out_buffer_lock = Lock() | ||
Gael Varoquaux
|
r1437 | |||
Gael Varoquaux
|
r1624 | # The different line markers used to higlight the prompts. | ||
Gael Varoquaux
|
r1472 | _markers = dict() | ||
Gael Varoquaux
|
r1349 | #-------------------------------------------------------------------------- | ||
# Public API | ||||
#-------------------------------------------------------------------------- | ||||
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, | ||||
gvaroquaux
|
r1630 | size=wx.DefaultSize, | ||
style=wx.CLIP_CHILDREN|wx.WANTS_CHARS, | ||||
Gael Varoquaux
|
r1349 | *args, **kwds): | ||
""" Create Shell instance. | ||||
""" | ||||
ConsoleWidget.__init__(self, parent, id, pos, size, style) | ||||
gvaroquaux
|
r1484 | PrefilterFrontEnd.__init__(self, **kwds) | ||
gvaroquaux
|
r1654 | |||
# Stick in our own raw_input: | ||||
self.ipython0.raw_input = self.raw_input | ||||
Gael Varoquaux
|
r1349 | |||
Gael Varoquaux
|
r1472 | # Marker for complete buffer. | ||
self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND, | ||||
background=_COMPLETE_BUFFER_BG) | ||||
# Marker for current input buffer. | ||||
self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND, | ||||
background=_INPUT_BUFFER_BG) | ||||
Gael Varoquaux
|
r1384 | # Marker for tracebacks. | ||
self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND, | ||||
background=_ERROR_BG) | ||||
Gael Varoquaux
|
r1374 | |||
Gael Varoquaux
|
r1437 | # A time for flushing the write buffer | ||
BUFFER_FLUSH_TIMER_ID = 100 | ||||
self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID) | ||||
wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush) | ||||
Gael Varoquaux
|
r1374 | |||
gvaroquaux
|
r1484 | if 'debug' in kwds: | ||
self.debug = kwds['debug'] | ||||
kwds.pop('debug') | ||||
Gael Varoquaux
|
r1472 | # Inject self in namespace, for debug | ||
if self.debug: | ||||
self.shell.user_ns['self'] = self | ||||
gvaroquaux
|
r1654 | # Inject our own raw_input in namespace | ||
self.shell.user_ns['raw_input'] = self.raw_input | ||||
Gael Varoquaux
|
r1472 | |||
gvaroquaux
|
r1455 | |||
def raw_input(self, prompt): | ||||
""" A replacement from python's raw_input. | ||||
""" | ||||
self.new_prompt(prompt) | ||||
gvaroquaux
|
r1480 | self._input_state = 'raw_input' | ||
Gael Varoquaux
|
r1489 | if hasattr(self, '_cursor'): | ||
del self._cursor | ||||
gvaroquaux
|
r1484 | self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS)) | ||
gvaroquaux
|
r1455 | self.waiting = True | ||
self.__old_on_enter = self._on_enter | ||||
def my_on_enter(): | ||||
self.waiting = False | ||||
self._on_enter = my_on_enter | ||||
# XXX: Busy waiting, ugly. | ||||
while self.waiting: | ||||
wx.Yield() | ||||
sleep(0.1) | ||||
self._on_enter = self.__old_on_enter | ||||
self._input_state = 'buffering' | ||||
gvaroquaux
|
r1484 | self._cursor = wx.BusyCursor() | ||
gvaroquaux
|
r1462 | return self.input_buffer.rstrip('\n') | ||
gvaroquaux
|
r1455 | |||
def system_call(self, command_string): | ||||
self._input_state = 'subprocess' | ||||
self._running_process = PipedProcess(command_string, | ||||
out_callback=self.buffered_write, | ||||
end_callback = self._end_system_call) | ||||
self._running_process.start() | ||||
# XXX: another one of these polling loops to have a blocking | ||||
# call | ||||
wx.Yield() | ||||
while self._running_process: | ||||
wx.Yield() | ||||
sleep(0.1) | ||||
# Be sure to flush the buffer. | ||||
self._buffer_flush(event=None) | ||||
Gael Varoquaux
|
r1375 | def do_calltip(self): | ||
gvaroquaux
|
r1455 | """ Analyse current and displays useful calltip for it. | ||
""" | ||||
Gael Varoquaux
|
r1437 | if self.debug: | ||
print >>sys.__stdout__, "do_calltip" | ||||
Gael Varoquaux
|
r1382 | separators = re.compile('[\s\{\}\[\]\(\)\= ,:]') | ||
gvaroquaux
|
r1462 | symbol = self.input_buffer | ||
Gael Varoquaux
|
r1382 | symbol_string = separators.split(symbol)[-1] | ||
Gael Varoquaux
|
r1375 | base_symbol_string = symbol_string.split('.')[0] | ||
if base_symbol_string in self.shell.user_ns: | ||||
symbol = self.shell.user_ns[base_symbol_string] | ||||
elif base_symbol_string in self.shell.user_global_ns: | ||||
symbol = self.shell.user_global_ns[base_symbol_string] | ||||
elif base_symbol_string in __builtin__.__dict__: | ||||
symbol = __builtin__.__dict__[base_symbol_string] | ||||
else: | ||||
return False | ||||
Gael Varoquaux
|
r1380 | try: | ||
gvaroquaux
|
r1484 | for name in symbol_string.split('.')[1:] + ['__doc__']: | ||
symbol = getattr(symbol, name) | ||||
Gael Varoquaux
|
r1381 | self.AutoCompCancel() | ||
wx.Yield() | ||||
Gael Varoquaux
|
r1380 | self.CallTipShow(self.GetCurrentPos(), symbol) | ||
Gael Varoquaux
|
r1382 | except: | ||
Gael Varoquaux
|
r1380 | # The retrieve symbol couldn't be converted to a string | ||
pass | ||||
Gael Varoquaux
|
r1375 | |||
Gael Varoquaux
|
r1379 | |||
gvaroquaux
|
r1455 | def _popup_completion(self, create=False): | ||
Gael Varoquaux
|
r1380 | """ Updates the popup completion menu if it exists. If create is | ||
true, open the menu. | ||||
""" | ||||
Gael Varoquaux
|
r1437 | if self.debug: | ||
Gael Varoquaux
|
r1624 | print >>sys.__stdout__, "_popup_completion" | ||
gvaroquaux
|
r1462 | line = self.input_buffer | ||
Gael Varoquaux
|
r1624 | if (self.AutoCompActive() and line and not line[-1] == '.') \ | ||
Gael Varoquaux
|
r1380 | or create==True: | ||
suggestion, completions = self.complete(line) | ||||
offset=0 | ||||
if completions: | ||||
Gael Varoquaux
|
r1382 | complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]') | ||
Gael Varoquaux
|
r1380 | residual = complete_sep.split(line)[-1] | ||
offset = len(residual) | ||||
Gael Varoquaux
|
r1381 | self.pop_completion(completions, offset=offset) | ||
Gael Varoquaux
|
r1437 | if self.debug: | ||
print >>sys.__stdout__, completions | ||||
Gael Varoquaux
|
r1371 | |||
gvaroquaux
|
r1455 | def buffered_write(self, text): | ||
""" A write method for streams, that caches the stream in order | ||||
to avoid flooding the event loop. | ||||
gvaroquaux
|
r1447 | |||
gvaroquaux
|
r1455 | This can be called outside of the main loop, in separate | ||
threads. | ||||
Gael Varoquaux
|
r1388 | """ | ||
gvaroquaux
|
r1455 | self._out_buffer_lock.acquire() | ||
self._out_buffer.append(text) | ||||
self._out_buffer_lock.release() | ||||
if not self._buffer_flush_timer.IsRunning(): | ||||
gvaroquaux
|
r1503 | wx.CallAfter(self._buffer_flush_timer.Start, | ||
milliseconds=100, oneShot=True) | ||||
gvaroquaux
|
r1455 | |||
#-------------------------------------------------------------------------- | ||||
# LineFrontEnd interface | ||||
#-------------------------------------------------------------------------- | ||||
Gael Varoquaux
|
r1388 | |||
Gael Varoquaux
|
r1374 | def execute(self, python_string, raw_string=None): | ||
gvaroquaux
|
r1447 | self._input_state = 'buffering' | ||
Gael Varoquaux
|
r1380 | self.CallTipCancel() | ||
Gael Varoquaux
|
r1371 | self._cursor = wx.BusyCursor() | ||
Gael Varoquaux
|
r1374 | if raw_string is None: | ||
raw_string = python_string | ||||
end_line = self.current_prompt_line \ | ||||
+ max(1, len(raw_string.split('\n'))-1) | ||||
for i in range(self.current_prompt_line, end_line): | ||||
Gael Varoquaux
|
r1472 | if i in self._markers: | ||
self.MarkerDeleteHandle(self._markers[i]) | ||||
self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER) | ||||
gvaroquaux
|
r1649 | # Use a callafter to update the display robustly under windows | ||
def callback(): | ||||
self.GotoPos(self.GetLength()) | ||||
PrefilterFrontEnd.execute(self, python_string, | ||||
raw_string=raw_string) | ||||
wx.CallAfter(callback) | ||||
Gael Varoquaux
|
r1391 | |||
Gael Varoquaux
|
r1473 | def save_output_hooks(self): | ||
self.__old_raw_input = __builtin__.raw_input | ||||
PrefilterFrontEnd.save_output_hooks(self) | ||||
Gael Varoquaux
|
r1391 | |||
def capture_output(self): | ||||
gvaroquaux
|
r1503 | self.SetLexer(stc.STC_LEX_NULL) | ||
Gael Varoquaux
|
r1391 | PrefilterFrontEnd.capture_output(self) | ||
gvaroquaux
|
r1654 | __builtin__.raw_input = self.raw_input | ||
Gael Varoquaux
|
r1391 | |||
def release_output(self): | ||||
Gael Varoquaux
|
r1388 | __builtin__.raw_input = self.__old_raw_input | ||
Gael Varoquaux
|
r1472 | PrefilterFrontEnd.release_output(self) | ||
gvaroquaux
|
r1503 | self.SetLexer(stc.STC_LEX_PYTHON) | ||
Gael Varoquaux
|
r1374 | |||
Gael Varoquaux
|
r1371 | |||
def after_execute(self): | ||||
PrefilterFrontEnd.after_execute(self) | ||||
gvaroquaux
|
r1455 | # Clear the wait cursor | ||
Gael Varoquaux
|
r1373 | if hasattr(self, '_cursor'): | ||
del self._cursor | ||||
gvaroquaux
|
r1484 | self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR)) | ||
Gael Varoquaux
|
r1371 | |||
Gael Varoquaux
|
r1437 | |||
Gael Varoquaux
|
r1384 | def show_traceback(self): | ||
start_line = self.GetCurrentLine() | ||||
PrefilterFrontEnd.show_traceback(self) | ||||
gvaroquaux
|
r1651 | self.ProcessEvent(wx.PaintEvent()) | ||
#wx.Yield() | ||||
Gael Varoquaux
|
r1384 | for i in range(start_line, self.GetCurrentLine()): | ||
Gael Varoquaux
|
r1472 | self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER) | ||
Gael Varoquaux
|
r1384 | |||
gvaroquaux
|
r1455 | |||
Gael Varoquaux
|
r1349 | #-------------------------------------------------------------------------- | ||
gvaroquaux
|
r1639 | # FrontEndBase interface | ||
#-------------------------------------------------------------------------- | ||||
def render_error(self, e): | ||||
start_line = self.GetCurrentLine() | ||||
self.write('\n' + e + '\n') | ||||
for i in range(start_line, self.GetCurrentLine()): | ||||
self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER) | ||||
#-------------------------------------------------------------------------- | ||||
gvaroquaux
|
r1455 | # ConsoleWidget interface | ||
Gael Varoquaux
|
r1349 | #-------------------------------------------------------------------------- | ||
gvaroquaux
|
r1455 | |||
def new_prompt(self, prompt): | ||||
""" Display a new prompt, and start a new input buffer. | ||||
""" | ||||
self._input_state = 'readline' | ||||
ConsoleWidget.new_prompt(self, prompt) | ||||
Gael Varoquaux
|
r1473 | i = self.current_prompt_line | ||
self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER) | ||||
gvaroquaux
|
r1455 | |||
Gael Varoquaux
|
r1458 | def write(self, *args, **kwargs): | ||
# Avoid multiple inheritence, be explicit about which | ||||
# parent method class gets called | ||||
ConsoleWidget.write(self, *args, **kwargs) | ||||
Gael Varoquaux
|
r1349 | def _on_key_down(self, event, skip=True): | ||
""" Capture the character events, let the parent | ||||
widget handle them, and put our logic afterward. | ||||
""" | ||||
Gael Varoquaux
|
r1451 | # FIXME: This method needs to be broken down in smaller ones. | ||
Gael Varoquaux
|
r1349 | current_line_number = self.GetCurrentLine() | ||
gvaroquaux
|
r1447 | if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown(): | ||
# Capture Control-C | ||||
if self._input_state == 'subprocess': | ||||
if self.debug: | ||||
print >>sys.__stderr__, 'Killing running process' | ||||
gvaroquaux
|
r1648 | if hasattr(self._running_process, 'process'): | ||
self._running_process.process.kill() | ||||
gvaroquaux
|
r1447 | elif self._input_state == 'buffering': | ||
if self.debug: | ||||
gvaroquaux
|
r1455 | print >>sys.__stderr__, 'Raising KeyboardInterrupt' | ||
raise KeyboardInterrupt | ||||
gvaroquaux
|
r1447 | # XXX: We need to make really sure we | ||
# get back to a prompt. | ||||
Gael Varoquaux
|
r1450 | elif self._input_state == 'subprocess' and ( | ||
( event.KeyCode<256 and | ||||
gvaroquaux
|
r1460 | not event.ControlDown() ) | ||
Gael Varoquaux
|
r1450 | or | ||
( event.KeyCode in (ord('d'), ord('D')) and | ||||
event.ControlDown())): | ||||
gvaroquaux
|
r1447 | # We are running a process, we redirect keys. | ||
Gael Varoquaux
|
r1437 | ConsoleWidget._on_key_down(self, event, skip=skip) | ||
Gael Varoquaux
|
r1450 | char = chr(event.KeyCode) | ||
# Deal with some inconsistency in wx keycodes: | ||||
if char == '\r': | ||||
char = '\n' | ||||
elif not event.ShiftDown(): | ||||
char = char.lower() | ||||
if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')): | ||||
char = '\04' | ||||
self._running_process.process.stdin.write(char) | ||||
gvaroquaux
|
r1449 | self._running_process.process.stdin.flush() | ||
Gael Varoquaux
|
r1437 | elif event.KeyCode in (ord('('), 57): | ||
# Calltips | ||||
Gael Varoquaux
|
r1381 | event.Skip() | ||
self.do_calltip() | ||||
Gael Varoquaux
|
r1493 | elif self.AutoCompActive() and not event.KeyCode == ord('\t'): | ||
Gael Varoquaux
|
r1371 | event.Skip() | ||
Gael Varoquaux
|
r1373 | if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE): | ||
gvaroquaux
|
r1455 | wx.CallAfter(self._popup_completion, create=True) | ||
Gael Varoquaux
|
r1373 | elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT, | ||
Gael Varoquaux
|
r1493 | wx.WXK_RIGHT, wx.WXK_ESCAPE): | ||
gvaroquaux
|
r1455 | wx.CallAfter(self._popup_completion) | ||
Gael Varoquaux
|
r1349 | else: | ||
Gael Varoquaux
|
r1371 | # Up history | ||
if event.KeyCode == wx.WXK_UP and ( | ||||
( current_line_number == self.current_prompt_line and | ||||
event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) ) | ||||
or event.ControlDown() ): | ||||
new_buffer = self.get_history_previous( | ||||
gvaroquaux
|
r1462 | self.input_buffer) | ||
Gael Varoquaux
|
r1371 | if new_buffer is not None: | ||
gvaroquaux
|
r1462 | self.input_buffer = new_buffer | ||
Gael Varoquaux
|
r1371 | if self.GetCurrentLine() > self.current_prompt_line: | ||
# Go to first line, for seemless history up. | ||||
self.GotoPos(self.current_prompt_pos) | ||||
# Down history | ||||
elif event.KeyCode == wx.WXK_DOWN and ( | ||||
( current_line_number == self.LineCount -1 and | ||||
event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) ) | ||||
or event.ControlDown() ): | ||||
new_buffer = self.get_history_next() | ||||
if new_buffer is not None: | ||||
gvaroquaux
|
r1462 | self.input_buffer = new_buffer | ||
Gael Varoquaux
|
r1390 | # Tab-completion | ||
Gael Varoquaux
|
r1371 | elif event.KeyCode == ord('\t'): | ||
gvaroquaux
|
r1652 | current_line, current_line_number = self.CurLine | ||
if not re.match(r'^\s*$', current_line): | ||||
gvaroquaux
|
r1463 | self.complete_current_input() | ||
Gael Varoquaux
|
r1493 | if self.AutoCompActive(): | ||
wx.CallAfter(self._popup_completion, create=True) | ||||
Gael Varoquaux
|
r1371 | else: | ||
event.Skip() | ||||
else: | ||||
ConsoleWidget._on_key_down(self, event, skip=skip) | ||||
Gael Varoquaux
|
r1349 | |||
Gael Varoquaux
|
r1373 | def _on_key_up(self, event, skip=True): | ||
gvaroquaux
|
r1455 | """ Called when any key is released. | ||
""" | ||||
Gael Varoquaux
|
r1437 | if event.KeyCode in (59, ord('.')): | ||
Gael Varoquaux
|
r1373 | # Intercepting '.' | ||
event.Skip() | ||||
Gael Varoquaux
|
r1624 | wx.CallAfter(self._popup_completion, create=True) | ||
Gael Varoquaux
|
r1373 | else: | ||
ConsoleWidget._on_key_up(self, event, skip=skip) | ||||
gvaroquaux
|
r1447 | |||
gvaroquaux
|
r1436 | def _on_enter(self): | ||
gvaroquaux
|
r1455 | """ Called on return key down, in readline input_state. | ||
""" | ||||
gvaroquaux
|
r1436 | if self.debug: | ||
gvaroquaux
|
r1462 | print >>sys.__stdout__, repr(self.input_buffer) | ||
gvaroquaux
|
r1436 | PrefilterFrontEnd._on_enter(self) | ||
Gael Varoquaux
|
r1349 | |||
Gael Varoquaux
|
r1393 | |||
gvaroquaux
|
r1455 | #-------------------------------------------------------------------------- | ||
gvaroquaux
|
r1503 | # EditWindow API | ||
#-------------------------------------------------------------------------- | ||||
def OnUpdateUI(self, event): | ||||
""" Override the OnUpdateUI of the EditWindow class, to prevent | ||||
syntax highlighting both for faster redraw, and for more | ||||
consistent look and feel. | ||||
""" | ||||
if not self._input_state == 'readline': | ||||
ConsoleWidget.OnUpdateUI(self, event) | ||||
#-------------------------------------------------------------------------- | ||||
gvaroquaux
|
r1455 | # Private API | ||
#-------------------------------------------------------------------------- | ||||
gvaroquaux
|
r1447 | def _end_system_call(self): | ||
""" Called at the end of a system call. | ||||
""" | ||||
self._input_state = 'buffering' | ||||
self._running_process = False | ||||
Gael Varoquaux
|
r1393 | |||
gvaroquaux
|
r1447 | |||
def _buffer_flush(self, event): | ||||
""" Called by the timer to flush the write buffer. | ||||
This is always called in the mainloop, by the wx timer. | ||||
""" | ||||
self._out_buffer_lock.acquire() | ||||
_out_buffer = self._out_buffer | ||||
self._out_buffer = [] | ||||
self._out_buffer_lock.release() | ||||
gvaroquaux
|
r1449 | self.write(''.join(_out_buffer), refresh=False) | ||
gvaroquaux
|
r1503 | |||
Gael Varoquaux
|
r1393 | |||
Gael Varoquaux
|
r1473 | def _colorize_input_buffer(self): | ||
""" Keep the input buffer lines at a bright color. | ||||
""" | ||||
gvaroquaux
|
r1480 | if not self._input_state in ('readline', 'raw_input'): | ||
gvaroquaux
|
r1475 | return | ||
gvaroquaux
|
r1478 | end_line = self.GetCurrentLine() | ||
if not sys.platform == 'win32': | ||||
end_line += 1 | ||||
Gael Varoquaux
|
r1473 | for i in range(self.current_prompt_line, end_line): | ||
if i in self._markers: | ||||
self.MarkerDeleteHandle(self._markers[i]) | ||||
self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER) | ||||
Gael Varoquaux
|
r1393 | |||
Gael Varoquaux
|
r1349 | if __name__ == '__main__': | ||
class MainWindow(wx.Frame): | ||||
def __init__(self, parent, id, title): | ||||
wx.Frame.__init__(self, parent, id, title, size=(300,250)) | ||||
self._sizer = wx.BoxSizer(wx.VERTICAL) | ||||
Gael Varoquaux
|
r1389 | self.shell = WxController(self) | ||
Gael Varoquaux
|
r1349 | self._sizer.Add(self.shell, 1, wx.EXPAND) | ||
self.SetSizer(self._sizer) | ||||
self.SetAutoLayout(1) | ||||
self.Show(True) | ||||
app = wx.PySimpleApp() | ||||
frame = MainWindow(None, wx.ID_ANY, 'Ipython') | ||||
frame.shell.SetFocus() | ||||
Gael Varoquaux
|
r1380 | frame.SetSize((680, 460)) | ||
Gael Varoquaux
|
r1349 | self = frame.shell | ||
app.MainLoop() | ||||