使用lambda命令时的Python tkinter NameError

时间:2016-08-11 11:56:00

标签: python lambda tkinter nameerror

我是Python tkinter模块的新手,所以我会感激任何帮助,甚至解释为什么这根本不可能(如果是这样的话)。

我有(foo, bar, x, y)形式的4元组列表;像这样的东西:

    TUPLE_LIST = [('Hello', 'World', 0, 0),
                  ('Hovercraft', 'Eels', 50, 100),
                  etc.]

以及稍后的for循环,理想情况下将变量foo实例化为带有文本bar的按钮,每个按钮都具有将其各自的bar添加到其中的功能已定义的Entry窗口小部件,然后将其放置在坐标x, y

    for foo, bar, x, y in TUPLE_LIST:
        exec("{0} = Button(self, text='{1}', command=lambda: self.update_text('{1}'))".format(foo, bar))
        eval(name).place(x=x, y=y)

按钮放置完美,但如果我点击其中一个按钮,我会收到以下错误:

     Traceback (most recent call last):
      File "...\lib\tkinter\__init__.py", line 1550, in __call__
        return self.func(*args)
      File "<string>", line 1, in <lambda>
     NameError: name 'self' is not defined

我猜测这与命令是用lambda定义的事实有关,因此不能定义&#39;功能&#39;本身,但我有看到其他人定义他们的按钮&#39;使用lambdas的命令。这也与使用exec有关吗?另外,这里是update_text的代码(如果重要的话):

    def update_text(self, new_char):
        old_str = self.text_box_str.get()
        # Above is a StringVar() defined in __init__ and used as textvariable in Entry widget creation.
        self.text_box_str.set(old_str + new_char)

非常感谢任何帮助。提前谢谢。

1 个答案:

答案 0 :(得分:2)

这是一个非常糟糕的主意。没有理由干,以动态创建变量名称。它使您的代码更复杂,更难维护,更难阅读。

如果您想按名称引用小部件,请使用字典:

buttons = {}
for foo, bar, x, y in TUPLE_LIST:
    buttons[foo] = Button(self, text=bar, ...)
    buttons[foo].place(...)

至于self的问题,还没有足够的代码来说明问题所在。如果您正在使用类,则代码看起来没问题。如果你不是,你就不应该使用self

要创建更新窗口小部件的功能,您只需将该窗口小部件或窗口小部件的名称传递给该功能即可。我们不清楚您想要更新哪个小部件。它似乎是一个入口小部件。我无法判断您是否有一个所有按钮必须更新的条目小部件,或每个按钮一个条目。我会假设前者。

在下面的示例中,我将展示如何将要更改的变量与要添加的文本一起传递给函数。此解决方案不使用textvariable属性,但您可以根据需要使用。只需传递它而不是小部件。

buttons[foo] = Button(..., command=lambda widget=self.text_box, value=bar: self.update_text(widget, value))
...
def update_text(self, widget, value):
    old_value = widget.get()
    new_value = old_value + value
    widget.delete(0, "end")
    widget.insert(0, new_value)

当然,如果你所做的只是附加字符串,你可以用widget.insert("end", value)替换这四行