ipython_history.py
475 lines
| 19.5 KiB
| text/x-python
|
PythonLexer
ville
|
r988 | #!/usr/bin/python | ||
# -*- coding: iso-8859-15 -*- | ||||
import wx | ||||
import wx.stc as stc | ||||
import keyword | ||||
#----------------------------------------- | ||||
# History widget for IPython | ||||
__version__ = 0.5 | ||||
__author__ = "Laurent Dufrechou" | ||||
__email__ = "laurent.dufrechou _at_ gmail.com" | ||||
__license__ = "BSD" | ||||
#----------------------------------------- | ||||
class IPythonHistoryPanel(wx.Panel): | ||||
def __init__(self, parent,flt_empty=True, | ||||
flt_doc=True,flt_cmd=True,flt_magic=True): | ||||
wx.Panel.__init__(self,parent,-1) | ||||
#text_ctrl = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE) | ||||
text_ctrl = PythonSTC(self, -1) | ||||
st_filt = wx.StaticText(self, -1, " Filter:") | ||||
self.filter_empty = wx.CheckBox(self, -1, "Empty commands") | ||||
self.filter_doc = wx.CheckBox(self, -1, "?: Doc commands") | ||||
self.filter_cmd = wx.CheckBox(self, -1, "!: Sys commands") | ||||
self.filter_magic = wx.CheckBox(self, -1, "%: Magic keys") | ||||
ldufrechou
|
r1176 | self.options={'filter_empty':{'value':'True', | ||
'checkbox':self.filter_empty,'True':True,'False':False, | ||||
'setfunc':lambda x:None}, | ||||
'filter_doc':{'value':'True', | ||||
'checkbox':self.filter_doc,'True':True,'False':False, | ||||
'setfunc':lambda x:None}, | ||||
'filter_cmd':{'value':'True', | ||||
'checkbox':self.filter_cmd,'True':True,'False':False, | ||||
'setfunc':lambda x:None}, | ||||
'filter_magic':{'value':'True', | ||||
'checkbox':self.filter_magic,'True':True,'False':False, | ||||
'setfunc':lambda x:None}, | ||||
} | ||||
self.reloadOptions(self.options) | ||||
self.filter_empty.Bind(wx.EVT_CHECKBOX, self.evtCheckEmptyFilter) | ||||
self.filter_doc.Bind(wx.EVT_CHECKBOX, self.evtCheckDocFilter) | ||||
self.filter_cmd.Bind(wx.EVT_CHECKBOX, self.evtCheckCmdFilter) | ||||
self.filter_magic.Bind(wx.EVT_CHECKBOX, self.evtCheckMagicFilter) | ||||
#self.filter_empty.SetValue(flt_empty) | ||||
#self.filter_doc.SetValue(flt_doc) | ||||
#self.filter_cmd.SetValue(flt_cmd) | ||||
#self.filter_magic.SetValue(flt_magic) | ||||
ville
|
r988 | |||
sizer = wx.BoxSizer(wx.VERTICAL) | ||||
sizer.Add(text_ctrl, 1, wx.EXPAND) | ||||
sizer.AddMany( [(5,5), | ||||
st_filt, | ||||
(10,10), | ||||
self.filter_empty, | ||||
self.filter_doc, | ||||
self.filter_cmd, | ||||
self.filter_magic, | ||||
(10,10), | ||||
]) | ||||
self.SetAutoLayout(True) | ||||
sizer.Fit(self) | ||||
sizer.SetSizeHints(self) | ||||
self.SetSizer(sizer) | ||||
self.text_ctrl=text_ctrl | ||||
#text_ctrl.SetText(demoText + open('Main.py').read()) | ||||
text_ctrl.EmptyUndoBuffer() | ||||
text_ctrl.Colourise(0, -1) | ||||
# line numbers in the margin | ||||
text_ctrl.SetMarginType(1, stc.STC_MARGIN_NUMBER) | ||||
text_ctrl.SetMarginWidth(1, 15) | ||||
def write(self,history_line): | ||||
add = True | ||||
if self.filter_empty.GetValue() == True and history_line == '': | ||||
add = False | ||||
if len(history_line)>0: | ||||
if self.filter_doc.GetValue() == True and history_line[-1:] == '?': | ||||
add = False | ||||
if self.filter_cmd.GetValue() == True and history_line[0] == '!': | ||||
add = False | ||||
if self.filter_magic.GetValue() == True and history_line[0] == '%': | ||||
add = False | ||||
Gael Varoquaux
|
r1103 | if add: | ||
self.text_ctrl.AppendText(history_line+'\n') | ||||
ville
|
r988 | |||
ldufrechou
|
r1176 | #------------------------ Option Section ----------------------------------- | ||
def processOptionCheckedEvt(self, event, name): | ||||
if event.IsChecked(): | ||||
self.options[name]['value']='True' | ||||
else: | ||||
self.options[name]['value']='False' | ||||
self.updateOptionTracker(name, | ||||
self.options[name]['value']) | ||||
def evtCheckEmptyFilter(self, event): | ||||
self.processOptionCheckedEvt(event, 'filter_empty') | ||||
def evtCheckDocFilter(self, event): | ||||
self.processOptionCheckedEvt(event, 'filter_doc') | ||||
def evtCheckCmdFilter(self, event): | ||||
self.processOptionCheckedEvt(event, 'filter_cmd') | ||||
def evtCheckMagicFilter(self, event): | ||||
self.processOptionCheckedEvt(event, 'filter_magic') | ||||
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) | ||||
#------------------------ Hook Section ----------------------------------- | ||||
def updateOptionTracker(self,name,value): | ||||
''' | ||||
Default history tracker (does nothing) | ||||
''' | ||||
pass | ||||
def setOptionTrackerHook(self,func): | ||||
''' | ||||
Define a new history tracker | ||||
''' | ||||
self.updateOptionTracker = func | ||||
ville
|
r988 | |||
ldufrechou
|
r1176 | |||
ville
|
r988 | #---------------------------------------------------------------------- | ||
# Font definition for Styled Text Control | ||||
if wx.Platform == '__WXMSW__': | ||||
faces = { 'times': 'Times New Roman', | ||||
'mono' : 'Courier New', | ||||
'helv' : 'Arial', | ||||
'other': 'Comic Sans MS', | ||||
'size' : 8, | ||||
'size2': 6, | ||||
} | ||||
elif wx.Platform == '__WXMAC__': | ||||
faces = { 'times': 'Times New Roman', | ||||
'mono' : 'Monaco', | ||||
'helv' : 'Arial', | ||||
'other': 'Comic Sans MS', | ||||
'size' : 8, | ||||
'size2': 6, | ||||
} | ||||
else: | ||||
faces = { 'times': 'Times', | ||||
'mono' : 'Courier', | ||||
'helv' : 'Helvetica', | ||||
'other': 'new century schoolbook', | ||||
'size' : 8, | ||||
'size2': 6, | ||||
} | ||||
#---------------------------------------------------------------------- | ||||
class PythonSTC(stc.StyledTextCtrl): | ||||
fold_symbols = 3 | ||||
def __init__(self, parent, ID, | ||||
pos=wx.DefaultPosition, size=wx.DefaultSize, | ||||
style=0): | ||||
stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style) | ||||
#self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN) | ||||
#self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT) | ||||
self.SetLexer(stc.STC_LEX_PYTHON) | ||||
self.SetKeyWords(0, " ".join(keyword.kwlist)) | ||||
#self.SetProperty("fold", "1") | ||||
#self.SetProperty("tab.timmy.whinge.level", "1") | ||||
#self.SetMargins(0,0) | ||||
#self.SetViewWhiteSpace(False) | ||||
#self.SetBufferedDraw(False) | ||||
#self.SetViewEOL(True) | ||||
self.SetEOLMode(stc.STC_EOL_CRLF) | ||||
#self.SetUseAntiAliasing(True) | ||||
self.SetEdgeMode(stc.STC_EDGE_LINE) | ||||
self.SetEdgeColumn(80) | ||||
self.SetEdgeColour(wx.LIGHT_GREY) | ||||
self.SetLayoutCache(stc.STC_CACHE_PAGE) | ||||
# Setup a margin to hold fold markers | ||||
#self.SetFoldFlags(16) ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER? | ||||
self.SetMarginType(2, stc.STC_MARGIN_SYMBOL) | ||||
self.SetMarginMask(2, stc.STC_MASK_FOLDERS) | ||||
self.SetMarginSensitive(2, True) | ||||
self.SetMarginWidth(2, 12) | ||||
if self.fold_symbols == 0: | ||||
# Arrow pointing right for contracted folders, arrow pointing down for expanded | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_ARROWDOWN, "black", "black") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_ARROW, "black", "black") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "black", "black") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "black", "black") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black") | ||||
elif self.fold_symbols == 1: | ||||
# Plus for contracted folders, minus for expanded | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_MINUS, "white", "black") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_PLUS, "white", "black") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "white", "black") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "white", "black") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black") | ||||
elif self.fold_symbols == 2: | ||||
# Like a flattened tree control using circular headers and curved joins | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_CIRCLEMINUS, "white", "#404040") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_CIRCLEPLUS, "white", "#404040") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#404040") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNERCURVE, "white", "#404040") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_CIRCLEPLUSCONNECTED, "white", "#404040") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE, "white", "#404040") | ||||
elif self.fold_symbols == 3: | ||||
# Like a flattened tree control using square headers | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "#808080") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "#808080") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#808080") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "#808080") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080") | ||||
self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "#808080") | ||||
self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI) | ||||
self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick) | ||||
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed) | ||||
# Make some styles, The lexer defines what each style is used for, we | ||||
# just have to define what each style looks like. This set is adapted from | ||||
# Scintilla sample property files. | ||||
# Global default styles for all languages | ||||
self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces) | ||||
self.StyleClearAll() # Reset all to be like the default | ||||
# Global default styles for all languages | ||||
self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces) | ||||
self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces) | ||||
self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces) | ||||
self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold") | ||||
self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold") | ||||
# Python styles | ||||
# Default | ||||
self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces) | ||||
# Comments | ||||
self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces) | ||||
# Number | ||||
self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % faces) | ||||
# String | ||||
self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces) | ||||
# Single quoted string | ||||
self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces) | ||||
# Keyword | ||||
self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % faces) | ||||
# Triple quotes | ||||
self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % faces) | ||||
# Triple double quotes | ||||
self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % faces) | ||||
# Class name definition | ||||
self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:#0000FF,bold,underline,size:%(size)d" % faces) | ||||
# Function or method name definition | ||||
self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % faces) | ||||
# Operators | ||||
self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % faces) | ||||
# Identifiers | ||||
self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % faces) | ||||
# Comment-blocks | ||||
self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % faces) | ||||
# End of line where string is not closed | ||||
self.StyleSetSpec(stc.STC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces) | ||||
self.SetCaretForeground("BLUE") | ||||
# register some images for use in the AutoComplete box. | ||||
#self.RegisterImage(1, images.getSmilesBitmap()) | ||||
#self.RegisterImage(2, | ||||
# wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16))) | ||||
#self.RegisterImage(3, | ||||
# wx.ArtProvider.GetBitmap(wx.ART_COPY, size=(16,16))) | ||||
def OnKeyPressed(self, event): | ||||
if self.CallTipActive(): | ||||
self.CallTipCancel() | ||||
key = event.GetKeyCode() | ||||
if key == 32 and event.ControlDown(): | ||||
pos = self.GetCurrentPos() | ||||
# Tips | ||||
if event.ShiftDown(): | ||||
self.CallTipSetBackground("yellow") | ||||
self.CallTipShow(pos, 'lots of of text: blah, blah, blah\n\n' | ||||
'show some suff, maybe parameters..\n\n' | ||||
'fubar(param1, param2)') | ||||
# Code completion | ||||
else: | ||||
#lst = [] | ||||
#for x in range(50000): | ||||
# lst.append('%05d' % x) | ||||
#st = " ".join(lst) | ||||
#print len(st) | ||||
#self.AutoCompShow(0, st) | ||||
kw = keyword.kwlist[:] | ||||
kw.sort() # Python sorts are case sensitive | ||||
self.AutoCompSetIgnoreCase(False) # so this needs to match | ||||
# Images are specified with a appended "?type" | ||||
for i in range(len(kw)): | ||||
if kw[i] in keyword.kwlist: | ||||
kw[i] = kw[i]# + "?1" | ||||
self.AutoCompShow(0, " ".join(kw)) | ||||
else: | ||||
event.Skip() | ||||
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) | ||||
def OnMarginClick(self, evt): | ||||
# fold and unfold as needed | ||||
if evt.GetMargin() == 2: | ||||
if evt.GetShift() and evt.GetControl(): | ||||
self.FoldAll() | ||||
else: | ||||
lineClicked = self.LineFromPosition(evt.GetPosition()) | ||||
if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG: | ||||
if evt.GetShift(): | ||||
self.SetFoldExpanded(lineClicked, True) | ||||
self.Expand(lineClicked, True, True, 1) | ||||
elif evt.GetControl(): | ||||
if self.GetFoldExpanded(lineClicked): | ||||
self.SetFoldExpanded(lineClicked, False) | ||||
self.Expand(lineClicked, False, True, 0) | ||||
else: | ||||
self.SetFoldExpanded(lineClicked, True) | ||||
self.Expand(lineClicked, True, True, 100) | ||||
else: | ||||
self.ToggleFold(lineClicked) | ||||
def FoldAll(self): | ||||
lineCount = self.GetLineCount() | ||||
expanding = True | ||||
# find out if we are folding or unfolding | ||||
for lineNum in range(lineCount): | ||||
if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG: | ||||
expanding = not self.GetFoldExpanded(lineNum) | ||||
break | ||||
lineNum = 0 | ||||
while lineNum < lineCount: | ||||
level = self.GetFoldLevel(lineNum) | ||||
if level & stc.STC_FOLDLEVELHEADERFLAG and \ | ||||
(level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE: | ||||
if expanding: | ||||
self.SetFoldExpanded(lineNum, True) | ||||
lineNum = self.Expand(lineNum, True) | ||||
lineNum = lineNum - 1 | ||||
else: | ||||
lastChild = self.GetLastChild(lineNum, -1) | ||||
self.SetFoldExpanded(lineNum, False) | ||||
if lastChild > lineNum: | ||||
self.HideLines(lineNum+1, lastChild) | ||||
lineNum = lineNum + 1 | ||||
def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): | ||||
lastChild = self.GetLastChild(line, level) | ||||
line = line + 1 | ||||
while line <= lastChild: | ||||
if force: | ||||
if visLevels > 0: | ||||
self.ShowLines(line, line) | ||||
else: | ||||
self.HideLines(line, line) | ||||
else: | ||||
if doExpand: | ||||
self.ShowLines(line, line) | ||||
if level == -1: | ||||
level = self.GetFoldLevel(line) | ||||
if level & stc.STC_FOLDLEVELHEADERFLAG: | ||||
if force: | ||||
if visLevels > 1: | ||||
self.SetFoldExpanded(line, True) | ||||
else: | ||||
self.SetFoldExpanded(line, False) | ||||
line = self.Expand(line, doExpand, force, visLevels-1) | ||||
else: | ||||
if doExpand and self.GetFoldExpanded(line): | ||||
line = self.Expand(line, True, force, visLevels-1) | ||||
else: | ||||
line = self.Expand(line, False, force, visLevels-1) | ||||
else: | ||||
line = line + 1 | ||||
return line | ||||
#---------------------------------------------------------------------- | ||||