GenericAgent 桌面自动化坐标体系实战ljqCtrl 物理坐标换算 SOP 全解析【免费下载链接】GenericAgentSelf-evolving agent: grows skill tree from 3.3K-line seed, achieving full system control with 6x less token consumption项目地址: https://gitcode.com/GitHub_Trending/pc/GenericAgent导读本文是 GenericAgent 项目在桌面 GUI 自动化场景下的核心坐标使用规范。ljqCtrl 是一套以「物理像素坐标」为唯一标准的高 DPI 安全键鼠控制库本文完整讲解其 API 签名、逻辑/物理坐标换算公式、截图 bbox 到屏幕坐标的转换代码以及 Windows/macOS 双平台的避坑清单与辅助功能AX/UIA控件点击通路。读完你将掌握一套可复现、可运行的桌面自动化坐标换算方法论避免「点歪、点错、点失焦」三大高频事故。0. 背景为什么这套 SOP 存在GenericAgent 在 Windows 与 macOS 上执行真实 GUI 操作点击、按键、截图、找图时遵循一条铁律即文档开篇的must call update working ckp提示一律使用物理坐标禁 pyautogui操作前先激活窗口其背后原因有三Windows 高 DPI 缩放未调用SetProcessDPIAware()时GetWindowRect / ClientToScreen / GetClientRect返回的多是逻辑坐标与屏幕物理像素存在缩放系数差异直接混用必然点偏。工具链冲突pyautogui 会污染win32api导致逻辑冲突源码 memory/ljqCtrl.py 首行即声明「严禁 import pyautogui」。截图坐标系截图内容像素天然是物理坐标必须让「截图内找到的元素坐标」与「屏幕上的点击坐标」处于同一坐标系才能做到所见即所点。ljqCtrl 是整个桌面自动化链路窗口枚举 → 截图 → ui_detect/vision 定位 → 点击的鼠标键盘控制层完整链路说明见 memory/computer_use.md。1. ljqCtrl API 快速参考文档给出以下核心签名这里结合 memory/ljqCtrl.py 源码补充每个参数的默认值与行为细节API签名与默认值说明ljqCtrl.dpi_scalefloat缩放系数 逻辑宽度 / 物理宽度。Windows 版在 import 时自动计算cwidth / swidth见源码第 18–34 行普通 100% 缩放下为 1.0ljqCtrl.ClickClick(x, yNone, checkTrue)模拟点击支持Click((x, y))或Click(x, y)两种传参checkTrue时自动对比点击前后周边像素变化并返回截图ljqCtrl.PressPress(cmd, staytime0)模拟按键如Press(ctrlc)支持连接的组合键如Press(ctrlshiftesc)按下顺序执行、抬起顺序反向ljqCtrl.FindBlockFindBlock(fn, wrectNone, threshold0.8)模板匹配找图OpenCVmatchTemplateTM_CCOEFF_NORMED返回((center_x, center_y), is_found)坐标为物理坐标ljqCtrl.GrabWindowGrabWindow(hwnd_or_name)前台截图内部先Activate可传 hwndint或窗口标题子串str返回 PIL Image只截客户区ljqCtrl.GrabWindowBgGrabWindowBg(hwnd_or_name, timeout5)WGCWindows Graphics Capture后台截图Win10依赖windows-capture无需激活窗口ljqCtrl.MouseDClickMouseDClick(staytime0.05)鼠标双击从源码可确认几个关键实现细节物理坐标入口SetCursorPos内部会做int(v * dpi_scale)换算memory/ljqCtrl.py因此传给它的一律是物理坐标不要自己提前除以缩放系数再传否则会二次缩放。Click 自带校验Click(..., checkTrue)点击前用ScreenCapAt(x, y)抓取周边图像点击后 0.5 秒再抓一次计算像素差异百分比与前台窗口是否变化memory/ljqCtrl.py这是「0% 像素变化 点歪」诊断的数据来源。Activate 前台锁绕过Activate(hwnd)先恢复最小化窗口SW_RESTORE再发假 Alt-up 骗过 Windows 前台锁然后SetForegroundWindow失败时回退BringWindowToTop SetFocusmemory/ljqCtrl.py。DPI 自愈模块 import 时即调用ctypes.windll.user32.SetProcessDPIAware()并从GetDeviceCaps读取物理分辨率、从GetSystemMetrics读取逻辑分辨率计算dpi_scalememory/ljqCtrl.py。2. 核心High-DPI 物理坐标换算ljqCtrl 的Click / MoveTo / SetCursorPos接口接收的是物理像素坐标。当使用pygetwindow等第三方工具获取窗口位置时拿到的通常是逻辑坐标必须换算换算公式物理坐标 逻辑坐标 / ljqCtrl.dpi_scale例如一台 3840×2160 物理分辨率、系统缩放 200% 的显示器上dpi_scale 1920/3840 0.5逻辑坐标 (960, 540) 对应的物理坐标是 (1920, 1080)。macOS 版macljqCtrl坐标约定与 Windows 完全一致memory/macljqCtrl.py对外 API 接收物理像素坐标dpi_scale 逻辑点 / 物理像素Retina 屏为 0.5普通屏为 1.0CGEvent 内部使用逻辑点由库自行换算。3. 截图 bbox → 屏幕物理坐标核心公式这是本文档最重要的实操代码。ui_detectmemory/ui_detect.py返回的元素bbox是截图内的像素坐标物理坐标而窗口在屏幕上的位置需要单独获取二者相加才是真正的屏幕物理坐标# ui_detect 获取的都是物理坐标截图内 # ClientToScreen 拿客户区原点(逻辑) → 除 dpi_scale 得物理偏移 import ljqCtrl import win32gui cx, cy win32gui.ClientToScreen(hwnd, (0, 0)) ox, oy int(cx / ljqCtrl.dpi_scale), int(cy / ljqCtrl.dpi_scale) # bbox [x1, y1, x2, y2]取中心点加客户区物理偏移 ljqCtrl.Click(ox (bbox[0] bbox[2]) // 2, oy (bbox[1] bbox[3]) // 2)要点必须针对窗口截图禁止全屏ImageGrab文档明令禁止因为只有窗口截图才与ClientToScreen客户区原点严格对应。所有逻辑坐标都要转物理无论来自pygetwindow、GetWindowRect还是 AX API。为什么用ClientToScreen(hwnd, (0, 0))而非GetWindowRect左上角GetWindowRect返回的矩形包含标题栏和边框而GrabWindow截图内容只含客户区源码 memory/ljqCtrl.py 明确只截客户区bbox 统一用ClientToScreen原点做偏移。若直接用窗口矩形左上角 截图坐标点击会整体偏移一个标题栏/边框的高度。同理禁止用DwmGetWindowAttribute(hwnd, 9, ...)DWMWA_EXTENDED_FRAME_BOUNDS取窗口矩形替代ClientToScreen因为它同样包含标题栏与阴影。macOS 版对应写法macljqCtrl.GrabScreen(bbox)区域截图后图内点转屏幕绝对物理坐标必须走封装好的CropToScreen(bbox, px, py)import macljqCtrl as ljqCtrl img ljqCtrl.GrabScreen(bbox) # bbox(l, u, r, b) 物理像素 px, py find_element_in_image(img) # 图内找到的点 sx, sy ljqCtrl.CropToScreen(bbox, px, py) # 转屏幕物理坐标 ljqCtrl.Click(sx, sy)CropToScreen的实现本质是「纯加裁剪原点偏移、不做缩放」因为裁剪图与 bbox 同为物理像素等价于 macOS 版的ClientToScreenmemory/macljqCtrl.py。别手搓screencapture -R该命令的参数按逻辑点解析源码在调用时先做物理→逻辑转换memory/macljqCtrl.py直接传物理坐标会点歪。4. 避坑指南坐标体系的六大陷阱文档中的避坑清单是实战踩坑经验的沉淀逐条展开如下4.1 一律使用物理坐标传给ljqCtrl.Click / SetCursorPos的坐标必须是物理坐标 截图像素坐标禁止传入逻辑坐标。若对已是物理坐标的值再次做/dpi_scale或*dpi_scale就是二次换算点击位置必然偏移。4.2 物理验证操作前先激活窗口模拟操作前必须确保窗口已通过activate()置于前台。若目标窗口处于后台/最小化物理坐标点击可能落在遮挡它的窗口上产生「点到背后别的应用」的失焦事故。混乱时先枚举窗口确认前台状态。4.3 坐标对齐原则物理坐标 截图坐标ljqCtrl 已自动处理 DPI 换算SetCursorPos内部乘以dpi_scale因此禁止手动重复计算。你只需要做好一件事把「截图内坐标」加上「客户区原点的物理偏移」。4.4 窗口坐标转换陷阱高频事故源再次强调win32gui.GetWindowRect(hwnd)与DwmGetWindowAttribute(hwnd, 9, ...)拿到的矩形都包含标题栏/边框/阴影而截图内容是客户区。点击截图内元素必须用win32gui.ClientToScreen(hwnd, (0, 0)) # 客户区原点 → 屏幕坐标禁止直接用「GetWindowRect 左上角 截图坐标」。4.5 Click 后 0% 像素变化 点歪了强制诊断信号ljqCtrl.ClickcheckTrue 时会报告点击前后像素变化百分比[Click check] 12345/250000 px changed (4.9%) | fg: 记事本若为0% 或接近 0%说明点击落在了错误位置必须立即停下来诊断坐标转换逻辑禁止盲目重试。常见原因按优先级排查用了错误的窗口原点 API用了GetWindowRect而非ClientToScreen忘记/ dpi_scale逻辑坐标未转物理混淆了客户区与窗口矩形标题栏偏移macOS 上多为忘了加裁剪原点应走CropToScreen或对已是物理像素的坐标又做了* dpi_scale。macOS 版Click同样内置该检测memory/macljqCtrl.py当像素变化 0.5% 时打印[WARN]并提示上述错因。4.6 win32 DPI 坐标陷阱未调用SetProcessDPIAware()时GetWindowRect / ClientToScreen / GetClientRect等拿到的窗口/客户区坐标通常是逻辑坐标必须换算。ljqCtrl 已在 import 时自动调用该 API 并计算dpi_scale所以你只需要先import ljqCtrl之后一律使用物理坐标。4.7 文本输入ljqCtrl 无 TypeTextljqCtrlWindows 版没有 TypeText/SendKeys 接口。向输入框键入文本的标准姿势ljqCtrl.Click(x, y) # 点击定位光标或三击选中已有内容 import pyperclip pyperclip.copy(要输入的文本) ljqCtrl.Press(ctrlv) # 粘贴注意 macOS 镜像版macljqCtrl反而提供了TypeText(s)直接键入 Unicode无需剪贴板memory/macljqCtrl.py与Paste(text)剪贴板 cmdv 一步到位这是两平台能力差异跨平台代码需做分支。5. macOSOCR/vision 认不准图标时走辅助功能 API 枚举真实控件这是文档为 macOS 场景给出的强推荐方案图标类按钮「···更多」「铅笔编辑」「关闭」等靠 OCR/vision 极易误判误点应优先用辅助功能AXAPI 读取真实控件树。5.1 两条通路通路①macljqCtrl.py原生 pyobjc AX API首选免 shellmacljqCtrl已封装完整的 AX 能力memory/macljqCtrl.pyAXElements(pid或bundle_id或app名)枚举控件树每项带role / desc / title / id / value / enabled / x / y / w / h后四者为物理坐标AXFind(..., enabled_onlyTrue)按role / desc / title / identifier子串过滤控件AXClick(node)AXPress 优先免坐标点击失败自动回退到控件中心点的物理坐标Click。import macljqCtrl as ljqCtrl nodes ljqCtrl.AXFind(com.tencent.meeting, descxxx_button_more, enabled_onlyTrue) if nodes: ljqCtrl.AXClick(nodes[0]) # AXPress 优先失败回退物理坐标点击从源码看AXClick在控件enabledFalse时会打印[WARN]警告memory/macljqCtrl.py呼应 SOP 中「点前查 disabled」的纪律AXFind的enabled_only参数正是为此设计memory/macljqCtrl.py。通路②无 pyobjc 时的 osascript 回退方案tell application System Events tell process App -- 递归枚举所有窗口的 entire contents end tell end tell通过 System Events 递归entire contents枚举进程所有窗口的真实控件拿到AXRole description(标识符) position直接perform action AXPress点中。5.2 关键坑位弹窗/详情卡是独立子窗口front window只返回主窗口如红绿灯按钮所在的主窗目标控件往往在独立子窗口里。必须every window遍历 entire contents否则找不到目标控件。优先按 description/identifier 匹配而非坐标控件常自带语义化标识如xxx_button_more按 description 精确匹配比坐标稳定枚举一次记下目标标识即可跨会话复用。坐标换算AX 返回的是逻辑坐标截图/Click 用物理坐标Retina 屏 ×2文档给出实测逻辑 (537,121) ↔ 物理 (1074,242)。AX 的AXPress直接作用元素免换算若 AX 偶发 NOTFOUND时序波动用换算后的物理坐标Click兜底。失焦陷阱点击坐标若落在窗口边界外会点到背后别的应用导致目标失焦。用osascript tell application App to activate激活比 ljqCtrl 的 ActivateApp 更可靠激活后用frontmost确认。5.3 macOS 权限与依赖前置首次使用前必须自检import macljqCtrl as ljqCtrl ljqCtrl.check_permissions() # 返回 (accessibility_ok, screen_recording_ok)辅助功能Accessibility系统设置 隐私与安全性 辅助功能授权 GA 宿主进程缺失则键鼠静默失败屏幕录制Screen Recording同上 屏幕录制缺失则截图静默失败。依赖pip install pyobjc-framework-Quartz pyobjc-framework-CocoaAX 相关另需pyobjc-framework-ApplicationServices属于软依赖——未安装时键鼠/截图正常仅 AX 函数不可用memory/macljqCtrl.py。6. 跨平台代码写法与操作节奏6.1 环境载入与 API 镜像# Windows import ljqCtrl # macOSAPI 镜像一行切换 import macljqCtrl as ljqCtrlmacljqCtrl对 Windows 版做了完整的 drop-in 镜像click / press / activate / VK_CODE等别名齐全memory/macljqCtrl.pyFindBlock / GrabWindow / ScreenCapAt / MouseClick / MouseDClick签名一致跨平台自动化代码几乎无需改动。唯一注意点Windows 版Activate(hwnd)收窗口句柄macOS 版ActivateApp收应用名/pid且 macOS 前台是应用粒度而非窗口粒度。6.2 推荐的 GUI 操作节奏来自 computer_use SOP来自 memory/computer_use.md 的配套纪律与 ljqCtrl 配合使用先探测后操作进入新界面先枚举窗口 截图 ui_detect读完实际输出再决定下一步不要在未知状态下把多步决策写进大脚本小步验证明确一个操作后在同一轮执行短暂等待再枚举窗口 截图验证新状态探测/定位工具按优先级降级win32gui 窗口枚举始终可用→ Python UIA 控件树首选游戏禁用→ ui_detect截图视觉检测→ vision VLM仅语义理解不可信其坐标坐标转换纪律ui_detect 的 bbox 是截图内坐标点击前必须用ClientToScreen(hwnd, (0,0)) / dpi_scale bbox中心转屏幕物理坐标禁用GetWindowRect或 DWM 窗口矩形直接加截图坐标兜底硬件ljqCtrl 失效或目标为网络游戏时必须使用硬件键鼠Xbananakb / Arduino Leonardo如有网络游戏除非用户明确允许严禁普通键鼠事件临时文件纪律用 PIL 传输图像或用统一1.png覆盖存储截图避免截图文件堆积。7. 附后台窗口控制ljqCtrlBg若确实需要不激活窗口的后台操作如用户明确要求或前台激活会干扰任务仓库还提供 memory/ljqCtrlBg.py它通过向目标窗口 PostMessage 注入鼠标/键盘消息永不激活窗口、不移动光标、不注入全局输入并提供CaptureResult返回客户区原点与尺寸origin_screen_phys / client_size_phys与本文的物理坐标体系一致。但其消息注入是 best-effort必须用截图验证实际效果。默认情况下仍以本文档的「先 Activate 到前台」为主路径。8. 总结一套坐标心法把整套 SOP 浓缩为五条可执行规则先import ljqCtrl——它会自动SetProcessDPIAware并算好dpi_scale只用物理坐标——逻辑坐标一律/ dpi_scalemacOS AX 逻辑点同理客户区原点用ClientToScreen(hwnd, (0,0))——绝不用GetWindowRect/ DWM 矩形含标题栏Click的像素变化是探针——0% 立即停诊断坐标链不盲目重试macOS 优先 AXAXElements/AXFind/AXClick——免坐标、语义匹配稳定失焦时用 osascriptactivate兜底。相关文档与源码SOP 本体 memory/ljqCtrl_sop.md、Windows 实现 memory/ljqCtrl.py、macOS 实现 memory/macljqCtrl.py、后台控制 memory/ljqCtrlBg.py、配套操作纪律 memory/computer_use.md、视觉检测 memory/ui_detect.py。【免费下载链接】GenericAgentSelf-evolving agent: grows skill tree from 3.3K-line seed, achieving full system control with 6x less token consumption项目地址: https://gitcode.com/GitHub_Trending/pc/GenericAgent创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考