If the ListboxSelect callback is executed without having an entry selected, i.e the click in the respective listbox has been placed below the last item in the empty space, the "curselection" method always returns the index of the last item in the listbox. Even more, if one tries to use the default "browse" selection method for the listbox and moves the mouse cursor over the listbox while holding mouse button pressed, any move will execute the callback. I tried to find a hint on how to fix it... no luck though.
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 = ['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 )
for w in words:
self.lb.insert ( 0, w )
def process_item ( self, event ):
selection = self.lb.curselection ( )
self.do_stuff_with_item ( self.lb.get ( selection ) )
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()
I tried it with python 2.7, 3.6 ( on Linux ) and with python 3.8 on Win
CodePudding user response:
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()