nikuiz
nikuiz

Reputation: 1

Tkinter pass a button as argument

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

Answers (3)

chikibamboni
chikibamboni

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()

enter image description here

Upvotes: 0

Ahmed AEK
Ahmed AEK

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

chepner
chepner

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

Related Questions