greg@268: greg@268: ''' greg@268: greg@268: wxPython Custom Widget Collection 20060207 greg@268: Written By: Edward Flick (eddy -=at=- cdf-imaging -=dot=- com) greg@268: Michele Petrazzo (michele -=dot=- petrazzo -=at=- unipex -=dot=- it) greg@268: Will Sadkin (wsadkin-=at=- nameconnector -=dot=- com) greg@268: Copyright 2006 (c) CDF Inc. ( http://www.cdf-imaging.com ) greg@268: Contributed to the wxPython project under the wxPython project's license. greg@268: greg@268: ''' greg@268: greg@268: import locale, wx, sys, cStringIO greg@268: greg@268: import wx.lib.mixins.listctrl as listmix greg@268: import cPickle greg@268: from wx import ImageFromStream, BitmapFromImage greg@268: #---------------------------------------------------------------------- greg@268: def getSmallUpArrowData(): greg@268: return \ greg@268: '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ greg@268: \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ greg@268: \x00\x00C\xb0\x89\ greg@268: \xd3.\x10\xd1m\xc3\xe5*\xbc.\x80i\xc2\x17.\x8c\xa3y\x81\x01\x00\xa1\x0e\x04e\ greg@268: ?\x84B\xef\x00\x00\x00\x00IEND\xaeB`\x82" greg@268: greg@268: def getSmallDnArrowBitmap(): greg@268: return BitmapFromImage(getSmallDnArrowImage()) greg@268: greg@268: def getSmallDnArrowImage(): greg@268: stream = cStringIO.StringIO(getSmallDnArrowData()) greg@268: return ImageFromStream(stream) greg@268: #---------------------------------------------------------------------- greg@268: greg@268: class myListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): greg@268: def __init__(self, parent, ID=-1, pos=wx.DefaultPosition, greg@268: size=wx.DefaultSize, style=0): greg@268: wx.ListCtrl.__init__(self, parent, ID, pos, size, style) greg@268: listmix.ListCtrlAutoWidthMixin.__init__(self) greg@268: greg@268: class TextCtrlAutoComplete (wx.TextCtrl, listmix.ColumnSorterMixin ): greg@268: greg@268: def __init__ ( self, parent, colNames=None, choices = None, greg@268: multiChoices=None, showHead=True, dropDownClick=True, greg@268: colFetch=-1, colSearch=0, hideOnNoMatch=True, greg@268: selectCallback=None, entryCallback=None, matchFunction=None, greg@268: element_path=None, **therest) : greg@268: ''' greg@268: Constructor works just like wx.TextCtrl except you can pass in a greg@268: list of choices. You can also change the choice list at any time greg@268: by calling setChoices. greg@268: ''' greg@268: greg@268: if therest.has_key('style'): greg@268: therest['style']=wx.TE_PROCESS_ENTER | therest['style'] greg@268: else: greg@268: therest['style']=wx.TE_PROCESS_ENTER greg@268: greg@268: wx.TextCtrl.__init__(self, parent, **therest ) greg@268: greg@268: #Some variables greg@268: self._dropDownClick = dropDownClick greg@268: self._colNames = colNames greg@268: self._multiChoices = multiChoices greg@268: self._showHead = showHead greg@268: self._choices = choices greg@268: self._lastinsertionpoint = 0 greg@268: self._hideOnNoMatch = hideOnNoMatch greg@268: self._selectCallback = selectCallback greg@268: self._entryCallback = entryCallback greg@268: self._matchFunction = matchFunction greg@268: greg@268: self._screenheight = wx.SystemSettings.GetMetric( wx.SYS_SCREEN_Y ) greg@268: self.element_path = element_path greg@268: #sort variable needed by listmix greg@268: self.itemDataMap = dict() greg@268: greg@268: #widgets greg@268: self.dropdown = wx.PopupWindow( self ) greg@268: greg@268: #Control the style greg@268: flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING greg@268: if not (showHead and multiChoices) : greg@268: flags = flags | wx.LC_NO_HEADER greg@268: greg@268: #Create the list and bind the events greg@268: self.dropdownlistbox = myListCtrl( self.dropdown, style=flags, greg@268: pos=wx.Point( 0, 0) ) greg@268: greg@268: #initialize the parent greg@268: if multiChoices: ln = len(multiChoices) greg@268: else: ln = 1 greg@268: #else: ln = len(choices) greg@268: listmix.ColumnSorterMixin.__init__(self, ln) greg@268: greg@268: #load the data greg@268: if multiChoices: self.SetMultipleChoices (multiChoices, colSearch=colSearch, colFetch=colFetch) greg@268: else: self.SetChoices ( choices ) greg@268: greg@268: #gp = self greg@268: #while ( gp != None ) : greg@268: # gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp ) greg@268: # gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp ) greg@268: # gp = gp.GetParent() greg@268: greg@268: self.Bind( wx.EVT_KILL_FOCUS, self.onControlChanged, self ) greg@268: self.Bind( wx.EVT_TEXT , self.onEnteredText, self ) greg@268: self.Bind( wx.EVT_KEY_DOWN , self.onKeyDown, self ) greg@268: greg@268: #If need drop down on left click greg@268: if dropDownClick: greg@268: self.Bind ( wx.EVT_LEFT_DOWN , self.onClickToggleDown, self ) greg@268: self.Bind ( wx.EVT_LEFT_UP , self.onClickToggleUp, self ) greg@268: greg@268: self.dropdown.Bind( wx.EVT_LISTBOX , self.onListItemSelected, self.dropdownlistbox ) greg@268: self.dropdownlistbox.Bind(wx.EVT_LEFT_DOWN, self.onListClick) greg@268: self.dropdownlistbox.Bind(wx.EVT_LEFT_DCLICK, self.onListDClick) greg@268: self.dropdownlistbox.Bind(wx.EVT_LIST_COL_CLICK, self.onListColClick) greg@268: greg@268: self.il = wx.ImageList(16, 16) greg@268: greg@268: self.sm_dn = self.il.Add(getSmallDnArrowBitmap()) greg@268: self.sm_up = self.il.Add(getSmallUpArrowBitmap()) greg@268: greg@268: self.dropdownlistbox.SetImageList(self.il, wx.IMAGE_LIST_SMALL) greg@268: self._ascending = True greg@268: greg@268: greg@268: #-- methods called from mixin class greg@268: def GetSortImages(self): greg@268: return (self.sm_dn, self.sm_up) greg@268: greg@268: def GetListCtrl(self): greg@268: return self.dropdownlistbox greg@268: greg@268: # -- event methods greg@268: def onListClick(self, evt): greg@268: toSel, flag = self.dropdownlistbox.HitTest( evt.GetPosition() ) greg@268: #no values on poition, return greg@268: if toSel == -1: return greg@268: self.dropdownlistbox.Select(toSel) greg@268: greg@268: def onListDClick(self, evt): greg@268: self._setValueFromSelected() greg@268: greg@268: def onListColClick(self, evt): greg@268: col = evt.GetColumn() greg@268: greg@268: #reverse the sort greg@268: if col == self._colSearch: greg@268: self._ascending = not self._ascending greg@268: greg@268: self.SortListItems( evt.GetColumn(), ascending=self._ascending ) greg@268: self._colSearch = evt.GetColumn() greg@268: evt.Skip() greg@268: greg@268: def onEnteredText(self, event): greg@268: text = event.GetString() greg@268: greg@268: if self._entryCallback: greg@268: self._entryCallback() greg@268: greg@268: if not text: greg@268: # control is empty; hide dropdown if shown: greg@268: if self.dropdown.IsShown(): greg@268: self._showDropDown(False) greg@268: event.Skip() greg@268: return greg@268: greg@268: greg@268: found = False greg@268: if self._multiChoices: greg@268: #load the sorted data into the listbox greg@268: dd = self.dropdownlistbox greg@268: choices = [dd.GetItem(x, self._colSearch).GetText() greg@268: for x in xrange(dd.GetItemCount())] greg@268: else: greg@268: choices = self._choices greg@268: greg@268: for numCh, choice in enumerate(choices): greg@268: if self._matchFunction and self._matchFunction(text, choice): greg@268: found = True greg@268: elif choice.lower().startswith(text.lower()) : greg@268: found = True greg@268: if found: greg@268: self._showDropDown(True) greg@268: item = self.dropdownlistbox.GetItem(numCh) greg@268: toSel = item.GetId() greg@268: self.dropdownlistbox.Select(toSel) greg@268: break greg@268: greg@268: if not found: greg@268: self.dropdownlistbox.Select(self.dropdownlistbox.GetFirstSelected(), False) greg@268: if self._hideOnNoMatch: greg@268: self._showDropDown(False) greg@268: greg@268: self._listItemVisible() greg@268: greg@268: event.Skip () greg@268: greg@268: def onKeyDown ( self, event ) : greg@268: """ Do some work when the user press on the keys: greg@268: up and down: move the cursor greg@268: left and right: move the search greg@268: """ greg@268: skip = True greg@268: sel = self.dropdownlistbox.GetFirstSelected() greg@268: visible = self.dropdown.IsShown() greg@268: greg@268: KC = event.GetKeyCode() greg@268: if KC == wx.WXK_DOWN : greg@268: if sel < (self.dropdownlistbox.GetItemCount () - 1) : greg@268: self.dropdownlistbox.Select ( sel+1 ) greg@268: self._listItemVisible() greg@268: self._showDropDown () greg@268: skip = False greg@268: elif KC == wx.WXK_UP : greg@268: if sel > 0 : greg@268: self.dropdownlistbox.Select ( sel - 1 ) greg@268: self._listItemVisible() greg@268: self._showDropDown () greg@268: skip = False greg@268: elif KC == wx.WXK_LEFT : greg@268: if not self._multiChoices: return greg@268: if self._colSearch > 0: greg@268: self._colSearch -=1 greg@268: self._showDropDown () greg@268: elif KC == wx.WXK_RIGHT: greg@268: if not self._multiChoices: return greg@268: if self._colSearch < self.dropdownlistbox.GetColumnCount() -1: greg@268: self._colSearch += 1 greg@268: self._showDropDown() greg@268: greg@268: if visible : greg@268: if event.GetKeyCode() == wx.WXK_RETURN : greg@268: self._setValueFromSelected() greg@268: skip = False greg@268: if event.GetKeyCode() == wx.WXK_ESCAPE : greg@268: self._showDropDown( False ) greg@268: skip = False greg@268: if skip : greg@268: event.Skip() greg@268: greg@268: def onListItemSelected (self, event): greg@268: self._setValueFromSelected() greg@268: event.Skip() greg@268: greg@268: def onClickToggleDown(self, event): greg@268: self._lastinsertionpoint = self.GetInsertionPoint() greg@268: event.Skip () greg@268: greg@268: def onClickToggleUp ( self, event ) : greg@268: if ( self.GetInsertionPoint() == self._lastinsertionpoint ) : greg@268: self._showDropDown ( not self.dropdown.IsShown() ) greg@268: event.Skip () greg@268: greg@268: def onControlChanged(self, event): greg@268: res = self.GetValue() greg@268: config = wx.ConfigBase.Get() greg@268: listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([])))) greg@268: if len(res) and res not in listentries: greg@268: config.Write(self.element_path, cPickle.dumps((listentries + [res])[-10:])) greg@268: config.Flush() greg@268: self.SetChoices((listentries + [res])[-10:]) greg@268: self._showDropDown( False ) greg@268: event.Skip() greg@268: greg@268: greg@268: # -- Interfaces methods greg@268: def SetMultipleChoices(self, choices, colSearch=0, colFetch=-1): greg@268: ''' Set multi-column choice greg@268: ''' greg@268: self._multiChoices = choices greg@268: self._choices = None greg@268: if not isinstance(self._multiChoices, list): greg@268: self._multiChoices = [ x for x in self._multiChoices] greg@268: greg@268: flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING greg@268: if not self._showHead: greg@268: flags |= wx.LC_NO_HEADER greg@268: self.dropdownlistbox.SetWindowStyleFlag(flags) greg@268: greg@268: #prevent errors on "old" systems greg@268: if sys.version.startswith("2.3"): greg@268: self._multiChoices.sort(lambda x, y: cmp(x[0].lower(), y[0].lower())) greg@268: else: greg@268: self._multiChoices.sort(key=lambda x: locale.strxfrm(x[0]).lower() ) greg@268: greg@268: self._updateDataList(self._multiChoices) greg@268: greg@268: lChoices = len(choices) greg@268: if lChoices < 2: greg@268: raise ValueError, "You have to pass me a multi-dimension list" greg@268: greg@268: for numCol, rowValues in enumerate(choices[0]): greg@268: greg@268: if self._colNames: colName = self._colNames[numCol] greg@268: else: colName = "Select %i" % numCol greg@268: greg@268: self.dropdownlistbox.InsertColumn(numCol, colName) greg@268: greg@268: for numRow, valRow in enumerate(choices): greg@268: greg@268: for numCol, colVal in enumerate(valRow): greg@268: if numCol == 0: greg@268: index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1) greg@268: self.dropdownlistbox.SetStringItem(index, numCol, colVal) greg@268: self.dropdownlistbox.SetItemData(index, numRow) greg@268: greg@268: self._setListSize() greg@268: self._colSearch = colSearch greg@268: self._colFetch = colFetch greg@268: greg@268: def SetChoices(self, choices): greg@268: ''' greg@268: Sets the choices available in the popup wx.ListBox. greg@268: The items will be sorted case insensitively. greg@268: ''' greg@268: self._choices = choices greg@268: self._multiChoices = None greg@268: flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING | wx.LC_NO_HEADER greg@268: self.dropdownlistbox.SetWindowStyleFlag(flags) greg@268: greg@268: #if not isinstance(choices, list): greg@268: # self._choices = [ x for x in choices if len(x)] greg@268: self._choices = [ x for x in choices if len(x)] greg@268: #prevent errors on "old" systems greg@268: if sys.version.startswith("2.3"): greg@268: self._choices.sort(lambda x, y: cmp(x.lower(), y.lower())) greg@268: else: greg@268: self._choices.sort(key=lambda x: locale.strxfrm(x).lower()) greg@268: greg@268: self._updateDataList(self._choices) greg@268: greg@268: self.dropdownlistbox.InsertColumn(0, "") greg@268: greg@268: for num, colVal in enumerate(self._choices): greg@268: index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1) greg@268: greg@268: self.dropdownlistbox.SetStringItem(index, 0, colVal) greg@268: self.dropdownlistbox.SetItemData(index, num) greg@268: greg@268: self._setListSize() greg@268: greg@268: # there is only one choice for both search and fetch if setting a single column: greg@268: self._colSearch = 0 greg@268: self._colFetch = -1 greg@268: greg@268: def GetChoices(self): greg@268: if self._choices: greg@268: return self._choices greg@268: else: greg@268: return self._multiChoices greg@268: greg@268: def SetSelectCallback(self, cb=None): greg@268: self._selectCallback = cb greg@268: greg@268: def SetEntryCallback(self, cb=None): greg@268: self._entryCallback = cb greg@268: greg@268: def SetMatchFunction(self, mf=None): greg@268: self._matchFunction = mf greg@268: greg@268: greg@268: #-- Internal methods greg@268: def _setValueFromSelected( self ) : greg@268: ''' greg@268: Sets the wx.TextCtrl value from the selected wx.ListCtrl item. greg@268: Will do nothing if no item is selected in the wx.ListCtrl. greg@268: ''' greg@268: sel = self.dropdownlistbox.GetFirstSelected() greg@268: if sel > -1: greg@268: if self._colFetch != -1: col = self._colFetch greg@268: else: col = self._colSearch greg@268: greg@268: itemtext = self.dropdownlistbox.GetItem(sel, col).GetText() greg@268: if self._selectCallback: greg@268: dd = self.dropdownlistbox greg@268: values = [dd.GetItem(sel, x).GetText() greg@268: for x in xrange(dd.GetColumnCount())] greg@268: self._selectCallback( values ) greg@268: greg@268: self.SetValue (itemtext) greg@268: self.SetInsertionPointEnd () greg@268: self.SetSelection ( -1, -1 ) greg@268: self._showDropDown ( False ) greg@268: greg@268: greg@268: def _showDropDown ( self, show = True ) : greg@268: ''' greg@268: Either display the drop down list (show = True) or hide it (show = False). greg@268: ''' greg@268: if show : greg@268: size = self.dropdown.GetSize() greg@268: width, height = self . GetSizeTuple() greg@268: x, y = self . ClientToScreenXY ( 0, height ) greg@268: if size.GetWidth() != width : greg@268: size.SetWidth(width) greg@268: self.dropdown.SetSize(size) greg@268: self.dropdownlistbox.SetSize(self.dropdown.GetClientSize()) greg@268: if (y + size.GetHeight()) < self._screenheight : greg@268: self.dropdown . SetPosition ( wx.Point(x, y) ) greg@268: else: greg@268: self.dropdown . SetPosition ( wx.Point(x, y - height - size.GetHeight()) ) greg@268: self.dropdown.Show ( show ) greg@268: greg@268: def _listItemVisible( self ) : greg@268: ''' greg@268: Moves the selected item to the top of the list ensuring it is always visible. greg@268: ''' greg@268: toSel = self.dropdownlistbox.GetFirstSelected () greg@268: if toSel == -1: return greg@268: self.dropdownlistbox.EnsureVisible( toSel ) greg@268: greg@268: def _updateDataList(self, choices): greg@268: #delete, if need, all the previous data greg@268: if self.dropdownlistbox.GetColumnCount() != 0: greg@268: self.dropdownlistbox.DeleteAllColumns() greg@268: self.dropdownlistbox.DeleteAllItems() greg@268: greg@268: #and update the dict greg@268: if choices: greg@268: for numVal, data in enumerate(choices): greg@268: self.itemDataMap[numVal] = data greg@268: else: greg@268: numVal = 0 greg@268: self.SetColumnCount(numVal) greg@268: greg@268: def _setListSize(self): greg@268: if self._multiChoices: greg@268: choices = self._multiChoices greg@268: else: greg@268: choices = self._choices greg@268: greg@268: longest = 0 greg@268: for choice in choices : greg@268: longest = max(len(choice), longest) greg@268: greg@268: longest += 3 greg@268: itemcount = min( len( choices ) , 7 ) + 2 greg@268: charheight = self.dropdownlistbox.GetCharHeight() greg@268: charwidth = self.dropdownlistbox.GetCharWidth() greg@268: self.popupsize = wx.Size( charwidth*longest, charheight*itemcount ) greg@268: self.dropdownlistbox.SetSize ( self.popupsize ) greg@268: self.dropdown.SetClientSize( self.popupsize ) greg@268: greg@268: greg@268: greg@268: greg@268: class test: greg@268: def __init__(self): greg@268: args = dict() greg@268: if 1: greg@268: args["colNames"] = ("col1", "col2") greg@268: args["multiChoices"] = [ ("Zoey","WOW"), ("Alpha", "wxPython"), greg@268: ("Ceda","Is"), ("Beta", "fantastic"), greg@268: ("zoebob", "!!")] greg@268: args["colFetch"] = 1 greg@268: else: greg@268: args["choices"] = ["123", "cs", "cds", "Bob","Marley","Alpha"] greg@268: greg@268: args["selectCallback"] = self.selectCallback greg@268: greg@268: self.dynamic_choices = [ greg@268: 'aardvark', 'abandon', 'acorn', 'acute', 'adore', greg@268: 'aegis', 'ascertain', 'asteroid', greg@268: 'beautiful', 'bold', 'classic', greg@268: 'daring', 'dazzling', 'debonair', 'definitive', greg@268: 'effective', 'elegant', greg@268: 'http://python.org', 'http://www.google.com', greg@268: 'fabulous', 'fantastic', 'friendly', 'forgiving', 'feature', greg@268: 'sage', 'scarlet', 'scenic', 'seaside', 'showpiece', 'spiffy', greg@268: 'www.wxPython.org', 'www.osafoundation.org' greg@268: ] greg@268: greg@268: greg@268: app = wx.PySimpleApp() greg@268: frm = wx.Frame(None,-1,"Test",style=wx.TAB_TRAVERSAL|wx.DEFAULT_FRAME_STYLE) greg@268: panel = wx.Panel(frm) greg@268: sizer = wx.BoxSizer(wx.VERTICAL) greg@268: greg@268: self._ctrl = TextCtrlAutoComplete(panel, **args) greg@268: but = wx.Button(panel,label="Set other multi-choice") greg@268: but.Bind(wx.EVT_BUTTON, self.onBtMultiChoice) greg@268: but2 = wx.Button(panel,label="Set other one-colum choice") greg@268: but2.Bind(wx.EVT_BUTTON, self.onBtChangeChoice) greg@268: but3 = wx.Button(panel,label="Set the starting choices") greg@268: but3.Bind(wx.EVT_BUTTON, self.onBtStartChoices) greg@268: but4 = wx.Button(panel,label="Enable dynamic choices") greg@268: but4.Bind(wx.EVT_BUTTON, self.onBtDynamicChoices) greg@268: greg@268: sizer.Add(but, 0, wx.ADJUST_MINSIZE, 0) greg@268: sizer.Add(but2, 0, wx.ADJUST_MINSIZE, 0) greg@268: sizer.Add(but3, 0, wx.ADJUST_MINSIZE, 0) greg@268: sizer.Add(but4, 0, wx.ADJUST_MINSIZE, 0) greg@268: sizer.Add(self._ctrl, 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0) greg@268: panel.SetAutoLayout(True) greg@268: panel.SetSizer(sizer) greg@268: sizer.Fit(panel) greg@268: sizer.SetSizeHints(panel) greg@268: panel.Layout() greg@268: app.SetTopWindow(frm) greg@268: frm.Show() greg@268: but.SetFocus() greg@268: app.MainLoop() greg@268: greg@268: def onBtChangeChoice(self, event): greg@268: #change the choices greg@268: self._ctrl.SetChoices(["123", "cs", "cds", "Bob","Marley","Alpha"]) greg@268: self._ctrl.SetEntryCallback(None) greg@268: self._ctrl.SetMatchFunction(None) greg@268: greg@268: def onBtMultiChoice(self, event): greg@268: #change the choices greg@268: self._ctrl.SetMultipleChoices( [ ("Test","Hello"), ("Other word","World"), greg@268: ("Yes!","it work?") ], colFetch = 1 ) greg@268: self._ctrl.SetEntryCallback(None) greg@268: self._ctrl.SetMatchFunction(None) greg@268: greg@268: def onBtStartChoices(self, event): greg@268: #change the choices greg@268: self._ctrl.SetMultipleChoices( [ ("Zoey","WOW"), ("Alpha", "wxPython"), greg@268: ("Ceda","Is"), ("Beta", "fantastic"), greg@268: ("zoebob", "!!")], colFetch = 1 ) greg@268: self._ctrl.SetEntryCallback(None) greg@268: self._ctrl.SetMatchFunction(None) greg@268: greg@268: def onBtDynamicChoices(self, event): greg@268: ''' greg@268: Demonstrate dynamic adjustment of the auto-complete list, based on what's greg@268: been typed so far: greg@268: ''' greg@268: self._ctrl.SetChoices(self.dynamic_choices) greg@268: self._ctrl.SetEntryCallback(self.setDynamicChoices) greg@268: self._ctrl.SetMatchFunction(self.match) greg@268: greg@268: greg@268: def match(self, text, choice): greg@268: ''' greg@268: Demonstrate "smart" matching feature, by ignoring http:// and www. when doing greg@268: matches. greg@268: ''' greg@268: t = text.lower() greg@268: c = choice.lower() greg@268: if c.startswith(t): return True greg@268: if c.startswith(r'http://'): c = c[7:] greg@268: if c.startswith(t): return True greg@268: if c.startswith('www.'): c = c[4:] greg@268: return c.startswith(t) greg@268: greg@268: def setDynamicChoices(self): greg@268: ctrl = self._ctrl greg@268: text = ctrl.GetValue().lower() greg@268: current_choices = ctrl.GetChoices() greg@268: choices = [choice for choice in self.dynamic_choices if self.match(text, choice)] greg@268: if choices != current_choices: greg@268: ctrl.SetChoices(choices) greg@268: greg@268: def selectCallback(self, values): greg@268: """ Simply function that receive the row values when the greg@268: user select an item greg@268: """ greg@268: print "Select Callback called...:", values greg@268: greg@268: greg@268: if __name__ == "__main__": greg@268: test() greg@268: