1

コード:

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])" を追加しようとしましたが、何も変わりませんでした。

4

2 に答える 2

2

exec「それ」が何であれ、使用はほとんどの場合間違った方法です。

そして、変数を動的に作成することは、ほとんどの場合、間違ったことです。

そして、これを機能させるための問題は、その理由の完全な例です。


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ん。変数がまだ存在しないためです。上で行ったように、文字を文字列として渡す必要があります。


しかし、よく考えてみると、これらのボタン変数は、a であろうとなかろうと、まったく必要ありませんdict。各ボタンを独自のコールバックのターゲットとしてバインドする方法が必要なだけですよね? これを行うにはいくつかの方法がありますが、明らかな方法は、クラスを継承または委譲することButtonです (または、この場合、ボタンとして使用する必要がないか、記憶する必要がないため、どちらも使用しません。作成後)。

17その間、余分な括弧などを削除して読みにくくし、2 つの異なるグループに属しているように見える 問題を修正しましょう…</p>

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'デバッグ中に表示されるのは数字ではなく文字であるため、を使用するとさらに明確になる場合があります。

于 2013-07-10T20:41:28.717 に答える
0

への最初の呼び出しの文字列は、次のようにexec評価されます。

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

したがってA、定義する前に (名前) を参照しています。2番目の周り​​の一重引用符を忘れたと思いますalphabet[i]

exec(alphabet[i] + " = Button(" + frame + ", text = '" + alphabet[i] + "', command = letterAction('" + alphabet[i] + "'))")

これは を呼び出すことletterAction('A')に注意してください。つまり、文字列にはメソッドがないため、'A'.destroy()がスローされます。何を達成することになっていますか?AttributeErrordestroy()letterAction

于 2013-07-10T20:43:35.647 に答える