Skip to content

Instantly share code, notes, and snippets.

@findneo
Created December 18, 2024 09:25
Show Gist options
  • Select an option

  • Save findneo/84bc3232c73d26b91733270160917e2e to your computer and use it in GitHub Desktop.

Select an option

Save findneo/84bc3232c73d26b91733270160917e2e to your computer and use it in GitHub Desktop.
护眼啦平替
# 这是一个运行在Windows上的程序,每X分钟休息Y分钟
# 工作期间在右下角显示倒计时
# 休息期间会全屏使用黑色遮罩,屏幕正中间有一个黄色的按钮,点击按钮可以关闭遮罩,继续工作
# # 安装 PyInstaller
# pip install pyinstaller pywin32
# 在程序目录下运行打包命令
# pyinstaller --noconsole --onefile careeyes.py
import tkinter as tk
from tkinter import ttk
import time
import threading
import win32gui
import win32con
from datetime import datetime, timedelta
from tkinter import messagebox
import winreg
import os
import sys
class CareEyes:
def __init__(self, work_minutes=45, rest_minutes=3):
self.work_minutes = work_minutes
self.rest_minutes = rest_minutes
self.is_resting = False
self.timer_running = True # 添加线程控制标志
# Create main window for rest overlay
self.root = tk.Tk()
self.root.withdraw()
self.root.attributes('-alpha', 0.9)
self.root.attributes('-fullscreen', True)
self.root.configure(bg='black')
# Create continue button
self.continue_btn = ttk.Button(
self.root,
text="Continue Working",
command=self.end_rest
)
self.continue_btn.place(relx=0.5, rely=0.5, anchor='center')
# Create timer label window
self.timer_window = tk.Toplevel()
self.timer_window.overrideredirect(True)
self.timer_window.attributes('-topmost', True)
# self.timer_window.attributes('-alpha', 0.7) # 设置透明度
self.timer_window.configure(bg='black')
# self.timer_window.attributes('-transparentcolor', 'black') # 设置透明色为黑色
self.timer_label = ttk.Label(
self.timer_window,
background='black',
foreground='white',
font=('Arial', 10)
)
self.timer_label.pack(padx=5, pady=5)
# Position timer in bottom right
self.position_timer()
# Start work timer
self.timer_thread = threading.Thread(target=self.timer_loop, daemon=True)
self.timer_thread.start()
# 添加拖拽相关变量
self.drag_data = {"x": 0, "y": 0}
# 绑定拖拽事件
self.timer_window.bind('<Button-1>', self.start_drag)
self.timer_window.bind('<B1-Motion>', self.on_drag)
# 创建右键菜单
self.context_menu = tk.Menu(self.timer_window, tearoff=0)
self.context_menu.add_command(label="立即休息", command=self.start_rest)
self.context_menu.add_command(label="重新开始倒计时", command=self.restart_timer)
self.context_menu.add_command(label="设置", command=self.show_settings)
self.context_menu.add_checkbutton(label="开机自启动", command=self.toggle_autostart)
self.context_menu.add_command(label="退出", command=self.root.quit)
# 绑定右键菜单
self.timer_window.bind('<Button-3>', self.show_context_menu)
self.settings_window = None
# 检查自启动状态
# self.check_autostart()
# def check_autostart(self):
# """检查是否设置了开机自启动"""
# try:
# key = winreg.OpenKey(
# winreg.HKEY_CURRENT_USER,
# r"Software\Microsoft\Windows\CurrentVersion\Run",
# 0,
# winreg.KEY_READ
# )
# try:
# if winreg.QueryValueEx(key, "CareEyes"):
# self.context_menu.entryconfigure("开机自启动", selected=True)
# else:
# self.context_menu.entryconfigure("开机自启动", selected=False)
# except Exception as e:
# self.context_menu.entryconfigure("开机自启动", selected=False)
# finally:
# key.Close()
# except Exception as e:
# # 处理其他可能的错误
# # tk.messagebox.showerror("错误", f"检查自启动状态时出错: {str(e)}")
# self.context_menu.entryconfig("开机自启动", selected=False)
def toggle_autostart(self):
"""切换开机自启动状态"""
try:
key = winreg.OpenKey(
winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Run",
0,
winreg.KEY_SET_VALUE | winreg.KEY_QUERY_VALUE
)
# 获取程序路径
if getattr(sys, 'frozen', False):
# 如果是打包后的 exe
app_path = sys.executable
else:
# 如果是 python 脚本
app_path = os.path.abspath(sys.argv[0])
try:
winreg.QueryValueEx(key, "CareEyes")
# 如果键存在,删除它
winreg.DeleteValue(key, "CareEyes")
except:
# 如果键不存在,创建它
winreg.SetValueEx(key, "CareEyes", 0, winreg.REG_SZ, f'"{app_path}"')
except Exception as e:
tk.messagebox.showerror("错误", f"设置开机自启动失败: {str(e)}")
finally:
try:
key.Close()
except:
pass
def show_settings(self):
"""显示设置窗口"""
if self.settings_window is None or not tk.Toplevel.winfo_exists(self.settings_window):
self.settings_window = tk.Toplevel()
self.settings_window.title("设置")
self.settings_window.geometry("300x200")
# 工作时间设置
work_frame = ttk.Frame(self.settings_window)
work_frame.pack(pady=10, padx=20, fill='x')
ttk.Label(work_frame, text="工作时间(分钟):").pack(side='left')
work_entry = ttk.Entry(work_frame, width=10)
work_entry.insert(0, str(self.work_minutes))
work_entry.pack(side='left', padx=5)
# 休息时间设置
rest_frame = ttk.Frame(self.settings_window)
rest_frame.pack(pady=10, padx=20, fill='x')
ttk.Label(rest_frame, text="休息时间(分钟):").pack(side='left')
rest_entry = ttk.Entry(rest_frame, width=10)
rest_entry.insert(0, str(self.rest_minutes))
rest_entry.pack(side='left', padx=5)
def save_settings():
try:
new_work = int(work_entry.get())
new_rest = int(rest_entry.get())
if new_work <= 0 or new_rest <= 0:
raise ValueError("时间必须大于0")
self.work_minutes = new_work
self.rest_minutes = new_rest
self.restart_timer() # 重启计时器使用新设置
self.settings_window.destroy()
except ValueError as e:
tk.messagebox.showerror("错误", "请输入有效的数字!")
# 保存按钮
save_btn = ttk.Button(
self.settings_window,
text="保存",
command=save_settings
)
save_btn.pack(pady=20)
# 窗口居中
self.settings_window.update_idletasks()
width = self.settings_window.winfo_width()
height = self.settings_window.winfo_height()
x = (self.settings_window.winfo_screenwidth() // 2) - (width // 2)
y = (self.settings_window.winfo_screenheight() // 2) - (height // 2)
self.settings_window.geometry(f'{width}x{height}+{x}+{y}')
def start_drag(self, event):
"""开始拖拽"""
self.drag_data["x"] = event.x
self.drag_data["y"] = event.y
def on_drag(self, event):
"""拖拽中"""
x = self.timer_window.winfo_x() + (event.x - self.drag_data["x"])
y = self.timer_window.winfo_y() + (event.y - self.drag_data["y"])
self.timer_window.geometry(f"+{x}+{y}")
def show_context_menu(self, event):
"""显示右键菜单"""
self.context_menu.tk_popup(event.x_root, event.y_root)
def restart_timer(self):
"""重新开始倒计时"""
# 停止当前计时器线程
self.timer_running = False
if self.timer_thread.is_alive():
self.timer_thread.join(timeout=1) # 等待线程结束
# 重置状态并启动新线程
self.is_resting = False
self.root.withdraw()
self.timer_running = True
self.timer_thread = threading.Thread(target=self.timer_loop, daemon=True)
self.timer_thread.start()
def position_timer(self):
screen_width = self.timer_window.winfo_screenwidth()
screen_height = self.timer_window.winfo_screenheight()
# window_width = 100
# window_height = 30
# x = screen_width - window_width - 20
# y = screen_height - window_height - 40
window_width = 50
window_height = 30
x = screen_width - window_width - 20
y = screen_height - window_height - 40
self.timer_window.geometry(f'{window_width}x{window_height}+{x}+{y}')
def timer_loop(self):
while self.timer_running: # 使用标志控制线程
if self.is_resting:
end_time = datetime.now() + timedelta(minutes=self.rest_minutes)
while datetime.now() < end_time and self.is_resting and self.timer_running:
remaining = end_time - datetime.now()
mins = remaining.seconds // 60
secs = remaining.seconds % 60
self.timer_label['text'] = f'{mins:02d}:{secs:02d}'
time.sleep(1)
if self.is_resting and self.timer_running: # 检查标志
self.end_rest()
else:
end_time = datetime.now() + timedelta(minutes=self.work_minutes)
while datetime.now() < end_time and not self.is_resting and self.timer_running:
remaining = end_time - datetime.now()
mins = remaining.seconds // 60
secs = remaining.seconds % 60
self.timer_label['text'] = f'{mins:02d}:{secs:02d}'
time.sleep(1)
if not self.is_resting and self.timer_running: # 检查标志
self.start_rest()
def start_rest(self):
self.is_resting = True
self.root.deiconify()
self.root.lift()
self.root.focus_force()
def end_rest(self):
self.is_resting = False
self.root.withdraw()
def run(self):
self.timer_window.mainloop()
if __name__ == '__main__':
app = CareEyes(35,5)
app.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment