Reputation: 284
I'm trying to learn how to make GUI with tkinter in Python and I'm getting an error that my callback function is undefined. Basically I want a grid of white squares and when you click on the square it will become red.
from tkinter import *
root = Tk()
class Application(Frame):
def __init__(self, master, *args, **kwargs):
Frame.__init__(self, master, *args, ** kwargs)
self.createWidgets()
def mouse_click(self):
self.squares.bg = 'red'
def createWidgets(self):
for i in range(10):
for j in range(10):
self.squares = Button(self, height = 3, width = 7, bg = 'white',
command = lambda: mouse_click())
self.squares.grid(row = i, column = j)
Application(root).grid()
root.mainloop()
Upvotes: 0
Views: 4074
Reputation: 206707
You need to use ommand = lambda: self.mouse_click()
.
However, that does not solve all the problems. The other problem is that you are creating a 2D grid of buttons but are storing only the last of them in your class.
The line
self.squares = Button(...)
stores only one button. You end up storing a handle to the last Button
. The other Button
s that you created in previous steps are lost to your class.
Here's my suggestion (not verified):
class Application(Frame):
def __init__(self, master, *args, **kwargs):
Frame.__init__(self, master, *args, ** kwargs)
self.createWidgets()
def mouse_click(self, i, j):
self.squares[i][j].bg = 'red'
def createWidgets(self):
# Creeate a 2D array that contains None in all the elements.
self.squares = [[None for x in range(10)] for y in range(10)]
# Fill up the array.
for i in range(10):
for j in range(10):
# Create a button and display it in the i-th row and j-th
# column in a grid.
button = Button(self,
height = 3,
width = 7,
bg = 'white',
command = lambda: self.mouse_click(i, j))
button.grid(row = i, column = j)
# Store the button for the callback function
self.squares[i][j] = button
Upvotes: 1
Reputation: 43276
There are multiple problems in your code.
mouse_click
is an instance method, so it must be referenced as self.mouse_click
.self.squares
in each iteration of your loop, so in the end it references the last button you created. This isn't useful and can be removed.The clean way to solve this is to rewrite the mouse_click
method to take a button as an argument:
def mouse_click(self, square):
square['bg'] = 'red'
And then give each button's command
function a reference to the button itself. This can be done with functools.partial
:
def createWidgets(self):
for i in range(10):
for j in range(10):
square = Button(self, height = 3, width = 7, bg = 'white')
square['command'] = functools.partial(self.mouse_click, square)
square.grid(row = i, column = j)
This makes it so that the button's click event handler receives the button as an argument. With these two changes, everything works as intended.
Upvotes: 2