#!/usr/bin/python3 # The MIT License (MIT) # # Copyright © 2020-2025 pacman64 # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # countdown.pyw [minutes...] # # Show a small window with a live second-by-second countdown you can # change/restart any time using the slider control. When not given a # number, the default first duration to count down from is 20 minutes. # # Keyboard shortcuts: # # esc -> quit # right arrow -> restart countdown using 1 more minute # left arrow -> restart countdown using 1 fewer minute from sys import argv from typing import Any from datetime import datetime from tkinter import Tk, Label, Scale, TOP, BOTTOM, HORIZONTAL minutes = 20 # how many minutes to count down from timer = '' # timer id to allow cancelling start = datetime.now() # to determine the time left every second # handle optional cmd-line arg to change default countdown minutes if len(argv) > 1: try: n = int(argv[1]) if n > 0: minutes = n except Exception: pass def check_key(event: Any) -> None: char: str = event.char # quit when esc key is pressed if char != '' and ord(char) == 27: win.quit() def restart(): global timer, minutes, start # don't force main window on top while a countdown is happening win.attributes('-topmost', False) m = 0 try: m = float(input.get()) except Exception: m = 20 minutes = m if timer != '': win.after_cancel(timer) start = datetime.now() tick() def tick() -> None: global timer diff = datetime.now() - start sl = 60 * minutes - diff.total_seconds() # seconds left if sl < 0: # ensure main window is showing when countdown is over win.attributes('-topmost', True) output.configure(text='done') # returning prevents any further scheduled callback return output.configure(text=f'{int(sl/60):02d}:{int(sl%60):02d}') timer = win.after(1000, tick) win = Tk() win.title('Countdown') win.resizable(False, False) win.bind('', check_key) input = Scale(win, from_=1, to=90, orient=HORIZONTAL) input.configure(command=lambda _: restart()) input.pack(side=TOP, padx=5) input.set(minutes) input.focus() font = ('Lucida Console', 72) output = Label(text='', font=font) output.pack(side=BOTTOM, padx=10, pady=5) tick() win.mainloop()