Based on my testing, this is because threads can only be started once, and as the timer relies on a thread, the timer can only be started once. This means that the only way to re-start the timer would be to do:
def newTimer():
global t
t = Timer(10.0,api_call)
newTimer()
instead of the t = Timer part, and do
t.cancel()
newTimer()
t.start()
instead of the current re-start code.
This makes your full code:
from threading import Timer
def api_call():
print("Call that there api")
def newTimer():
global t
t = Timer(10.0,api_call)
newTimer()
def my_callback(channel):
if something_true:
print('reset timer and start again')
t.cancel()
newTimer()
t.start()
print("\n timer started")
elif something_else_true:
t.cancel()
print("timer canceled")
else:
t.cancel()
print('cancel timer for sure')
try:
if outside_input_that_can_happen_a_lot:
my_callback()
finally:
#cleanup objects
Hope this helps.
Answer from CrazySqueak on Stack OverflowBased on my testing, this is because threads can only be started once, and as the timer relies on a thread, the timer can only be started once. This means that the only way to re-start the timer would be to do:
def newTimer():
global t
t = Timer(10.0,api_call)
newTimer()
instead of the t = Timer part, and do
t.cancel()
newTimer()
t.start()
instead of the current re-start code.
This makes your full code:
from threading import Timer
def api_call():
print("Call that there api")
def newTimer():
global t
t = Timer(10.0,api_call)
newTimer()
def my_callback(channel):
if something_true:
print('reset timer and start again')
t.cancel()
newTimer()
t.start()
print("\n timer started")
elif something_else_true:
t.cancel()
print("timer canceled")
else:
t.cancel()
print('cancel timer for sure')
try:
if outside_input_that_can_happen_a_lot:
my_callback()
finally:
#cleanup objects
Hope this helps.
Here is a small class which does exactly that from CrazySqueak's answer. So please upvote his answer not mine!
import threading
class AdvTimer():
def __init__(self, interval, callback):
self.interval = interval
self.callback = callback
def restart(self):
self.timer.cancel()
self.start()
def start(self):
self.timer = threading.Timer(self.interval, self.callback)
self.timer.start()
Created My First Tkinter Project: Count-Down Timer : Reset/pause and resume buttons.
python - How to set a timer & clear a timer? - Stack Overflow
Python: Run code every n seconds and restart timer on condition - Stack Overflow
Timer cannot restart after it is being stopped in Python - Stack Overflow
Videos
Been learning functions and how to add them to buttons and working with time in general. So wanted to create a little project that will test me on those areas.
import tkinter as tk
from tkinter import *
import time
root = Tk()
root.title(' COUNT-DOWN TIMER')
#root.geometry("800x400")
root['background']='#2F4F4F'
total_time = 0
rest = 0
pause = "No"
#####ADD/MINUS/RESET FUNCTIONS
def add_time_hour():
global total_time
total_time += 3600
countdown_label.config(text=f"{total_time//3600:02}:{(total_time//60)%60:02}:{total_time%60:02}",fg = '#00FF00',font=("Arial", 24))
def add_time_min():
global total_time
total_time += 60
countdown_label.config(text=f"{total_time//3600:02}:{(total_time//60)%60:02}:{total_time%60:02}",fg= "#00FF00",font=("Arial", 24))
def add_time_sec():
global total_time
total_time += 1
countdown_label.config(text=f"{total_time//3600:02}:{(total_time//60)%60:02}:{total_time%60:02}",fg= "#00FF00",font=("Arial", 24))
def add_rest_min():
global rest
rest += 60
rest_label.config(text=f"{(rest // 3600) % 3600:02}:{(rest // 60) % 60:02}:{rest % 60:02}",fg= "#00FF00",font=("Arial", 24))
def reset():
global total_time, rest,pause
pause = "No"
total_time = 0
rest = 0
countdown_label.config(text=f"{total_time//3600:02}:{(total_time//60)%60:02}:{total_time%60:02}",fg= "#00FF00",font=("Arial", 24))
rest_label.config(text=f"{(rest // 3600) % 3600:02}:{(rest // 60) % 60:02}:{rest % 60:02}",fg= "#00FF00",font=("Arial", 24))
############TIME FUNCTIONS#############################################
def countdown():
global total_time,rest, pause
if total_time > 0 and pause == "No":
total_time -= 1
countdown_label.config(text=f"{(total_time // 3600) % 3600:02}:{(total_time // 60) % 60:02}:{total_time % 60:02}",fg="#00FF00", font=("Arial", 24))
root.after(1000,countdown)
if total_time == 0:
countdown_label.config(
text=f"{(total_time // 3600) % 3600:02}:{(total_time // 60) % 60:02}:{total_time % 60:02}", fg="red",
font=("Arial", 24))
elif total_time == 0 and rest > 0 and pause == "No":
rest -= 1
rest_label.config(text=f"{(rest// 3600) % 3600:02}:{(rest // 60) % 60:02}:{rest % 60:02}", fg="#00FF00", font=("Arial", 24))
root.after(1000,countdown)
if rest == 0:
rest_label.config(text=f"{(rest // 3600) % 3600:02}:{(rest // 60) % 60:02}:{rest % 60:02}", fg="red",
font=("Arial", 24))
def countdown_pause():
global pause
pause = "Yes"
def countdown_unpause():
global pause
pause = "No"
countdown()
#############LABELS####################
countdown_label = tk.Label(text = "00:00:00",fg="#00FF00",bg='#2F4F4F', font=("Arial", 24))
countdown_label.grid(row= 0, column = 2, columnspan=2)
countdown_label_title = tk.Label(text = "RUNNING: ",fg="#00FF00",bg='#2F4F4F', font=("Arial", 24))
countdown_label_title.grid(row= 0, column = 0, columnspan=2)
rest_label = tk.Label(text = "00:00:00", fg= "#00FF00",bg='#2F4F4F',font=("Arial", 24))
rest_label.grid(row= 1, column = 2, columnspan=2)
rest_label_title = tk.Label(text = "RESTING: ",fg="#00FF00",bg='#2F4F4F', font=("Arial", 24))
rest_label_title.grid(row= 1, column = 0, columnspan= 2)
#BUTTONS###
add_button = tk.Button(root, text="START", command=countdown, bg='#36648B', font=("Arial", 18))
add_button.grid(row= 3, column = 1)
add_button = tk.Button(root, text="+HOUR", command=add_time_hour, bg='#36648B', font=("Arial", 18))
add_button.grid(row= 2, column = 0)
add_button = tk.Button(root, text="+MNTS", command=add_time_min, bg='#36648B', font=("Arial", 18))
add_button.grid(row= 2, column = 1)
add_button = tk.Button(root, text="+SCND", command=add_time_sec, bg='#36648B', font=("Arial", 18))
add_button.grid(row= 2, column = 2)
add_button = tk.Button(root, text="RESET ", command=reset, bg='#36648B', font=("Arial", 18))
add_button.grid(row= 3, column = 0)
add_button = tk.Button(root, text="RST-MIN", command=add_rest_min, bg='#36648B', font=("Arial", 18))
add_button.grid(row= 2, column = 3)
add_button = tk.Button(root, text="PAUSE", command=countdown_pause, bg='#36648B', font=("Arial", 18))
add_button.grid(row= 3, column = 2)
add_button = tk.Button(root, text="RESUME", command=countdown_unpause, bg='#36648B', font=("Arial", 18))
add_button.grid(row= 3, column = 3)
root.mainloop()You could probably accomplish it with threading really elegantly but if you need a quick fix you could try
import time
timer = 15 * 60 # 60 seconds times 15 mins
while timer > 0:
time.sleep(0.985) # don't sleep for a full second or else you'll be off
timer -= 1
if someCondition:
timer = 15 * 60
executeCode() # called when time is zero and while loop is exited
If the whole process is as simple as you say, I would go about it like this (semi-psuedo-code):
def run_every_fifteen_minutes():
pass
def should_reset_timer():
pass
def main():
timer = 0
while True:
time.sleep(1)
timer+=1
if should_reset_timer():
timer = 0
if timer == 15*60:
run_every_fifteen_minutes()
timer = 0
Note that this won't be exactly fifteen minutes in. It might be late by a few seconds. The sleep isn't guaranteed to sleep only 1 second and the rest of the loop will take some time, too. You could add a system time compare in there if you need it to be really accurate.
Here is a corrected version:
from __future__ import print_function
from threading import Timer
def hello():
print("Hello World!")
class RepeatingTimer(object):
def __init__(self, interval, f, *args, **kwargs):
self.interval = interval
self.f = f
self.args = args
self.kwargs = kwargs
self.timer = None
def callback(self):
self.f(*self.args, **self.kwargs)
self.start()
def cancel(self):
self.timer.cancel()
def start(self):
self.timer = Timer(self.interval, self.callback)
self.timer.start()
t = RepeatingTimer(3, hello)
t.start()
Example Run:
$ python -i foo.py
>>> Hello World!
>>> Hello World!
>>> t.cancel()
The reason your timer is not restarting is because you never reset self.interval to True before trying to restart the timer. However, if that's the only change you make, you will find your timer is vulnerable to a race condition that will result in more than one timer running concurrently.
Try this:
import time
import keyboard
x=60
while True:
try:
if keyboard.is_pressed('space'):
x=60
print(f"Timer reset to {x}")
elif keyboard.is_pressed('space'):
break
else:
print(f"{x} seconds left")
time.sleep(1)
x-=1
except Exception:
pass
This is my suggestion. The problem is, that time.sleep is blocking, so when it's sleeping you cannot break it by pressing escape. You will have to spam the escape button, but it works.
import keyboard
import time
t=60
last = False
current = False
while True:
current = keyboard.is_pressed('space') # triggered when space bar is pressed
if last == 0 and current == 1:
while t>=0:
if keyboard.is_pressed('escape'):
t = 60
last = False
current = False
print("Timer stopped.")
break
print(t)
t-=1
time.sleep(1)
last = current # reset
Sample output/test:
60
59
58
57
56
60
59
58
57
56
60
59
60
59
58
You would call the cancel method after you start the timer:
import time
import threading
def hello():
print "hello, world"
time.sleep(2)
t = threading.Timer(3.0, hello)
t.start()
var = 'something'
if var == 'something':
t.cancel()
You might consider using a while-loop on a Thread, instead of using a Timer.
Here is an example appropriated from Nikolaus Gradwohl's answer to another question:
import threading
import time
class TimerClass(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.event = threading.Event()
self.count = 10
def run(self):
while self.count > 0 and not self.event.is_set():
print self.count
self.count -= 1
self.event.wait(1)
def stop(self):
self.event.set()
tmr = TimerClass()
tmr.start()
time.sleep(3)
tmr.stop()
I'm not sure if I understand correctly. Do you want to write something like in this example?
>>> import threading
>>> t = None
>>>
>>> def sayHello():
... global t
... print "Hello!"
... t = threading.Timer(0.5, sayHello)
... t.start()
...
>>> sayHello()
Hello!
Hello!
Hello!
Hello!
Hello!
>>> t.cancel()
>>>
Here's an example that implements a reset method to "extend" the timer by the original interval. It uses an internal Timer object rather than subclassing threading.Timer.
from threading import Timer
import time
class ResettableTimer(object):
def __init__(self, interval, function):
self.interval = interval
self.function = function
self.timer = Timer(self.interval, self.function)
def run(self):
self.timer.start()
def reset(self):
self.timer.cancel()
self.timer = Timer(self.interval, self.function)
self.timer.start()
if __name__ == '__main__':
t = time.time()
tim = ResettableTimer(5, lambda: print("Time's Up! Took ", time.time() - t, "seconds"))
time.sleep(3)
tim.reset()
Output:
Time's Up! Took 8.011203289031982 seconds
Here is a simple example. You should consider adding locks.
import threading
import time
def hi( ):
print('hi')
mine.start()
class ReusableTime():
def __init__(self, t, func):
self._t = t
self._func = func
def start(self):
self._thread = threading.Timer(self._t, self.handler)
self._thread.start()
def handler(self):
self._func()
mine = ReusableTime(2, hi)
mine.start()
time.sleep(100)
I've searched around the internet and I can't seem to find an answer. I have a series of checkboxes that when clicked, calls a function that is a countdown timer. If I wait for the timer to end, and click another checkbox, it works fine. But if I check a second box while the other is still running, it starts another timer so then I have 2 timers running... or 3 or 4 depending on how many checkboxes I click. If I click another checkbox while the timer is still running, how do I get it to stop the first timer and start over?
def countdown(count): import beepy label['text'] = count if count >= 0: root.after(1000, countdown, count - 1) else: beepy.beep(4) label['text'] = 0
each checkbox has this code to call the countdown function:
ttk.Checkbutton(routine, command=lambda: countdown(45))