import tkinter as tk from tkinter import ttk import time from datetime import datetime, timedelta import threading import queue import ctypes import sys class PreciseTimerGUI: def __init__(self): self.root = tk.Tk() self.root.title("Microsoft") self.root.geometry("620x380") self.root.resizable(False, False) # 取消窗口置顶 # self.root.attributes('-topmost', True) # 已注释,不再置顶 # 变量定义 self.target_time = None self.is_running = False self.update_queue = queue.Queue() self.time_locked = False # 时间锁定标志 # 按键映射 self.key_map = { "1": 0x31, "2": 0x32, "3": 0x33, "4": 0x34, "9": 0x39, "D": 0x44, "M": 0x4D, "O": 0x4F, "P": 0x50, "回车": 0x0D } self.setup_ui() self.bind_events() self.update_time() self.check_timer() def setup_ui(self): """设置用户界面""" # 主框架 main_frame = ttk.Frame(self.root, padding="15") main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # 当前系统时间显示(放到下面) ttk.Label(main_frame, text="当前系统时间:", font=("Arial", 10, "bold")).grid(row=3, column=0, sticky=tk.W, pady=(20, 5)) self.time_var = tk.StringVar() self.time_label = ttk.Label(main_frame, textvariable=self.time_var, font=("Courier", 28, "bold"), foreground="blue") self.time_label.grid(row=4, column=0, columnspan=3, pady=(0, 20)) # 分隔线 ttk.Separator(main_frame, orient='horizontal').grid(row=5, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 15)) # 定时设置区域(上面) ttk.Label(main_frame, text="定时设置:", font=("Arial", 10, "bold")).grid(row=0, column=0, sticky=tk.W, pady=(0, 10)) # 时间输入区域 time_frame = ttk.Frame(main_frame) time_frame.grid(row=1, column=0, columnspan=2, pady=(0, 10), sticky=tk.W) ttk.Label(time_frame, text="目标时间:").pack(side=tk.LEFT, padx=(0, 8)) # 时、分、秒、毫秒输入框 self.hour_var = tk.StringVar(value="00") self.min_var = tk.StringVar(value="00") self.sec_var = tk.StringVar(value="00") self.ms_var = tk.StringVar(value="000") # 时 self.hour_spin = ttk.Spinbox(time_frame, from_=0, to=23, width=3, textvariable=self.hour_var, format="%02.0f") self.hour_spin.pack(side=tk.LEFT, padx=2) ttk.Label(time_frame, text=":").pack(side=tk.LEFT) # 分 self.min_spin = ttk.Spinbox(time_frame, from_=0, to=59, width=3, textvariable=self.min_var, format="%02.0f") self.min_spin.pack(side=tk.LEFT, padx=2) ttk.Label(time_frame, text=":").pack(side=tk.LEFT) # 秒 self.sec_spin = ttk.Spinbox(time_frame, from_=0, to=59, width=3, textvariable=self.sec_var, format="%02.0f") self.sec_spin.pack(side=tk.LEFT, padx=2) ttk.Label(time_frame, text=".").pack(side=tk.LEFT) # 毫秒 self.ms_spin = ttk.Spinbox(time_frame, from_=0, to=999, width=4, textvariable=self.ms_var, format="%03.0f") self.ms_spin.pack(side=tk.LEFT, padx=2) # 按键选择区域(与定时设置同一排) key_frame = ttk.Frame(main_frame) key_frame.grid(row=1, column=2, pady=(0, 10), sticky=tk.W, padx=(20, 0)) ttk.Label(key_frame, text="触发按键:").pack(side=tk.LEFT, padx=(0, 8)) self.key_var = tk.StringVar(value="回车") self.key_combo = ttk.Combobox(key_frame, textvariable=self.key_var, values=list(self.key_map.keys()), width=8, state="readonly") self.key_combo.pack(side=tk.LEFT) # 按钮区域 button_frame = ttk.Frame(main_frame) button_frame.grid(row=2, column=0, columnspan=3, pady=(10, 15)) self.start_button = ttk.Button(button_frame, text="▶ 启动定时", command=self.start_timer, width=12) self.start_button.pack(side=tk.LEFT, padx=5) self.stop_button = ttk.Button(button_frame, text="■ 停止定时", command=self.stop_timer, state=tk.DISABLED, width=12) self.stop_button.pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="设置当前时间", command=self.set_current_time, width=13).pack(side=tk.LEFT, padx=5) # 状态显示区域(使用Label直接设置样式) self.status_var = tk.StringVar(value="就绪") self.status_label = tk.Label(main_frame, textvariable=self.status_var, font=("Arial", 12, "bold"), fg="red", # 默认红色 bg=self.root.cget("bg")) self.status_label.grid(row=6, column=0, columnspan=3, pady=(10, 0)) def bind_events(self): """绑定回车键切换输入框事件""" # 为每个输入框绑定回车事件,清空下一个输入框的值 self.hour_spin.bind("", self.on_hour_enter) self.min_spin.bind("", self.on_min_enter) self.sec_spin.bind("", self.on_sec_enter) self.ms_spin.bind("", self.on_ms_enter) self.key_combo.bind("", lambda e: self.start_timer()) # 绑定启动定时快捷键(Ctrl+S) self.root.bind("", lambda e: self.start_timer()) # 绑定停止定时快捷键(Ctrl+E) self.root.bind("", lambda e: self.stop_timer()) # 绑定输入框获得焦点时自动全选 for widget in [self.hour_spin, self.min_spin, self.sec_spin, self.ms_spin]: widget.bind("", self.select_all_text) def select_all_text(self, event): """选中输入框中的所有文本""" event.widget.select_range(0, tk.END) event.widget.icursor(tk.END) return "break" def on_hour_enter(self, event): """小时输入框按回车""" # 清空分钟输入框的值 # self.min_var.set("00") # 光标移到分钟输入框 self.min_spin.focus_set() return "break" def on_min_enter(self, event): """分钟输入框按回车""" # 清空秒输入框的值 # self.sec_var.set("00") # 光标移到秒输入框 self.sec_spin.focus_set() return "break" def on_sec_enter(self, event): """秒输入框按回车""" # 清空毫秒输入框的值 # self.ms_var.set("000") # 光标移到毫秒输入框 self.ms_spin.focus_set() return "break" def on_ms_enter(self, event): """毫秒输入框按回车""" # 清空按键选择的值 self.key_var.set("回车") # 光标移到按键选择框 self.key_combo.focus_set() return "break" def update_time(self): """更新时间显示(精确到毫秒)""" now = datetime.now() time_str = now.strftime("%Y-%m-%d %H:%M:%S") + f".{now.microsecond//1000:03d}" self.time_var.set(time_str) # 使用after方法实现低CPU占用的定时更新 self.root.after(10, self.update_time) def set_current_time(self): """将目标时间设置为当前时间""" # 如果定时已启动,禁止修改时间 if self.time_locked: self.status_var.set("定时已启动,请先停止定时再修改时间") self.root.after(2000, lambda: self.update_status_text()) return now = datetime.now() self.hour_var.set(f"{now.hour:02d}") self.min_var.set(f"{now.minute:02d}") self.sec_var.set(f"{now.second:02d}") self.ms_var.set(f"{(now.microsecond//1000):03d}") self.status_var.set("已设置为当前时间") self.root.after(2000, lambda: self.update_status_text()) def update_status_text(self): """恢复状态文本""" if not self.is_running: self.status_var.set("就绪") self.status_label.config(fg="red") def start_timer(self): """启动定时器""" # 如果已经启动,禁止修改时间 if self.is_running: self.status_var.set("定时已运行中,请先停止") self.root.after(2000, lambda: self.update_status_text()) return try: # 解析目标时间 hour = int(self.hour_var.get()) minute = int(self.min_var.get()) second = int(self.sec_var.get()) millisecond = int(self.ms_var.get()) # 验证时间有效性 if not (0 <= hour <= 23 and 0 <= minute <= 59 and 0 <= second <= 59 and 0 <= millisecond <= 999): raise ValueError("时间超出范围") now = datetime.now() target = now.replace(hour=hour, minute=minute, second=second, microsecond=millisecond*1000) # 如果目标时间小于等于当前时间,设置为明天 if target <= now: target += timedelta(days=1) self.target_time = target self.is_running = True self.time_locked = True # 锁定时间,禁止修改 # 禁用时间输入框 self.hour_spin.config(state=tk.DISABLED) self.min_spin.config(state=tk.DISABLED) self.sec_spin.config(state=tk.DISABLED) self.ms_spin.config(state=tk.DISABLED) self.start_button.config(state=tk.DISABLED) self.stop_button.config(state=tk.NORMAL) # 显示目标时间和倒计时 time_remaining = (target - now).total_seconds() self.status_var.set(f"目标时间: {target.strftime('%H:%M:%S')}.{millisecond:03d},剩余: {time_remaining:.2f}秒") self.status_label.config(fg="red", font=("Arial", 12, "bold")) # 始终保持红色加粗 except ValueError as e: self.status_var.set(f"请输入有效的时间: {str(e)}") self.root.after(2000, lambda: self.update_status_text()) def stop_timer(self): """停止定时器""" self.is_running = False self.time_locked = False # 解锁时间 self.target_time = None # 启用时间输入框 self.hour_spin.config(state=tk.NORMAL) self.min_spin.config(state=tk.NORMAL) self.sec_spin.config(state=tk.NORMAL) self.ms_spin.config(state=tk.NORMAL) self.start_button.config(state=tk.NORMAL) self.stop_button.config(state=tk.DISABLED) self.status_var.set("定时已停止") self.status_label.config(fg="red", font=("Arial", 12, "bold")) self.root.after(2000, lambda: self.update_status_text()) def check_timer(self): """检查定时是否到达""" if self.is_running and self.target_time: now = datetime.now() if now >= self.target_time: self.trigger_key() self.stop_timer() self.status_var.set("✓ 定时已触发!") self.status_label.config(fg="green", font=("Arial", 12, "bold")) self.root.after(3000, lambda: self.update_status_text()) # 更新倒计时显示(始终保持红色) if self.is_running and self.target_time: now = datetime.now() if now < self.target_time: remaining = (self.target_time - now).total_seconds() # 格式化显示剩余时间 if remaining <= 5: # 最后5秒显示毫秒 remaining_ms = (self.target_time - now).total_seconds() status_msg = f"目标时间: {self.target_time.strftime('%H:%M:%S')}.{self.target_time.microsecond//1000:03d},⚠ 即将触发!剩余: {remaining_ms:.3f}秒" else: minutes = int(remaining // 60) seconds = int(remaining % 60) ms = int((remaining - int(remaining)) * 1000) if minutes > 0: status_msg = f"目标时间: {self.target_time.strftime('%H:%M:%S')}.{self.target_time.microsecond//1000:03d},剩余: {minutes}分{seconds}秒" else: status_msg = f"目标时间: {self.target_time.strftime('%H:%M:%S')}.{self.target_time.microsecond//1000:03d},剩余: {remaining:.2f}秒" self.status_var.set(status_msg) self.status_label.config(fg="red", font=("Arial", 12, "bold")) # 始终保持红色加粗 # 每10毫秒检查一次,确保毫秒级精度 self.root.after(10, self.check_timer) def trigger_key(self): """触发按键""" key_name = self.key_var.get() key_code = self.key_map.get(key_name) if key_code: # 在单独线程中执行按键操作,避免阻塞UI threading.Thread(target=self._send_key, args=(key_code,), daemon=True).start() def _send_key(self, key_code): """实际发送按键(Windows平台)""" try: if sys.platform == "win32": # Windows平台使用keybd_event # 发送按键按下 ctypes.windll.user32.keybd_event(key_code, 0, 0x0000, 0) time.sleep(0.02) # 短暂延时模拟真实按键 # 发送按键释放 ctypes.windll.user32.keybd_event(key_code, 0, 0x0002, 0) # 更新状态显示 self.root.after(0, lambda: self.status_var.set(f"✓ 已触发按键: {self.key_var.get()}")) self.root.after(0, lambda: self.status_label.config(fg="green", font=("Arial", 12, "bold"))) else: print(f"触发按键: {self.key_var.get()} (仅Windows平台支持真实按键)") self.root.after(0, lambda: self.status_var.set(f"模拟触发按键: {self.key_var.get()}")) except Exception as e: print(f"按键触发失败: {e}") self.root.after(0, lambda: self.status_var.set(f"按键触发失败: {str(e)}")) def run(self): """运行应用""" self.root.mainloop() if __name__ == "__main__": app = PreciseTimerGUI() app.run()