Python中tkinter的绑定函数问题

时间:2015-01-14 18:37:14

标签: python tkinter

我正在开发一个应该支持从控制台和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))
    #...

每当鼠标悬停在选项上时,还会有一个“帮助标签”显示选项的描述,因此我将绑定这些功能。

发生的事情是,虽然我确实成功添加了一个带按钮的新字段,但绑定功能似乎搞得一团糟,结果就是这样:

enter image description here

帮助标签始终显示添加的最后一个选项的说明,无论我悬停在哪个按钮上。如果我直接修改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

感谢您的时间

2 个答案:

答案 0 :(得分:1)

问题是在

之后评估了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模型。在某些时候,这肯定会再次咬你。

答案 1 :(得分:0)

我不知道原始代码的实际问题是什么,但我只是绕过了它。我添加了一个字典,按钮作为键,选项作为值,我只是使用par_event.widget来获取选项及其描述,这是正常的:

buttonOption = {}

def onMouseEnter(par_event):
    helpLabel.configure(text = buttonOption[par_event.widget].desc)
    return

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

class GUIMode(Mode):
    def run(self):
        #...
        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.button.bind('<Leave>', lambda par_event: onMouseLeave(par_event))
            buttonOption[iOption.button] = iOption
        #...