##// END OF EJS Templates
Tweak magics.
Tweak magics.

File last commit:

r1424:eb7a7934
r1424:eb7a7934
Show More
console_widget.py
421 lines | 15.0 KiB | text/x-python | PythonLexer
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 # encoding: utf-8
"""
gvaroquaux
Docstrings tweaks.
r1296 A Wx widget to act as a console and input commands.
This widget deals with prompts and provides an edit buffer
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 restricted to after the last prompt.
"""
__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
import wx.stc as stc
Gael Varoquaux
Update wx frontend.
r1349 from wx.py import editwindow
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 import re
# FIXME: Need to provide an API for non user-generated display on the
# screen: this should not be editable by the user.
Gael Varoquaux
Update wx frontend.
r1349 if wx.Platform == '__WXMSW__':
_DEFAULT_SIZE = 80
else:
_DEFAULT_SIZE = 10
_DEFAULT_STYLE = {
'stdout' : 'fore:#0000FF',
'stderr' : 'fore:#007f00',
'trace' : 'fore:#FF0000',
'default' : 'size:%d' % _DEFAULT_SIZE,
'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
'bracebad' : 'fore:#000000,back:#FF0000,bold',
# properties for the various Python lexer styles
Gael Varoquaux
Nice background color for already entered code....
r1374 'comment' : 'fore:#007F00',
'number' : 'fore:#007F7F',
'string' : 'fore:#7F007F,italic',
'char' : 'fore:#7F007F,italic',
'keyword' : 'fore:#00007F,bold',
'triple' : 'fore:#7F0000',
'tripledouble' : 'fore:#7F0000',
'class' : 'fore:#0000FF,bold,underline',
'def' : 'fore:#007F7F,bold',
'operator' : 'bold'
Gael Varoquaux
Update wx frontend.
r1349 }
# new style numbers
_STDOUT_STYLE = 15
_STDERR_STYLE = 16
_TRACE_STYLE = 17
Gael Varoquaux
Improve tab-completion.
r1373 # system colors
Gael Varoquaux
Usability tweaks. Better auto tab completion. Terminal size more...
r1380 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
Gael Varoquaux
Improve tab-completion.
r1373
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 #-------------------------------------------------------------------------------
# The console widget class
#-------------------------------------------------------------------------------
Gael Varoquaux
Update wx frontend.
r1349 class ConsoleWidget(editwindow.EditWindow):
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 """ Specialized styled text control view for console-like workflow.
gvaroquaux
Docstrings tweaks.
r1296
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 This widget is mainly interested in dealing with the prompt and
keeping the cursor inside the editing line.
"""
Gael Varoquaux
Capture Xterm title sequence.
r1359 title = 'Console'
Gael Varoquaux
Update wx frontend.
r1349 style = _DEFAULT_STYLE.copy()
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 # Translation table from ANSI escape sequences to color. Override
# this to specify your colors.
ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
'0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
'0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
'0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
'1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
'1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
'1;34': [12, 'LIGHT BLUE'], '1;35':
[13, 'MEDIUM VIOLET RED'],
'1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
# The color of the carret (call _apply_style() after setting)
carret_color = 'BLACK'
#--------------------------------------------------------------------------
# Public API
#--------------------------------------------------------------------------
Gael Varoquaux
Update wx frontend.
r1349 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
Gael Varoquaux
Usability tweaks. Better auto tab completion. Terminal size more...
r1380 size=wx.DefaultSize, style=0, ):
Gael Varoquaux
Update wx frontend.
r1349 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
self.configure_scintilla()
# FIXME: we need to retrieve this from the interpreter.
self.prompt = \
'\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
self.new_prompt(self.prompt % 1)
self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
Gael Varoquaux
History is now working. + misc keybindings.
r1358 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
Gael Varoquaux
Update wx frontend.
r1349
def configure_scintilla(self):
# Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
# the widget
self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
# Also allow Ctrl Shift "=" for poor non US keyboard users.
self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
stc.STC_CMD_ZOOMIN)
Gael Varoquaux
History is now working. + misc keybindings.
r1358 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
# stc.STC_CMD_PAGEUP)
Gael Varoquaux
Update wx frontend.
r1349
Gael Varoquaux
History is now working. + misc keybindings.
r1358 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
# stc.STC_CMD_PAGEDOWN)
Gael Varoquaux
Update wx frontend.
r1349
# Keys: we need to clear some of the keys the that don't play
# well with a console.
self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
self.SetEOLMode(stc.STC_EOL_CRLF)
self.SetWrapMode(stc.STC_WRAP_CHAR)
self.SetWrapMode(stc.STC_WRAP_WORD)
self.SetBufferedDraw(True)
self.SetUseAntiAliasing(True)
self.SetLayoutCache(stc.STC_CACHE_PAGE)
self.SetUndoCollection(False)
self.SetUseTabs(True)
self.SetIndent(4)
self.SetTabWidth(4)
self.EnsureCaretVisible()
Gael Varoquaux
Usability tweaks. Better auto tab completion. Terminal size more...
r1380 # we don't want scintilla's autocompletion to choose
# automaticaly out of a single choice list, as we pop it up
# automaticaly
self.AutoCompSetChooseSingle(False)
Gael Varoquaux
Correct small history bug. Add busy cursor.
r1371 self.AutoCompSetMaxHeight(10)
Gael Varoquaux
Update wx frontend.
r1349 self.SetMargins(3, 3) #text is moved away from border with 3px
# Suppressing Scintilla margins
self.SetMarginWidth(0, 0)
self.SetMarginWidth(1, 0)
self.SetMarginWidth(2, 0)
self._apply_style()
Gael Varoquaux
Capture Xterm title sequence.
r1359
# Xterm escape sequences
Gael Varoquaux
Update wx frontend.
r1349 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
Gael Varoquaux
Capture Xterm title sequence.
r1359 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
Gael Varoquaux
Update wx frontend.
r1349
#self.SetEdgeMode(stc.STC_EDGE_LINE)
#self.SetEdgeColumn(80)
# styles
p = self.style
self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
self.StyleClearAll()
self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
self.StyleSetSpec(_TRACE_STYLE, p['trace'])
self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
self.StyleSetSpec(stc.STC_P_STRING, p['string'])
self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
Gael Varoquaux
Nice background color for already entered code....
r1374 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
Gael Varoquaux
Update wx frontend.
r1349 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 def write(self, text):
""" Write given text to buffer, while translating the ansi escape
sequences.
"""
Gael Varoquaux
Clean up the title-setting code.
r1393 # XXX: do not put print statements in this method, the print
# statements will call this method, and you will end up with
# an infinit loop
Gael Varoquaux
Capture Xterm title sequence.
r1359 title = self.title_pat.split(text)
Gael Varoquaux
Clean up the title-setting code.
r1393 if len(title)>1:
self.title = title[-2]
Gael Varoquaux
Capture Xterm title sequence.
r1359
text = self.title_pat.sub('', text)
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 segments = self.color_pat.split(text)
segment = segments.pop(0)
self.StartStyling(self.GetLength(), 0xFF)
self.AppendText(segment)
Gael Varoquaux
Capture Xterm title sequence.
r1359
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 if segments:
Gael Varoquaux
Correct styling of ANSI patterns.
r1386 for ansi_tag, text in zip(segments[::2], segments[1::2]):
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 self.StartStyling(self.GetLength(), 0xFF)
Gael Varoquaux
Correct styling of ANSI patterns.
r1386 self.AppendText(text)
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295
Gael Varoquaux
Tweak magics.
r1424 if ansi_tag not in self.ANSI_STYLES:
Gael Varoquaux
Correct styling of ANSI patterns.
r1386 style = 0
else:
style = self.ANSI_STYLES[ansi_tag][0]
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295
Gael Varoquaux
Correct styling of ANSI patterns.
r1386 self.SetStyling(len(text), style)
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295
self.GotoPos(self.GetLength())
Gael Varoquaux
Synchronous stdout/stderr output.
r1383 wx.Yield()
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295
Gael Varoquaux
Deal with raw_input.
r1388
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 def new_prompt(self, prompt):
""" Prints a prompt at start of line, and move the start of the
current block there.
The prompt can be give with ascii escape sequences.
"""
self.write(prompt)
# now we update our cursor giving end of prompt
self.current_prompt_pos = self.GetLength()
self.current_prompt_line = self.GetCurrentLine()
Gael Varoquaux
Rework multiline input
r1361 wx.Yield()
self.EnsureCaretVisible()
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295
def replace_current_edit_buffer(self, text):
""" Replace currently entered command line with given text.
"""
self.SetSelection(self.current_prompt_pos, self.GetLength())
self.ReplaceSelection(text)
self.GotoPos(self.GetLength())
def get_current_edit_buffer(self):
""" Returns the text in current edit buffer.
"""
return self.GetTextRange(self.current_prompt_pos,
self.GetLength())
#--------------------------------------------------------------------------
# Private API
#--------------------------------------------------------------------------
def _apply_style(self):
""" Applies the colors for the different text elements and the
carret.
"""
self.SetCaretForeground(self.carret_color)
Gael Varoquaux
Improve tab-completion.
r1373 #self.StyleClearAll()
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
"fore:#FF0000,back:#0000FF,bold")
self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
"fore:#000000,back:#FF0000,bold")
for style in self.ANSI_STYLES.values():
self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
Gael Varoquaux
Usability tweaks. Better auto tab completion. Terminal size more...
r1380 def write_completion(self, possibilities):
# FIXME: This is non Wx specific and needs to be moved into
# the base class.
current_buffer = self.get_current_edit_buffer()
self.write('\n')
max_len = len(max(possibilities, key=len)) + 1
#now we check how much symbol we can put on a line...
chars_per_line = self.GetSize()[0]/self.GetCharWidth()
symbols_per_line = max(1, chars_per_line/max_len)
pos = 1
buf = []
for symbol in possibilities:
if pos < symbols_per_line:
buf.append(symbol.ljust(max_len))
pos += 1
else:
buf.append(symbol.rstrip() +'\n')
pos = 1
self.write(''.join(buf))
self.new_prompt(self.prompt % (self.last_result['number'] + 1))
self.replace_current_edit_buffer(current_buffer)
def pop_completion(self, possibilities, offset=0):
""" Pops up an autocompletion menu. Offset is the offset
in characters of the position at which the menu should
appear, relativ to the cursor.
"""
self.AutoCompSetIgnoreCase(False)
self.AutoCompSetAutoHide(False)
self.AutoCompSetMaxHeight(len(possibilities))
self.AutoCompShow(offset, " ".join(possibilities))
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295
Gael Varoquaux
Update wx frontend.
r1349
def scroll_to_bottom(self):
maxrange = self.GetScrollRange(wx.VERTICAL)
self.ScrollLines(maxrange)
Gael Varoquaux
Rework multiline input
r1361
Gael Varoquaux
Traceback capture now working.
r1360 def _on_enter(self):
Gael Varoquaux
History is now working. + misc keybindings.
r1358 """ Called when the return key is hit.
"""
pass
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295
Gael Varoquaux
Update wx frontend.
r1349 def _on_key_down(self, event, skip=True):
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 """ Key press callback used for correcting behavior for
console-like interfaces: the cursor is constraint to be after
the last prompt.
Return True if event as been catched.
"""
Gael Varoquaux
Added "ctr-K" to delete current buffer.
r1363 catched = True
Gael Varoquaux
History is now working. + misc keybindings.
r1358 # Intercept some specific keys.
Gael Varoquaux
Update wx frontend.
r1349 if event.KeyCode == ord('L') and event.ControlDown() :
self.scroll_to_bottom()
Gael Varoquaux
Correct small history bug. Add busy cursor.
r1371 elif event.KeyCode == ord('K') and event.ControlDown() :
Gael Varoquaux
Added "ctr-K" to delete current buffer.
r1363 self.replace_current_edit_buffer('')
Gael Varoquaux
History is now working. + misc keybindings.
r1358 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
self.ScrollPages(-1)
elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
self.ScrollPages(1)
Gael Varoquaux
Usability tweaks. Better auto tab completion. Terminal size more...
r1380 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
self.ScrollLines(-1)
elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
Gael Varoquaux
Minor bug.
r1389 self.ScrollLines(1)
Gael Varoquaux
Added "ctr-K" to delete current buffer.
r1363 else:
catched = False
Gael Varoquaux
Update wx frontend.
r1349
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 if self.AutoCompActive():
event.Skip()
else:
Gael Varoquaux
History is now working. + misc keybindings.
r1358 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
catched = True
Gael Varoquaux
Synchronous stdout/stderr output.
r1383 self.CallTipCancel()
Gael Varoquaux
Make code execution more robust.
r1362 self.write('\n')
Gael Varoquaux
History is now working. + misc keybindings.
r1358 self._on_enter()
elif event.KeyCode == wx.WXK_HOME:
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
self.GotoPos(self.current_prompt_pos)
catched = True
Gael Varoquaux
Update wx frontend.
r1349 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
Gael Varoquaux
History is now working. + misc keybindings.
r1358 # FIXME: This behavior is not ideal: if the selection
# is already started, it will jump.
self.SetSelectionStart(self.current_prompt_pos)
self.SetSelectionEnd(self.GetCurrentPos())
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 catched = True
Gael Varoquaux
Update wx frontend.
r1349 elif event.KeyCode == wx.WXK_UP:
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 if self.GetCurrentLine() > self.current_prompt_line:
if self.GetCurrentLine() == self.current_prompt_line + 1 \
and self.GetColumn(self.GetCurrentPos()) < \
self.GetColumn(self.current_prompt_pos):
self.GotoPos(self.current_prompt_pos)
else:
event.Skip()
catched = True
Gael Varoquaux
Update wx frontend.
r1349 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295 if self.GetCurrentPos() > self.current_prompt_pos:
event.Skip()
catched = True
if skip and not catched:
event.Skip()
return catched
Gael Varoquaux
History is now working. + misc keybindings.
r1358 def _on_key_up(self, event, skip=True):
""" If cursor is outside the editing region, put it back.
"""
event.Skip()
if self.GetCurrentPos() < self.current_prompt_pos:
self.GotoPos(self.current_prompt_pos)
gvaroquaux
Start of Wx frontend. Console widget impemented....
r1295
if __name__ == '__main__':
# Some simple code to test the console widget.
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.console_widget = ConsoleWidget(self)
self._sizer.Add(self.console_widget, 1, wx.EXPAND)
self.SetSizer(self._sizer)
self.SetAutoLayout(1)
self.Show(True)
app = wx.PySimpleApp()
w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
w.SetSize((780, 460))
w.Show()
app.MainLoop()