1

I was creating simple listbox containing numbers from 0 to 9. I wanted to print number when it get clicked so i bind list box with Button-1. Im facing problem that is when ever i select any number and try to get its location using list_box.curselection() it does not print any thing(return empty tuple), if i click on on any other number then it print previous selected number. I want to get current selected number.

from tkinter import *
root = Tk()
root.title("test listbox")


list_box = Listbox(root)
list_box.pack()
for i in range(0,10):
    list_box.insert("end",i)

def def_fun(event):
    print(list_box.curselection())
list_box.bind("<Button-1>",def_fun)
root.mainloop()

2 Answers2

2

You don't have to bind to <Button-1> or anything, there is a virtual event with Listbox that you can use here:

def def_fun(event):
    print(event.widget.curselection()) # The widget that triggers the event is event.widget

list_box.bind("<<ListboxSelect>>",def_fun) # Gets triggered each time something is selected

Just in case you are wondering why the Button-1 did not work, it is because there is a delay, the delay might be due to binding order, you can read more about it here but here is a gist:

In the default case, your binding on <Key> happens before the class binding, and it is the class binding where the text is actually inserted into the widget. That is why your binding always seems to be one character behind.

Delrius Euphoria
  • 14,910
  • 3
  • 15
  • 46
1

Change the binding to releasing mouse button, this will also be more user friendly (for example if they accidentally clicked on a selection they didn't want to select, they can move their mouse to a one they want and only releasing will call the function):

from tkinter import Tk, Listbox


def def_fun(event=None):
    print(list_box.curselection())


root = Tk()
root.title("test listbox")

list_box = Listbox(root)
list_box.pack()
for i in range(0, 10):
    list_box.insert("end", i)

list_box.bind("<ButtonRelease-1>", def_fun)
root.mainloop()

Another option if you want to call the function on select is either use @CoolCloud answer or you can also set a delay like this (although it will most certainly work in 99.9% of cases, there might be a case where it doesn't):

list_box.bind("<Button-1>", lambda e: root.after(10, def_fun))

The reason is that .curselection() gets the current selection but Button-1 is triggered before anything gets selected so it will print the previous selection because that is what was selected before and where the current selection is now, and then immediately after this, it will move the current selection to the item you clicked.

Important (because it may cause hard-to-debug issues):
I strongly advise against using wildcard (*) when importing something, You should either import what You need, e.g. from module import Class1, func_1, var_2 and so on or import the whole module: import module then You can also use an alias: import module as md or sth like that, the point is that don't import everything unless You actually know what You are doing; name clashes are the issue.

Also:
I strongly suggest following PEP 8 - Style Guide for Python Code. Function and variable names should be in snake_case, class names in CapitalCase. Don't have space around = if it is used as a part of keyword argument (func(arg='value')) but use if it is used for assigning a value (variable = 'some value'). Have two blank lines around function and class declarations.

Matiiss
  • 5,970
  • 2
  • 12
  • 29