代码:
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按钮。我可以创建好按钮,但我似乎无法为它们创建回调。当我尝试时,它告诉我,我用于按钮的变量没有定义。我尝试添加&#34; exec(&#34; global&#34; + alphabet [i])&#34;上面我定义了按钮,但没有改变任何东西。
答案 0 :(得分:2)
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
然后设置为command
。
您需要lambda: letterAction(letter)
或partial(letterAction, letter)
来解决此问题。
另外,您无法编写代码以将按钮变量本身传递给letter
,无论是现在还是以后,因为该变量尚不存在。您必须像上面一样将字母作为字符串传递。
但实际上,如果你考虑一下,你根本不需要这些按钮变量 - 无论是dict
还是其他。你只需要一种方法将每个按钮绑定为自己的回调目标,对吗?有很多方法可以做到这一点,但显而易见的是一个类,继承或委托给Button
(或者,在这种情况下,两者都没有,因为你不需要将它用作按钮,或者甚至在创作之后记住它。
虽然我们正在努力,但是让我们删除一些无关的内容,这样只会让事情更难以阅读并解决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'
可能更清晰,因为它是字母而不是你在调试时会看到的数字。
答案 1 :(得分:0)
第一次调用exec
时的字符串评估为:
"A = Button(<frame>, text = 'A', command = letterAction(A))"
因此,在定义之前,您正在引用A
(名称)。我猜你忘记了第二个alphabet[i]
周围的单引号:
exec(alphabet[i] + " = Button(" + frame + ", text = '" + alphabet[i] + "', command = letterAction('" + alphabet[i] + "'))")
请注意,这会调用letterAction('A')
,即'A'.destroy()
,因为字符串没有AttributeError
方法,所以会引发destroy()
。什么是letterAction
应该达到的目标?