Search code examples
pythontkintertimepython-multithreading

How to create a function that activates within a time limit unless user gives input in python


I am trying to create a button in tkinter that once pressed, program waits for a specific time (here 1 second) and if the user doesn't press that button again, the function fun() is executed.
How can I get it to work?
To note, the button by default executes fun(), and also executes fun2() first time only.

import time
import threading
from tkinter import *

def fun():
    global label
    restart = time.perf_counter()
    label.pack()

def fun2(event, start):
    global restart
    restart = start
    but.unbind("<Button-1>", bind_no)
    t1 = threading.Thread(target = fun3)
    t1.start()

def fun3():
    global restart
    while True:
        while time.perf_counter()-restart<1:
            pass
        label.pack()


root = Tk()

label = Label(root, text="fun")

but = Button(root, text= "Click", command= fun)
bind_no = but.bind("<Button-1>", lambda eff: fun2(eff, time.perf_counter()))
but.pack()


root.mainloop()

Solution

  • You do not need threading or time; the mechanism to achieve the delayed/cancel behavior you describe can be implemented using tk.after

    Maybe like this:

    1- the button calls delay_action_fun
    2- delay_action_fun triggers a callback that will call fun in one second.
    3- if the button is not pressed again, fun is executed.
    3.1 - if the button is pressed again before the callback is executed, the callback to fun is cancelled.

    rinse & repeat.

    import tkinter as tk
    
    def fun():
        global call_back
        print('executing fun')
        call_back = None
    
    def delay_action_fun():
        """calls fun to xeq in 1 second, but cancels the callback if
        triggered again within that time period
        """
        global call_back
        print('pressed')
        if call_back is None:
            call_back = root.after(1000, fun)
        else:
            print('cancelling cb')
            root.after_cancel(call_back)
            call_back = None
    
    root = tk.Tk()
    
    call_back = None
    tk.Button(root, text= "xeq fun", command=delay_action_fun).pack()
    
    root.mainloop()