How would I access a different button each time in Tkinter?

Viewed 119

I have nine buttons created like so:

MyButton1 = Button(game, text="BUTTON1", image=pixel, compound="c", width=200, height=200, command=callback(1))
MyButton1.grid(row=0, column=0)

MyButton2 = Button(game, text="BUTTON2", image=pixel, compound="c", width=200, height=200, command=callback(2))
MyButton2.grid(row=1, column=0)

MyButton3 = Button(game, text="BUTTON3", image=pixel, compound="c", width=200, height=200, command=callback(3))
MyButton3.grid(row=2, column=0)

# and so on

I want to change the text of a particular button when it is clicked, and I know that button.config can be used to change some things about the button. For example, if I wanted to change the text for MyButton1, I could do something like this: MyButton1.config(text="X"). But I think it would be quite messy to have a different function for the command for each button, so I could create a function like this:

def callback(id):
    print(id)
    # change the text of the button

But, my problem is that I don't know how to access the button when the button I am trying to get differs each time. I can't do "MyButton" + str(id).config, for obvious reasons (it's a string). So how should I do this?

2 Answers

Define the button first and then place it inside the callback like so

def changetext(button):
  button.config(text="Changed text")

b = Button(master, text="Text here")
b.config(command=lambda button=b: changetext(button))

Use a list of buttons. You can reference the index in a lambda command.

By using a list to store our buttons we can reference their index to do whatever we need to. this is very useful when working with widgets dynamically or large amounts of them. This way we can build a simple function to take the index of a list in order to edit what we need. Lambda is good here to set up our commands for each button in a loop.

It is important to note that you cannot simply do command= update_button(x) as this will call the function the instance the button is created rather than waiting for you to press it. This is because in the command we called the function instead of saving a reference to it. In order to save a reference to a function we simply omit the the parenthesis like this command= update_button.

That said in this situation we really need to pass a variable to it so in order to do this we can write a nameless function called a lambda to our command to execute the other command while also passing out variable.

import tkinter as tk


def update_button(ndex):
    button_list[ndex].config(text='x')


root = tk.Tk()

button_list = []
for i in range(9):
    button_list.append(tk.Button(root, text='Button {}'.format(i), command=lambda x=i: update_button(x)))
    button_list[-1].pack(fill='x')

root.mainloop()

Results app where a few buttons have been pressed:

enter image description here

Here is an example using grid with some math to handle grid placement in columns and rows.

import tkinter as tk


def update_button(ndex):
    button_list[ndex].config(text='x')


root = tk.Tk()

button_list = []
row = 0
column = 0
for i in range(15):
    button_list.append(tk.Button(root, text='Button {}'.format(i+1), command=lambda x=i: update_button(x)))
    button_list[-1].grid(row=row, column=column, sticky='ew')
    if row % 4 == 0 and row != 0:
        column += 1
        row = 0
    else:
        row += 1

root.mainloop()

Results:

enter image description here

If you would rather run columns first then rows simply flip the math:

if column % 4 == 0 and column != 0:
    row += 1
    column = 0
else:
    column += 1

Results:

enter image description here

Related