Reputation: 1
I'm new to Tkinter and as my first project I wanted to create a Tic Tac Toe. I want to create 9 buttons, that will change their background image when I click on them, the problem is that I dont want to create a function for every single button but one function that will take the button in argument and will change its background image.
The code I wrote:
def play(bid):
if player == "X":
bid.config(image=cross)
if player == "O":
bid.config(image=circle)
b1 = tk.Button(app, text="", image=white, command=lambda id=b1: play(id))
b1.grid(column=0, row=0)
How can I pass b1 as an argument to play() function? Thanks
I tried to use b1 as an argument to play(), and use play() to change b1's image. When I try to run this code I get "name b1 is not defined".
Upvotes: 0
Views: 387
Reputation: 60
Here is what I wrote at my leisure, it may be useful. Just create a folder 'icons' and put 100x100 px images there
from tkinter import *
import random
class Main:
def __init__(self):
self.root = Tk()
#self.root.geometry('900x100')
def run(self):
self.variables()
self.interface()
self.root.mainloop()
def variables(self):
self.PHOTO_COUNTER = 0
self.photo_list = [
PhotoImage(file="icons/bublegum.png"),
PhotoImage(file="icons/fin.png"),
PhotoImage(file="icons/jake.png"),
PhotoImage(file="icons/marcelin.png"),
PhotoImage(file="icons/navel.png"),
PhotoImage(file="icons/winter_king.png"),
]
def interface(self):
self.Buttons = []
for i in range(10):
item = random.choice(self.photo_list)
self.btn = Button(self.root, image=item, command=lambda c=i: self.click(c))
self.btn.pack(fill=BOTH, expand=1, side=LEFT)
self.Buttons.append(self.btn)
def click(self, i):
btn = self.Buttons[i]
item = random.choice(self.photo_list)
btn.config(image=item)
A = Main()
A.run()
Upvotes: 0
Reputation: 17496
One way you can do it is to separate the creation of the button from the assignment using the .config
method.
b1 = tk.Button(app, text="", image=white)
b1.config(command=lambda btn_id=b1: play(btn_id))
This is not really the best way to go about it, it's better to instead pass something that defines the button like an Enum or text.
b1 = tk.Button(app, text="", image=white, command=lambda : play("b1"))
then check that "b1" is the input in your function, this way b1
can be redefined as what would happen in loops.
Upvotes: 1
Reputation: 530920
Define a single function to create, configure, and bind your button; then your callback can close over the necessary variable referring to your button. Something like
def play(bid):
if player == "X":
bid.config(image=cross)
if player == "O":
bid.config(image=circle)
def add_button(app, r, c):
b = tk.Button(app, text="", image=white)
b.config(command=lambda: play(b))
b.grid(column=c, row=r)
return b
for row in [0,1,2]:
for col in [0,1,2]:
# Save the return value somewhere if necessary
addButton(app, row, col)
The lambda expression contains a free variable b
, which refers to the variable b
in the closest enclosing scope, which is the call to add_button
where the lambda expression is evaluated.
Upvotes: 1