| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- 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("<Return>", self.on_hour_enter)
- self.min_spin.bind("<Return>", self.on_min_enter)
- self.sec_spin.bind("<Return>", self.on_sec_enter)
- self.ms_spin.bind("<Return>", self.on_ms_enter)
- self.key_combo.bind("<Return>", lambda e: self.start_timer())
-
- # 绑定启动定时快捷键(Ctrl+S)
- self.root.bind("<Control-s>", lambda e: self.start_timer())
- # 绑定停止定时快捷键(Ctrl+E)
- self.root.bind("<Control-e>", lambda e: self.stop_timer())
-
- # 绑定输入框获得焦点时自动全选
- for widget in [self.hour_spin, self.min_spin, self.sec_spin, self.ms_spin]:
- widget.bind("<FocusIn>", 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()
|