pywinauto 如何读取控件树中不可见的 UIA 元素名称?
结论
IsControlElement=false 且 IsContentElement=false 的元素通常不会出现在 pywinauto 默认使用的控件视图中,因此用 descendants() 或 child_window() 按名称查找时可能找不到它。可以从已知的 TextBox 获取底层 UIA 元素,再用 UI Automation 的 RawViewWalker 遍历原始视图,并读取子元素的 CurrentName。pywinauto 源码中的 IUIA().raw_tree_walker 可用于访问 UIA 原始树遍历器。pywinauto 的 UIA 定义
按原始视图遍历子元素
Inspect.exe 信息显示,TextBlock 的名称是 "Excavation steps",父元素是 TextBox。先根据稳定的窗口条件找到这个 TextBox,再检查它的原始视图子元素:
from pywinauto import Application
from pywinauto.windows.uia_defines import IUIA
app = Application(backend="uia").connect(title="窗口标题")
edit = app.window(title="窗口标题").child_window(
class_name="TextBox",
control_type="Edit",
).wrapper_object()
walker = IUIA().raw_tree_walker
child = walker.GetFirstChildElement(edit.element_info.element)
while child:
name = child.CurrentName
if name == "Excavation steps":
print(name)
break
child = walker.GetNextSiblingElement(child)
将示例中的 "窗口标题" 替换为实际窗口标题。如果窗口中有多个 TextBox,还需要添加适用于该应用的定位条件。题目给出的 AutomationId="txtAddRow" 属于 TextBlock,不能据此假设父 TextBox 也有相同的 AutomationId。
为什么默认查找会漏掉它
UIA 将界面元素组织为原始视图、控件视图和内容视图。Inspect.exe 能在 UIA 原始树中显示某个元素,但如果该元素没有标记为控件元素或内容元素,使用 pywinauto 常见的控件树查找时仍可能看不到它。pywinauto 的 IUIA 初始化代码暴露了 RawViewWalker,可用于遍历原始树。pywinauto 源码
这份 Inspect.exe 数据中,TextBlock 的 Name 和 LegacyIAccessible.Name 都是 "Excavation steps";LegacyIAccessible.Value 则为空字符串,且 IsTextPatternAvailable、IsValuePatternAvailable 都为 false。因此,应优先读取名称属性,而不是通过 ValuePattern 或 TextPattern 获取内容。
使用时的注意事项
- 上面的代码只遍历
TextBox的直接子元素。若目标元素在更深层级,需要递归调用GetFirstChildElement和GetNextSiblingElement遍历后代。 - 原始视图包含的元素通常更多,遍历范围越大,查找开销也越高。已知父元素时,从该元素开始查找更合适。
RuntimeId适合识别当前 UIA 会话中的元素,不宜作为跨运行或跨会话的固定定位值。- 如果
CurrentName为空,或应用没有向 UIA 暴露文本,pywinauto 可能无法读取该文本。这时需要检查应用提供的其他 UIA 属性或自动化接口。
备注:内容仅供参考。
