wx_frontend.py
207 lines
| 7.1 KiB
| text/x-python
|
PythonLexer
Gael Varoquaux
|
r1349 | # encoding: utf-8 -*- test-case-name: | ||
# FIXME: Need to add tests. | ||||
# ipython1.frontend.cocoa.tests.test_cocoa_frontend -*- | ||||
"""Classes to provide a Wx frontend to the | ||||
ipython1.kernel.engineservice.EngineService. | ||||
""" | ||||
__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 | ||||
#------------------------------------------------------------------------------- | ||||
import wx | ||||
from console_widget import ConsoleWidget | ||||
import re | ||||
import IPython | ||||
from IPython.kernel.engineservice import EngineService | ||||
from IPython.frontend.frontendbase import FrontEndBase | ||||
#------------------------------------------------------------------------------- | ||||
# Classes to implement the Wx frontend | ||||
#------------------------------------------------------------------------------- | ||||
class IPythonWxController(FrontEndBase, ConsoleWidget): | ||||
output_prompt = \ | ||||
'\n\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02' | ||||
# Are we entering multi line input? | ||||
multi_line_input = False | ||||
# The added tab stop to the string. It may, for instance, come from | ||||
# copy and pasting something with tabs. | ||||
tab_stop = 0 | ||||
# FIXME: We still have to deal with this. | ||||
#-------------------------------------------------------------------------- | ||||
# Public API | ||||
#-------------------------------------------------------------------------- | ||||
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, | ||||
size=wx.DefaultSize, style=wx.CLIP_CHILDREN, | ||||
*args, **kwds): | ||||
""" Create Shell instance. | ||||
""" | ||||
ConsoleWidget.__init__(self, parent, id, pos, size, style) | ||||
FrontEndBase.__init__(self, engine=EngineService(), | ||||
) | ||||
# FIXME: Something is wrong with the history, I instanciate it | ||||
# with an empty cache, but this is not the way to do. | ||||
self.lines = {} | ||||
# Start the IPython engine | ||||
self.engine.startService() | ||||
# Capture Character keys | ||||
self.Bind(wx.EVT_KEY_DOWN, self._on_key_down) | ||||
#FIXME: print banner. | ||||
banner = """IPython1 %s -- An enhanced Interactive Python.""" \ | ||||
% IPython.__version__ | ||||
def appWillTerminate_(self, notification): | ||||
"""appWillTerminate""" | ||||
self.engine.stopService() | ||||
def complete(self, token): | ||||
"""Complete token in engine's user_ns | ||||
Parameters | ||||
---------- | ||||
token : string | ||||
Result | ||||
------ | ||||
Deferred result of | ||||
IPython.kernel.engineservice.IEngineBase.complete | ||||
""" | ||||
return self.engine.complete(token) | ||||
def render_result(self, result): | ||||
if 'stdout' in result and result['stdout']: | ||||
self.write('\n' + result['stdout']) | ||||
if 'display' in result and result['display']: | ||||
self.write("%s%s\n" % ( | ||||
self.output_prompt % result['number'], | ||||
result['display']['pprint'] | ||||
) ) | ||||
def render_error(self, failure): | ||||
self.insert_text('\n\n'+str(failure)+'\n\n') | ||||
return failure | ||||
#-------------------------------------------------------------------------- | ||||
# Private API | ||||
#-------------------------------------------------------------------------- | ||||
def _on_key_down(self, event, skip=True): | ||||
""" Capture the character events, let the parent | ||||
widget handle them, and put our logic afterward. | ||||
""" | ||||
current_line_number = self.GetCurrentLine() | ||||
# Capture enter | ||||
if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \ | ||||
event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN): | ||||
self._on_enter() | ||||
# Up history | ||||
elif 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( | ||||
self.get_current_edit_buffer()) | ||||
if new_buffer is not None: | ||||
self.replace_current_edit_buffer(new_buffer) | ||||
# 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: | ||||
self.replace_current_edit_buffer(new_buffer) | ||||
else: | ||||
ConsoleWidget._on_key_down(self, event, skip=True) | ||||
def _on_enter(self): | ||||
""" Called when the return key is pressed in a line editing | ||||
buffer. | ||||
""" | ||||
current_buffer = self.get_current_edit_buffer() | ||||
current_buffer = current_buffer.replace('\r\n', '\n') | ||||
current_buffer = current_buffer.replace('\t', 4*' ') | ||||
cleaned_buffer = '\n'.join(l.rstrip() | ||||
for l in current_buffer.split('\n')) | ||||
if ( not self.multi_line_input | ||||
or re.findall(r"\n[\t ]*$", cleaned_buffer)): | ||||
if self.is_complete(cleaned_buffer): | ||||
self._add_history(None, cleaned_buffer.rstrip()) | ||||
result = self.engine.shell.execute(cleaned_buffer) | ||||
self.render_result(result) | ||||
self.new_prompt(self.prompt % (result['number'] + 1)) | ||||
self.multi_line_input = False | ||||
else: | ||||
if self.multi_line_input: | ||||
self.write('\n' + self._get_indent_string(current_buffer)) | ||||
else: | ||||
self.multi_line_input = True | ||||
self.write('\n\t') | ||||
else: | ||||
self.write('\n'+self._get_indent_string(current_buffer)) | ||||
def _get_indent_string(self, string): | ||||
string = string.split('\n')[-1] | ||||
indent_chars = len(string) - len(string.lstrip()) | ||||
indent_string = '\t'*(indent_chars // 4) + \ | ||||
' '*(indent_chars % 4) | ||||
return indent_string | ||||
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) | ||||
self.shell = IPythonWxController(self) | ||||
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() | ||||
frame.SetSize((660, 460)) | ||||
self = frame.shell | ||||
app.MainLoop() | ||||