I want to populate a wx.ComboBox with a large number of items but rather than having to scroll through them all, I’d like to type a string into the ComboBox and narrow the items in the ComboBox using fuzzy search (I’m using ‘thefuzz’).
So I’ve almost got it working but there’s something amiss in the plumbing. I suspect I need to capture a wider range of events so that I can implement backspace and perhaps escape, but the main problem is that I can’t seem to manipulate the TextEntry in the ComboBox the way I’d like.
Has anyone already done this?
Here’s my code so far:
<code>import wx
from thefuzz import process
class ThingSelector(wx.Frame):
def __init__(self, parent, title, thing_descriptions):
super(ThingSelector, self).__init__(parent, title=title, size=(400, 200))
self.thing_descriptions = thing_descriptions
self.init_ui()
def init_ui(self):
panel = wx.Panel(self)
self.thing_choice = wx.ComboBox(panel, choices=self.thing_descriptions)
self.thing_choice_block = False
self.thing_choice_filter = ""
self.thing_choice.Bind(wx.EVT_TEXT, self.on_text)
accept_button = wx.Button(panel, label="Accept")
accept_button.Bind(wx.EVT_BUTTON, self.accept_button_click)
cancel_button = wx.Button(panel, label="Cancel")
cancel_button.Bind(wx.EVT_BUTTON, self.on_cancel_button)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.thing_choice, 0, wx.ALL | wx.EXPAND, 5)
buttons = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(buttons, 0, wx.ALL | wx.EXPAND, 5)
buttons.Add(accept_button, 0, wx.ALL | wx.EXPAND, 5)
buttons.Add(cancel_button, 0, wx.ALL | wx.EXPAND, 5)
panel.SetSizer(sizer)
self.Show()
def update_choices(self, filter):
matches = process.extractBests(filter, self.thing_descriptions, limit=10)
self.thing_choice.SetItems([match[0] for match in matches])
self.thing_choice.SetInsertionPointEnd()
self.thing_choice.Popup()
def on_text(self, event):
# avoid recursion when I call ChangeValue
if not self.thing_choice_block:
self.thing_choice_block = True
current_text = event.GetString()
if len(current_text) == 1:
self.thing_choice_filter += current_text
self.update_choices(self.thing_choice_filter)
self.thing_choice_block = False
else:
self.thing_choice_filter = ""
self.thing_choice.ChangeValue(current_text)
def accept_button_click(self, event):
selected_thing = ""
selection = self.thing_choice.GetSelection()
if selection != wx.NOT_FOUND:
selected_thing = self.thing_choice.GetString(selection)
print(selected_thing)
return
def on_cancel_button(self, event):
self.Close()
# Create list of 'thing's:
thing_descriptions = [ "aaa", "bbb", "ccc" ]
# GUI setup
app = wx.App(False)
frame = ThingSelector(None, title="Select Thing", thing_descriptions=thing_descriptions)
app.MainLoop()
</code>
<code>import wx
from thefuzz import process
class ThingSelector(wx.Frame):
def __init__(self, parent, title, thing_descriptions):
super(ThingSelector, self).__init__(parent, title=title, size=(400, 200))
self.thing_descriptions = thing_descriptions
self.init_ui()
def init_ui(self):
panel = wx.Panel(self)
self.thing_choice = wx.ComboBox(panel, choices=self.thing_descriptions)
self.thing_choice_block = False
self.thing_choice_filter = ""
self.thing_choice.Bind(wx.EVT_TEXT, self.on_text)
accept_button = wx.Button(panel, label="Accept")
accept_button.Bind(wx.EVT_BUTTON, self.accept_button_click)
cancel_button = wx.Button(panel, label="Cancel")
cancel_button.Bind(wx.EVT_BUTTON, self.on_cancel_button)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.thing_choice, 0, wx.ALL | wx.EXPAND, 5)
buttons = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(buttons, 0, wx.ALL | wx.EXPAND, 5)
buttons.Add(accept_button, 0, wx.ALL | wx.EXPAND, 5)
buttons.Add(cancel_button, 0, wx.ALL | wx.EXPAND, 5)
panel.SetSizer(sizer)
self.Show()
def update_choices(self, filter):
matches = process.extractBests(filter, self.thing_descriptions, limit=10)
self.thing_choice.SetItems([match[0] for match in matches])
self.thing_choice.SetInsertionPointEnd()
self.thing_choice.Popup()
def on_text(self, event):
# avoid recursion when I call ChangeValue
if not self.thing_choice_block:
self.thing_choice_block = True
current_text = event.GetString()
if len(current_text) == 1:
self.thing_choice_filter += current_text
self.update_choices(self.thing_choice_filter)
self.thing_choice_block = False
else:
self.thing_choice_filter = ""
self.thing_choice.ChangeValue(current_text)
def accept_button_click(self, event):
selected_thing = ""
selection = self.thing_choice.GetSelection()
if selection != wx.NOT_FOUND:
selected_thing = self.thing_choice.GetString(selection)
print(selected_thing)
return
def on_cancel_button(self, event):
self.Close()
# Create list of 'thing's:
thing_descriptions = [ "aaa", "bbb", "ccc" ]
# GUI setup
app = wx.App(False)
frame = ThingSelector(None, title="Select Thing", thing_descriptions=thing_descriptions)
app.MainLoop()
</code>
import wx
from thefuzz import process
class ThingSelector(wx.Frame):
def __init__(self, parent, title, thing_descriptions):
super(ThingSelector, self).__init__(parent, title=title, size=(400, 200))
self.thing_descriptions = thing_descriptions
self.init_ui()
def init_ui(self):
panel = wx.Panel(self)
self.thing_choice = wx.ComboBox(panel, choices=self.thing_descriptions)
self.thing_choice_block = False
self.thing_choice_filter = ""
self.thing_choice.Bind(wx.EVT_TEXT, self.on_text)
accept_button = wx.Button(panel, label="Accept")
accept_button.Bind(wx.EVT_BUTTON, self.accept_button_click)
cancel_button = wx.Button(panel, label="Cancel")
cancel_button.Bind(wx.EVT_BUTTON, self.on_cancel_button)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.thing_choice, 0, wx.ALL | wx.EXPAND, 5)
buttons = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(buttons, 0, wx.ALL | wx.EXPAND, 5)
buttons.Add(accept_button, 0, wx.ALL | wx.EXPAND, 5)
buttons.Add(cancel_button, 0, wx.ALL | wx.EXPAND, 5)
panel.SetSizer(sizer)
self.Show()
def update_choices(self, filter):
matches = process.extractBests(filter, self.thing_descriptions, limit=10)
self.thing_choice.SetItems([match[0] for match in matches])
self.thing_choice.SetInsertionPointEnd()
self.thing_choice.Popup()
def on_text(self, event):
# avoid recursion when I call ChangeValue
if not self.thing_choice_block:
self.thing_choice_block = True
current_text = event.GetString()
if len(current_text) == 1:
self.thing_choice_filter += current_text
self.update_choices(self.thing_choice_filter)
self.thing_choice_block = False
else:
self.thing_choice_filter = ""
self.thing_choice.ChangeValue(current_text)
def accept_button_click(self, event):
selected_thing = ""
selection = self.thing_choice.GetSelection()
if selection != wx.NOT_FOUND:
selected_thing = self.thing_choice.GetString(selection)
print(selected_thing)
return
def on_cancel_button(self, event):
self.Close()
# Create list of 'thing's:
thing_descriptions = [ "aaa", "bbb", "ccc" ]
# GUI setup
app = wx.App(False)
frame = ThingSelector(None, title="Select Thing", thing_descriptions=thing_descriptions)
app.MainLoop()