我正在开发一个应支持从控制台和GUI运行的应用程序。该应用程序有多个选项可供选择,并且由于在两种运行模式下该程序显然都具有相同的选项,因此我做了一个概括:

class Option:
    def __init__(self, par_name, par_desc):
        self.name = par_name
        self.desc = par_desc

class Mode():
    def __init__(self):
        self.options = []
        self.options.append(Option('Option1', 'Desc1'))
        self.options.append(Option('Option2', 'Desc2'))
        self.options.append(Option('Option3', 'Desc3'))
        self.options.append(Option('Option4', 'Desc4'))
        self.options.append(Option('Option5', 'Desc5'))
        #And so on


问题在于,在GUI中,这些选项将成为按钮,因此我必须向Option类添加一个新字段,并且这样做:

def onMouseEnter(par_event, par_option):
    helpLabel.configure(text = par_option.desc)
    return

def onMouseLeave(par_event):
    helpLabel.configure(text = '')
    return

class GUIMode(Mode):
    #...
    for iOption in self.options:
        iOption.button = Button(wrapper, text = iOption.name, bg = '#004A7F', fg = 'white')
        iOption.button.bind('<Enter>', lambda par_event: onMouseEnter(par_event, iOption))
        iOption.button.bind('<Leave>', lambda par_event: onMouseLeave(par_event))
    #...


每次将鼠标悬停在该选项上时,还会有一个“帮助标签”显示该选项的说明,因此我在其中绑定了这些功能。

发生的是,虽然我确实确实成功地添加了带有按钮的新字段,但是bind函数似乎混乱了,结果是:



无论我将鼠标悬停在哪个按钮上,帮助标签总是显示最后添加的选项的描述。如果我直接修改Option类,问题似乎就消失了,就像这样:

class Option:
    def __init__(self, par_name, par_desc):
        self.name = par_name
        self.desc = par_desc
        self.button = Button(wrapper, text = self.name, bg = '#004A7F', fg = 'white')
        self.button.bind('<Enter>', lambda par_event: onMouseEnter(par_event, self))
        self.button.bind('<Leave>', lambda par_event: onMouseLeave(par_event))


但是我显然不能保持这种方式,因为控制台模式也会获得我真正不想要的那些字段。这不是一回事吗?如果在带有self的构造函数中或以后在循环中执行它,为什么有关系?因此,我认为问题可能在于我将字段动态添加到类中?

这是完整的最小且可运行的测试代码,或者它的全部名称,如果您想弄乱它的话:http://pastebin.com/0PWnF2P0

感谢您的时间

最佳答案

问题在于,iOption的值是在

for iOption in self.option:


循环完成。由于您在每次迭代中都重置了iOption,因此当循环完成时,iOption具有相同的值,即self.options中的最后一个元素。您可以使用片段演示此事件时绑定:

    def debug_late_bind(event):
        print(iOption)
        onMouseEnter(event, iOption)

    for iOption in self.options:
        iOption.button = Button(wrapper, text = iOption.name,
            bg = '#004A7F', fg = 'white')
        iOption.button.bind('<Enter>', debug_late_bind)


这将显示所有iOption事件具有相同的值。

我将iOption的使用拆分到debug_late_bind,以显示iOption来自类范围,并且在执行bind()调用时不对其进行评估。一个更简单的例子是

def print_i():
     print(i)

for i in range(5):
    pass

print_i()


打印“ 4”,因为这是分配给i的最后一个值。这就是为什么代码中每个对onMouseEnter(par_event, iOption)的调用都具有相同的iOption值的原因;它是在事件发生时评估的,而不是在绑定时评估的。我建议您阅读model view controller并了解如何使视图和控制器纠结在一起。发生这种情况的主要原因是您有两个视图(控制台和tk),这些视图应与模型较少耦合。

提取事件的.widget属性是一个不错的解决方法,但最好还是不要覆盖标量iOption,而是使用单个按钮的列表。代码

for n, iOption in enumerate(self.options):


将有助于创建列表。在建议的解决方法中,您在tkinter视图中编码了过多的iOption模型。那一定会再次咬你。

关于python - 来自Tkinter在Python中的绑定(bind)函数问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27950090/

10-12 20:21