2010-11-21 15 views
3

Je veux générer n quantité de boutons Tkinter qui font des choses différentes. J'ai ce code:Générer des boutons Tkinter dynamiquement

import Tkinter as tk 

for i in range(boardWidth): 
    newButton = tk.Button(root, text=str(i+1), 
     command=lambda: Board.playColumn(i+1, Board.getCurrentPlayer())) 
    Board.boardButtons.append(newButton) 

Si boardWidth est 5, bien que je reçois des boutons étiquetés 1-5, lorsque vous cliquez dessus, ils font tous Board.playColumn (5, Board.getCurrentPlayer()).

J'ai besoin du premier bouton pour faire Board.playColumn (1, Board.getCurrentPlayer()), le second pour faire Board.playColumn (2, Board.getCurrentPlayer()) et ainsi de suite.

Merci pour toute aide!

Répondre

8

Je pense que le problème est que le lambda capte la valeur finale de i après la fin de la boucle for. Cela devrait résoudre ce problème (non testé):

import Tkinter as tk 

for i in range(boardWidth): 
    newButton = tk.Button(root, text=str(i+1), 
     command=lambda j=i+1: Board.playColumn(j, Board.getCurrentPlayer())) 
    Board.boardButtons.append(newButton) 

Mise à jour

BTW, cela a fonctionné en ajoutant un argument à la fonction lambda avec une valeur par défaut calculée à partir de la valeur de i lors de sa création au lieu de renvoyer à la valeur finale de i à travers une fermeture lorsque l'expression en son sein s'exécute plus tard.

+0

Ouais, ça a marché. Merci! – rikkit

2

Votre problème est que vous créez beaucoup d'objets lambda dans le même espace de noms, et ceux lambda font référence à des noms dans la portée externe. Cela signifie qu'ils ne deviennent pas des fermetures et qu'ils ne stockent des références aux objets que plus tard ... Quand cela arrive, tous les lambdas se réfèrent à la dernière valeur de i.

Essayez d'utiliser une usine de rappel pour résoudre ce problème:

import Tkinter as tk 

def callbackFactory(b, n): 
    def _callback(): 
     return b.playColumn(n, b.getCurrentPlayer()) 
    return _callback 

for i in range(boardWidth): 
    newButton = tk.Button(root, text=str(i+1), 
     command=callbackFactory(Board, i+1)) 
    Board.boardButtons.append(newButton) 

Une autre idée est de stocker la valeur actuelle de i en tant que valeur d'argument par défaut dans l'objet lambda, au lieu de compter sur le comportement de fermeture pour stocker la référence :

for i in range(boardWidth): 
    newButton = tk.Button(root, text=str(i+1), 
     command=lambda x=i: Board.playColumn(x+1, Board.getCurrentPlayer())) 
    Board.boardButtons.append(newButton) 
+0

J'ai essayé votre deuxième exemple, selon la suggestion de Martin. Merci d'expliquer pourquoi c'est arrivé! – rikkit