Skip to content

Commit 2d7f41c

Browse files
author
duzm
committed
feat: add timer stop controls
1 parent e122911 commit 2d7f41c

3 files changed

Lines changed: 71 additions & 13 deletions

File tree

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 迷你番茄钟 (Mini-Tomato)
22

3-
Windows平台上一个可自由拖动的悬浮半透明小窗番茄工作法计时器,支持最小化到系统托盘,并通过托盘菜单快速开始专注、休息或恢复窗口
3+
Windows平台上一个可自由拖动的悬浮半透明小窗番茄工作法计时器,支持最小化到系统托盘,并通过托盘菜单快速开始专注、休息、停止计时或恢复窗口
44

55
![演示](./images/demo.gif)
66

@@ -22,6 +22,11 @@ Windows平台上一个可自由拖动的悬浮半透明小窗番茄工作法计
2222
python py_to_exe.py
2323
```
2424

25+
## 功能说明
26+
- 支持专注和休息两个倒计时阶段,阶段自然结束后会自动切换到下一阶段。
27+
- 支持最小化到系统托盘,并从托盘快速开始专注、休息、停止计时或恢复窗口;仅在倒计时进行中显示“停止计时”。
28+
- 支持在主窗口点击“停止”按钮,立即中断当前计时并关闭倒计时悬浮窗。
29+
2530
## 文件说明
2631
- `main.py`: 主程序
2732
- `constant.py`: 常量配置

constant.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# APP常量配置
2-
VERSION = "v1.1.0"
2+
VERSION = "v1.1.1"
33
APP_NAME = "迷你番茄钟"
44
ICON_FILE = "icon.ico"
55
AUTHOR = "duzm"

main.py

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,20 @@
2121

2222
# 全局变量,用于跟踪当前的悬浮窗口
2323
float_window = None
24+
timer_job = None
25+
timer_active = False
26+
current_phase = None
2427
root = None
2528
entry = None
29+
start_button = None
2630
tray_icon = None
2731
tray_thread = None
2832
tray_visible = False
2933
action_queue = queue.SimpleQueue()
3034

3135

3236
if sys.platform == "win32":
37+
3338
class TrayIcon(pystray.Icon):
3439
def _on_notify(self, wparam, lparam):
3540
if lparam == pystray_win32.win32.WM_LBUTTONUP:
@@ -43,7 +48,8 @@ def _on_notify(self, wparam, lparam):
4348
hmenu, descriptors = self._menu_handle
4449
index = pystray_win32.win32.TrackPopupMenuEx(
4550
hmenu,
46-
pystray_win32.win32.TPM_LEFTALIGN | pystray_win32.win32.TPM_BOTTOMALIGN
51+
pystray_win32.win32.TPM_LEFTALIGN
52+
| pystray_win32.win32.TPM_BOTTOMALIGN
4753
| pystray_win32.win32.TPM_RETURNCMD,
4854
point.x,
4955
point.y,
@@ -128,12 +134,48 @@ def start_break_session():
128134
start_timer(work_time=get_focus_minutes() * 60, break_time=DEFAULT_BREAK_TIME, is_work_time=False)
129135

130136

137+
def update_timer_controls():
138+
if start_button is not None and root is not None and root.winfo_exists():
139+
if timer_active:
140+
start_button.config(text="停止", command=stop_current_timer)
141+
else:
142+
start_button.config(text="开始", command=start_focus_session)
143+
refresh_tray_menu()
144+
145+
146+
def clear_timer_state(cancel_job=True, destroy_window=True):
147+
global float_window, timer_job, timer_active, current_phase
148+
149+
timer_active = False
150+
current_phase = None
151+
152+
if cancel_job and timer_job is not None and root is not None and root.winfo_exists():
153+
try:
154+
root.after_cancel(timer_job)
155+
except tk.TclError:
156+
pass
157+
timer_job = None
158+
159+
if destroy_window and float_window is not None:
160+
try:
161+
if float_window.winfo_exists():
162+
float_window.destroy()
163+
except tk.TclError:
164+
pass
165+
float_window = None
166+
update_timer_controls()
167+
168+
169+
def stop_current_timer():
170+
clear_timer_state()
171+
172+
131173
def start_timer(work_time=DEFAULT_WORK_TIME, break_time=DEFAULT_BREAK_TIME, is_work_time=True):
132-
global float_window
174+
global float_window, timer_job, timer_active, current_phase
133175

134-
# 如果已经存在一个悬浮窗口,先销毁它
135-
if float_window is not None:
136-
float_window.destroy()
176+
clear_timer_state()
177+
timer_active = True
178+
current_phase = "work" if is_work_time else "break"
137179

138180
# 创建新的悬浮窗口
139181
float_window = tk.Toplevel(root)
@@ -167,16 +209,22 @@ def do_move(event):
167209

168210
float_window.bind("<Button-1>", start_move)
169211
float_window.bind("<B1-Motion>", do_move)
212+
update_timer_controls()
170213

171214
# 倒计时功能
172215
def countdown(count):
216+
global timer_job
217+
218+
if not timer_active or float_window is None or not float_window.winfo_exists():
219+
return
220+
173221
mins, secs = divmod(count, 60)
174222
time_format = '{:02d}:{:02d}'.format(mins, secs)
175223
label.config(text=time_format)
176224
if count > 0:
177-
float_window.after(1000, countdown, count - 1)
225+
timer_job = float_window.after(1000, countdown, count - 1)
178226
else:
179-
float_window.destroy()
227+
clear_timer_state(cancel_job=False)
180228
if is_work_time:
181229
messagebox.showinfo("时间到", "工作时间结束!休息一下吧~")
182230
start_timer(work_time=work_time, break_time=break_time, is_work_time=False) # 开始休息时间
@@ -202,6 +250,10 @@ def get_window_toggle_text(item=None):
202250
return "最小化窗口" if is_root_visible() else "显示窗口"
203251

204252

253+
def is_timer_active(item=None):
254+
return timer_active
255+
256+
205257
def refresh_tray_menu():
206258
if tray_icon is not None:
207259
tray_icon.update_menu()
@@ -217,6 +269,7 @@ def create_tray_menu():
217269
pystray.MenuItem(get_window_toggle_text, enqueue_tray_action("toggle_window")),
218270
pystray.MenuItem("开始专注", enqueue_tray_action("start_focus")),
219271
pystray.MenuItem("开始休息", enqueue_tray_action("start_break")),
272+
pystray.MenuItem("停止计时", enqueue_tray_action("stop_timer"), visible=is_timer_active),
220273
pystray.MenuItem("退出", enqueue_tray_action("quit")),
221274
)
222275

@@ -356,11 +409,8 @@ def on_leave(event):
356409

357410

358411
def quit_application():
359-
global float_window
360-
412+
stop_current_timer()
361413
hide_tray_icon()
362-
if float_window is not None and float_window.winfo_exists():
363-
float_window.destroy()
364414
root.destroy()
365415

366416

@@ -380,6 +430,8 @@ def process_tray_actions():
380430
start_focus_session()
381431
elif action == "start_break":
382432
start_break_session()
433+
elif action == "stop_timer":
434+
stop_current_timer()
383435
elif action == "quit":
384436
quit_application()
385437
return
@@ -494,6 +546,7 @@ def on_leave(event):
494546
about_button.place(relx=0.9, rely=0.1, anchor='center')
495547

496548
root.update_idletasks()
549+
update_timer_controls()
497550
show_tray_icon()
498551
root.deiconify()
499552
root.after(200, process_tray_actions)

0 commit comments

Comments
 (0)