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("精准定时按键工具") self.root.geometry("500x300") self.root.resizable(False, False) # 设置窗口图标和置顶 self.root.attributes('-topmost', True) # 变量定义 self.target_time = None self.is_running = False self.update_queue = queue.Queue() # 按键映射(实际发送虚拟按键需要对应键盘码) 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.update_time() self.check_timer() def setup_ui(self): """设置用户界面""" # 主框架 main_frame = ttk.Frame(self.root, padding="20") main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # 当前时间显示 ttk.Label(main_frame, text="当前系统时间:", font=("Arial", 10)).grid(row=0, column=0, sticky=tk.W, pady=(0, 5)) self.time_var = tk.StringVar() self.time_label = ttk.Label(main_frame, textvariable=self.time_var, font=("Courier", 24, "bold")) self.time_label.grid(row=1, column=0, columnspan=2, pady=(0, 20)) # 分隔线 ttk.Separator(main_frame, orient='horizontal').grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 20)) # 定时设置区域 ttk.Label(main_frame, text="定时设置:", font=("Arial", 10, "bold")).grid(row=3, column=0, sticky=tk.W, pady=(0, 10)) # 时间输入区域 time_frame = ttk.Frame(main_frame) time_frame.grid(row=4, column=0, columnspan=2, pady=(0, 10)) ttk.Label(time_frame, text="目标时间:").pack(side=tk.LEFT, padx=(0, 5)) # 时分秒毫秒输入框 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") hour_spin = ttk.Spinbox(time_frame, from_=0, to=23, width=3, textvariable=self.hour_var, format="%02.0f") hour_spin.pack(side=tk.LEFT, padx=2) ttk.Label(time_frame, text=":").pack(side=tk.LEFT) min_spin = ttk.Spinbox(time_frame, from_=0, to=59, width=3, textvariable=self.min_var, format="%02.0f") min_spin.pack(side=tk.LEFT, padx=2) ttk.Label(time_frame, text=":").pack(side=tk.LEFT) sec_spin = ttk.Spinbox(time_frame, from_=0, to=59, width=3, textvariable=self.sec_var, format="%02.0f") sec_spin.pack(side=tk.LEFT, padx=2) ttk.Label(time_frame, text=".").pack(side=tk.LEFT) ms_spin = ttk.Spinbox(time_frame, from_=0, to=999, width=4, textvariable=self.ms_var, format="%03.0f") ms_spin.pack(side=tk.LEFT, padx=2) # 按键选择 key_frame = ttk.Frame(main_frame) key_frame.grid(row=5, column=0, columnspan=2, pady=(0, 10)) ttk.Label(key_frame, text="触发按键:").pack(side=tk.LEFT, padx=(0, 10)) self.key_var = tk.StringVar(value="回车") key_combo = ttk.Combobox(key_frame, textvariable=self.key_var, values=list(self.key_map.keys()), width=10, state="readonly") key_combo.pack(side=tk.LEFT) # 按钮区域 button_frame = ttk.Frame(main_frame) button_frame.grid(row=6, column=0, columnspan=2, pady=(10, 0)) 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=15).pack(side=tk.LEFT, padx=5) # 状态显示 self.status_var = tk.StringVar(value="就绪") status_label = ttk.Label(main_frame, textvariable=self.status_var, font=("Arial", 9), foreground="gray") status_label.grid(row=7, column=0, columnspan=2, pady=(20, 0)) 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): """将目标时间设置为当前时间""" 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.status_var.set("就绪")) def start_timer(self): """启动定时器""" 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()) 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.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}秒") except ValueError: self.status_var.set("请输入有效的时间") def stop_timer(self): """停止定时器""" self.is_running = False self.target_time = None self.start_button.config(state=tk.NORMAL) self.stop_button.config(state=tk.DISABLED) self.status_var.set("定时已停止") 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.root.after(3000, lambda: self.status_var.set("就绪")) # 更新倒计时显示 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秒显示毫秒 self.status_var.set(f"即将触发!剩余: {remaining:.3f}秒") elif remaining <= 60: self.status_var.set(f"剩余: {remaining:.1f}秒") else: minutes = int(remaining // 60) seconds = int(remaining % 60) self.status_var.set(f"目标时间: {self.target_time.strftime('%H:%M:%S')},剩余: {minutes}分{seconds}秒") # 每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 import ctypes from ctypes import wintypes # 定义常量 KEYEVENTF_KEYDOWN = 0x0000 KEYEVENTF_KEYUP = 0x0002 # 发送按键按下 ctypes.windll.user32.keybd_event(key_code, 0, KEYEVENTF_KEYDOWN, 0) time.sleep(0.02) # 短暂延时模拟真实按键 # 发送按键释放 ctypes.windll.user32.keybd_event(key_code, 0, KEYEVENTF_KEYUP, 0) # 更新状态显示(通过队列跨线程) self.root.after(0, lambda: self.status_var.set(f"已触发按键: {self.key_var.get()}")) 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()