|
|
# 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()
|
|
|
|
|
|
|