UFO 源码实战 (3):它怎么“点”鼠标的?通过源码掌握 Windows 自动化控制

前言

在上一篇中,我们揭秘了 UFO 的“火眼金睛”:它通过 Set-of-Marks (SoM) 技术,把屏幕上的每个按钮都标记上了 ID。

当 GPT-4V 看着满屏的红框说:“我觉得应该点击 5号 按钮”时,UFO 的大脑工作就结束了,轮到**“四肢” (Execution Layer)** 登场了。

  • 它是瞬间把鼠标“瞬移”过去的吗?

  • 它是怎么输入文字的?

  • 如果点歪了怎么办?

今天这一篇,我们深入 ufo/automator 模块,拆解 Action Execution 的全过程,带你掌握 Windows 自动化控制的核心代码。


🦾 第一步:指令翻译 (从文本到动作)

GPT-4V 返回给 UFO 的通常是一段 JSON 格式的字符串,比如:

JSON

{
  "thought": "I need to save the file.",
  "control_label": "5",
  "action": "click"
}

UFO 的控制器首先要把这个“文本指令”翻译成 Python 的函数调用。这部分逻辑通常在 AppAgent 的响应解析部分。

关键流程

  1. 提取 ID:拿到 control_label: "5"

  2. 查表:回到我们在上一篇建立的 control_map(ID 到控件对象的映射表)。

  3. 锁定对象:找到 ID 为 5 的那个 pywinauto 控件对象。


🖱️ 第二步:鼠标点击的玄学 (click vs click_input)

这是新手最容易踩坑的地方。在 ufo/automator/ui_control/controller.py (或类似文件) 中,你会发现 UFO 并没有使用普通的点击方法。

源码核心逻辑:

Python

# 伪代码:UFO 的点击逻辑封装

def execution_click(element):
    # 1. 确保控件可见并置顶
    element.set_focus() 
    
    # 2. 高亮一下(为了让用户知道我要点这里了,视觉反馈)
    highlight_element(element) 
    
    # 3. 核心点击操作
    # 注意:这里用的是 click_input() 而不是 click()
    element.click_input(coords=(x, y)) 

这里的门道 (必考题):

pywinauto 提供了两种点击方式,UFO 为什么选 click_input

  • click() (消息级):直接给窗口发一个 Windows Message (WM_CLICK)。

    • 优点:快,鼠标光标不需要移动,可以在后台运行。

    • 缺点:很多现代 UI(如 WPF, UWP, Electron)根本不理会这种假消息,点击经常无效。

  • click_input() (硬件模拟级)

    • 原理真实的移动鼠标光标到指定坐标,按下物理左键,再松开。

    • 优点兼容性无敌,和真人操作一模一样。

    • 缺点:会独占你的鼠标,操作期间你不能动。

UFO 为了保证在各种软件里都能跑通,果断选择了模拟真实硬件的 click_input()


⌨️ 第三步:键盘输入的艺术

除了点击,UFO 还需要打字。这在源码中对应 type 动作。

Python

def execution_type(element, text):
    # 1. 先点一下输入框,确保焦点在里面
    element.click_input()
    
    # 2. 输入文字
    # with_spaces=True 保证空格不会被吞掉
    element.type_keys(text, with_spaces=True)

特殊按键处理:

你可能会在源码里看到类似 {ENTER} 或 ^a (Ctrl+A) 的字符串。这是 pywinauto 的特殊语法。

UFO 在这里做了一层封装,允许 GPT 输出 "Press Enter",然后内部转化为 type_keys("{ENTER}"),从而实现回车、删除、复制粘贴等操作。


🛡️ 第四步:安全与高亮 (Visual Feedback)

UFO 有一个非常贴心的设计:在点击之前,会在屏幕上画一个框或圆圈,闪烁一下

这不仅仅是为了酷炫,更是一种 Safety Check (安全机制)。

代码位于 ufo/automator/ui_control/screenshot.py 的绘图工具中。

源码逻辑

  1. 获取目标控件的 RECT (矩形范围)。

  2. 在执行 click_input 之前,调用 GDIPyQt 绘制一个半透明的红色矩形覆盖在目标上。

  3. time.sleep(0.5) 暂停半秒。

  4. 清除矩形,执行点击。

为什么这么做?

  • 调试:如果你发现红框画歪了,说明上一轮的坐标识别有问题。

  • 介入:如果你发现 UFO 要点击“删除文件”,这半秒的延迟给了你惊呼和拔电源的机会(笑)。


🚧 异常处理:如果不动了怎么办?

源码分析不仅要看正常流程,还要看它怎么处理“翻车”。

controller.py 中,通常包裹着大量的 try...except 块:

Python

try:
    control.click_input()
except ElementNotFoundError:
    # 控件找不到了?可能是界面刷新了
    print("Error: Element not found, triggering re-observation...")
    # 触发重新截图,重新识别流程
    return "FAIL_RETRY"
except Exception as e:
    # 其他未知错误
    print(f"Unknown error: {e}")

UFO 引入了自我修正机制:如果动作执行失败,它不会直接报错退出,而是会将“执行失败”作为反馈传回给 GPT-4V。

GPT-4V 收到反馈后,往往会说:“哦,可能是菜单没展开,那我先点一下菜单,再点按钮。”


📝 总结与实战建议

通过分析 UFO 的动作执行源码,我们学到了 Windows 自动化的三个黄金法则:

  1. 模拟真实:尽量使用 click_input() 模拟物理硬件,而不是发消息,以获得最大兼容性。

  2. 状态确认:操作前必须 set_focus(),操作后最好检查结果。

  3. 视觉反馈:给用户展示机器人的“意图”(高亮),是提升 AI Agent 体验的关键。

实战作业:

打开你的 UFO 源码,找到 controller.py,尝试修改一下 type_keys 的速度(pywinauto.timings),看看能不能让 UFO 打字快得像黑客帝国一样!

下期预告:

现在 UFO 能看能动了,但它怎么记得住之前的操作?如果任务很长(比如“把所有未读邮件整理到Excel”),它怎么规划步骤?

下期文章:《UFO 源码实战 (4):拆解 prompter 模块,看微软如何调教 GPT-4V 》,我们将深入 AI 的大脑皮层!


点赞是免费的,但知识是无价的。我们下期见! 🤖


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天进步2015

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值