定时按某键.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import tkinter as tk
  2. from tkinter import ttk
  3. import time
  4. from datetime import datetime, timedelta
  5. import threading
  6. import queue
  7. import ctypes
  8. import sys
  9. class PreciseTimerGUI:
  10. def __init__(self):
  11. self.root = tk.Tk()
  12. self.root.title("精准定时按键工具")
  13. self.root.geometry("500x300")
  14. self.root.resizable(False, False)
  15. # 设置窗口图标和置顶
  16. self.root.attributes('-topmost', True)
  17. # 变量定义
  18. self.target_time = None
  19. self.is_running = False
  20. self.update_queue = queue.Queue()
  21. # 按键映射(实际发送虚拟按键需要对应键盘码)
  22. self.key_map = {
  23. "1": 0x31, "2": 0x32, "3": 0x33, "4": 0x34,
  24. "9": 0x39, "D": 0x44, "M": 0x4D, "O": 0x4F,
  25. "P": 0x50, "回车": 0x0D
  26. }
  27. self.setup_ui()
  28. self.update_time()
  29. self.check_timer()
  30. def setup_ui(self):
  31. """设置用户界面"""
  32. # 主框架
  33. main_frame = ttk.Frame(self.root, padding="20")
  34. main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
  35. # 当前时间显示
  36. ttk.Label(main_frame, text="当前系统时间:", font=("Arial", 10)).grid(row=0, column=0, sticky=tk.W, pady=(0, 5))
  37. self.time_var = tk.StringVar()
  38. self.time_label = ttk.Label(main_frame, textvariable=self.time_var, font=("Courier", 24, "bold"))
  39. self.time_label.grid(row=1, column=0, columnspan=2, pady=(0, 20))
  40. # 分隔线
  41. ttk.Separator(main_frame, orient='horizontal').grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 20))
  42. # 定时设置区域
  43. ttk.Label(main_frame, text="定时设置:", font=("Arial", 10, "bold")).grid(row=3, column=0, sticky=tk.W, pady=(0, 10))
  44. # 时间输入区域
  45. time_frame = ttk.Frame(main_frame)
  46. time_frame.grid(row=4, column=0, columnspan=2, pady=(0, 10))
  47. ttk.Label(time_frame, text="目标时间:").pack(side=tk.LEFT, padx=(0, 5))
  48. # 时分秒毫秒输入框
  49. self.hour_var = tk.StringVar(value="00")
  50. self.min_var = tk.StringVar(value="00")
  51. self.sec_var = tk.StringVar(value="00")
  52. self.ms_var = tk.StringVar(value="000")
  53. hour_spin = ttk.Spinbox(time_frame, from_=0, to=23, width=3, textvariable=self.hour_var, format="%02.0f")
  54. hour_spin.pack(side=tk.LEFT, padx=2)
  55. ttk.Label(time_frame, text=":").pack(side=tk.LEFT)
  56. min_spin = ttk.Spinbox(time_frame, from_=0, to=59, width=3, textvariable=self.min_var, format="%02.0f")
  57. min_spin.pack(side=tk.LEFT, padx=2)
  58. ttk.Label(time_frame, text=":").pack(side=tk.LEFT)
  59. sec_spin = ttk.Spinbox(time_frame, from_=0, to=59, width=3, textvariable=self.sec_var, format="%02.0f")
  60. sec_spin.pack(side=tk.LEFT, padx=2)
  61. ttk.Label(time_frame, text=".").pack(side=tk.LEFT)
  62. ms_spin = ttk.Spinbox(time_frame, from_=0, to=999, width=4, textvariable=self.ms_var, format="%03.0f")
  63. ms_spin.pack(side=tk.LEFT, padx=2)
  64. # 按键选择
  65. key_frame = ttk.Frame(main_frame)
  66. key_frame.grid(row=5, column=0, columnspan=2, pady=(0, 10))
  67. ttk.Label(key_frame, text="触发按键:").pack(side=tk.LEFT, padx=(0, 10))
  68. self.key_var = tk.StringVar(value="回车")
  69. key_combo = ttk.Combobox(key_frame, textvariable=self.key_var, values=list(self.key_map.keys()), width=10, state="readonly")
  70. key_combo.pack(side=tk.LEFT)
  71. # 按钮区域
  72. button_frame = ttk.Frame(main_frame)
  73. button_frame.grid(row=6, column=0, columnspan=2, pady=(10, 0))
  74. self.start_button = ttk.Button(button_frame, text="启动定时", command=self.start_timer, width=12)
  75. self.start_button.pack(side=tk.LEFT, padx=5)
  76. self.stop_button = ttk.Button(button_frame, text="停止定时", command=self.stop_timer, state=tk.DISABLED, width=12)
  77. self.stop_button.pack(side=tk.LEFT, padx=5)
  78. ttk.Button(button_frame, text="设置当前时间", command=self.set_current_time, width=15).pack(side=tk.LEFT, padx=5)
  79. # 状态显示
  80. self.status_var = tk.StringVar(value="就绪")
  81. status_label = ttk.Label(main_frame, textvariable=self.status_var, font=("Arial", 9), foreground="gray")
  82. status_label.grid(row=7, column=0, columnspan=2, pady=(20, 0))
  83. def update_time(self):
  84. """更新时间显示(精确到毫秒)"""
  85. now = datetime.now()
  86. time_str = now.strftime("%Y-%m-%d %H:%M:%S") + f".{now.microsecond//1000:03d}"
  87. self.time_var.set(time_str)
  88. # 使用after方法实现低CPU占用的定时更新
  89. self.root.after(10, self.update_time)
  90. def set_current_time(self):
  91. """将目标时间设置为当前时间"""
  92. now = datetime.now()
  93. self.hour_var.set(f"{now.hour:02d}")
  94. self.min_var.set(f"{now.minute:02d}")
  95. self.sec_var.set(f"{now.second:02d}")
  96. self.ms_var.set(f"{(now.microsecond//1000):03d}")
  97. self.status_var.set("已设置为当前时间")
  98. self.root.after(2000, lambda: self.status_var.set("就绪"))
  99. def start_timer(self):
  100. """启动定时器"""
  101. try:
  102. # 解析目标时间
  103. hour = int(self.hour_var.get())
  104. minute = int(self.min_var.get())
  105. second = int(self.sec_var.get())
  106. millisecond = int(self.ms_var.get())
  107. now = datetime.now()
  108. target = now.replace(hour=hour, minute=minute, second=second, microsecond=millisecond*1000)
  109. # 如果目标时间小于当前时间,设置为明天
  110. if target <= now:
  111. target += timedelta(days=1)
  112. self.target_time = target
  113. self.is_running = True
  114. self.start_button.config(state=tk.DISABLED)
  115. self.stop_button.config(state=tk.NORMAL)
  116. # 显示目标时间和倒计时
  117. time_remaining = (target - now).total_seconds()
  118. self.status_var.set(f"定时已启动,目标时间: {target.strftime('%H:%M:%S')}.{millisecond:03d},剩余: {time_remaining:.2f}秒")
  119. except ValueError:
  120. self.status_var.set("请输入有效的时间")
  121. def stop_timer(self):
  122. """停止定时器"""
  123. self.is_running = False
  124. self.target_time = None
  125. self.start_button.config(state=tk.NORMAL)
  126. self.stop_button.config(state=tk.DISABLED)
  127. self.status_var.set("定时已停止")
  128. def check_timer(self):
  129. """检查定时是否到达"""
  130. if self.is_running and self.target_time:
  131. now = datetime.now()
  132. if now >= self.target_time:
  133. self.trigger_key()
  134. self.stop_timer()
  135. self.status_var.set("定时已触发!")
  136. self.root.after(3000, lambda: self.status_var.set("就绪"))
  137. # 更新倒计时显示
  138. if self.is_running and self.target_time:
  139. now = datetime.now()
  140. if now < self.target_time:
  141. remaining = (self.target_time - now).total_seconds()
  142. if remaining <= 5: # 最后5秒显示毫秒
  143. self.status_var.set(f"即将触发!剩余: {remaining:.3f}秒")
  144. elif remaining <= 60:
  145. self.status_var.set(f"剩余: {remaining:.1f}秒")
  146. else:
  147. minutes = int(remaining // 60)
  148. seconds = int(remaining % 60)
  149. self.status_var.set(f"目标时间: {self.target_time.strftime('%H:%M:%S')},剩余: {minutes}分{seconds}秒")
  150. # 每10毫秒检查一次,确保毫秒级精度
  151. self.root.after(10, self.check_timer)
  152. def trigger_key(self):
  153. """触发按键(跨平台支持)"""
  154. key_name = self.key_var.get()
  155. key_code = self.key_map.get(key_name)
  156. if key_code:
  157. # 在单独线程中执行按键操作,避免阻塞UI
  158. threading.Thread(target=self._send_key, args=(key_code,), daemon=True).start()
  159. def _send_key(self, key_code):
  160. """实际发送按键(Windows平台)"""
  161. try:
  162. if sys.platform == "win32":
  163. # Windows平台使用keybd_event
  164. import ctypes
  165. from ctypes import wintypes
  166. # 定义常量
  167. KEYEVENTF_KEYDOWN = 0x0000
  168. KEYEVENTF_KEYUP = 0x0002
  169. # 发送按键按下
  170. ctypes.windll.user32.keybd_event(key_code, 0, KEYEVENTF_KEYDOWN, 0)
  171. time.sleep(0.02) # 短暂延时模拟真实按键
  172. # 发送按键释放
  173. ctypes.windll.user32.keybd_event(key_code, 0, KEYEVENTF_KEYUP, 0)
  174. # 更新状态显示(通过队列跨线程)
  175. self.root.after(0, lambda: self.status_var.set(f"已触发按键: {self.key_var.get()}"))
  176. else:
  177. print(f"触发按键: {self.key_var.get()} (仅Windows平台支持真实按键)")
  178. self.root.after(0, lambda: self.status_var.set(f"模拟触发按键: {self.key_var.get()}"))
  179. except Exception as e:
  180. print(f"按键触发失败: {e}")
  181. self.root.after(0, lambda: self.status_var.set(f"按键触发失败: {str(e)}"))
  182. def run(self):
  183. """运行应用"""
  184. self.root.mainloop()
  185. if __name__ == "__main__":
  186. app = PreciseTimerGUI()
  187. app.run()