import pyautogui import cv2 import numpy as np import time import threading from pynput import keyboard import os class MacImageClicker: def __init__(self): self.is_monitoring = False self.target_image = None self.confidence_threshold = 0.95 self.hotkey = keyboard.Key.f8 self.check_interval = 0.000001 # macOS 特定设置 pyautogui.FAILSAFE = True pyautogui.PAUSE = 0.1 # 查找目标图片 self.target_image_path = self.find_target_image() def find_target_image(self): """在项目根目录查找目标图片""" current_dir = os.getcwd() image_files = [] # 查找所有可能的图片文件 for file in os.listdir(current_dir): if file.lower().endswith(('.png', '.jpg', '.jpeg')): image_files.append(file) if not image_files: print("在项目根目录未找到任何图片文件 (png, jpg, jpeg)") return None # 优先选择包含 target 关键字的图片 target_files = [f for f in image_files if 'target' in f.lower()] if target_files: selected = target_files[0] else: selected = image_files[0] full_path = os.path.join(current_dir, selected) print(f"找到目标图片: {selected}") return full_path def load_target_image(self): """加载目标图片""" if not self.target_image_path: print("未找到目标图片文件") return False try: self.target_image = cv2.imread(self.target_image_path) if self.target_image is not None: print(f"成功加载目标图片: {os.path.basename(self.target_image_path)}") print(f"图片尺寸: {self.target_image.shape[1]}x{self.target_image.shape[0]}") return True else: print("图片加载失败,可能是格式不支持") return False except Exception as e: print(f"加载图片时出错: {e}") return False def get_screenshot(self): """获取屏幕截图 - macOS 版本""" try: # 使用 pyautogui 截图,在 macOS 上兼容性更好 screenshot = pyautogui.screenshot() screenshot_cv = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR) return screenshot_cv except Exception as e: print(f"截图失败: {e}") return None def find_and_click(self): """在屏幕上查找目标图片并点击""" if self.target_image is None: return False try: # 获取屏幕截图 screenshot = self.get_screenshot() if screenshot is None: return False # 模板匹配 result = cv2.matchTemplate(screenshot, self.target_image, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) # 调试信息(可选,可以注释掉以减少输出) # print(f"当前匹配度: {max_val:.3f}") if max_val >= self.confidence_threshold: # 计算目标中心位置 h, w = self.target_image.shape[:2] center_x = max_loc[0] + w // 2 center_y = max_loc[1] + h // 2 # 先记录当前位置 current_pos = pyautogui.position() print(f"📍 从当前位置 ({current_pos.x}, {current_pos.y}) 移动到目标位置 ({center_x}, {center_y})") pyautogui.moveTo(center_x, center_y, duration=0.0001) # 执行点击 pyautogui.click() print(f"✅ 已点击位置: ({center_x}, {center_y}), 相似度: {max_val:.3f}") return True except Exception as e: print(f"查找或点击时出错: {e}") return False def monitoring_loop(self): """监控循环""" print("监控线程启动") while True: if self.is_monitoring: self.find_and_click() time.sleep(self.check_interval) def toggle_monitoring(self): """切换监控状态""" self.is_monitoring = not self.is_monitoring if self.is_monitoring: # 开启监控时确保图片已加载 if self.target_image is None: if not self.load_target_image(): self.is_monitoring = False print("❌ 无法加载目标图片,监控未开启") return status = "🟢 开启" if self.is_monitoring else "🔴 关闭" print(f"监控状态: {status}") if self.is_monitoring: print("开始监控屏幕,寻找目标图片...") def on_press(self, key): """键盘按下事件""" if key == self.hotkey: self.toggle_monitoring() elif key == keyboard.Key.esc: # ESC 键退出程序 print("退出程序") os._exit(0) def check_mac_permissions(self): """检查 macOS 权限""" try: # 尝试截图来测试权限 test_screenshot = pyautogui.screenshot() print("✅ 屏幕录制权限: 已授权") return True except Exception: print("❌ 屏幕录制权限: 未授权") print("请前往: 系统设置 > 隐私与安全性 > 屏幕录制") print("为终端或您使用的 IDE 开启屏幕录制权限") return False def start(self): """启动程序""" print("🖥️ macOS 图像点击工具") print("=" * 40) # 检查权限 if not self.check_mac_permissions(): return # 查找并加载目标图片 if not self.load_target_image(): print("请确保项目根目录有图片文件 (png, jpg, jpeg)") return print(f"🎯 目标图片: {os.path.basename(self.target_image_path)}") print(f"📏 相似度阈值: {self.confidence_threshold}") print(f"🎹 控制快捷键:") print(f" - F8: 开启/关闭监控") print(f" - ESC: 退出程序") print("=" * 40) print("等待指令...") # 启动监控线程 monitor_thread = threading.Thread(target=self.monitoring_loop, daemon=True) monitor_thread.start() # 启动键盘监听 try: with keyboard.Listener(on_press=self.on_press) as listener: listener.join() except KeyboardInterrupt: print("\n程序被用户中断") except Exception as e: print(f"程序出错: {e}") if __name__ == "__main__": # 确保程序在 macOS 上运行 import platform if platform.system() != 'Darwin': print("警告: 这个工具是针对 macOS 优化的") print("但检测到您正在运行:", platform.system()) print("继续运行可能遇到兼容性问题") response = input("是否继续? (y/n): ") if response.lower() != 'y': exit() clicker = MacImageClicker() clicker.start()