码:

def createLetters(frame, startX, startY, width, height, spacing):

    alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I",
                "J", "K", "L", "M", "N", "O", "P", "Q", "R",
                "S", "T", "U", "V", "W", "X", "Y", "Z"]

    def letterAction(letter):
        letter.destroy()

    for i in range(0, 26):

        if (i >= 9 and i <= 17):
            y = startY +  height + 2 * spacing
            x = startX + ((width + spacing) * (i - 9))

        elif (i >= 17):
            y = startY + 2 * height + 3 * spacing
            x = (width + spacing) / 2 + startX + ((width + spacing) * (i - 18))

        elif (i <= 8):
            y = startY + spacing
            x = startX + ((width + spacing) * i)

        exec(alphabet[i] + " = Button(" + frame + ", text = '" + alphabet[i] + "', command = letterAction(" + alphabet[i] + "))")
        exec(alphabet[i] + ".place(x = " + str(x) + ", y = " + str(y) + ", width = " + str(width) + ", height = " + str(height) + ")")


错误:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python33\lib\tkinter\__init__.py", line 1442, in __call__
    return self.func(*args)
  File "E:\Hangman\hangmanTk.py", line 106, in playScreen
    createLetters("playFrame", 175, 250, 50, 50, 0)
  File "E:\Hangman\hangmanTk.py", line 95, in createLetters
    exec(alphabet[i] + " = Button(" + frame + ", text = '" + alphabet[i] + "', command = letterAction(" + alphabet[i] + "))")
  File "<string>", line 1, in <module>
NameError: name 'A' is not defined


我试图用一个循环创建多个tkinter按钮。我可以很好地创建按钮,但似乎无法为其创建回调。每当我尝试时,它会告诉我未定义用于按钮的变量。我尝试在定义按钮的上方添加“ exec(“ global” + Alphabet [i])“,但这并没有改变任何内容。

最佳答案

Using exec is almost always the wrong way to do it, no matter what "it" is.

And creating variables dynamically is almost always the wrong thing to do.

而让此方法起作用的问题就是原因的完美例证。



只需创建一个dict将名称映射到按钮:

buttons = {}

# ...

letter = alphabet[i]
buttons[letter] = Button(frame, text = letter, command = letterAction(letter))
buttons[letter].place(x = x, y = y, width = width, height = height)


如果您真的想将dict转储到locals()(或者类似地,将self.__dict__globals()或…)转储,则是不重要的。但是你没有。您唯一需要使用该变量的位置是您的letterAction函数。所以:

def createLetters(frame, startX, startY, width, height, spacing):

    alphabet = string.ascii_uppercase
    buttons = {}

    def letterAction(letter):
        buttons[letter].destroy()

    for i, letter in enumerate(alphabet):

        if (i >= 9 and i <= 17):
            y = startY +  height + 2 * spacing
            x = startX + ((width + spacing) * (i - 9))

        elif (i >= 17):
            y = startY + 2 * height + 3 * spacing
            x = (width + spacing) / 2 + startX + ((width + spacing) * (i - 18))

        elif (i <= 8):
            y = startY + spacing
            x = startX + ((width + spacing) * i)

        buttons[letter] = Button(frame, text = letter, command = letterAction(letter))
        buttons[letter].place(x = x, y = y, width = width, height = height)




但是请注意,这做错了事。 command = letterAction(letter)(无论是直接运行还是通过exec运行)现在都将调用letterAction(letter),甚至在创建按钮之前先销毁按钮,然后返回None,然后将其设置为。

您需要commandlambda: letterAction(letter)来解决此问题。

另外,无论现在还是以后,您都无法编写将按钮变量本身传递给partial(letterAction, letter)的代码,因为该变量尚不存在。您必须像上面一样将字母作为字符串传递。



但是,实际上,如果您考虑一下,则根本不需要这些按钮变量,无论是letter还是其他形式。您只需要一种将每个按钮绑定为自己的回调目标的方法,对吗?有很多方法可以做到这一点,但是很明显的是一个类,可以继承或委托给dict(或者,在这种情况下,两者都不是,因为您不需要将其用作按钮,甚至创建后记住它)。

在此过程中,让我们删除一些多余的括号,以免使阅读和解决Button似乎属于两个不同组的问题变得更加困难。

class SelfDestructiveButton(object):
    def __init__(self, frame, letter, x, y, width, height):
        self.button = Button(frame, text=letter, command=self.command)
        self.button.place(x=x, y=y, width=width, height=height)
    def command(self):
        self.button.destroy()

def createLetters(frame, startX, startY, width, height, spacing):
    for i, letter in enumerate(string.ascii_uppercase):
        if 9 <= i <= 17:
            y = startY +  height + 2 * spacing
            x = startX + ((width + spacing) * (i - 9))
        elif i > 17:
            y = startY + 2 * height + 3 * spacing
            x = (width + spacing) / 2 + startX + ((width + spacing) * (i - 18))
        else:
            y = startY + spacing
            x = startX + ((width + spacing) * i)
        SelfDestructiveButton(frame, letter, x, y, width, height)


使用17可能更清楚,因为它是字母,而不是调试时将看到的数字。

09-06 01:12