How can I identify buttons, created in a loop?

后端 未结 4 1068
借酒劲吻你
借酒劲吻你 2020-12-06 15:29

I am trying to program a minesweeper game on python using tkinter. I started off by creating a grid of buttons using a two dimensional list of 10x10. Then I created each but

4条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-06 16:00

    While creating buttons in a loop, we can create (actually get) the unique identity.

    For example: if we create a button:

    button = Button(master, text="text")
    

    we can identify it immediately:

    print(button)
    > 
    

    If we store this identity into a list and asign a command to the button(s), linked to their index during creation, we can get their specific identity when pressed.

    The only thing we have to to then is to fetch the button's identity by index, once the button is pressed.

    To be able to set a command for the buttons with the index as argument, we use functools' partial.

    Simplified example (python3)

    In the simplified example below, we create the buttons in a loop, add their identities to the list (button_identities). The identity is fetched by looking it up with: bname = (button_identities[n]).

    Now we have the identity, we can subsequently make the button do anything, including editing- or killing itself, since we have its identity.

    In the example below, pressing the button will change its label to "clicked"

    from tkinter import *
    from functools import partial
    
    win = Tk()
    button_identities = []
    
    def change(n):
        # function to get the index and the identity (bname)
        print(n)
        bname = (button_identities[n])
        bname.configure(text = "clicked")
    
    for i in range(5):
        # creating the buttons, assigning a unique argument (i) to run the function (change)
        button = Button(win, width=10, text=str(i), command=partial(change, i))
        button.pack()
        # add the button's identity to a list:
        button_identities.append(button)
    
    # just to show what happens:
    print(button_identities)
    
    win.mainloop()
    

    Or if we make it destroy the buttons once clicked:

    from tkinter import *
    from functools import partial
    
    win = Tk()
    button_identities = []
    
    def change(n):
        # function to get the index and the identity (bname)
        print(n)
        bname = (button_identities[n])
        bname.destroy()
    
    for i in range(5):
        # creating the buttons, assigning a unique argument (i) to run the function (change)
        button = Button(win, width=10, text=str(i), command=partial(change, i))
        button.place(x=0, y=i*30)
        # add the button's identity to a list:
        button_identities.append(button)
    
    # just to show what happens:
    print(button_identities)
    
    win.mainloop()
    

    Simplified code for your matrix (python3):

    In the example below, I used itertools's product() to generate the coordinates for the matrix.

    #!/usr/bin/env python3
    from tkinter import *
    from functools import partial
    from itertools import product
    
    # produce the set of coordinates of the buttons
    positions = product(range(10), range(10))
    button_ids = []
    
    def change(i):
        # get the button's identity, destroy it
        bname = (button_ids[i])
        bname.destroy()
    
    win = Tk()
    frame = Frame(win)
    frame.pack()
    
    for i in range(10):
        # shape the grid
        setsize = Canvas(frame, width=30, height=0).grid(row=11, column=i)
        setsize = Canvas(frame, width=0, height=30).grid(row=i, column=11)
    
    for i, item in enumerate(positions):
        button = Button(frame, command=partial(change, i))
        button.grid(row=item[0], column=item[1], sticky="n,e,s,w")
        button_ids.append(button)
    
    win.minsize(width=270, height=270)
    win.title("Too many squares")
    win.mainloop()
    

    More options, destroying a button by coordinates

    Since product() also produces the x,y coordinates of the button(s), we can additionally store the coordinates (in coords in the example), and identify the button's identity by coordinates.

    In the example below, the function hide_by_coords(): destroys the button by coordinates, which can be useful in minesweeper -like game. As an example, clicking one button als destroys the one on the right:

    #!/usr/bin/env python3
    from tkinter import *
    from functools import partial
    from itertools import product
    
    positions = product(range(10), range(10))
    button_ids = []; coords = []
    
    def change(i):
        bname = (button_ids[i])
        bname.destroy()
        # destroy another button by coordinates
        # (next to the current one in this case)
        button_nextto = coords[i]
        button_nextto = (button_nextto[0] + 1, button_nextto[1])
        hide_by_coords(button_nextto)
    
    def hide_by_coords(xy):
        # this function can destroy a button by coordinates
        # in the matrix (topleft = (0, 0). Argument is a tuple
        try:
            index = coords.index(xy)
            button = button_ids[index]
            button.destroy()
        except (IndexError, ValueError):
            pass
    
    win = Tk()
    frame = Frame(win)
    frame.pack()
    
    for i in range(10):
        # shape the grid
        setsize = Canvas(frame, width=30, height=0).grid(row=11, column=i)
        setsize = Canvas(frame, width=0, height=30).grid(row=i, column=11)
    
    for i, item in enumerate(positions):
        button = Button(frame, command=partial(change, i))
        button.grid(column=item[0], row=item[1], sticky="n,e,s,w")
        button_ids.append(button)
        coords.append(item)
    
    win.minsize(width=270, height=270)
    win.title("Too many squares")
    win.mainloop()
    

提交回复
热议问题