commit df05dbe6bd7c0ec05518d766ea07d012b0589b98 Author: jack Date: Wed Nov 12 15:59:27 2025 +0800 first commit diff --git a/main.py b/main.py new file mode 100755 index 0000000..6012ac7 --- /dev/null +++ b/main.py @@ -0,0 +1,216 @@ +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() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100755 index 0000000..af59379 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,21 @@ +MouseInfo==0.1.3 +numpy==2.2.6 +opencv-python==4.12.0.88 +pillow==11.3.0 +PyAutoGUI==0.9.54 +PyGetWindow==0.0.9 +PyMsgBox==2.0.1 +pynput==1.8.1 +pyobjc-core==11.1 +pyobjc-framework-ApplicationServices==11.1 +pyobjc-framework-Cocoa==11.1 +pyobjc-framework-CoreText==11.1 +pyobjc-framework-Quartz==11.1 +pyperclip==1.11.0 +PyRect==0.2.0 +PyScreeze==1.0.1 +pytweening==1.2.0 +rubicon-objc==0.5.2 +setuptools==78.1.1 +six==1.17.0 +wheel==0.45.1 diff --git a/target_img.png b/target_img.png new file mode 100644 index 0000000..57fa6cf Binary files /dev/null and b/target_img.png differ diff --git a/temp_mouse_control.py b/temp_mouse_control.py new file mode 100755 index 0000000..6b28369 --- /dev/null +++ b/temp_mouse_control.py @@ -0,0 +1,49 @@ +import subprocess +import time +import os + +def test_mouse_movement_cliclick(): + print("🐭 鼠标移动测试程序 (cliclick 版本)") + print("=" * 40) + + # 检查 cliclick 是否安装 + try: + result = subprocess.run(['which', 'cliclick'], capture_output=True, text=True) + if result.returncode != 0: + print("❌ cliclick 未安装") + print("请先安装: brew install cliclick") + return + except: + print("❌ 无法检查 cliclick 安装状态") + return + + # 获取屏幕尺寸 + try: + screen_width, screen_height = 2560, 1440 # 常见 Mac 分辨率,您可能需要调整 + center_x = screen_width // 2 + center_y = screen_height // 2 + + print(f"假设屏幕尺寸: {screen_width} x {screen_height}") + print(f"中心点坐标: ({center_x}, {center_y})") + + print("3秒后移动鼠标到屏幕中心...") + for i in range(3, 0, -1): + print(f"{i}...") + time.sleep(1) + + # 使用 cliclick 移动鼠标 + command = f"cliclick m:{center_x},{center_y}" + print(f"执行命令: {command}") + + result = subprocess.run(command, shell=True, capture_output=True, text=True) + + if result.returncode == 0: + print("✅ cliclick 移动成功!") + else: + print(f"❌ cliclick 移动失败: {result.stderr}") + + except Exception as e: + print(f"❌ 出错: {e}") + +if __name__ == "__main__": + test_mouse_movement_cliclick() \ No newline at end of file