0

I have a 16x16 matrix of tkinter buttons:

for c in range(self.track_size):
    row = []
    for r in range(self.track_size):
        button = tk.Button(
            self.ground_data_frame, 
            text="0"
        )
        button.grid(row=r, column=c, sticky=tk.NW+tk.NE+tk.SW+tk.SE+tk.W+tk.E+tk.N+tk.S)
        row.append(button)
    self.ground_data_buttons.append(row)

enter image description here

I define a command function to each one of them post-definition:

for r in self.ground_data_buttons:
    for b in r:
        b.config(command=lambda: b.config(text=self.ground_data_values[
                self.values_list_box.get(tk.ACTIVE)
            ]))

The idea is to update the clicked button text with the value selected in the ListBox. The problem is, when I click on the first button, for example, this is what happens:

enter image description here

It updates only the last button. It completely ignores the commands of all the previous buttons. It's ridiculous to not use a loop for this, to create all of the buttons manually. Why this happens?

EDIT: The problem persists even if I try with tk.StringVar().

for c in range(self.track_size):
    row = []
    for r in range(self.track_size):
        button_text = tk.StringVar()
        button = tk.Button(
            self.ground_data_frame, 
            textvariable=button_text
        )
        button.grid(row=r, column=c, sticky=tk.NW+tk.NE+tk.SW+tk.SE+tk.W+tk.E+tk.N+tk.S)
        row.append((button, button_text))
    self.ground_data_buttons.append(row)
for r in self.ground_data_buttons:
    for b in r:
        b[0].config(command=lambda: b[1].set(self.ground_data_values[
                self.values_list_box.get(tk.ACTIVE)
            ]))

EDIT2: Even using a class, the problem persists.

class Button(tk.Button):

    def __init__(self, frame):
        self.textvariable = tk.StringVar()
        tk.Button.__init__(self, frame, textvariable=self.textvariable)
        self.update_value("0")

    def update_value(self, value):
        self.textvariable.set(value)

    def set_command(self, f):
        self.config(command=f)

...

        for c in range(self.track_size):
            row = []
            for r in range(self.track_size):
                button = Button(self.ground_data_frame)
                button.grid(row=r, column=c, sticky=tk.NW+tk.NE+tk.SW+tk.SE+tk.W+tk.E+tk.N+tk.S)
                row.append(button)
            self.ground_data_buttons.append(row)
        for r in self.ground_data_buttons:
            for b in r:
                b.set_command(lambda: b.update_value(self.ground_data_values[self.values_list_box.get(tk.ACTIVE)]))
Ericson Willians
  • 7,606
  • 11
  • 63
  • 114

1 Answers1

1

Using this answer as a guide, the solution is to add default values to variables in the lambda callback function. In your case it would look something like this:

def updateButton(row, column):
    buttons[row][column].config(text="1")
for row in range(len(buttons)):
    for col in range(len(buttons[row])):
        buttons[row][col].config(command=lambda row=row, col=col: updateButton(row, col))
Alex Bohm
  • 170
  • 8
  • I've solved it like this: `b.config(command=lambda b=b: b.config(text=self.ground_data_values[self.values_list_box.get(tk.ACTIVE)]))`. Yeah, that was the problem. Geez, this closure detail is really hard to remember. Thank you! – Ericson Willians Nov 08 '17 at 03:09