用纯 Tkinter 实现一个全屏久坐提醒工具(无任何第三方依赖)
目标:
使用 Python 标准库实现一个真正有“强制休息效果”的久坐提醒工具。
不依赖任何第三方库,仅使用:
- tkinter(标准库 GUI)
- datetime(标准库)
一、功能说明
本版本实现:
- 全屏显示
- 始终置顶
- 倒计时提醒
- 每秒刷新动画
- 时间到自动关闭
- 无需任何额外依赖
这是一个“强提醒”版本,而不是普通弹窗版本。
二、实现思路
核心机制:
- 使用
root.wm_attributes("-fullscreen", 1)实现全屏 - 使用
root.wm_attributes("-topmost", 1)实现置顶 - 使用
after()每秒执行更新函数 - 使用
datetime显示当前时间 - 倒计时结束调用
root.destroy()自动关闭
程序流程:
启动 → 全屏显示 → 每秒更新 → 倒计时结束 → 自动关闭
三、完整代码
提示版
python
# -*- coding: utf-8 -*-
"""
不需要任何额外依赖
tkinter (Python 标准库 GUI)
datetime (Python 标准库)
全屏久坐提醒小程序
功能:
1. 全屏 + 置顶
2. 倒计时提醒站起来活动
3. 每秒更新内容
4. 时间到自动关闭
"""
import datetime as dt
import tkinter as tk
sec = 30 # 倒计时秒数
root = tk.Tk()
root.title("动起来 - 别久坐")
root.config(bg="black")
root.wm_attributes("-topmost", 1)
root.wm_attributes("-fullscreen", 1)
label = tk.Label(
root,
font=("Consolas", 50),
bg="black",
fg="white"
)
label.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
msg_template = "{}\n 站起来 {} \n 动一动 {}\n{}"
arrow_map = {
0: "↑",
1: "→",
2: "↓",
3: "←"
}
move_map = {
0: "~~_↑_~~",
1: "~_↑ ↑_~"
}
def update(count):
if count > 0:
now_time = dt.datetime.now().ctime()
text = msg_template.format(
count,
arrow_map[count % 4],
move_map[count % 2],
now_time
)
label.config(text=text)
root.after(1000, update, count - 1)
else:
root.destroy()
update(sec)
root.mainloop()完整版(BreakReminder.py)
# -*- coding: utf-8 -*-
import tkinter as tk
import datetime as dt
REMIND_INTERVAL_SEC = 60 * 60
COUNTDOWN_SECONDS = 60 * 2
root = tk.Tk()
root.title("久坐提醒")
root.geometry("400x300") # 稍微大一点
is_paused = False
next_trigger_time = dt.datetime.now() + dt.timedelta(
seconds=REMIND_INTERVAL_SEC
)
# =======================
# 使用 grid 布局(更稳定)
# =======================
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
main_frame = tk.Frame(root)
main_frame.grid(sticky="nsew", padx=10, pady=10)
main_frame.columnconfigure(0, weight=1)
status_label = tk.Label(main_frame, text="运行中",
font=("Consolas", 14))
status_label.grid(row=0, column=0, pady=5)
countdown_label = tk.Label(main_frame,
font=("Consolas", 16))
countdown_label.grid(row=1, column=0, pady=5)
clock_label = tk.Label(main_frame,
font=("Consolas", 10))
clock_label.grid(row=2, column=0, pady=5)
btn_frame = tk.Frame(main_frame)
btn_frame.grid(row=3, column=0, pady=10)
# =======================
# 弹窗提醒
# =======================
def show_reminder():
win = tk.Toplevel()
win.title("动起来 - 别久坐")
win.config(bg="black")
win.wm_attributes("-topmost", 1)
win.wm_attributes("-fullscreen", 1)
# 支持 ESC 退出当前提醒窗口
win.bind("<Escape>", lambda e: win.destroy())
label = tk.Label(win, font=("Consolas", 50),
bg="black", fg="white")
label.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
msg_template = "{}\n 站起来 {} \n 动一动 {}\n{}"
arrow_map = {0: "↑", 1: "→", 2: "↓", 3: "←"}
move_map = {0: "~~_↑_~~", 1: "~_↑ ↑_~"}
def update(count):
if count > 0:
now_time = dt.datetime.now().ctime()
text = msg_template.format(
count,
arrow_map[count % 4],
move_map[count % 2],
now_time
)
label.config(text=text)
win.after(1000, update, count - 1)
else:
win.destroy()
update(COUNTDOWN_SECONDS)
# =======================
# 主界面更新
# =======================
def update_main_ui():
global next_trigger_time
now = dt.datetime.now()
clock_label.config(
text=now.strftime("当前时间: %Y-%m-%d %H:%M:%S")
)
if not is_paused:
remaining = (next_trigger_time - now).total_seconds()
if remaining <= 0:
show_reminder()
next_trigger_time = dt.datetime.now() + dt.timedelta(
seconds=REMIND_INTERVAL_SEC)
remaining = REMIND_INTERVAL_SEC
countdown_label.config(
text=f"下次提醒: {int(remaining)} 秒"
)
status_label.config(text="运行中")
else:
countdown_label.config(text="已暂停")
status_label.config(text="暂停中")
root.after(1000, update_main_ui)
# =======================
# 控制按钮
# =======================
def toggle_pause():
global is_paused
is_paused = not is_paused
def trigger_now():
global next_trigger_time
show_reminder()
next_trigger_time = dt.datetime.now() + dt.timedelta(
seconds=REMIND_INTERVAL_SEC)
tk.Button(btn_frame, text="立即提醒",
command=trigger_now, width=12).pack(side="left", padx=5)
tk.Button(btn_frame, text="暂停 / 恢复",
command=toggle_pause, width=12).pack(side="left", padx=5)
# =======================
# 启动
# =======================
update_main_ui()
root.mainloop()四、运行方式
bash
python BreakReminder.pyLinux 后台运行:
bash
nohup python BreakReminder.py > /dev/null 2>&1 &bash
python BreakReminder.py > /dev/null 2>&1 & disown五、技术要点解析
为什么使用 after 而不是 sleep?
- tkinter 是单线程事件循环
- sleep 会阻塞 GUI
- after 是非阻塞定时回调
为什么使用 destroy 而不是 quit?
- destroy 会彻底销毁窗口
- quit 只退出主循环
对于一次性全屏提醒,destroy 更干净。
六、总结
这是一个极简但强力的久坐提醒实现。
优点:
- 纯标准库
- 无依赖
- 强制提醒
- 结构极简
适合学习 Tkinter 事件循环与 GUI 定时更新机制。