繁体   English   中英

Tkinter 和绑定到 ListboxSelect 事件的两个列表框的意外行为

[英]Unexpected behavior with Tkinter and two Listboxes bound to ListboxSelect event

我在我正在处理的脚本中遇到了一个不寻常的问题。 该程序是用 Python3/Tkinter 编写的。 它初始化两个列表框,包装在两个单独的框架中,并根据用户的命令显示一个或另一个。 当用户选择第一个 Listbox 中的一个项目,然后决定更改屏幕并选择第二个框架中的一个项目时,就会出现异常。

我已经创建了一个示例代码(如下)来复制这个问题。 使用此代码,在运行时,单击第一个列表框(白色背景)中的一个项目会显示第二个列表框(橙色背景)。 此外,第一个对象已“解包”,因此用户不应再与其进行交互。 最后,单击第二个 Listbox 中的项目,会在第一个对象的“on_select_1”方法中引发 IndexError。 为什么那个方法会在那个事件之后执行? 我很清楚自己作为程序员的局限性,但我很确定这不是应该发生的。

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()

默认情况下,tkinter 一次只允许一个小部件保存选择。 因此,当您在第二个列表框中选择某些内容时,第一个列表框中选择的项目将被取消选择。 这会导致您的函数被调用。 发生这种情况时, self.lb1.curselection()[0])会抛出错误,因为选择为空。

当您在第二个列表框中选择某些内容时,允许选择在第一个列表框中保持不变的简单解决方案是将两个列表框的exportselection选择选项设置为False

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

基于这个答案,来自@BryanOakley 的评论:

该事件不代表单击,该事件代表“当前项目已更改”,它可能并不总是具有 x 和 ay(即:如果您使用键盘更改当前选择)。 event.widget.curselection() 是你应该使用的。

因此,如果列表框被销毁,则新选择将按照 文档

.curselection()
返回包含所选元素的行号的元组,从 0 开始计数。如果未选择任何内容,则返回一个空元组。 .

因此,您将 IndexError 作为self.lb1.curselection()==() ,这导致()[0]

这个问题肯定存在于 Windows 10 上的 Python 3.8 中,甚至将我的两个列表框之一更改为export=FALSE解决了这个问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM