Python 中来自 tkinter 的绑定函数的问题
Problems with a bind function from tkinter in 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 中,这些选项将成为按钮,所以我必须向选项添加一个新字段 class,我是这样做的:
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))
#...
还有一个 "help label" 每次鼠标悬停在选项上时显示选项的描述,所以我绑定了这些功能。
发生的事情是,虽然我确实成功地添加了一个带有按钮的新字段,但绑定功能似乎搞砸了,结果是这样的:
无论我悬停在哪个按钮上,帮助标签总是显示最后添加的选项的描述。如果我直接修改选项 class,问题似乎就消失了,像这样:
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
的构造函数中或稍后在循环中执行此操作很重要?因此,我认为问题可能出在我将字段动态添加到 class?
这是完整的最小且可运行的测试代码或任何它调用的代码,如果你想弄乱它:http://pastebin.com/0PWnF2P0
感谢您的宝贵时间
我不知道我的原始代码的实际问题是什么,但我只是绕过了它。我添加了一个字典,其中按钮作为键,选项作为值,我只是使用 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
#...
问题是 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 来自 class 范围并且 不是 在绑定时评估( ) 调用被执行。一个更简单的例子是
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 模型。这一定会在某个时候再次咬你。
我正在开发一个应用程序,它应该支持来自控制台和 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 中,这些选项将成为按钮,所以我必须向选项添加一个新字段 class,我是这样做的:
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))
#...
还有一个 "help label" 每次鼠标悬停在选项上时显示选项的描述,所以我绑定了这些功能。
发生的事情是,虽然我确实成功地添加了一个带有按钮的新字段,但绑定功能似乎搞砸了,结果是这样的:
无论我悬停在哪个按钮上,帮助标签总是显示最后添加的选项的描述。如果我直接修改选项 class,问题似乎就消失了,像这样:
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
的构造函数中或稍后在循环中执行此操作很重要?因此,我认为问题可能出在我将字段动态添加到 class?
这是完整的最小且可运行的测试代码或任何它调用的代码,如果你想弄乱它:http://pastebin.com/0PWnF2P0
感谢您的宝贵时间
我不知道我的原始代码的实际问题是什么,但我只是绕过了它。我添加了一个字典,其中按钮作为键,选项作为值,我只是使用 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
#...
问题是 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 来自 class 范围并且 不是 在绑定时评估( ) 调用被执行。一个更简单的例子是
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 模型。这一定会在某个时候再次咬你。