Python中来自tkinter的bind函数的问题

2024-04-25 23:33:54 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在开发一个应用程序,它应该既支持从控制台运行,也支持从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函数似乎出了问题,结果是:

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

谢谢你抽出时间


Tags: textnameselfeventbinddef选项button
2条回答

问题是iOption的值是在

for iOption in self.option:

循环已完成。由于在每次迭代中重置iOption,因此循环完成时iOption具有相同的值,即循环中的最后一个元素自我选择. 您可以通过代码段在事件时绑定来演示这一点:

    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用于调试\u late \u 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,并理解您是如何将视图和控制器混为一谈的。发生这种情况的主要原因是您有两个视图(console和tk),这两个视图与模型的耦合应该较少。你知道吗

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

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

有助于创建一个列表。在建议的解决方法中,您在tkinter视图中对iOption模型进行了太多编码。总有一天会再咬你的。你知道吗

我不知道我原来的代码到底有什么问题,但我只是绕过了它。我添加了一个dictionary,其中button作为键,option作为值,我只是使用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
        #...

相关问题 更多 >