main.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import pyautogui
  2. import cv2
  3. import numpy as np
  4. import time
  5. import threading
  6. from pynput import keyboard
  7. import os
  8. class MacImageClicker:
  9. def __init__(self):
  10. self.is_monitoring = False
  11. self.target_image = None
  12. self.confidence_threshold = 0.95
  13. self.hotkey = keyboard.Key.f8
  14. self.check_interval = 0.5
  15. # macOS 特定设置
  16. pyautogui.FAILSAFE = True
  17. pyautogui.PAUSE = 0.1
  18. # 查找目标图片
  19. self.target_image_path = self.find_target_image()
  20. def find_target_image(self):
  21. """在项目根目录查找目标图片"""
  22. current_dir = os.getcwd()
  23. image_files = []
  24. # 查找所有可能的图片文件
  25. for file in os.listdir(current_dir):
  26. if file.lower().endswith(('.png', '.jpg', '.jpeg')):
  27. image_files.append(file)
  28. if not image_files:
  29. print("在项目根目录未找到任何图片文件 (png, jpg, jpeg)")
  30. return None
  31. # 优先选择包含 target 关键字的图片
  32. target_files = [f for f in image_files if 'target' in f.lower()]
  33. if target_files:
  34. selected = target_files[0]
  35. else:
  36. selected = image_files[0]
  37. full_path = os.path.join(current_dir, selected)
  38. print(f"找到目标图片: {selected}")
  39. return full_path
  40. def load_target_image(self):
  41. """加载目标图片"""
  42. if not self.target_image_path:
  43. print("未找到目标图片文件")
  44. return False
  45. try:
  46. self.target_image = cv2.imread(self.target_image_path)
  47. if self.target_image is not None:
  48. print(f"成功加载目标图片: {os.path.basename(self.target_image_path)}")
  49. print(f"图片尺寸: {self.target_image.shape[1]}x{self.target_image.shape[0]}")
  50. return True
  51. else:
  52. print("图片加载失败,可能是格式不支持")
  53. return False
  54. except Exception as e:
  55. print(f"加载图片时出错: {e}")
  56. return False
  57. def get_screenshot(self):
  58. """获取屏幕截图 - macOS 版本"""
  59. try:
  60. # 使用 pyautogui 截图,在 macOS 上兼容性更好
  61. screenshot = pyautogui.screenshot()
  62. screenshot_cv = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
  63. return screenshot_cv
  64. except Exception as e:
  65. print(f"截图失败: {e}")
  66. return None
  67. def find_and_click(self):
  68. """在屏幕上查找目标图片并点击"""
  69. if self.target_image is None:
  70. return False
  71. try:
  72. # 获取屏幕截图
  73. screenshot = self.get_screenshot()
  74. if screenshot is None:
  75. return False
  76. # 模板匹配
  77. result = cv2.matchTemplate(screenshot, self.target_image, cv2.TM_CCOEFF_NORMED)
  78. min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
  79. # 调试信息(可选,可以注释掉以减少输出)
  80. # print(f"当前匹配度: {max_val:.3f}")
  81. if max_val >= self.confidence_threshold:
  82. # 计算目标中心位置
  83. h, w = self.target_image.shape[:2]
  84. center_x = max_loc[0] + w // 2
  85. center_y = max_loc[1] + h // 2
  86. # 先记录当前位置
  87. current_pos = pyautogui.position()
  88. print(f"📍 从当前位置 ({current_pos.x}, {current_pos.y}) 移动到目标位置 ({center_x}, {center_y})")
  89. pyautogui.moveTo(center_x, center_y, duration=0.0001)
  90. # 执行点击
  91. pyautogui.click()
  92. print(f"✅ 已点击位置: ({center_x}, {center_y}), 相似度: {max_val:.3f}")
  93. return True
  94. except Exception as e:
  95. print(f"查找或点击时出错: {e}")
  96. return False
  97. def monitoring_loop(self):
  98. """监控循环"""
  99. print("监控线程启动")
  100. while True:
  101. if self.is_monitoring:
  102. self.find_and_click()
  103. time.sleep(self.check_interval)
  104. def toggle_monitoring(self):
  105. """切换监控状态"""
  106. self.is_monitoring = not self.is_monitoring
  107. if self.is_monitoring:
  108. # 开启监控时确保图片已加载
  109. if self.target_image is None:
  110. if not self.load_target_image():
  111. self.is_monitoring = False
  112. print("❌ 无法加载目标图片,监控未开启")
  113. return
  114. status = "🟢 开启" if self.is_monitoring else "🔴 关闭"
  115. print(f"监控状态: {status}")
  116. if self.is_monitoring:
  117. print("开始监控屏幕,寻找目标图片...")
  118. def on_press(self, key):
  119. """键盘按下事件"""
  120. if key == self.hotkey:
  121. self.toggle_monitoring()
  122. elif key == keyboard.Key.esc:
  123. # ESC 键退出程序
  124. print("退出程序")
  125. os._exit(0)
  126. def check_mac_permissions(self):
  127. """检查 macOS 权限"""
  128. try:
  129. # 尝试截图来测试权限
  130. test_screenshot = pyautogui.screenshot()
  131. print("✅ 屏幕录制权限: 已授权")
  132. return True
  133. except Exception:
  134. print("❌ 屏幕录制权限: 未授权")
  135. print("请前往: 系统设置 > 隐私与安全性 > 屏幕录制")
  136. print("为终端或您使用的 IDE 开启屏幕录制权限")
  137. return False
  138. def start(self):
  139. """启动程序"""
  140. print("🖥️ macOS 图像点击工具")
  141. print("=" * 40)
  142. # 检查权限
  143. if not self.check_mac_permissions():
  144. return
  145. # 查找并加载目标图片
  146. if not self.load_target_image():
  147. print("请确保项目根目录有图片文件 (png, jpg, jpeg)")
  148. return
  149. print(f"🎯 目标图片: {os.path.basename(self.target_image_path)}")
  150. print(f"📏 相似度阈值: {self.confidence_threshold}")
  151. print(f"🎹 控制快捷键:")
  152. print(f" - F8: 开启/关闭监控")
  153. print(f" - ESC: 退出程序")
  154. print("=" * 40)
  155. print("等待指令...")
  156. # 启动监控线程
  157. monitor_thread = threading.Thread(target=self.monitoring_loop, daemon=True)
  158. monitor_thread.start()
  159. # 启动键盘监听
  160. try:
  161. with keyboard.Listener(on_press=self.on_press) as listener:
  162. listener.join()
  163. except KeyboardInterrupt:
  164. print("\n程序被用户中断")
  165. except Exception as e:
  166. print(f"程序出错: {e}")
  167. if __name__ == "__main__":
  168. # 确保程序在 macOS 上运行
  169. import platform
  170. if platform.system() != 'Darwin':
  171. print("警告: 这个工具是针对 macOS 优化的")
  172. print("但检测到您正在运行:", platform.system())
  173. print("继续运行可能遇到兼容性问题")
  174. response = input("是否继续? (y/n): ")
  175. if response.lower() != 'y':
  176. exit()
  177. clicker = MacImageClicker()
  178. clicker.start()