2

I am making a simple program that creates a 3x3 matrix of tkinter.Button() and when a button is pressed it should display the "Clicked" text on it. But the result seems like buttons that lie on the same column get bound to the same command - the one that targets the button of the last row of that column.

from tkinter import *

root = Tk()

text = [[None]*3]*3
buttons = [[None]*3]*3

def action(i, j):
    text[i][j].set('Clicked')

for i in range(3):
    for j in range(3):
        text[i][j] = StringVar()
        text[i][j].set('(%d, %d)' % (i,j))
        buttons[i][j] = Button(root, command = lambda i=i, j=j : action(i, j))
        buttons[i][j].config(textvariable = text[i][j], width = 9, height = 5)
        buttons[i][j].grid(row = i, column = j)

root.mainloop()

Image here

Miraj50
  • 4,257
  • 1
  • 21
  • 34

1 Answers1

3

The problem is not in your commands, but in the way you create your lists.

When you multiply a list, you actually multiply a reference to this single list (see this question). You can see this when running the following code:

text = [[None]*3]*3
print([id(x) for x in text])

So when you change an item in one of the lists, the item is changed in all lists. Therefore your list doesn't look like [[1,2,3],[4,5,6],[7,8,9]], but like [[7,8,9],[7,8,9],[7,8,9]]. Then, when you think you set StringVar number 1, you actually set Stringvar number 7 so button number 7 is changed.

You can create three separate lists by using list comprehension instead of multiplication. As you can see with the following code, this produces three separate lists.

text = [[None]*3 for _ in range(3)]
print([id(x) for x in text])
fhdrsdg
  • 10,297
  • 2
  • 41
  • 62