How do I delay the execution of a function within tkinter button command

34 Views Asked by At

I am trying to simulate a computer player "clicking" a button after a human user clicks a button In a grid of buttons. If I use the .after method the 'state' of the button change is delayed but it executes my check_state() method which doesn't detect the change. When I try time.sleep() method it prolongs the human click but still immediately invokes the 'auto' click regardless of where I put it in my code. I want a delay between the human click and 'auto' click.

I have tried widget.after(1000) which gives the desired delay of 'auto' click, but doesn't allow my the change to be seen by my check_state() function. I have tried time.sleep() which delays the execution of the human button click but the 'auto' click is still immediately invokes regardless of which order I place the sleep() function in relation to the call to auto_click(). I know there are better practices for this code implementation like using class based structure which I plan on using once my logic and functionality issues are resolved. My code is as follows:

import tkinter as tk

import random

def app():

def auto_click():

     

    

     

    

    grid_state = get_grid_state()

    possible_clicks = []

    



    for i in range(0, len(grid_state)):

        if grid_state[i] == " ":

            possible_clicks.append(i)      



#debug.config(text=possible_moves)

    

    click = random.choice(possible_clicks)

    

    

    buttons[click].after(1000, lambda: buttons[click].config(text = "auto", state=tk.DISABLED))

    #time.sleep(1)

    

    check_grid_state()

    check_grid_full()

    

    debug.config(text="test")



def onclick(*arg):

    

    global is_full



    



    

    buttons[arg[0]].config(text = "clicked", state=tk.DISABLED)

    

    

    

    check_grid_state()

    check_grid_full()

    if not is_full:

        auto_click()

    

    

def check_grid_full():

    global is_full

    

    result=[]

    for i in range(len(buttons)):

        result.append(buttons[i].cget('state'))

        r = [*set(result)]

    if r == ['disabled']:

        

        is_full = True

        grid_status.config(text=is_full) 

    else:

        is_full = False

    

Retrieve the current state of the grid

def get_grid_state():   



   grid_state =[]

   for i in range(len(buttons)):

       grid_state.append(buttons[i].cget('text'))

   

   return grid_state

Check grid state

def check_grid_state():

    

    grid_states.config(text=get_grid_state()) 

Global Variables

is_full = False

buttons = []

c=0    

        

Window

root = tk.Tk()

root.title("Title")

Heading

label = tk.Label(root, text="grid state", font = ("Ariel black",22, "bold"))

label.pack()

Grid Frame

frame = tk.Frame(root)

frame.pack()

for row in range(3):

    for column in range(3):

    

        buttons.append(tk.Button(frame, text=f" ", font=("arial", "22"), state=tk.ACTIVE,  height=2, width=2, command=lambda c=c: onclick(c)))

        buttons[c].grid(row=row, column=column)

        c += 1

Status bar

grid_states = tk.Label(root, text=f"")

grid_states.pack()

grid_status = tk.Label(root, text=f"")

grid_status.pack()

#btn_is = tk.Label(root, text=f"")

#btn_is.pack()

Debugging output label

debug = tk.Label(root, text="debug")

debug.pack()

Event loop

root.mainloop()

if name == "main":

app()
0

There are 0 best solutions below