'tkinter 2d button list command not working

I recently got back into using Python 3's tkinter and am working on some simple problems. I am working on creating a Tic Tac Toe program, but I am running into an issue. I am using a 2d list of buttons to call a command function when they are clicked. I have used partial to run functions with parameters previously. The program does use command functions correctly. Why is the command not executing when the buttons in the 2d list are clicked on?

from tkinter import *
import tkinter as tk
from functools import partial

class App(tk.Tk):
    def __init__(self):
        super().__init__()
        pixel = tk.PhotoImage(width=1, height=1)


        # configure the root window
        self.title('Tic Tac Toe')
        self.geometry('500x500')

        self.turn=0
        self.options=["X","O"]

        # label
        self.label = tk.Label(self, text='Tic Tac Toe')
        self.label.config(font="Calibri 48")
        self.label.pack()

        self.gameframe=Frame(self)
        self.gameframe.pack()
        self.buttons=[[Button for i in range(3)] for j in range(3)]

        #The main issue with the code
        for i in range(3):
            for j in range(3):
                #self.buttons[i][j]['command'] = self.hello
                buttonfunction = partial(self.clickbutton, i, j)

                self.buttons[i][j]=Button(self.gameframe,image=pixel)

                self.buttons[i][j].config(command= buttonfunction)
                self.buttons[i][j].config(width=100,height=100)
                self.buttons[i][j].grid(row=i,column=j)

        print(self.buttons)
        self.button = Button(self, text='Test')
        self.button['command'] = self.hello
        self.button.pack()


    def hello(self):
        print("Hello")

    
    def clickbutton(self,x,y):
        print(x,y)
        buttontext=self.options[self.turn]
        self.buttons[y][x].config(text=buttontext)
        self.turn=(self.turn+1)%2
    

if __name__ == "__main__":
    app = App()
    app.mainloop()

Image of the Python 3 Tkinter Tic Tac Toe Layout



Solution 1:[1]

This is one of the class tkinter blunders. The ISSUE here is that pixel is local to the __init__ function, and the Button class doesn't grab a reference. When the function ends, that image is destroyed, the button becomes invalid and no longer responds.

Use:

        self.pixel = tk.PhotoImage(width=1, height=1)
...
                image = self.pixel,

and things work better. You may need to specify compound='c' if you want both text and image.

And Paul M. is right, you need to change your button function to

        buttonfunction = partial(self.clickButton, j, i)

to get the rows and columns right. Here's the code I have, which works:

        for i in range(3):
            for j in range(3):
                buttonfunction = partial(self.clickbutton, j, i)

                self.buttons[i][j]=Button(self.gameframe,
                    image=self.pixel,
                    text = '--',
                    compound='c',
                    width=100,
                    height=100,
                    command=buttonfunction)
                self.buttons[i][j].grid(row=i,column=j)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tim Roberts