8

ive tried searching for a solution but couldn't find one that works. I have a 2d list of tkinter Buttons, and i want to change their Text when it is clicked by the mouse. I tried doing this:

def create_board(number):
    print(number)
    for i in range (0,number):
        buttonList.append([])
        for j in range(0,number):
            print(i,j)
            buttonList[i].append(Button(root, text = " ", command = lambda: update_binary_text(i,j)))
            buttonList[i][j].pack()

Then when it is clicked it calls this function:

def update_binary_text(first,second):
    print(first,second)
    buttonList[first][second]["text"] = "1"

When i click a button, it simply does nothing, i had the program display the indexes of the button that was clicked, and they ALL show 4, 4 (this is when the variable number=5) Is there an solution to this?
this is my first python attempt for a class.

Thanks

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
vap
  • 95
  • 1
  • 1
  • 7
  • Does this answer your question? [Python Lambda in a loop](https://stackoverflow.com/questions/19837486/python-lambda-in-a-loop) – Davis Herring Jan 20 '20 at 00:39

2 Answers2

10

You can fix this problem by creating a closure for i and j with the creation of each lambda:

command = lambda i=i, j=j: update_binary_text(i, j)

You could also create a callback factory with references to the button objects themselves:

def callback_factory(button):
    return lambda: button["text"] = "1"

And then in your initialization code:

for j in range(0, number):
    new_button = Button(root, text=" ")
    new_button.configure(command=callback_factory(new_button))
    new_button.pack()
    buttonList.append(new_button)
Joel Cornett
  • 24,192
  • 9
  • 66
  • 88
  • WOW! your solution worked! thank you sir! For now i stuck with the first example you posted. But i will definitely study the second example you showed as well. Thanks again! – vap Apr 26 '13 at 02:20
1

Whenever I need a collection of similar widgets, I find it's simplest to enclose them in an object and pass a bound-method as callback rather than playing tricks with lambda. So, instead of having a list like buttonList[] with widgets, create an object:

class MyButton(object):
    def __init__(self, i, j):
        self.i = i
        self.j = j
        self.button = Button(..., command = self.callback)

    def callback(self):
        . . .

Now, you have a list buttonList[] of these objects, rather than the widgets themselves. To update the text, either provide a method for that, or else access the member directly: buttonList[i].button.configure(. . .) And when the callback is activated, it has the entire object and whatever attributes you might need in self.

Lee Daniel Crocker
  • 12,927
  • 3
  • 29
  • 55