4

I am trying to implement a game called "Five In a Row". And I create a 15×15 list to put the buttons. (I used range(16) because I also want a row and a column to display the row number and column number)

I hope my implementation will be like when a button is clicked, it becomes a label. But I don't know which button the user clicks.

How am I able to implement that? Thanks!

from tkinter import *
root=Tk()
root.wm_title("Five In a Row")
buttonlst=[ list(range(16)) for i in range(16)]

chess=Label(root,width=2,height=2,text='0')

def p(button):
    gi=button.grid_info()
    x=gi['row']
    y=gi['column']
    button.grid_forget()
    chess.grid(row=x,column=y)
    buttonlst[x][y]=chess

for i in range(16):
    for j in range(16):
        if i==0:
            obj=Label(root,width=2,text=hex(j)[-1].upper())
        elif j==0:
            obj=Label(root,width=2,text=hex(i)[-1].upper())
        else:
            obj=Button(root,relief=FLAT,width=2,command=p(obj))
        obj.grid(row=i,column=j)
        buttonlst[i][j]=obj

root.mainloop()

There is a similar question How to determine which button is pressed out of Button grid in Python TKinter?. But I don't quite get that.

Seaky Lone
  • 992
  • 1
  • 10
  • 29
  • This looks like an exact duplicate of the question you link to. What part of the answers don't you get? Can you be more specific? – Bryan Oakley Nov 14 '17 at 22:04
  • @BryanOakley I don't know how he stores the info of the button. – Seaky Lone Nov 14 '17 at 22:27
  • You can have a function that takes button position info as argument at the very least if you want to determine which button is pressed. – Nae Nov 14 '17 at 22:31

2 Answers2

9

To pass the button instance to the command, you must do it in two steps. First, create the button, and then in a second step you configure the command. Also, you must use a lambda to create what's called a closure.

For example:

obj=Button(root,relief=FLAT,width=2)
obj.configure(command=lambda button=obj: p(button))
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
1

When you use command = p(obj) you are actually calling the function p. If you want to pass a function with parameters you should create a lambda function. Therefore, the command assignment should be something like:

command = lambda: p(obj)

That will pass the object properly into p function.

  • 1
    This won't work if this code is called in a loop and obj is different for each iteration. – Bryan Oakley Nov 14 '17 at 22:34
  • Why not? `obj` is different in every iteration as it is assigned previously, so `command` takes the right variable reference for each new button. – Sergio Abreu García Nov 14 '17 at 22:40
  • 2
    `obj` is different during the loop, but the lambda doesn't run in the loop so it will pick up the last value that was stored in `obj`. See https://stackoverflow.com/q/13355233/7432 for one explanation. – Bryan Oakley Nov 14 '17 at 22:43