Last active
March 18, 2022 09:06
-
-
Save hhsprings/bc2a181ce30824661760d6b709dd608d to your computer and use it in GitHub Desktop.
Something like a kitchen timer (intended for a tkinter demo)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
from __future__ import unicode_literals | |
import sys | |
import re | |
import time | |
import operator | |
import functools | |
try: | |
import tkinter | |
from tkinter.simpledialog import Dialog | |
except ImportError: | |
import Tkinter as tkinter # python 2.x, but sorry, I'm not testing it. | |
from tkSimpleDialog import Dialog | |
try: | |
import idlelib # for ToolTip, etc. | |
try: | |
from idlelib.tooltip import Hovertip | |
except ImportError: | |
from idlelib.ToolTip import ToolTip as Hovertip | |
except ImportError: | |
def Hovertip(*args): pass | |
def _split_by_units(src, factors=(24, 60, 60), first_prod=True): | |
result = [] | |
f = functools.reduce(operator.mul, factors) | |
d = abs(src) * (f if first_prod else 1.0) | |
for fi in factors: | |
d, m = divmod(d, f) | |
result.append(d) | |
d = m | |
f /= fi | |
result.append(m) | |
return result | |
class OkCancelTimeout(Dialog): | |
def __init__(self, parent, title, elapse, timeout_is_cancel): | |
self._title = title | |
self._arrivetime = time.time() + elapse | |
self._pausedtime = None | |
self._timeout_is_cancel = timeout_is_cancel | |
self.excode = 1 # for sys.exit(1) | |
Dialog.__init__(self, parent, "settings") | |
def _fmtresttime(self, now): | |
secs = int(now - self._arrivetime) | |
r = _split_by_units(secs, first_prod=False) | |
r = [(int(r[i]), u) for i, u in enumerate(("d", "h", "m", "s"))] | |
return " ".join(["{}{}".format(v, u) for v, u in r if v]) | |
def body(self, master): | |
font = ["courier", 16, "normal"] | |
self._master = master | |
fr = tkinter.Frame(master) | |
tit = tkinter.Label(fr, text=self._title, font=font) | |
tit.pack() | |
self._cdtxt = tkinter.StringVar() | |
self._cdtxt.set(self._fmtresttime(time.time())) | |
cdt = tkinter.Entry(fr, textvariable=self._cdtxt, font=font, state="readonly") | |
cdt.pack() | |
fr.pack() | |
if Hovertip is not None: | |
Hovertip(fr, "try ^p, Up/Dn, and PgUp/PgDn to modify elapse") | |
cdt.bind("<Control-p>", self._toggle_pause_timer) | |
cdt.bind("<Up>", functools.partial(self._adv_arrivetime, 1)) | |
cdt.bind("<Control-Up>", functools.partial(self._adv_arrivetime, 15)) | |
cdt.bind("<Alt-Up>", functools.partial(self._adv_arrivetime, 60)) | |
cdt.bind("<Prior>", functools.partial(self._adv_arrivetime, 15*60)) | |
cdt.bind("<Control-Prior>", functools.partial(self._adv_arrivetime, 30*60)) | |
cdt.bind("<Alt-Prior>", functools.partial(self._adv_arrivetime, 60*60)) | |
cdt.bind("<Down>", functools.partial(self._adv_arrivetime, -1)) | |
cdt.bind("<Control-Down>", functools.partial(self._adv_arrivetime, -15)) | |
cdt.bind("<Alt-Down>", functools.partial(self._adv_arrivetime, -60)) | |
cdt.bind("<Next>", functools.partial(self._adv_arrivetime, -15*60)) | |
cdt.bind("<Control-Next>", functools.partial(self._adv_arrivetime, -30*60)) | |
cdt.bind("<Alt-Next>", functools.partial(self._adv_arrivetime, -60*60)) | |
self._master.after(100, self._timerfun) | |
return cdt | |
def _timerfun(self, *args): | |
if self._pausedtime: | |
return | |
now = time.time() | |
self._cdtxt.set(self._fmtresttime(now)) | |
if now < self._arrivetime: | |
self._master.after(100, self._timerfun) | |
else: | |
if not self._timeout_is_cancel: | |
self.ok() | |
else: | |
self.cancel() | |
def _toggle_pause_timer(self, *args): | |
if not self._pausedtime: | |
self._pausedtime = time.time() | |
else: | |
self._arrivetime += (time.time() - self._pausedtime) | |
self._pausedtime = None | |
self._master.after(100, self._timerfun) | |
def _adv_arrivetime(self, *args): | |
self._arrivetime += args[0] | |
def apply(self): | |
self.excode = 0 | |
if __name__ == '__main__': | |
import argparse | |
ap = argparse.ArgumentParser() | |
ap.add_argument( | |
"elapse", nargs="?", | |
default="3m", | |
help="NUMBER[SUFFIX]: SUFFIX may be `s' for seconds (the default), \ | |
`m' for minutes, `h' for hours or `d' for days. Unlike most implementations \ | |
that require NUMBER be an integer, here NUMBER may be an arbitrary floating \ | |
point number.") | |
ap.add_argument("--title", default="Please wait...") | |
ap.add_argument( | |
"--timeout_is_cancel", action="store_true") | |
args = ap.parse_args() | |
elapse = args.elapse.strip() | |
m = re.match(r"^([\d.eE+-]+)([smhd]){0,1}$", elapse) | |
if not m: | |
ap.error("invalid elapse") | |
v = float(m.group(1)) | |
s = m.group(2) | |
elapse = (v * {"m": 60, "h": 60*60, "d": 60*60*24, "s": 1, None: 1}[s]) | |
root = tkinter.Tk() | |
root.withdraw() | |
dlg = OkCancelTimeout(root, args.title, elapse, args.timeout_is_cancel) | |
sys.exit(dlg.excode) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment