造轮子的快乐 - 我的番茄钟

Posted on Mon 29 January 2024 in Journal

Abstract 造轮子的快乐 - 我的番茄钟
Authors Walter Fan
 Category    learning note  
Status v1.0
Updated 2024-01-29
License CC-BY-NC-ND 4.0

工业界有句俗语 - "不要重复造轮子", 轮子早就发明出来了, 圆圆的, 有轮毂, 有轮胎, 各种各样的都有, 何必自己再重复造轮子呢?

可是程序员就不信这个邪, 就喜欢重复造轮子, 别人的轮子再好, 也不如自己造的顺手, 比如番茄钟, 我以前就写过一个 python 版本的, 代码很简单, 参见 git@github.com:walterfan/tomato-timer.git

tomato timer

其实代码没多少, 也就 120 行

#!/usr/bin/env python3

import time
from datetime import datetime, timezone
from tkinter import Tk, StringVar, Entry, Label, Button, ttk, messagebox
from playsound import playsound

DEFAULT_MINS = 25
TIME_FORMAT = "%Y-%m-%d %H:%M:%S"

class TomatoTimer:

    def __init__(self, title = "Tomato Timer", frameSize="360x240"):
        self._root = Tk()
        self._style = ttk.Style()
        self._style.theme_use('alt')

        self._root.geometry(frameSize)
        self._root.title(title)

        self._hour=StringVar()
        self._minute=StringVar()
        self._second=StringVar()

        self.init_ui()

        self._started = False
        self._tomato_count = 0

    def set_time(self, hour=0, min=DEFAULT_MINS, sec=0):
        self._hour.set("{0:2d}".format(hour))
        self._minute.set("{0:2d}".format(min))
        self._second.set("{0:2d}".format(sec))

    def create_time_box(self, label):
        return Entry(self._root, width=4,font=("Arial",18,""), justify='center', textvariable=label)

    def init_ui(self):

        self.set_time()
        x_value = 90
        y_value = 80
        x_step = 60

        hour_box = self.create_time_box(self._hour)
        hour_box.place(x=x_value, y=y_value)

        x_value += x_step
        mins_box = self.create_time_box(self._minute)
        mins_box.place(x=x_value,y=y_value)

        x_value += x_step
        sec_box = self.create_time_box(self._second)
        sec_box.place(x=x_value,y=y_value)

        label = Label(self._root, text= "Focus on righ now",font=('Helvetica bold', 20))

        label.pack(pady=20)

        y_value += 40
        btnStart = Button(self._root, text='Start', bd='5', fg="green", command= self.countdown)
        btnStart.place(x = 80,y = y_value)

        btnStop = Button(self._root, text='Stop', bd='5', fg="blue", command= self.pause)
        btnStop.place(x = 140,y = y_value)

        btnReset = Button(self._root, text='Reset', bd='5', fg="red", command= self.reset)
        btnReset.place(x = 200,y = y_value)

    def stay_on_top(self):
        self._root.lift()
        self._root.after(300_000, self.stay_on_top)

    def run(self):
        self.stay_on_top()
        self._root.mainloop()

    def reset(self):
        self._started = False
        self.set_time()

    def pause(self):
        self._started = False

    def get_hour_min_sec(self, left_seconds):

        mins,secs = divmod(left_seconds, 60)

        hours=0
        if mins > 60:
            hours, mins = divmod(mins, 60)
        return hours, mins, secs

    def countdown(self):
        self._started = True
        try:
            left_seconds = int(self._hour.get())*3600 + int(self._minute.get())*60 + int(self._second.get())
        except:
            messagebox.showwarning('', 'Invalid Input Value!')

        while self._started:

            hours, mins, secs = self.get_hour_min_sec(left_seconds)
            self.set_time(hours, mins, secs)

            self._root.update()
            time.sleep(1)

            if (left_seconds == 0):
                self._tomato_count += 1
                print("It is time: {}, count={}".format( datetime.now().strftime(TIME_FORMAT),self._tomato_count))
                playsound('starlet.wav', block = False)
                break

            left_seconds -= 1


if __name__ == '__main__':
    timer = TomatoTimer()
    timer.run()

本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。