#!/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") 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) 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 if add: self.text_ctrl.AppendText(history_line+'\n') #------------------------ 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 #---------------------------------------------------------------------- # 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 #----------------------------------------------------------------------