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()
Timer cannot restart after it is being stopped in Python - Stack Overflow
Python: Run code every n seconds and restart timer on condition - Stack Overflow
How do I stop a function and restart it when it is called again?
Created My First Tkinter Project: Count-Down Timer : Reset/pause and resume buttons.
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.
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.
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))
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()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