How to avoid tkinter <<ListboxSelect>> and .curselection() detecting events/selection outside of Listbox?

Question:

I am using the following tkinter widgets: Entry and Listbox. I want the Entry widget to display a selected item in the Listbox The Listbox is configured to allow selectmode=tk.SINGLE. Selection is triggered by tkinter built-in virtual event <<ListboxSelect>>. My test script is shown below.

Entry is correctly updated when a Listbox selection is made. However, I am encountering the following issues after a Listbox selection has occurred:

  1. When the mousing pointer is in Entry, and a Left-Button Double Click takes place which leads to selecting the items in Entry, 2 exceptions is thrown up.
  2. If the mouse pointer is place over any windows other than the tkinter GUI, example over IDLE where the test script is shown or over a terminal or ….etc., and when I do a Left-Button Double Click which then selects a word in that window or when I purposely depress the Left Mouse Button to select a paragraph, the same exceptions mentioned in 1. appears.

The errors are shown below.

Note: The above mentioned issues do not occur when there is no selection made in the Listbox.

How do I avoid these two issues? I suspect these issues are related to <<ListboxSelect>> and widget.curselection() is allowed to be triggered outside of Listbox the but don’t know how to investigate this further.

What I want to happen?

  1. The activities of the mouse pointer when in other windows or not over Listbox should not be registered.
  2. Double clicking in Entry should not affect the selection in the Listbox.

Thank you.

Test Script:

import tkinter as tk

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.entrytext = tk.StringVar()
        self.entry = tk.Entry(textvariable=self.entrytext)
        self.listbox = tk.Listbox(selectmode=tk.SINGLE)
        self.entry.pack(side="top", fill="x")
        self.listbox.pack(side="top", fill="both", expand=True)
        for i in range(100):
            self.listbox.insert("end", "item %s" % i)
        self.listbox.bind("<<ListboxSelect>>", self.ListboxSelect)


    def ListboxSelect(self, event):
        widget = event.widget
        try:
            selection=widget.curselection()
            print('nselection = ', selection)
            selection_index = int(selection[0])
            print('selection_index = ', selection_index)
            selection_value = widget.get(selection[0])
            print("selection_value = {} ".format(selection_value))
            self.entrytext.set(selection_value)
        except:
            raise


if __name__=='__main__':
    a = App()
    a.grid()

Exceptions:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.5/idlelib/run.py", line 119, in main
    seq, request = rpc.request_queue.get(block=True, timeout=0.05)
  File "/usr/lib/python3.5/queue.py", line 172, in get
    raise Empty
queue.Empty

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.5/tkinter/__init__.py", line 1553, in __call__
    return self.func(*args)
  File "~/entry_listbox_example.py", line 22, in ListboxSelect
    selection_index = int(selection[0])
IndexError: tuple index out of range
Asked By: Sun Bear

||

Answers:

How to avoid tkinter <<ListboxSelect>> and .curselection() detecting events/selection outside of Listbox?

That depends on exactly what you mean. The <<ListboxSelect>> event was explicitly designed to fire whenever the selection in the listbox changes, no matter how it changes. That could mean when the user selects something new in the listbox, or when the selection is removed from the listbox.

The errors you get are because you assume that there is a selection, which may or may not be true. You need to check for a selection and only run your code if something is selected.

Another solution, or part of the overall solution, might be to set the exportselection option of the listbox to False. When set to True — the default — the selection will be unset whenever any other widget gets the selection. When set to False, the selection won’t change just because another widget gets some or all of its data selected.

Answered By: Bryan Oakley
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.