2013-07-10 32 views
1

代碼:的Python的Tkinter - 名稱沒有被定義

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])」,但這並沒有改變任何東西。

+0

'import string;字母表= list(string.ascii_uppercase)'會少很多痛苦 – inspectorG4dget

+0

對於這個問題,沒有理由首先有一個列表而不是字符串,因爲他需要做的就是索引它。 (循環遍歷'enumerate'會使它變得更簡單。) – abarnert

+0

'enumerate'函數對字符串做了什麼?我找不到任何與字符串一起使用的示例。 – Guney

回答

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) 

如果你真的想轉儲dictlocals()(或類似地,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',因爲它是字母,而不是你在調試時看到的數字。

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應該實現?

+0

也許按鈕是爲了摧毀自己?在這種情況下,'A'(按鈕本身)尚未創建,'letterAction'應該返回'destroy'方法,而不是調用它。 –

+0

@tobias_k或者在'letterAction'函數中使用['functools.partial()'](http://docs.python.org/3/library/functools.html#functools.partial)。無論哪種方式'A'需要首先定義。 – andersschuller

+0

如果沒有使用按鈕名稱_as string_-lambda:letterAction('A')'或'partial(letterAction,'A')'',沒有辦法使這項工作成爲可能。這意味着'letterAction'必須在'locals()'中查找它。 (當然,它不是本地的,而是一個閉包變量......但是'locals()[letter]'將起作用。) – abarnert

相關問題