Search code examples
pythonlistboxwxpython

How can I update a wxPython ListBox based on some search string?


The Problem:

How can I update a wx.ListBox based on some search string ? In practical terms: - I have 2 objs: wx.TextCtrl + wx.ListBox - action: once a text is written down in wx.TextCtrl, the list wx.ListBox should be updated with matches

My Code:

def updateList(event):
    # Get all values from wx.ListBox obj 
    searchTerm = str([textareaExpectedResults.GetString(i) for i in range(textareaExpectedResults.GetCount())])
    print searchTerm
    # Get match
    matchValues =  sorted(['entry', 'test'])   
    textareaExpectedResults.Clear()
    i = 0
    for item in matchValues:
        if searchTerm.lower() in item.lower():
             i += 1
             textareaExpectedResults.Append(item)
        else:
             print "not found"
             pass

# Bind the function to search box
searchExpectedResults.Bind(wx.EVT_CHAR, updateList)

Current Output:

not found when I start writting.

Desired Output:

fetch matches, when I start writting. (If I type: "en" then the app should fetch the option "entry". Naturally that entry is present in listbox) Please share a hint on this.

EDIT 1:

# Basic app 

import wx
app = wx.App(redirect=False)
top = wx.Frame(None)
top.SetSize(320,280)
sizer = wx.GridBagSizer()

def on_char(event):
    getValue = searchExpectedResults.GetValue() # get the entered string in TextCtrl with GetValue method
    print getValue
    search_items = sorted(['test', 'entry']) # Create a list of all searchable items in a list
    for item in search_items:            
            if getValue in item:
                print item 
                textareaExpectedResults.Clear()
                textareaExpectedResults.Append(item) # Clear the ListBox and append the matching strings in search_items to the ListBox

searchExpectedResults = wx.TextCtrl(top, -1, "", size=(175, -1))
sizer.Add(searchExpectedResults,(2,8),(2,14),wx.EXPAND)
searchExpectedResults.Bind(wx.EVT_CHAR, on_char) # Bind an EVT_CHAR event to your TextCtrl
search_items = sorted(['test', 'entry'])
textareaExpectedResults = wx.ListBox(top, choices=search_items,  size=(270,250))
sizer.Add(textareaExpectedResults,(6,8),(2,14),wx.EXPAND)
top.Sizer = sizer
top.Sizer.Fit(top)
top.Show()
app.MainLoop()

Solution

  • Here is a step by step guide how to achieve your expectation

    1. Create a list of all searchable items in a list, e.g. name it search_items
    2. Bind an EVT_CHAR event to your TextCtrl, e.g. name your event handler as on_char
    3. In method on_char, get the entered string in TextCtrl with GetValue method
    4. Clear the ListBox and append the matching strings in search_items to the ListBox

    Note: Do not forget to clear the ListBox for every char event. If your searchable items' list is too big, you should use a different approach than clearing/appending method.


    EDIT:

    After reviewing your code, I fixed it as you want without changing it too much. I used wx.EVT_KEY_UP because when your handler is invoked by wx.EVT_CHAR event, you cannot get the latest value of the wx.TextCtrl. If you insist on wx.EVT_CHAR, you can use wx.CallAfter in def on_char(event) by giving a callback function which is guaranteed to be executed after wx.EVT_CHAR completed. Note: You called textareaExpectedResults.Clear() in the for loop which was wrong, I have also moved it before for loop.

    import wx
    app = wx.App(redirect=False)
    top = wx.Frame(None)
    top.SetSize((320, 280))
    sizer = wx.GridBagSizer()
    
    def on_char(event):
        event.Skip()
        getValue = searchExpectedResults.GetValue() # get the entered string in TextCtrl with GetValue method
        print getValue
        search_items = sorted(['test', 'entry']) # Create a list of all searchable items in a list
        textareaExpectedResults.Clear()
        for item in search_items:
            if getValue in item:
                print item
                textareaExpectedResults.Append(item) # Clear the ListBox and append the matching strings in search_items to the ListBox
    
    searchExpectedResults = wx.TextCtrl(top, -1, "", size=(175, -1))
    sizer.Add(searchExpectedResults, (2, 8), (2, 14), wx.EXPAND)
    searchExpectedResults.Bind(wx.EVT_KEY_UP, on_char) # Bind an EVT_CHAR event to your TextCtrl
    search_items = sorted(['test', 'entry'])
    textareaExpectedResults = wx.ListBox(top, choices=search_items, size=(270, 250))
    sizer.Add(textareaExpectedResults, (6, 8), (2, 14), wx.EXPAND)
    top.Sizer = sizer
    top.Sizer.Fit(top)
    top.Show()
    app.MainLoop()
    

    If you want to use wx.EVT_CHAR here is an example showing how to use wx.CallAfter

    ...
    def on_filter():
        getValue = searchExpectedResults.GetValue() # get the entered string in TextCtrl with GetValue method
        print getValue
        search_items = sorted(['test', 'entry']) # Create a list of all searchable items in a list
        textareaExpectedResults.Clear()
        for item in search_items:
            if getValue in item:
                print item
                textareaExpectedResults.Append(item) # Clear the ListBox and append the matching strings in search_items to the ListBox
    
    def on_char(event):
        event.Skip()
        wx.CallAfter(on_filter)
    
    ...
    searchExpectedResults.Bind(wx.EVT_CHAR, on_char) # Bind an EVT_CHAR event to your TextCtrl
    ...