未定义Python Tkinter名称

2024-10-02 02:36:22 发布

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

代码:

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) + ")")

错误:

^{pr2}$

我试图用一个循环创建多个tkinter按钮。我可以很好地创建按钮,但似乎不能为它们创建回调。当我尝试时,它告诉我我用于按钮的变量没有定义。我试着在我定义按钮的地方添加“exec(”global“+alphabet[I])”,但这并没有改变任何东西。在


Tags: 定义defwidth按钮frameexecheightelif
2条回答

{a1}

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__或{}或…),那么这很简单。但是你没有,你只需要在你的letterAction函数中使用变量。所以:

^{pr2}$

但请注意,这样做是错误的。command = letterAction(letter)-无论是直接运行还是通过exec-现在都要调用letterAction(letter),在创建之前销毁按钮,并返回{},然后将其设置为command。在

您需要lambda: letterAction(letter)partial(letterAction, letter)来修复此问题。在

另外,现在或以后都不能编写代码将button变量本身传递给letter,因为该变量还不存在。你必须将字母作为字符串传递,就像我上面所做的那样。在


但实际上,如果你仔细想想,你根本不需要这些按钮变量,不管是在dict中还是在其他地方。你只需要一种方法将每个按钮绑定为它自己的回调目标,对吧?有很多方法可以做到这一点,但最明显的一种方法是类,要么继承或委托给Button(或者,在本例中,既不需要将其用作按钮,也不需要在创建后记住它)。在

当我们在做的时候,让我们去掉一些无关的parens之类的东西,这样会使东西更难阅读,并解决17似乎属于两个不同的组的问题

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)

使用if 'J' <= letter <= 'R'可能会更清楚,因为调试它时您将看到的是字母而不是数字。在

第一次调用exec中的字符串计算为:

"A = Button(<frame>, text = 'A', command = letterAction(A))"

因此,您在定义A(名称)之前引用它。我猜你忘了第二个alphabet[i]左右的单引号:

^{pr2}$

注意这将调用letterAction('A'),即'A'.destroy(),这将抛出一个AttributeError,因为字符串没有destroy()方法。letterAction应该实现什么?在

相关问题 更多 >

    热门问题