##// END OF EJS Templates
Added a warning when twisted is not detected
Added a warning when twisted is not detected

File last commit:

r1622:aff2a1ef
r1810:599d41d9
Show More
ipython_view.py
913 lines | 33.9 KiB | text/x-python | PythonLexer
ville
initialization (no svn history)
r988 #!/usr/bin/python
# -*- coding: iso-8859-15 -*-
'''
Gael Varoquaux
Some cosmetic changes.
r1096 Provides IPython WX console widgets.
ville
initialization (no svn history)
r988
@author: Laurent Dufrechou
laurent.dufrechou _at_ gmail.com
This WX widget is based on the original work of Eitan Isaacson
that provided the console for the GTK toolkit.
Original work from:
@author: Eitan Isaacson
@organization: IBM Corporation
@copyright: Copyright (c) 2007 IBM Corporation
@license: BSD
All rights reserved. This program and the accompanying materials are made
available under the terms of the BSD which accompanies this distribution, and
is available at U{http://www.opensource.org/licenses/bsd-license.php}
'''
ldufrechou
Added an option to enable/disable threading as suggested by Ville.
r1621 __version__ = 0.9
ville
initialization (no svn history)
r988 __author__ = "Laurent Dufrechou"
__email__ = "laurent.dufrechou _at_ gmail.com"
__license__ = "BSD"
import wx
import wx.stc as stc
import re
from StringIO import StringIO
Cody Precord
Better encoding handling....
r1538 import sys
import codecs
import locale
for enc in (locale.getpreferredencoding(),
sys.getfilesystemencoding(),
sys.getdefaultencoding()):
try:
codecs.lookup(enc)
ENCODING = enc
break
except LookupError:
pass
else:
ENCODING = 'utf-8'
ldufrechou
Cleanup of the old stateMachine code...
r1101 from ipshell_nonblocking import NonBlockingIPShell
ldufrechou
Cleaned up version with initial WX callback (callafter) support
r1091
Gael Varoquaux
Some cosmetic changes.
r1096 class WxNonBlockingIPShell(NonBlockingIPShell):
ville
initialization (no svn history)
r988 '''
Gael Varoquaux
Some cosmetic changes.
r1096 An NonBlockingIPShell Thread that is WX dependent.
ville
initialization (no svn history)
r988 '''
Gael Varoquaux
Changed event into callbacks for the wxIpython
r1100 def __init__(self, parent,
ldufrechou
Cleaned up version with initial WX callback (callafter) support
r1091 argv=[],user_ns={},user_global_ns=None,
ville
initialization (no svn history)
r988 cin=None, cout=None, cerr=None,
ldufrechou
-revised code to correct ehaviour under linux....
r1153 ask_exit_handler=None):
ville
initialization (no svn history)
r988
Laurent Dufréchou
cleanup and linted a bit :)
r1221 NonBlockingIPShell.__init__(self, argv, user_ns, user_global_ns,
ldufrechou
cleaning...
r1099 cin, cout, cerr,
ldufrechou
-revised code to correct ehaviour under linux....
r1153 ask_exit_handler)
ville
initialization (no svn history)
r988
Gael Varoquaux
Changed event into callbacks for the wxIpython
r1100 self.parent = parent
ville
initialization (no svn history)
r988
Gael Varoquaux
Changed event into callbacks for the wxIpython
r1100 self.ask_exit_callback = ask_exit_handler
ldufrechou
Cleanup of the old stateMachine code...
r1101 self._IP.exit = self._askExit
Gael Varoquaux
Changed event into callbacks for the wxIpython
r1100
Laurent Dufréchou
cleanup and linted a bit :)
r1221 def addGUIShortcut(self, text, func):
Gael Varoquaux
Changed event into callbacks for the wxIpython
r1100 wx.CallAfter(self.parent.add_button_handler,
button_info={ 'text':text,
'func':self.parent.doExecuteLine(func)})
ldufrechou
Merged the work with gael
r1109
Gael Varoquaux
Merged the cosmetic changes with the thread refactoring.
r1097 def _askExit(self):
Gael Varoquaux
Changed event into callbacks for the wxIpython
r1100 wx.CallAfter(self.ask_exit_callback, ())
ville
initialization (no svn history)
r988
ldufrechou
Cleaned up version with initial WX callback (callafter) support
r1091 def _afterExecute(self):
Gael Varoquaux
Changed event into callbacks for the wxIpython
r1100 wx.CallAfter(self.parent.evtStateExecuteDone, ())
ville
initialization (no svn history)
r988
class WxConsoleView(stc.StyledTextCtrl):
'''
Specialized styled text control view for console-like workflow.
Gael Varoquaux
Add a very simple test case to test only the widget for the wxIpython...
r1107 We use here a scintilla frontend thus it can be reused in any GUI that
supports scintilla with less work.
ville
initialization (no svn history)
r988
Gael Varoquaux
Add a very simple test case to test only the widget for the wxIpython...
r1107 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.
(with Black background)
ville
initialization (no svn history)
r988 @type ANSI_COLORS_BLACK: dictionary
Gael Varoquaux
Add a very simple test case to test only the widget for the wxIpython...
r1107 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.
(with White background)
ville
initialization (no svn history)
r988 @type ANSI_COLORS_WHITE: dictionary
@ivar color_pat: Regex of terminal color pattern
@type color_pat: _sre.SRE_Pattern
'''
Laurent Dufréchou
cleanup and linted a bit :)
r1221 ANSI_STYLES_BLACK = {'0;30': [0, 'WHITE'], '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']}
ANSI_STYLES_WHITE = {'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']}
def __init__(self, parent, prompt, intro="", background_color="BLACK",
Gael Varoquaux
Cleaning up lignes longer than 80 characters.
r1106 pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
ldufrechou
[gui/wx/ipython] ...
r1159 style=0, autocomplete_mode = 'IPYTHON'):
ville
initialization (no svn history)
r988 '''
Initialize console view.
@param parent: Parent widget
@param prompt: User specified prompt
@type intro: string
@param intro: User specified startup introduction string
@type intro: string
@param background_color: Can be BLACK or WHITE
@type background_color: string
@param other: init param of styledTextControl (can be used as-is)
ldufrechou
[gui/wx/ipython] ...
r1159 @param autocomplete_mode: Can be 'IPYTHON' or 'STC'
'IPYTHON' show autocompletion the ipython way
'STC" show it scintilla text control way
ville
initialization (no svn history)
r988 '''
stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
Gael Varoquaux
Cleaning up lignes longer than 80 characters.
r1106 ####### Scintilla configuration ###################################
ville
initialization (no svn history)
r988
Gael Varoquaux
Cleaning up lignes longer than 80 characters.
r1106 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside
# the widget
ville
initialization (no svn history)
r988 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
ldufrechou
[/gui/wx] Added a checkbox to switch background color (white/black)
r1162 #We draw a line at position 80
self.SetEdgeMode(stc.STC_EDGE_LINE)
self.SetEdgeColumn(80)
self.SetEdgeColour(wx.LIGHT_GREY)
#self.SetViewWhiteSpace(True)
#self.SetViewEOL(True)
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)
ldufrechou
[/gui/wx/] cody precord : small patch for some changes and improvements to the ipython_view...
r1163 self.SetUndoCollection(False)
Laurent Dufréchou
cleanup and linted a bit :)
r1221 self.SetUseTabs(True)
self.SetIndent(4)
self.SetTabWidth(4)
ldufrechou
[/gui/wx] Added a checkbox to switch background color (white/black)
r1162
self.EnsureCaretVisible()
Laurent Dufréchou
cleanup and linted a bit :)
r1221 self.SetMargins(3, 3) #text is moved away from border with 3px
ldufrechou
[/gui/wx] Added a checkbox to switch background color (white/black)
r1162 # Suppressing Scintilla margins
Laurent Dufréchou
cleanup and linted a bit :)
r1221 self.SetMarginWidth(0, 0)
self.SetMarginWidth(1, 0)
self.SetMarginWidth(2, 0)
ldufrechou
[/gui/wx] Added a checkbox to switch background color (white/black)
r1162
self.background_color = background_color
self.buildStyles()
self.indent = 0
self.prompt_count = 0
self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
self.write(intro)
self.setPrompt(prompt)
self.showPrompt()
self.autocomplete_mode = autocomplete_mode
self.Bind(wx.EVT_KEY_DOWN, self._onKeypress)
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168
ldufrechou
[/gui/wx] Added a checkbox to switch background color (white/black)
r1162 def buildStyles(self):
ville
initialization (no svn history)
r988 #we define platform specific fonts
if wx.Platform == '__WXMSW__':
Laurent Dufréchou
cleanup and linted a bit :)
r1221 faces = { 'times': 'Times New Roman',
'mono' : 'Courier New',
'helv' : 'Arial',
'other': 'Comic Sans MS',
'size' : 10,
'size2': 8,
}
ville
initialization (no svn history)
r988 elif wx.Platform == '__WXMAC__':
faces = { 'times': 'Times New Roman',
'mono' : 'Monaco',
'helv' : 'Arial',
'other': 'Comic Sans MS',
'size' : 10,
'size2': 8,
}
else:
faces = { 'times': 'Times',
'mono' : 'Courier',
'helv' : 'Helvetica',
'other': 'new century schoolbook',
'size' : 10,
'size2': 8,
}
# make some styles
ldufrechou
[/gui/wx] Added a checkbox to switch background color (white/black)
r1162 if self.background_color != "BLACK":
ville
initialization (no svn history)
r988 self.background_color = "WHITE"
self.SetCaretForeground("BLACK")
self.ANSI_STYLES = self.ANSI_STYLES_WHITE
else:
self.SetCaretForeground("WHITE")
self.ANSI_STYLES = self.ANSI_STYLES_BLACK
Gael Varoquaux
Cleaning up lignes longer than 80 characters.
r1106 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
"fore:%s,back:%s,size:%d,face:%s"
% (self.ANSI_STYLES['0;30'][1],
self.background_color,
faces['size'], faces['mono']))
ville
initialization (no svn history)
r988 self.StyleClearAll()
Gael Varoquaux
Cleaning up lignes longer than 80 characters.
r1106 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
"fore:#FF0000,back:#0000FF,bold")
self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
"fore:#000000,back:#FF0000,bold")
ldufrechou
[/gui/wx] Added a checkbox to switch background color (white/black)
r1162
ville
initialization (no svn history)
r988 for style in self.ANSI_STYLES.values():
self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
#######################################################################
Laurent Dufréchou
cleanup and linted a bit :)
r1221 def setBackgroundColor(self, color):
ldufrechou
[/gui/wx] Added a checkbox to switch background color (white/black)
r1162 self.background_color = color
self.buildStyles()
ldufrechou
[gui/wx/ipython] ...
r1159
Laurent Dufréchou
cleanup and linted a bit :)
r1221 def getBackgroundColor(self, color):
ldufrechou
[/gui/wx] Added a checkbox to switch background color (white/black)
r1162 return self.background_color
ville
initialization (no svn history)
r988
ldufrechou
Little bug correcton from last commit...
r1169 def asyncWrite(self, text):
ldufrechou
-revised code to correct ehaviour under linux....
r1153 '''
Write given text to buffer in an asynchroneous way.
It is used from another thread to be able to acces the GUI.
@param text: Text to append
@type text: string
'''
try:
Laurent Dufréchou
cleanup and linted a bit :)
r1221 wx.MutexGuiEnter()
ldufrechou
-revised code to correct ehaviour under linux....
r1153
Laurent Dufréchou
cleanup and linted a bit :)
r1221 #be sure not to be interrutpted before the MutexGuiLeave!
self.write(text)
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168
ldufrechou
-revised code to correct ehaviour under linux....
r1153 except KeyboardInterrupt:
Laurent Dufréchou
cleanup and linted a bit :)
r1221 wx.MutexGuiLeave()
raise KeyboardInterrupt
ldufrechou
-revised code to correct ehaviour under linux....
r1153 wx.MutexGuiLeave()
ville
initialization (no svn history)
r988 def write(self, text):
'''
Write given text to buffer.
@param text: Text to append.
@type text: string
'''
segments = self.color_pat.split(text)
segment = segments.pop(0)
Laurent Dufréchou
cleanup and linted a bit :)
r1221 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
ville
initialization (no svn history)
r988 self.AppendText(segment)
if segments:
ansi_tags = self.color_pat.findall(text)
for tag in ansi_tags:
i = segments.index(tag)
Laurent Dufréchou
cleanup and linted a bit :)
r1221 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
ville
initialization (no svn history)
r988 self.AppendText(segments[i+1])
if tag != '0':
Laurent Dufréchou
cleanup and linted a bit :)
r1221 self.SetStyling(len(segments[i+1]), self.ANSI_STYLES[tag][0])
ville
initialization (no svn history)
r988
segments.pop(i)
self.moveCursor(self.getCurrentLineEnd())
def getPromptLen(self):
'''
Return the length of current prompt
'''
return len(str(self.prompt_count)) + 7
Laurent Dufréchou
cleanup and linted a bit :)
r1221 def setPrompt(self, prompt):
ville
initialization (no svn history)
r988 self.prompt = prompt
Laurent Dufréchou
cleanup and linted a bit :)
r1221 def setIndentation(self, indentation):
ville
initialization (no svn history)
r988 self.indent = indentation
Laurent Dufréchou
cleanup and linted a bit :)
r1221 def setPromptCount(self, count):
ville
initialization (no svn history)
r988 self.prompt_count = count
def showPrompt(self):
'''
Prints prompt at start of line.
@param prompt: Prompt to print.
@type prompt: string
'''
self.write(self.prompt)
#now we update the position of end of prompt
self.current_start = self.getCurrentLineEnd()
autoindent = self.indent*' '
autoindent = autoindent.replace(' ','\t')
self.write(autoindent)
def changeLine(self, text):
'''
Replace currently entered command line with given text.
@param text: Text to use as replacement.
@type text: string
'''
Laurent Dufréchou
cleanup and linted a bit :)
r1221 self.SetSelection(self.getCurrentPromptStart(), self.getCurrentLineEnd())
ville
initialization (no svn history)
r988 self.ReplaceSelection(text)
self.moveCursor(self.getCurrentLineEnd())
def getCurrentPromptStart(self):
return self.current_start
def getCurrentLineStart(self):
return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
def getCurrentLineEnd(self):
return self.GetLength()
def getCurrentLine(self):
'''
Get text in current command line.
@return: Text of current command line.
@rtype: string
'''
return self.GetTextRange(self.getCurrentPromptStart(),
self.getCurrentLineEnd())
def moveCursorOnNewValidKey(self):
#If cursor is at wrong position put it at last line...
if self.GetCurrentPos() < self.getCurrentPromptStart():
self.GotoPos(self.getCurrentPromptStart())
Laurent Dufréchou
cleanup and linted a bit :)
r1221 def removeFromTo(self, from_pos, to_pos):
ville
initialization (no svn history)
r988 if from_pos < to_pos:
Laurent Dufréchou
cleanup and linted a bit :)
r1221 self.SetSelection(from_pos, to_pos)
ville
initialization (no svn history)
r988 self.DeleteBack()
def removeCurrentLine(self):
self.LineDelete()
Laurent Dufréchou
cleanup and linted a bit :)
r1221 def moveCursor(self, position):
ville
initialization (no svn history)
r988 self.GotoPos(position)
def getCursorPos(self):
return self.GetCurrentPos()
Laurent Dufréchou
cleanup and linted a bit :)
r1221 def selectFromTo(self, from_pos, to_pos):
ville
initialization (no svn history)
r988 self.SetSelectionStart(from_pos)
self.SetSelectionEnd(to_pos)
Laurent Dufréchou
cleanup and linted a bit :)
r1221 def writeHistory(self, history):
self.removeFromTo(self.getCurrentPromptStart(), self.getCurrentLineEnd())
ville
initialization (no svn history)
r988 self.changeLine(history)
ldufrechou
[gui/wx/ipython] ...
r1159
def setCompletionMethod(self, completion):
Laurent Dufréchou
cleanup and linted a bit :)
r1221 if completion in ['IPYTHON', 'STC']:
ldufrechou
[gui/wx/ipython] ...
r1159 self.autocomplete_mode = completion
else:
raise AttributeError
def getCompletionMethod(self, completion):
return self.autocomplete_mode
ville
initialization (no svn history)
r988
def writeCompletion(self, possibilities):
ldufrechou
[gui/wx/ipython] ...
r1159 if self.autocomplete_mode == 'IPYTHON':
Laurent Dufréchou
cleanup and linted a bit :)
r1221 max_len = len(max(possibilities, key=len))
max_symbol = ' '*max_len
ldufrechou
[gui/wx/ipython] ...
r1159
#now we check how much symbol we can put on a line...
test_buffer = max_symbol + ' '*4
allowed_symbols = 80/len(test_buffer)
if allowed_symbols == 0:
Laurent Dufréchou
cleanup and linted a bit :)
r1221 allowed_symbols = 1
ldufrechou
[gui/wx/ipython] ...
r1159
pos = 1
buf = ''
for symbol in possibilities:
#buf += symbol+'\n'#*spaces)
Laurent Dufréchou
cleanup and linted a bit :)
r1221 if pos < allowed_symbols:
ldufrechou
[gui/wx/ipython] ...
r1159 spaces = max_len - len(symbol) + 4
buf += symbol+' '*spaces
pos += 1
else:
Laurent Dufréchou
cleanup and linted a bit :)
r1221 buf += symbol+'\n'
ldufrechou
[gui/wx/ipython] ...
r1159 pos = 1
self.write(buf)
else:
possibilities.sort() # Python sorts are case sensitive
self.AutoCompSetIgnoreCase(False)
ldufrechou
[gui/wx/ipython]...
r1160 self.AutoCompSetAutoHide(False)
ldufrechou
[gui/wx/ipython] ...
r1159 #let compute the length ot last word
Laurent Dufréchou
cleanup and linted a bit :)
r1221 splitter = [' ', '(', '[', '{']
ldufrechou
[gui/wx/ipython] ...
r1159 last_word = self.getCurrentLine()
for breaker in splitter:
last_word = last_word.split(breaker)[-1]
self.AutoCompShow(len(last_word), " ".join(possibilities))
ville
initialization (no svn history)
r988
def _onKeypress(self, event, skip=True):
'''
Key press callback used for correcting behavior for console-like
interfaces. For example 'home' should go to prompt, not to begining of
line.
@param widget: Widget that key press accored in.
@type widget: gtk.Widget
@param event: Event object
@type event: gtk.gdk.Event
@return: Return True if event as been catched.
@rtype: boolean
'''
ldufrechou
[gui/wx/ipython] ...
r1159 if not self.AutoCompActive():
if event.GetKeyCode() == wx.WXK_HOME:
if event.Modifiers == wx.MOD_NONE:
self.moveCursorOnNewValidKey()
ville
initialization (no svn history)
r988 self.moveCursor(self.getCurrentPromptStart())
ldufrechou
[gui/wx/ipython] ...
r1159 return True
elif event.Modifiers == wx.MOD_SHIFT:
self.moveCursorOnNewValidKey()
Laurent Dufréchou
cleanup and linted a bit :)
r1221 self.selectFromTo(self.getCurrentPromptStart(), self.getCursorPos())
ldufrechou
[gui/wx/ipython] ...
r1159 return True
else:
return False
elif event.GetKeyCode() == wx.WXK_LEFT:
if event.Modifiers == wx.MOD_NONE:
self.moveCursorOnNewValidKey()
self.moveCursor(self.getCursorPos()-1)
if self.getCursorPos() < self.getCurrentPromptStart():
self.moveCursor(self.getCurrentPromptStart())
return True
elif event.GetKeyCode() == wx.WXK_BACK:
self.moveCursorOnNewValidKey()
if self.getCursorPos() > self.getCurrentPromptStart():
event.Skip()
ville
initialization (no svn history)
r988 return True
ldufrechou
[gui/wx/ipython] ...
r1159
if skip:
Laurent Dufréchou
cleanup and linted a bit :)
r1221 if event.GetKeyCode() not in [wx.WXK_PAGEUP, wx.WXK_PAGEDOWN]\
and event.Modifiers == wx.MOD_NONE:
ldufrechou
[gui/wx/ipython] ...
r1159 self.moveCursorOnNewValidKey()
ldufrechou
Support for true raw_input:...
r1134 event.Skip()
ldufrechou
[gui/wx/ipython] ...
r1159 return True
return False
else:
ville
initialization (no svn history)
r988 event.Skip()
ldufrechou
[gui/wx/ipython] ...
r1159
ville
initialization (no svn history)
r988 def OnUpdateUI(self, evt):
# check for matching braces
braceAtCaret = -1
braceOpposite = -1
charBefore = None
caretPos = self.GetCurrentPos()
if caretPos > 0:
charBefore = self.GetCharAt(caretPos - 1)
styleBefore = self.GetStyleAt(caretPos - 1)
# check before
if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
braceAtCaret = caretPos - 1
# check after
if braceAtCaret < 0:
charAfter = self.GetCharAt(caretPos)
styleAfter = self.GetStyleAt(caretPos)
if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
braceAtCaret = caretPos
if braceAtCaret >= 0:
braceOpposite = self.BraceMatch(braceAtCaret)
if braceAtCaret != -1 and braceOpposite == -1:
self.BraceBadLight(braceAtCaret)
else:
self.BraceHighlight(braceAtCaret, braceOpposite)
#pt = self.PointFromPosition(braceOpposite)
#self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
#print pt
#self.Refresh(False)
Gael Varoquaux
Add a very simple test case to test only the widget for the wxIpython...
r1107 class IPShellWidget(wx.Panel):
ville
initialization (no svn history)
r988 '''
This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
Gael Varoquaux
Add a very simple test case to test only the widget for the wxIpython...
r1107 If you want to port this to any other GUI toolkit, just replace the
WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
from whatever container you want. I've choosed to derivate from a wx.Panel
because it seems to be more useful
Any idea to make it more 'generic' welcomed.
ville
initialization (no svn history)
r988 '''
ldufrechou
BIG improvement on cout management....
r1133
ldufrechou
Corrected bug #241...
r1130 def __init__(self, parent, intro=None,
Gael Varoquaux
Some cosmetic changes.
r1096 background_color="BLACK", add_button_handler=None,
ldufrechou
user can add local/global namespace
r1152 wx_ip_shell=None, user_ns={},user_global_ns=None,
Gael Varoquaux
Some cosmetic changes.
r1096 ):
ville
initialization (no svn history)
r988 '''
Initialize.
Instanciate an IPython thread.
Instanciate a WxConsoleView.
Redirect I/O to console.
'''
ldufrechou
[wxipython shell] Patch from cody precod to correct 'enter' key behaviour under MacOSX
r1158 wx.Panel.__init__(self,parent,wx.ID_ANY)
ville
initialization (no svn history)
r988
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168 self.parent = parent
ldufrechou
cleaning...
r1099 ### IPython non blocking shell instanciation ###
ville
initialization (no svn history)
r988 self.cout = StringIO()
ldufrechou
Cleaned up version with initial WX callback (callafter) support
r1091 self.add_button_handler = add_button_handler
Gael Varoquaux
Some cosmetic changes.
r1096 if wx_ip_shell is not None:
self.IP = wx_ip_shell
else:
self.IP = WxNonBlockingIPShell(self,
ldufrechou
Support for true raw_input:...
r1134 cout = self.cout, cerr = self.cout,
ldufrechou
-revised code to correct ehaviour under linux....
r1153 ask_exit_handler = self.askExitCallback)
ldufrechou
cleaning...
r1099
ville
initialization (no svn history)
r988 ### IPython wx console view instanciation ###
#If user didn't defined an intro text, we create one for him
ldufrechou
[/gui/wx/] cody precord : small patch for some changes and improvements to the ipython_view...
r1163 #If you really wnat an empty intro just call wxIPythonViewPanel
Gael Varoquaux
Cleaning up lignes longer than 80 characters.
r1106 #with intro=''
ldufrechou
Corrected bug #241...
r1130 if intro is None:
ville
initialization (no svn history)
r988 welcome_text = "Welcome to WxIPython Shell.\n\n"
welcome_text+= self.IP.getBanner()
welcome_text+= "!command -> Execute command in shell\n"
welcome_text+= "TAB -> Autocompletion\n"
ldufrechou
Corrected bug #241...
r1130 else:
welcome_text = intro
ville
initialization (no svn history)
r988
self.text_ctrl = WxConsoleView(self,
self.IP.getPrompt(),
intro=welcome_text,
background_color=background_color)
ldufrechou
BIG improvement on cout management....
r1133
ldufrechou
[/gui/wx/] cody precord : small patch for some changes and improvements to the ipython_view...
r1163 option_text = wx.StaticText(self, -1, "Options:")
self.completion_option = wx.CheckBox(self, -1, "Scintilla Completion")
ldufrechou
Removed \t detected by tools.py...
r1622 self.completion_option.SetToolTip(wx.ToolTip(
"Selects the completion type:\nEither Ipython default style or Scintilla one"))
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168 #self.completion_option.SetValue(False)
ldufrechou
[/gui/wx/] cody precord : small patch for some changes and improvements to the ipython_view...
r1163 self.background_option = wx.CheckBox(self, -1, "White Background")
ldufrechou
Removed \t detected by tools.py...
r1622 self.background_option.SetToolTip(wx.ToolTip(
"Selects the back ground color: BLACK or WHITE"))
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168 #self.background_option.SetValue(False)
ldufrechou
Added an option to enable/disable threading as suggested by Ville.
r1621 self.threading_option = wx.CheckBox(self, -1, "Execute in thread")
ldufrechou
Removed \t detected by tools.py...
r1622 self.threading_option.SetToolTip(wx.ToolTip(
"Use threading: infinite loop don't freeze the GUI and commands can be breaked\nNo threading: maximum compatibility"))
ldufrechou
Added an option to enable/disable threading as suggested by Ville.
r1621 #self.threading_option.SetValue(False)
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168
self.options={'completion':{'value':'IPYTHON',
'checkbox':self.completion_option,'STC':True,'IPYTHON':False,
'setfunc':self.text_ctrl.setCompletionMethod},
'background_color':{'value':'BLACK',
'checkbox':self.background_option,'WHITE':True,'BLACK':False,
'setfunc':self.text_ctrl.setBackgroundColor},
ldufrechou
Added an option to enable/disable threading as suggested by Ville.
r1621 'threading':{'value':'True',
'checkbox':self.threading_option,'True':True,'False':False,
'setfunc':self.IP.setThreading},
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168 }
ldufrechou
Added an option to enable/disable threading as suggested by Ville.
r1621
#self.cout.write dEfault option is asynchroneous because default sate is threading ON
self.cout.write = self.text_ctrl.asyncWrite
#we reloard options
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168 self.reloadOptions(self.options)
ville
initialization (no svn history)
r988
ldufrechou
[wxipython shell] Patch from cody precod to correct 'enter' key behaviour under MacOSX
r1158 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress)
ldufrechou
[/gui/wx] Added a checkbox to switch background color (white/black)
r1162 self.completion_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionCompletion)
self.background_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionBackgroundColor)
ldufrechou
Added an option to enable/disable threading as suggested by Ville.
r1621 self.threading_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionThreading)
ldufrechou
[gui/wx/ipython] ...
r1159
ville
initialization (no svn history)
r988 ### making the layout of the panel ###
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.text_ctrl, 1, wx.EXPAND)
ldufrechou
[gui/wx/ipython] ...
r1159 option_sizer = wx.BoxSizer(wx.HORIZONTAL)
ldufrechou
[/gui/wx/] cody precord : small patch for some changes and improvements to the ipython_view...
r1163 sizer.Add(option_sizer, 0)
option_sizer.AddMany([(10, 20),
(option_text, 0, wx.ALIGN_CENTER_VERTICAL),
(5, 5),
(self.completion_option, 0, wx.ALIGN_CENTER_VERTICAL),
(8, 8),
ldufrechou
Added an option to enable/disable threading as suggested by Ville.
r1621 (self.background_option, 0, wx.ALIGN_CENTER_VERTICAL),
(8, 8),
(self.threading_option, 0, wx.ALIGN_CENTER_VERTICAL)
ldufrechou
[gui/wx/ipython] ...
r1159 ])
ville
initialization (no svn history)
r988 self.SetAutoLayout(True)
sizer.Fit(self)
sizer.SetSizeHints(self)
self.SetSizer(sizer)
#and we focus on the widget :)
self.SetFocus()
ldufrechou
cleaning...
r1099 #widget state management (for key handling different cases)
self.setCurrentState('IDLE')
Gael Varoquaux
Some cosmetic changes.
r1096 self.pager_state = 'DONE'
ldufrechou
-revised code to correct ehaviour under linux....
r1153 self.raw_input_current_line = 0
ville
initialization (no svn history)
r988
ldufrechou
Corrected bug #241...
r1130 def askExitCallback(self, event):
self.askExitHandler(event)
Gael Varoquaux
Cleaning up lignes longer than 80 characters.
r1106 #---------------------- IPython Thread Management ------------------------
ldufrechou
Cleaned up version with initial WX callback (callafter) support
r1091 def stateDoExecuteLine(self):
ldufrechou
clean up...
r1138 lines=self.text_ctrl.getCurrentLine()
ldufrechou
BIG improvement on cout management....
r1133 self.text_ctrl.write('\n')
ldufrechou
-revised code to correct ehaviour under linux....
r1153 lines_to_execute = lines.replace('\t',' '*4)
ldufrechou
[gui/wx/ipython]...
r1160 lines_to_execute = lines_to_execute.replace('\r','')
Cody Precord
Better encoding handling....
r1538 self.IP.doExecute(lines_to_execute.encode(ENCODING))
ldufrechou
-revised code to correct ehaviour under linux....
r1153 self.updateHistoryTracker(lines)
ldufrechou
cleaning...
r1099 self.setCurrentState('WAIT_END_OF_EXECUTION')
ldufrechou
Cleaned up version with initial WX callback (callafter) support
r1091
def evtStateExecuteDone(self,evt):
self.doc = self.IP.getDocText()
ldufrechou
Added help() support...
r1094 self.help = self.IP.getHelpText()
ldufrechou
Cleaned up version with initial WX callback (callafter) support
r1091 if self.doc:
ldufrechou
cleaning...
r1099 self.pager_lines = self.doc[7:].split('\n')
ldufrechou
clean up...
r1138 self.pager_state = 'INIT'
ldufrechou
cleaning...
r1099 self.setCurrentState('SHOW_DOC')
ville
initialization (no svn history)
r988 self.pager(self.doc)
ldufrechou
Doc bug correction
r1102 elif self.help:
ldufrechou
cleaning...
r1099 self.pager_lines = self.help.split('\n')
ldufrechou
clean up...
r1138 self.pager_state = 'INIT'
ldufrechou
cleaning...
r1099 self.setCurrentState('SHOW_DOC')
ldufrechou
Doc bug correction
r1102 self.pager(self.help)
ldufrechou
Cleaned up version with initial WX callback (callafter) support
r1091 else:
self.stateShowPrompt()
def stateShowPrompt(self):
ldufrechou
cleaning...
r1099 self.setCurrentState('SHOW_PROMPT')
ldufrechou
Cleaned up version with initial WX callback (callafter) support
r1091 self.text_ctrl.setPrompt(self.IP.getPrompt())
self.text_ctrl.setIndentation(self.IP.getIndentation())
self.text_ctrl.setPromptCount(self.IP.getPromptCount())
ldufrechou
BIG improvement on cout management....
r1133 self.text_ctrl.showPrompt()
Gael Varoquaux
Some cosmetic changes.
r1096 self.IP.initHistoryIndex()
ldufrechou
cleaning...
r1099 self.setCurrentState('IDLE')
Gael Varoquaux
Cleaning up lignes longer than 80 characters.
r1106
ldufrechou
cleaning...
r1099 def setCurrentState(self, state):
self.cur_state = state
self.updateStatusTracker(self.cur_state)
ldufrechou
-revised code to correct ehaviour under linux....
r1153
ldufrechou
Support for true raw_input:...
r1134 def pager(self,text):
Gael Varoquaux
Changed tabs to whitespace in the wx frontend...
r1103
ldufrechou
pager cleanup
r1104 if self.pager_state == 'INIT':
Laurent Dufréchou
cleanup and linted a bit :)
r1221 #print >>sys.__stdout__,"PAGER state:",self.pager_state
self.pager_nb_lines = len(self.pager_lines)
self.pager_index = 0
self.pager_do_remove = False
self.text_ctrl.write('\n')
self.pager_state = 'PROCESS_LINES'
ldufrechou
clean up...
r1138
if self.pager_state == 'PROCESS_LINES':
Laurent Dufréchou
cleanup and linted a bit :)
r1221 #print >>sys.__stdout__,"PAGER state:",self.pager_state
if self.pager_do_remove == True:
self.text_ctrl.removeCurrentLine()
self.pager_do_remove = False
if self.pager_nb_lines > 10:
#print >>sys.__stdout__,"PAGER processing 10 lines"
if self.pager_index > 0:
self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
ldufrechou
clean up...
r1138 else:
Laurent Dufréchou
cleanup and linted a bit :)
r1221 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
self.pager_index += 10
self.pager_nb_lines -= 10
self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
self.pager_do_remove = True
self.pager_state = 'WAITING'
return
else:
#print >>sys.__stdout__,"PAGER processing last lines"
if self.pager_nb_lines > 0:
if self.pager_index > 0:
self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
else:
self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
self.pager_index += 1
self.pager_nb_lines -= 1
if self.pager_nb_lines > 0:
for line in self.pager_lines[self.pager_index:]:
self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
self.pager_nb_lines = 0
self.pager_state = 'DONE'
self.stateShowPrompt()
Gael Varoquaux
Cleaning up lignes longer than 80 characters.
r1106 #------------------------ Key Handler ------------------------------------
ville
initialization (no svn history)
r988 def keyPress(self, event):
'''
Key press callback with plenty of shell goodness, like history,
autocompletions, etc.
'''
if event.GetKeyCode() == ord('C'):
ldufrechou
[gui/wx/ipython] ...
r1159 if event.Modifiers == wx.MOD_CONTROL or event.Modifiers == wx.MOD_ALT:
ville
initialization (no svn history)
r988 if self.cur_state == 'WAIT_END_OF_EXECUTION':
#we raise an exception inside the IPython thread container
ldufrechou
revised threading model....
r1095 self.IP.ce.raise_exc(KeyboardInterrupt)
ville
initialization (no svn history)
r988 return
ldufrechou
[gui/wx/ipython] ...
r1159 #let this before 'wx.WXK_RETURN' because we have to put 'IDLE'
#mode if AutoComp has been set as inactive
if self.cur_state == 'COMPLETING':
if not self.text_ctrl.AutoCompActive():
self.cur_state = 'IDLE'
else:
event.Skip()
ville
initialization (no svn history)
r988 if event.KeyCode == wx.WXK_RETURN:
if self.cur_state == 'IDLE':
#we change the state ot the state machine
ldufrechou
cleaning...
r1099 self.setCurrentState('DO_EXECUTE_LINE')
ldufrechou
Cleaned up version with initial WX callback (callafter) support
r1091 self.stateDoExecuteLine()
ville
initialization (no svn history)
r988 return
ldufrechou
[wxipython shell] Patch from cody precod to correct 'enter' key behaviour under MacOSX
r1158
ville
initialization (no svn history)
r988 if self.pager_state == 'WAITING':
self.pager_state = 'PROCESS_LINES'
ldufrechou
Cleaned up version with initial WX callback (callafter) support
r1091 self.pager(self.doc)
ville
initialization (no svn history)
r988 return
ldufrechou
Support for true raw_input:...
r1134 if self.cur_state == 'WAITING_USER_INPUT':
line=self.text_ctrl.getCurrentLine()
self.text_ctrl.write('\n')
self.setCurrentState('WAIT_END_OF_EXECUTION')
return
ville
initialization (no svn history)
r988 if event.GetKeyCode() in [ord('q'),ord('Q')]:
if self.pager_state == 'WAITING':
self.pager_state = 'DONE'
ldufrechou
clean up...
r1138 self.text_ctrl.write('\n')
ldufrechou
Cleaned up version with initial WX callback (callafter) support
r1091 self.stateShowPrompt()
ville
initialization (no svn history)
r988 return
ldufrechou
BIG improvement on cout management....
r1133
ldufrechou
Support for true raw_input:...
r1134 if self.cur_state == 'WAITING_USER_INPUT':
event.Skip()
ldufrechou
[gui/wx/ipython] ...
r1159
Gael Varoquaux
Changed tabs to whitespace in the wx frontend...
r1103 if self.cur_state == 'IDLE':
ville
initialization (no svn history)
r988 if event.KeyCode == wx.WXK_UP:
history = self.IP.historyBack()
self.text_ctrl.writeHistory(history)
return
if event.KeyCode == wx.WXK_DOWN:
history = self.IP.historyForward()
self.text_ctrl.writeHistory(history)
return
if event.KeyCode == wx.WXK_TAB:
#if line empty we disable tab completion
if not self.text_ctrl.getCurrentLine().strip():
self.text_ctrl.write('\t')
return
completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
if len(possibilities) > 1:
ldufrechou
[gui/wx/ipython] ...
r1159 if self.text_ctrl.autocomplete_mode == 'IPYTHON':
cur_slice = self.text_ctrl.getCurrentLine()
self.text_ctrl.write('\n')
self.text_ctrl.writeCompletion(possibilities)
self.text_ctrl.write('\n')
self.text_ctrl.showPrompt()
self.text_ctrl.write(cur_slice)
self.text_ctrl.changeLine(completed or cur_slice)
else:
self.cur_state = 'COMPLETING'
self.text_ctrl.writeCompletion(possibilities)
else:
self.text_ctrl.changeLine(completed or cur_slice)
ville
initialization (no svn history)
r988 return
event.Skip()
ldufrechou
[gui/wx/ipython] ...
r1159
#------------------------ Option Section ---------------------------------
def evtCheckOptionCompletion(self, event):
if event.IsChecked():
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168 self.options['completion']['value']='STC'
ldufrechou
[gui/wx/ipython] ...
r1159 else:
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168 self.options['completion']['value']='IPYTHON'
self.text_ctrl.setCompletionMethod(self.options['completion']['value'])
self.updateOptionTracker('completion',
self.options['completion']['value'])
ldufrechou
[gui/wx/ipython] ...
r1159 self.text_ctrl.SetFocus()
ldufrechou
[/gui/wx] Added a checkbox to switch background color (white/black)
r1162
def evtCheckOptionBackgroundColor(self, event):
if event.IsChecked():
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168 self.options['background_color']['value']='WHITE'
ldufrechou
[/gui/wx] Added a checkbox to switch background color (white/black)
r1162 else:
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168 self.options['background_color']['value']='BLACK'
self.text_ctrl.setBackgroundColor(self.options['background_color']['value'])
self.updateOptionTracker('background_color',
self.options['background_color']['value'])
ldufrechou
[/gui/wx] Added a checkbox to switch background color (white/black)
r1162 self.text_ctrl.SetFocus()
ldufrechou
Added an option to enable/disable threading as suggested by Ville.
r1621
def evtCheckOptionThreading(self, event):
if event.IsChecked():
self.options['threading']['value']='True'
self.IP.setThreading(True)
self.cout.write = self.text_ctrl.asyncWrite
else:
self.options['threading']['value']='False'
self.IP.setThreading(False)
self.cout.write = self.text_ctrl.write
self.updateOptionTracker('threading',
self.options['threading']['value'])
self.text_ctrl.SetFocus()
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168 def getOptions(self):
return self.options
def reloadOptions(self,options):
self.options = options
for key in self.options.keys():
value = self.options[key]['value']
self.options[key]['checkbox'].SetValue(self.options[key][value])
self.options[key]['setfunc'](value)
ldufrechou
Added an option to enable/disable threading as suggested by Ville.
r1621 if self.options['threading']['value']=='True':
self.IP.setThreading(True)
self.cout.write = self.text_ctrl.asyncWrite
else:
self.IP.setThreading(False)
self.cout.write = self.text_ctrl.write
Gael Varoquaux
Cleaning up lignes longer than 80 characters.
r1106 #------------------------ Hook Section -----------------------------------
ldufrechou
[/gui/wx] Initial options support + conf file....
r1168 def updateOptionTracker(self,name,value):
'''
Default history tracker (does nothing)
'''
pass
def setOptionTrackerHook(self,func):
'''
Define a new history tracker
'''
self.updateOptionTracker = func
ville
initialization (no svn history)
r988 def updateHistoryTracker(self,command_line):
'''
Default history tracker (does nothing)
'''
pass
def setHistoryTrackerHook(self,func):
'''
Define a new history tracker
'''
self.updateHistoryTracker = func
Gael Varoquaux
Cleaning up lignes longer than 80 characters.
r1106
ville
initialization (no svn history)
r988 def updateStatusTracker(self,status):
'''
Default status tracker (does nothing)
'''
pass
def setStatusTrackerHook(self,func):
'''
Define a new status tracker
'''
self.updateStatusTracker = func
Gael Varoquaux
Add a very simple test case to test only the widget for the wxIpython...
r1107
ldufrechou
Corrected bug #241...
r1130 def askExitHandler(self, event):
'''
Default exit handler
'''
self.text_ctrl.write('\nExit callback has not been set.')
def setAskExitHandler(self, func):
'''
Define an exit handler
'''
self.askExitHandler = func
Gael Varoquaux
Add a very simple test case to test only the widget for the wxIpython...
r1107
if __name__ == '__main__':
# Some simple code to test the shell 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.shell = IPShellWidget(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.SetSize((780, 460))
shell = frame.shell
app.MainLoop()