【问题标题】:Why are multiple functions being called for this tkinter event binding? (solved)为什么要为此 tkinter 事件绑定调用多个函数? (解决了)
【发布时间】:2021-08-09 23:58:19
【问题描述】:

下面的代码 sn-p 创建了两个列表框。我已经编写了代码,以便:

  • 从“可用”列表中选择的项目在单击该项目时会传输到“已选择”列表(通过 OnASelect 方法控制)
  • 传输的项目的索引保存在“可用”列表中(它被替换为空字符串)
  • 从“已选择”列表中选择的项目在单击该项目时会转移回“可用”列表
  • 由于保留了“可用”列表的索引,因此无论单击什么顺序,项目总是按原始顺序传回。

这是我的问题:

  • 尽管小部件按预期工作,但在将项目返回到“可用”列表时,我仍然收到对 OnASelect 的不必要的函数调用,这会导致索引错误。这个小部件适用于我正在处理的一个更大的项目,我想避免这导致问题。
  • 出于故障排除的目的,我编写了代码,以便在发生传输时发送打印语句,显示“A>S 功能已激活”或“S>A 功能已激活”。李>
  • 将项目从“可用”转移到“已选”时的输出:
The A>S function was activated
  • 将项目从“已选择”转移到“可用”时的输出:
The S>A function was activated
The A>S function was activated
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\lib\tkinter\__init__.py", line 1883, in __call__
    return self.func(*args)
  File "x:/Mouselight Data Management/GUI_Branch/GUI_Menu_Related/Test/test2.py", line 29, in OnASelect
    AselectionIndex = int(event.widget.curselection()[0])
IndexError: tuple index out of range
PS X:\Mouselight Data Management\GUI_Branch> The A>S function was activate

我已经浪费了太多时间试图弄清楚它为什么会这样,我很难过。对此问题的任何帮助将不胜感激!

附言。我知道在 OnASelect 方法的开头添加一个 'if event.widget.curselection():' 会绕过错误,但我想阻止多个函数调用开始。

完整代码:

from tkinter import *

class DependentLists(Tk):
    def __init__(self): 
        Tk.__init__(self)
        
        self.mainframe = Frame(self)
        self.mainframe.place(relx=0.5, rely=0.25, anchor=CENTER)

        completelabel = Label(self.mainframe, text = "Available:")
        completelabel.grid(row=2, column = 0)
        selectedlabel = Label(self.mainframe, text = "Selected:")
        selectedlabel.grid(row=2, column = 1)
        
        self.Available = Listbox(self.mainframe)
        self.Available.grid(row=3, column = 0)
        self.AList = []
        for i in range(6):
            self.Available.insert(END,i)
            self.AList.append(i)
        self.Available.bind('<<ListboxSelect>>', self.OnASelect)

        self.Selected = Listbox(self.mainframe)
        self.Selected.grid(row=3, column = 1)
        self.Selected.bind('<<ListboxSelect>>', self.OnSSelect)

    def OnASelect(self, event):
        print('The A>S function was activated')
        AselectionIndex = int(event.widget.curselection()[0])
        Aselection = event.widget.get(AselectionIndex)
        self.Available.delete(AselectionIndex)
        self.Available.insert(AselectionIndex,'')
        self.Selected.insert(END, Aselection)

    def OnSSelect(self, event):
        print('The S>A function was activated')
        SselectionIndex = int(event.widget.curselection()[0])
        Sselection = event.widget.get(SselectionIndex)
        self.Selected.delete(ANCHOR)
        self.Available.delete(self.AList.index(Sselection))
        self.Available.insert(self.AList.index(Sselection), Sselection)

App = DependentLists()

screen_width = App.winfo_screenwidth()
screen_height = App.winfo_screenheight()
window_height = screen_height - 800
window_width = screen_width - 1400
x_cordinate = int((screen_width/2) - (window_width/2))
y_cordinate = int((screen_height/2) - (window_height/2))
App.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate))

App.mainloop()

编辑:解决方案在 BryanOakley 的回答中(谢谢!)以及该回答下的后续 cmets。

【问题讨论】:

  • 如果项目从一个框到另一个发生在鼠标点击时,您是否想要这种移动,或者您是否还希望用户能够使用键盘选择项目并将它们从一侧移动到另一侧?
  • @BryanOakley,只需点击鼠标

标签: python oop tkinter events listbox


【解决方案1】:

&lt;&lt;ListboxSelect&gt;&gt; 在选择更改时触发。这可能发生在用户单击某个项目时,但也可能发生在其他时间,例如用户使用键盘遍历列表框,或者您的代码删除了选定的内容。

如果你的 UI 应该只在鼠标点击时起作用,你应该绑定到那个而不是&lt;&lt;ListboxSelect&gt;&gt;

【讨论】:

  • 在这种情况下,选择和鼠标单击需要同时发生,因为如果我在没有进行实际选择的情况下单击 ListBox 内的某个位置,我不希望这些方法运行。有没有办法做到这一点?
  • @Elsayeda:选择会自动发生,您无需执行任何操作。如果您绑定到鼠标单击,您可以轻松检查您是否在某个项目上方,并允许选择或禁止选择。
  • 我认为 mouseclick 事件和我的方法中的后续代码在选择事件注册之前就已经运行了,因为我在点击时遇到了索引错误,并且在我进行另一个操作之前不会发生传输选择。在方法的其余部分之前使用条件“if self.Available.curselection():”是错误的解决方法,但我仍然必须双击才能进行传输(并且通过扩展,运行该方法两次)。选择确实会自动发生,但为时已晚,我的代码无法使用它。
  • “我认为 mouseclick 事件和我方法中的后续代码在选择事件注册之前就已经运行了” - 是的,这就是绑定在 tkinter 中的工作方式。听起来你不需要选择,你只需要知道被点击的项目。您可以在不依赖选择的情况下获得它。您可以执行event.widget.index(f"@{event.x},{event.y}") 之类的操作来获取被点击项目的索引。
猜你喜欢
  • 2015-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-07
  • 1970-01-01
  • 2019-08-03
相关资源
最近更新 更多