Getting a callback when a Tkinter Listbox selection is changed?
You can bind to the <<ListboxSelect>>
event. This event will be generated whenever the selection changes, whether it changes from a button click, via the keyboard, or any other method.
Here's a simple example which updates a label whenever you select something from the listbox:
import tkinter as tk
root = tk.Tk()
label = tk.Label(root)
listbox = tk.Listbox(root)
label.pack(side="bottom", fill="x")
listbox.pack(side="top", fill="both", expand=True)
listbox.insert("end", "one", "two", "three", "four", "five")
def callback(event):
selection = event.widget.curselection()
if selection:
index = selection[0]
data = event.widget.get(index)
label.configure(text=data)
else:
label.configure(text="")
listbox.bind("<<ListboxSelect>>", callback)
root.mainloop()
This event is mentioned in the canonical man page for listbox. All predefined virtual events can be found on the bind man page.
Why does my selection callback get called on my second Listbox?
It is because the event fires when the first list box loses the selection when you click on the other list box. The event fires whenever the selection changes, not just when it is set.
If you don't want the first listbox to lose its selection when you click in the second listbox, set exportselection
to False
for the listboxes. Otherwise, tkinter will only allow one to have a selection at a time.
Tkinter ListboxSelect callback always processes last item in the list
It was indeed tricky to achieve what You've asked for.
Default behavior of Listbox
is to select last item when it gains focus.
To fool it, You can add an empty word as a last item of Your words
list.
This will make it invisible in a Listbox
.
Then in Your process_item
callback, check if selected value is empty (optionally if it's last value as well). If so, remove focus from Listbox
and return from Your callback. This will look as if nothing happened.
Here is modified code:
from tkinter import *
class simple(Frame):
def __init__(self, parent=None):
Frame.__init__(self)
self.master.title('DEMO')
self.master.bind('<Control-q>', quit)
self.pack()
self.create_widgets()
def create_widgets(self):
# Words with dummy "" word in the end
words = ['An', '"empty"', 'selection', 'processes', 'the', 'last', 'item', 'in', 'this', 'list', ""]
self.lb = Listbox(self, width=12, height=25, selectmode=SINGLE, exportselection=False)
self.lb.pack(side=LEFT)
self.lb.bind('<<ListboxSelect>>', self.process_item)
Label(self, anchor=W, text='\n\nClick here\n\n<--\n\nin the emtpy space...').pack(side=RIGHT)
self.lb.insert(0, *words)
def process_item(self, event):
selection = self.lb.curselection()
item = self.lb.get(selection)
if item == "":
# It's last dummy item, remove focus and return
self.master.after(0, self.master.focus)
return
# Real item was clicked
self.do_stuff_with_item(item)
self.lb.delete(selection)
def do_stuff_with_item(self, item):
print(item)
def engage():
root = Tk()
sw = simple(root)
sw.pack()
root.mainloop()
if __name__ == '__main__':
engage()
Calling a function based on a Listbox current selection curselection() in Tkinter
The listbox will fire the virtual event <<ListboxSelect>>
whenever the selection changes. If you bind to it, your function will be called whenever the selection changes, even if it was changed via the keyboard.
Bind callback to selection change on Tkinter Text widget
According to the manual (in the section "The Selection"):
Whenever the sel tag range changes a virtual event <<Selection>> is generated.
so binding the widget for this event should do this for you.
I know you tagged the question for python use, but it is well worth checking the Tk documentation for the details on using the Tk widgets.
python tkinter listbox event binding
listbox
nearest item is found by y
, not x
.
self.nearest(event.x) # wrong
self.nearest(event.y) # right
Update: I didn't notice the real problem first:
listbox = Listbox(frame)
It's not the same listbox which you subclassed, it's another unrelated listbox. Your listbox (which is make_list) is empty, that's why it always returns -1 for nearest.
Perhaps subclassing a frame is a good idea (anyway, better than subclassing listbox and adding a frame with another listbox into it). Then you'll have to bind event on that real listbox which is not empty.
Quick way to see how it will work when fixed is to call nearest
of a real listbox with event.widget
:
self.curIndex = event.widget.nearest(event.y)
How to assign a command or callback to a tkinter listbox
Yes, you can bind <<ListboxSelect>>
to the list box.
listbox = tk.Listbox(root)
listbox.bind("<<ListboxSelect>>", callback)
tkinter Listbox loses its selection when clicking elsewhere on the form
You are doing it wrong. You need to use the config (or configure) method:
lb.configure(exportselection=False)
Related Topics
How to Use Qscrollarea to Make Scrollbars Appear
Check If Two Unordered Lists Are Equal
Underscore _ as Variable Name in Python
How to Retrieve Items from a Dictionary in the Order That They'Re Inserted
Move an Object Every Few Seconds in Pygame
Python: Converting from Iso-8859-1/Latin1 to Utf-8
Difference Between Methods and Functions, in Python Compared to C++
How to Prevent a C Shared Library to Print on Stdout in Python
How to Deal with Multi-Level Column Names Downloaded with Yfinance
How to Sort a List of Tuples According to Another List
How to Scroll Frame Using Mouse Wheel & Adding Horizontal Scrollbar
How to Dynamically Compose an or Query Filter in Django
How to Plot Multiple Functions on the Same Figure, in Matplotlib
How Did Python Implement the Built-In Function Pow()
Cs50: Like Operator, Variable Substitution with % Expansion