== Introduction == This recipe extends the MozillaStyleListBox recipe. {{{wx.ListBox}}} provides a truly annoying default type-selection behavior: Whatever key you type, it selects the first item that starts with that key. So if, for example, you have a large number of items beginning with '''''w''''' (such as in a list of wxPython constants!), you still have to use the arrow keys (a lot!) or the mouse. This recipe offers a {{{wx.ListBox}}} subclass that will jump directly to the item that starts with the phrase you type in (as opposed to selecting the next item that starts with the character you just typed). It will reset the phrase (to allow the type-selection to begin again from the start) after a definable interval of inactivity. == What Objects are Involved == * {{{wx.ListBox}}} * {{{string.printable}}} == Process Overview == When the user presses any printable key, that key is appended to a selection prefix. The {{{TypeSelectListBox}}} then deselects any items already selected and goes to the first item that begins with the selection phrase. The process is case insensitive. The selection prefix is automatically cleared after a configurable interval, to allow the user to pause, then type a new prefix. This behavior is ''very'' common in GUI applications. This timeout behavior can be suppressed by passing timeout=0 to the constructor. The user may type Backspace to remove the trailing character from the prefix, or Delete to clear the prefix and start over. The selection prefix reset timer is restarted when the user types any character, so the user has the specified interval to locate and press the next key. The timer is restarted even if the user types a non-printable character other than backspace or delete, to allow the user more time to find the key he or she ''meant'' to hit. :) == Special Concerns == If you plan to use any method other than {{{Set}}} to add items to the {{{wx.ListBox}}} (such as {{{Append}}}), you need to provide a method for it that sets self.choices to the items that make up the {{{wx.ListBox}}}. == Code Sample == {{{ #!python import wx from string import printable as _printableChars class TypeSelectListBox( wx.ListBox ): def __init__( self, parent, id, pos=wx.DefaultPosition, size=wx.DefaultSize, choices=[], timeout=1.6, style=0, validator=wx.DefaultValidator, name="listBox" ): """ @param timeout: Type-selection timeout in seconds. To suppress timeout behavior, pass timeout=0. Default value of 1.6 seconds is empirical. """ super(TypeSelectListBox,self).__init__( parent, id, pos, size, choices, style, validator, name ) self._choices = choices self._prefix = '' self._timeoutInterval = timeout if self._timeoutInterval: # Convert timeout interval to milliseconds self._timeoutInterval = int( 1000 * self._timeoutInterval ) # Timeout was specified, so initialize my timer. self._timerId = wx.NewId() self._timer = wx.Timer( self, self._timerId ) # Process timeouts self.Bind( wx.EVT_TIMER, self._onTimer ) # Intercept character key events self.Bind( wx.EVT_CHAR, self._onEvtChar ) def _onTimer( self, evt ): """ Clear prefix, but don't clear current selection. When user resumes typing, we'll start with a new prefix. Do not restart timer. It will be restarted when (if) user resumes typing. """ self._prefix = '' def _startTimer( self ): # (Re)start the timeout timer if self._timeoutInterval: self._timer.Start( self._timeoutInterval, wx.TIMER_ONE_SHOT ) def _onEvtChar( self, evt ): # Suspend timer self._timer.Stop() keycode = evt.GetKeyCode() if keycode == wx.WXK_BACK: # Backspace removes last letter self._prefix = self._prefix[:-1] elif keycode == wx.WXK_DELETE: # Delete erases prefix self._prefix = '' elif keycode>127 or chr(keycode) not in _printableChars: # Do the default action for nonascii keycodes evt.Skip() # Restart the timer self._startTimer() return else: # It's a printable character self._prefix += chr(keycode).lower() # De-select currently-selected item for item in self.GetSelections(): self.Deselect(item) if self._prefix != '': # Locate first item with this prefix for choice in self._choices: if choice.lower().startswith( self._prefix ): self.SetStringSelection( choice ) break # Restart the timer self._startTimer() def Set( self, choices ): self._choices = choices self._prefix = '' wx.ListBox.Set(self, choices) return 0 }}} === Comments === Please place any questions or comments here. I've subscribed to the page, so I should see any changes.