控件获取 - chatautox 开发文档
控件获取
扩展开发时,第一步通常是拿到目标控件的定位信息(Name、AutomationId、ClassName、ControlType),再编写点击、输入等自动化逻辑。本章介绍 chatautox 提供的控件获取方式。
合规提示:下文示例中的窗口标题、控件 Name 等字符串,均来自 UIA/Win32 枚举结果,仅作技术演示。详见 开发文档 — 商标与合规说明。
推荐流程
1. 导出控件树(dump / walk)
↓
2. 在树文本或 control_browser 中找到目标行
↓
3. 提取 Name / Aid / Class / ctrl_type
↓
4. 用 WinAuto.find() 验证能定位到控件
↓
5. 编写 click / input / get_list_items 等业务逻辑
方式一:导出控件树(调试首选)
WinAuto — 通用任意窗口
from chatautox import WinAuto
TARGET_CLASS = 'YourMainWndClass' # 改为你的窗口 ClassName
TARGET_TITLES = ('目标应用', 'MyApp')
hwnd = WinAuto.find_main_hwnd(TARGET_CLASS, TARGET_TITLES, find_window_name=TARGET_TITLES[0])
auto = WinAuto(hwnd)
windows = auto.dump_windows_controls(
max_windows=30, # 最多导出几个顶级窗
max_lines=800, # 每个窗最多几行控件
same_process_only=True, # 仅同进程窗口
)
WinAuto.print_windows_dump(windows)
输出示例(窗口标题 title= / 控件 Name= 为系统返回值,非 chatautox 品牌用语):
[window] hwnd=12345 pid=6789 class="YourMainWndClass" title="…" rect=[0, 0, 800, 600]
WindowControl Name="…" Class="YourMainWndClass" Aid="" Rect=(0,0,800,600)
PaneControl Name="" Class="" Aid="main_window" Rect=(0,0,800,600)
ListControl Name="" Class="" Aid="session_list" Rect=(0,60,250,600)
ListItemControl Name="示例会话" Class="ChatSessionCell" Aid="" Rect=(0,60,250,100)
EditControl Name="" Class="ChatInputBox" Aid="chat_input" Rect=(250,550,780,590)
ButtonControl Name="发送" Class="" Aid="" Rect=(780,550,800,590)
树文本每行格式:
{缩进}{ControlType} Name="..." Class="..." Aid="..." Rect=(left,top,right,bottom)
内置 IM 客户端 — 含子进程窗(高级)
部分 IM 客户端的笔记、合并消息等弹窗可能在独立进程。通过 auto.im_client() 获得客户端后,可调用 DumpWindowsControls(参数与 WinAuto.dump_windows_controls 类似):
im = auto.im_client(debug=False)
# 常用:主进程窗口
windows = im.DumpWindowsControls(
max_windows=30,
max_lines=800,
same_process_only=True,
)
若需 restrict_pid / extra_hwnds(笔记子进程、不可见 HWND),走内部 API:
# 指定 PID + 强制包含额外 HWND
windows = im._api.DumpWindowsControls(
max_windows=30,
max_lines=1000,
same_process_only=True,
restrict_pid=note_pid,
extra_hwnds=[note_hwnd],
)
for w in windows:
print(f'hwnd={w["hwnd"]} class={w["class_name"]} title={w["title"]}')
print(w.get('tree', ''))
GenericWnd — 结构化遍历(保留 ctrl 对象)
control_browser 控件浏览器底层使用此接口,返回带 UIA 控件对象 的列表:
from chatautox.ui.window import GenericWnd
wnd = GenericWnd(hwnd=12345)
controls = wnd.dump_controls(max_windows=50, max_lines=800)
for item in controls:
print(item['label']) # 格式化标签行
print(item['ctrl']) # UIA 控件对象,可直接传给 WinAuto.click
print(item['cx'], item['cy']) # 中心坐标
print(item['rect'], item['depth'])
walk_controls() 与 dump_controls() 类似,可单独遍历当前绑定窗口:
items = wnd.walk_controls(max_depth=30, max_lines=800, time_budget=5.0)
解析树文本为结构化列表
若已有 dump_windows_controls 的返回值,可解析为扁平控件列表(不含 ctrl 对象):
from chatautox.ui.window import GenericWnd
parsed = GenericWnd.parse_controls(windows)
for p in parsed:
print(p['label'], p['cx'], p['cy'], p.get('sender', ''))
方式二:按属性查找单个控件
WinAuto.find — 推荐
在已绑定 HWND 的窗口下,按 UIA 属性查找第一个匹配控件:
from chatautox import WinAuto
auto = WinAuto(hwnd)
ctrl = auto.find(
ctrl_type='EditControl', # UIA 类型名,空则泛型 Control
name='发送', # Name 精确匹配
aid='chat_input', # AutomationId
cls='ChatInputBox', # ClassName
depth=10, # 搜索深度,默认 10
)
if ctrl is None:
print('未找到控件')
else:
print(ctrl.Name, ctrl.AutomationId, ctrl.ClassName)
auto.click(ctrl)
查找参数可任意组合,至少提供一个非空条件。常用 ctrl_type:
WinAuto.wait_ctrl — 等待控件出现
弹窗、加载动画等场景,轮询直到控件出现或超时:
ctrl = auto.wait_ctrl(
name='另存为',
ctrl_type='WindowControl',
timeout=5.0, # 最长等待秒数
interval=0.2, # 轮询间隔
)
macro_helper — 函数式
流程编排器生成代码常用:
from chatautox.auto.macro_helper import get_hwnd, find_ctrl, ctrl_find
WIN_TITLE = '…' # 替换为目标窗口在任务栏显示的实际标题
hwnd = get_hwnd(win_title=WIN_TITLE, win_class='')
ctrl = find_ctrl(win_title=WIN_TITLE, name='发送', ctrl_type='ButtonControl')
ctrl = ctrl_find(hwnd, aid='chat_input', ctrl_type='EditControl')
方式三:批量查找(多个匹配项)
AppWindow — 返回列表
from chatautox.auto.uibase import AppWindow
win = AppWindow.from_hwnd(hwnd)
# 按 ClassName 查找所有匹配
cells = win.find_by_class('ChatSessionCell', depth=15)
# 按 Name 查找(exact=True 精确,False 模糊包含)
items = win.find_by_name('示例会话', exact=True, depth=15)
# 按 AutomationId
inputs = win.find_by_automationid('chat_input', depth=10)
# 原生 UIA FindAll
root = win.root_control
all_btns = [c for c in root.FindAll(maxDepth=10)
if c.ControlTypeName == 'ButtonControl']
遍历子控件
list_ctrl = auto.find(ctrl_type='ListControl', aid='session_list')
children = list_ctrl.GetChildren()
for child in children:
print(child.Name, child.ClassName, child.AutomationId)
方式四:列表项批量获取
适用于会话列表、联系人列表等 ListControl,一次性获取所有项的完整属性:
items = auto.get_list_items(
list_type='ListControl',
list_aid='session_list', # 列表 AutomationId
item_cls='ChatSessionCell', # 列表项 ClassName 过滤
name_field='name', # 提取名称字段:name / aid / text
unread_pattern=r'\[(\d+)条\]', # 未读数正则(可选)
mute_keyword='消息免打扰', # 免打扰关键词(可选)
depth=15,
)
for item in items:
print(item['name']) # 显示名称
print(item['unread']) # 未读数
print(item['mute']) # 是否免打扰
print(item['ctrl']) # UIA 控件对象
print(item['BoundingRectangle']) # 位置
print(item['AutomationId'])
get_list_items 返回的每项包含 _dump_ctrl_props 导出的完整属性:
IM 客户端会话列表快捷方法(以 chatautox 默认适配目标为例):
sessions = auto.get_sessions(list_aid='session_list', item_cls='ChatSessionCell')
unread = auto.get_unread_sessions()
found = auto.find_session('示例会话')
方式五:control_browser 可视化获取
项目内置 control_browser / control_browser_lite 可视化调试面板,适合不熟悉代码的控件定位:
基本操作
- 启动控件浏览器,拾取或绑定目标窗口(类名 + 标题,可重复执行、自动查找 HWND)
- 按 F5 刷新控件树
- 在左侧树中选中目标控件
- 右键 → 控件详情 查看完整属性
- 右键 → 生成代码 获取
WinAuto 调用代码
面板顶部可切换点击模式(UIA / PostMessage / 物理),与 WinAuto 的 mode 参数对应。
control_browser_lite:流程录制与代码执行
lite 版右侧提供代码编辑框与运行输出面板:
- 拾取窗口后自动生成绑定头(
WIN_CLASS / WIN_TITLES / auto = WinAuto(...))
- 点击、发送、键盘操作,或「所有操作 → 消息列表处理」等,会追加步骤到编辑器,不覆盖已有代码
- ▶ 执行:在独立子进程中运行编辑器脚本(
-u 无缓冲),print / wxlog 调试日志实时显示在「运行输出」
- ⏹ 停止:终止子进程(含
GetNextNewMessage 长时间阻塞时也可停止);执行中 ▶ 禁用
「消息列表处理 → GetNextNewMessage 循环 / GetNewMessage 循环」模板含 auto.activate() + while True 轮询。首帧通常不交付历史消息,属正常行为。
消息 API 通过 auto.im_client() 创建(默认内置 IM 适配器;绑定控件浏览器时会自动查找 IM 主窗口),步骤模板会自动补上 im = auto.im_client(...)。
所有操作 → 注册别名(给控件起名字)
一句话:先把「怎么找到某个控件」记在一个短名字里,后面写流程只写名字,不用反复抄 aid、name、depth。
可以把它理解成:给按钮、输入框、会话列表起外号,脚本里用外号操作。
什么时候用
和「列表操作」的区别:
- 列表操作:代码里直接写
list_aid='session_list',适合演示、写一次
- 注册别名:先
register('sessions', list_aid='session_list', ...),后面统一写 'sessions',适合多步骤流程
在 control_browser_lite 里怎么用
- 拾取目标窗口,左侧控件树 F5 刷新
- 打开 「所有操作」 → 分类选 「注册别名」
- 按下面顺序操作(每一步点 「生成代码」 追加到右侧编辑器):
注册列表 时参数示例:list_aid='session_list'(左侧会话列表示例,以你控件树里看到的 AutomationId 为准)。
注册控件 前务必在树里选中目标;步骤预览里会出现 target = auto.find(...) + register(...)。
生成代码长什么样
注册一个输入框:
# 查找聊天输入框(Aid 来自控件树)
target = auto.find(aid='chat_input', depth=12)
auto.register('input_box', ctrl_type='EditControl', aid='chat_input', depth=12)
注册左侧会话列表:
auto.register('sessions',
list_type='ListControl',
list_aid='session_list',
item_cls='ChatSessionCell',
unread_pattern=r'\[(\d+)条\]',
mute_keyword='消息免打扰')
后面只用别名(短、好读):
auto.input_cfg('input_box', '你好')
auto.send_msg_cfg('input_box', '你好') # 点击 + 输入 + 回车
auto.use('sessions').click_item('示例会话')
auto.use('sessions').click_unread(0) # 点第一个未读会话
# 链式写法(和上面等价,一行串起来)
auto.use('input_box').click().input('你好').enter()
「所有操作」里各项是干什么的
API 参数表见 WinAuto — 控件别名。
完整小流程示例(发送一条消息)
auto = WinAuto(hwnd, mode='uia')
auto.activate()
# ① 注册(流程开头做一次即可)
auto.register('input_box', ctrl_type='EditControl', aid='chat_input', depth=12)
# ② 使用别名
auto.use('input_box').click().input('你好,这是测试').enter()
若还要先点某个会话,可先 注册列表 + 代理点列表项,或直接用「列表操作 → 点击会话」。
读取控件属性
找到控件后,可直接读 UIA 属性或借助封装:
# 原生 UIA 属性
ctrl.Name
ctrl.AutomationId
ctrl.ClassName
ctrl.ControlTypeName
ctrl.BoundingRectangle # left/top/right/bottom
ctrl.IsEnabled
ctrl.Exists(maxSearchSeconds=0)
# ElementAction 包装(uibase)
from chatautox.auto.uibase import ElementAction
elem = ElementAction(ctrl, hwnd=hwnd)
print(elem.name, elem.automation_id, elem.bbox, elem.center)
texts = elem.get_all_text() # 子控件所有 Name
完整示例:从导出到操作
from chatautox import WinAuto
TARGET_CLASS = 'YourMainWndClass'
TARGET_TITLES = ('目标应用', 'MyApp')
hwnd = WinAuto.find_main_hwnd(TARGET_CLASS, TARGET_TITLES, find_window_name=TARGET_TITLES[0])
auto = WinAuto(hwnd, mode='uia')
# 1. 导出控件树,找到 chat_input 的 Aid
WinAuto.print_windows_dump(auto.dump_windows_controls(max_lines=500))
# 2. 验证定位
input_ctrl = auto.find(ctrl_type='EditControl', aid='chat_input')
assert input_ctrl is not None, '输入框未找到'
# 3. 注册别名,后续脚本复用
auto.register('input_box', ctrl_type='EditControl', aid='chat_input')
auto.register('send_btn', ctrl_type='ButtonControl', name='发送')
# 4. 操作
auto.use('input_box').click().input('你好').enter()
# 或
auto.send_msg_cfg('input_box', '你好')
常见问题
find 返回 None
- 检查
depth 是否足够(复杂界面建议 15~20)
- 确认
Name 是否与控件树中完全一致(含空格、换行)
- 弹窗可能在子进程,需用
DumpWindowsControls(restrict_pid=..., extra_hwnds=...)
- 控件可能尚未渲染,改用
wait_ctrl(timeout=5)
控件树过大 / 卡顿
- 减小
max_lines(如 500~800)
- 设置
same_process_only=True 过滤无关窗口
- 复杂界面建议
max_windows=30
Name 含换行
列表项 Name 常含多行文本,get_list_items 会自动取第一行作为 name 字段;手动查找时建议用 aid 或 cls 定位。
上一页WinAuto 总览下一页构造与绑定