2016-08-09 15 views
-5

Tkinterを使用してTic Tac Toeのゲーム用の簡単なGUIを実装しようとしています。最初のステップとして、ラベルを付けられていない状態からクリックされたときに「X」というラベルに変更される一連のボタンを作成しようとしています。私は、次のことを試してみた:Tkinterを使用してクリックするとテキストを変更するボタンの配列を作成する方法

import Tkinter as tk 

class ChangeButton: 

    def __init__(self, master, grid=np.diag(np.ones(3))): 
     frame = tk.Frame(master) 
     frame.pack() 
     self.grid = grid 
     self.buttons = [[tk.Button()]*3]*3 

     for i in range(3): 
      for j in range(3): 
       self.buttons[i][j] = tk.Button(frame, text="", command=self.toggle_text(self.buttons[i][j])) 
       self.buttons[i][j].grid(row=i, column=j) 

    def toggle_text(self, button): 
     if button["text"] == "": 
      button["text"] = "X" 


root = tk.Tk() 
root.title("Tic Tac Toe") 

app = ChangeButton(root) 

root.mainloop() 

しかし、結果のウィンドウは次のようになります。

enter image description here

やボタンクリック時に変更されません。なぜこれがうまくいかないのでしょうか?

+0

簡単に言えば: 'command'機能を必要とするが、self.toggle_text(self.buttons [i]が[j]を)'直後に評価されます(それが起こりますすぐに)、何も返さない(ボタンをクリックしても何も起こらない)。 'lambda'ラッパーが推奨されます。 – TigerhawkT3

+0

あなたはそれを修正し、任意のボタンをクリックするだけが最後のボタンに影響することを尋ねる質問はおそらく[この1つ]です(http://stackoverflow.com/questions/10865116/python-tkinter-creating-buttons-in -for-loop-passing-command-arguments)を使用します。 – TigerhawkT3

+0

@ TigerhawkT3さらに2つの問題があります。偽装はそのうちの1つのみを扱います。 –

答えて

0

主要な問題はcommand=self.toggle_text(self.buttons[i][j]))で、あなたはコールバック関数を呼び出して、右のパラメータでその関数を呼び出しますその結果コマンドcommand. Instead, you have to bind the function itself toから, or a lambda`をバインドということです。これを行うの素朴な方法は、次のようになります。

command=lambda: self.toggle_text(self.buttons[i][j]) # won't work! 

しかし、機能が実行されたときlambda内部変数はすなわち後のループ、を評価されますので、これは、ループ内で動作しません、すなわちiおよびjは、各機能の値の最後のの値をとります。より詳細な説明については、例えば、 here。この問題を解決する1つの方法は、これらの変数をlambdaのパラメータとして宣言すると同時に、ループの現在の値をデフォルト値として使用します。つまり、です。このようにして、ijは、関数が宣言されたときに評価され、呼び出されたときには評価されません。

あなたcommandと周囲のループは次のようになります。

for i in range(3): 
     for j in range(3): 
      self.buttons[i][j] = tk.Button(frame, text="", 
        command=lambda i=i, j=j: self.toggle_text(self.buttons[i][j])) 
      self.buttons[i][j].grid(row=i, column=j) 

そして、あなたはself.buttonsリストを初期化する方法と最初に関係のない別の問題は、そこにあります。 [[tk.Button()]*3]*3を実行すると、と同じリストへの3つの参照が保持され、それぞれがと同じボタンへの3つの参照を保持します。例えば、詳細についてはhereを参照してください。また、後でループ内でボタンを設定するときに、リスト内のボタンを初期化する必要はありません。私の代わりに、ネストされたリスト内包表記を使用することをお勧めしたい:

self.buttons = [[None for _ in range(3)] for _ in range(3)] 
+1

あなたの答えは、文字通り、3つの異なる重複へのリンクです。 – TigerhawkT3

+1

私はあなたのために提供しました。 – TigerhawkT3

+0

@ TigerhawkT3私はちょうど実際の答えがコメントと偽装の3つのリンクよりも有用であると感じました。はい、私はより完全な説明と答えへのリンクを含めました、なぜそうではありませんか?また、3つの問題すべてに対処するコードもあります。これが「役に立たない」または「質問に対する答えではない」と考える場合、自由にダウンボートしてください。しかし、あなたがすでに持っているようです。 –

関連する問題