Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
220 views
in Technique[技术] by (71.8m points)

python - Unexpected behavior with Tkinter and two Listboxes bound to ListboxSelect event

I’ve encountered an unusual problem in a script that I’m working on. The program is written in Python3/Tkinter. It initializes two Listboxes, wrapped in two separate Frames, and according to the user’s commands displays either one or the other. An exception arises when the user selects on one of the items in the first Listbox, then decides to change screen and selects an item in the second frame.

I’ve created a sample code (below), to replicate the problem. With this code, during runtime, clicking on one item in the first Listbox (white background) makes the second Listbox appear (orange background). Also, the first object is “unpacked” and thus the user should no longer be able to interact with it. Finally, clicking on an item in the second Listbox, raises an IndexError in the “on_select_1” method from the first object. Why would that method execute after that event?? I’m well aware of my limits as a programmer, but I’m pretty sure this is not what is supposed to happen.

import tkinter as tk


class Listbox1(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.lb1 = tk.Listbox(self)
        for i in ["item 1.1", "item 1.2", "item 1.3", "item 1.4"]:
            self.lb1.insert(tk.END, i)
        self.lb1.bind("<<ListboxSelect>>", self.on_select_1)
        self.lb1.pack()

    def on_select_1(self, *args):
        try:
            print("item {} selected in listbox 1".format(self.lb1.curselection()[0]))
        except IndexError:
            print("IndexError raised in method 'on_select_1'")

        self.master.switch()


class Listbox2(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.lb2 = tk.Listbox(self, bg='orange')
        for i in ["item 2.1", "item 2.2", "item 2.3", "item 2.4"]:
            self.lb2.insert(tk.END, i)
        self.lb2.bind("<<ListboxSelect>>", self.on_select_2)
        self.lb2.pack()

    def on_select_2(self, *args):
        print("item {} selected in listbox 2".format(self.lb2.curselection()[0]))


class App(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.pack()
        self.lb1_frame = Listbox1(self)
        self.lb2_frame = Listbox2(self)
        self.lb1_frame.pack()

    def switch(self):
        self.lb1_frame.pack_forget()
        self.lb2_frame.pack()


def main():
    root = tk.Tk()
    app = App(root)
    app.mainloop()


if __name__ == '__main__':
    main()
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

By default, tkinter only allows one widget to hold the selection at a time. Thus, when you select something in your second listbox, the item selected in the first listbox is deselected. That causes your function to be called. When this happens, self.lb1.curselection()[0]) throws an error because the selection is empty.

A simple solution that allows the selection to remain unchanged in the first listbox when you select something in the second listbox is to set the exportselection option to False for both listboxes.

self.lb1 = tk.Listbox(self, exportselection=False)
...
self.lb2 = tk.Listbox(self, bg='orange', exportselection=False)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...