Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Workout timer written in python using curses
[
{
"name":"REP",
"time_seconds":60
},
{
"name":"SWITCH",
"time_minutes":3
}
]
#!/usr/bin/env python
import curses
from time import time,sleep
import json
from dataclasses import dataclass
@dataclass
class Mode:
name:str
time:int
def load_modes(fname="modes.json"):
with open(fname,"r") as f:
return [
Mode(
name=d["name"],
time=d.get("time_seconds",0)+(d.get("time_minutes",0)*60)
)
for d in json.load(f)
]
class TUI:
FPS=60
MSG="[R]eset [SPACE] pause [ARROWS] to switch mode [Q]uit"
def __init__(self,screen):
self.screen=screen
self.stop_watch=StopWatch(screen,y=1)
self.modes=load_modes()
self.set_mode(0)
assert(self.modes)
screen.clear()
screen.nodelay(True)
curses.curs_set(0)
self.__quit=False
def set_mode(self,i):
if i<0:raise IndexError;
self.__current_mode=self.modes[i]
self.__current_index=i
self.stop_watch.set_time(self.__current_mode.time)
self.stop_watch.is_paused=True
def switch_mode(self,direction):
try:
self.set_mode(self.__current_index+direction)
except IndexError:pass
def quit(self):
self.__quit=True
def update(self,char):
self.screen.addstr(0,0,f"current mode:{self.__current_mode.name}")
self.stop_watch.update()
self.screen.addstr(2,0,TUI.MSG)
{
ord("r"): self.stop_watch.reset,
ord("R"): self.stop_watch.reset,
ord("q"): self.quit,
ord("Q"): self.quit,
ord(" "): self.stop_watch.pause,
curses.KEY_LEFT: lambda :self.switch_mode(-1),
curses.KEY_RIGHT: lambda :self.switch_mode(+1)
}.get(char,lambda :None)()
def run(self):
while self.__quit!=True:
self.screen.erase()
start=time()
self.update(self.screen.getch())
self.screen.refresh()
sleep(max(0,1/TUI.FPS - (time()-start)))
def format_time(time_seconds):
minutes=int((time_seconds+0.00001)//60)
time_seconds-=minutes*60
time_seconds=abs(time_seconds)
return f"{minutes:02}:{abs(time_seconds):05.2f}"
class StopWatch:
def __init__(self,screen,y):
self.screen=screen
self.is_paused=False
self.set_time(0)
self.y=y
def __update_remaining(self):
self.remaining=max(0,self.__start-time())
def set_time(self,time_seconds):
self.last_set_time=time_seconds
self.__start=time()+time_seconds
self.__update_remaining()
def reset(self):
self.set_time(self.last_set_time)
self.is_paused=True
def pause(self):
self.is_paused=not self.is_paused
def update(self):
if not self.is_paused:
self.__update_remaining()
else:
self.__start=time()+self.remaining
msg=f"{['▶️','⏹️'][self.is_paused]} {format_time(self.remaining)}"
self.screen.addstr(self.y,0,msg)
if __name__=="__main__":
curses.wrapper(lambda screen:TUI(screen).run())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment