Created
December 18, 2024 09:25
-
-
Save findneo/84bc3232c73d26b91733270160917e2e to your computer and use it in GitHub Desktop.
护眼啦平替
This file contains hidden or 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
| # 这是一个运行在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