godot
godot

Reputation: 3545

How can I construct button callbacks with Tkinter properly?

Here I have a simple python app using Tkinter

from Tkinter import *
def draw(i,j):
    button[(i,j)]['text'] = 'X'

if __name__ == '__main__':
    root = Tk()
    root.title('Number Recognation')

    button = {}



    for i in range(2):
        for j in range(2):
            button[(i,j)] = Button(width = 3,command = lambda: draw(i,j))
            button[(i,j)].grid(row = i,column = j)




    root.mainloop()

I want to make a little program where are four blank buttons and where you push one of them, it changes it's text to "X". So when I write this program using of for loops, it is not working correctly, on click it changes this button which stands in the second row and the second column. If I write without for loops

    button[(0,0)] = Button(width = 3,command = lambda: draw(0,0))
    button[(0,0)].grid(row = 0,column = 0)

    button[(0,1)] = Button(width = 3,command = lambda: draw(0,1))
    button[(0,1)].grid(row = 0,column = 1)

    button[(1,0)] = Button(width = 3,command = lambda: draw(1,0))
    button[(1,0)].grid(row = 1,column = 0)

    button[(1,1)] = Button(width = 3,command = lambda: draw(1,1))
    button[(1,1)].grid(row = 1,column = 1)

here is not a problem.

Really I want to use for loop, because I am going to have many buttons here.

Upvotes: 3

Views: 72

Answers (1)

falsetru
falsetru

Reputation: 369074

i, j is bound when the callback is called, not when the callback is created. You can use default parameter to avoid the late binding problem.

for i in range(2):
    for j in range(2):
        button[(i,j)] = Button(width = 3,command = lambda i=i, j=j: draw(i,j))  # <---
        button[(i,j)].grid(row = i,column = j)

Upvotes: 3

Related Questions