Home > Enterprise >  multiple lists with the same callback function lead to IndexError
multiple lists with the same callback function lead to IndexError

Time:01-31

I'm using tkinter for GUI and I create two lists:

# widgets     
create_list(win, 20, 20, ["Test", "Apfel", "Birne"])     
create_list(win, 220, 20, ["Alpha", "Beta", "Gamma", "Delta"])

where create_list is a function:

def create_list(win, xx, yy, items=\[\]):
    lb = Listbox(win)
    i = 1
    for item in items:
        lb.insert(i, item)
        i  = 1    
    lb.bind('<<ListboxSelect>>', on_select)
    lb.place(x=xx, y=yy) 

Nothing special so far. I have one on_select function as callback for selection changes for both lists.

def on_select(event):         
    w = event.widget         
    index = int(w.curselection()[0])         
    value = w.get(index)         
    print(f'You selected item {index}: {value}')`

it works as expected, prints me the selected item. However if I click an item from the second list (and wise versa) I get the error:

Exception in Tkinter callback Traceback (most recent call last):
File "/usr/lib/python3.8/tkinter/init.py", line 1892, in call return self.func(*args) File "/home/userx/projects/python/modules/ws_list.py", line 8, in on_select index = int(w.curselection()[0]) IndexError: tuple index out of range

If I then select another item in the same list, the issue is gone. How am I solving that? Do I need a different callback function for each list?

CodePudding user response:

According to this answer, the <<ListboxSelect>> event will be fired if selection is removed from a listbox, which is what happens when you switch between lists. This will then result in an empty curselection which is what causes your error. To avoid this, check if the selection is empty before using it.

def on_select(event):         
    w = event.widget
    selection = w.curselection()
    if selection:
        index = int(selection[0])         
        value = w.get(index)         
        print(f'You selected item {index}: {value}')

CodePudding user response:

When you change from listbox1 to listbox2, the on_select is triggered on both. So either you account for the posibility that w.curselection() is empty, and hence:

def on_select(event):         
    w = event.widget   
    if w.curselection():
       index = int(w.curselection()[0])         
       value = w.get(index)         
       print(f'You selected item {index}: {value} in listbox: {w}')

or use exportselection=False when creating the LB to allow selecting from them individually.

You can also give listboxes a name (name=xxx) and you'll be able to differentiate which listbox the on_select is called from.

  • Related