Generate Tkinter Buttons dynamically

I think the problem is that the lambda is picking up the final value of i after the for loop ends. This should fix that (untested):

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)

Update

BTW, this worked by adding an argument to the lambda function with a default value calculated from the value of i at the time each one is created in the loop rather than referring back to the final value of i through a closure when the expression within it executes later.


Your problem is that you create lots of lambda objects in the same namespace, and those lambdas make reference to names in the outer scope. That means they don't become closures and they don't store references to the objects until later... When it happens, all lambdas will refer to the last value of i.

Try using a callback factory to fix that:

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)

Another idea is to store the current value of i as a default argument value in the lambda object, instead of relying on closure behavior to store the reference:

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)