pywinauto:遍历 window 中的所有控件
pywinauto: Iterate through all controls in a window
我正在尝试编写一个通用测试脚本来查找新软件版本中的错误。我的想法是遍历 window 中的控件并与每个控件交互,记录导致的任何错误并在软件崩溃时重新启动软件。
我正在寻找一种动态查找控件标识符的方法,有点像 print_control_identifiers()
但输出是一个列表或我可以遍历的类似结构。
在 GitHub question 关于控件标识符的文章中提到:
it's possible to walk the hierarchy by using .children()
(immediate children only) and .descendants()
(the whole subtree as a plain list)
我假设我可以遍历我的 Application
对象的 descendants()
列表并为每个调用一个相关的交互方法,但是我不知道如何获得这个列表。我以为我可以做这样的事情,但我没有成功:
def test(application):
for child in application.descendants():
#interact with child control
software = Application(backend='uia').start(cmd_line=FILE_PATH)
test(software)
AttributeError:既未找到 GUI 元素(包装器)也未找到包装器方法 'descendants'(打字错误?)
编辑
我求助于 the code 并找到 print_control_identifiers
方法:
class Application(object):
def print_control_identifiers(self, depth=None, filename=None):
"""
Prints the 'identifiers'
Prints identifiers for the control and for its descendants to
a depth of **depth** (the whole subtree if **None**).
.. note:: The identifiers printed by this method have been made
unique. So if you have 2 edit boxes, they won't both have "Edit"
listed in their identifiers. In fact the first one can be
referred to as "Edit", "Edit0", "Edit1" and the 2nd should be
referred to as "Edit2".
"""
if depth is None:
depth = sys.maxsize
# Wrap this control
this_ctrl = self.__resolve_control(self.criteria)[-1]
# Create a list of this control and all its descendants
all_ctrls = [this_ctrl, ] + this_ctrl.descendants()
# Create a list of all visible text controls
txt_ctrls = [ctrl for ctrl in all_ctrls if ctrl.can_be_label and ctrl.is_visible() and ctrl.window_text()]
# Build a dictionary of disambiguated list of control names
name_ctrl_id_map = findbestmatch.UniqueDict()
for index, ctrl in enumerate(all_ctrls):
ctrl_names = findbestmatch.get_control_names(ctrl, all_ctrls, txt_ctrls)
for name in ctrl_names:
name_ctrl_id_map[name] = index
# Swap it around so that we are mapped off the control indices
ctrl_id_name_map = {}
for name, index in name_ctrl_id_map.items():
ctrl_id_name_map.setdefault(index, []).append(name)
这说明.descendants()
不是Application
class的一个方法,而是属于控件的。看来我错了。是否可以创建我自己的 print_control-identifiers()
版本,其中 returns 可以迭代的控制对象列表?
列出顶级 windows 的正确方法是 application.windows()
。然后你可以为每个列出的 window 调用 .descendants()
。在大多数情况下,应用程序只有一个顶级 window。特别是对于 backend="uia"
,甚至新对话框都是主要 window 的子对话框(对于 backend="win32"
,每个对话框都是顶级 window)。
我正在尝试编写一个通用测试脚本来查找新软件版本中的错误。我的想法是遍历 window 中的控件并与每个控件交互,记录导致的任何错误并在软件崩溃时重新启动软件。
我正在寻找一种动态查找控件标识符的方法,有点像 print_control_identifiers()
但输出是一个列表或我可以遍历的类似结构。
在 GitHub question 关于控件标识符的文章中提到:
it's possible to walk the hierarchy by using
.children()
(immediate children only) and.descendants()
(the whole subtree as a plain list)
我假设我可以遍历我的 Application
对象的 descendants()
列表并为每个调用一个相关的交互方法,但是我不知道如何获得这个列表。我以为我可以做这样的事情,但我没有成功:
def test(application):
for child in application.descendants():
#interact with child control
software = Application(backend='uia').start(cmd_line=FILE_PATH)
test(software)
AttributeError:既未找到 GUI 元素(包装器)也未找到包装器方法 'descendants'(打字错误?)
编辑
我求助于 the code 并找到 print_control_identifiers
方法:
class Application(object):
def print_control_identifiers(self, depth=None, filename=None):
"""
Prints the 'identifiers'
Prints identifiers for the control and for its descendants to
a depth of **depth** (the whole subtree if **None**).
.. note:: The identifiers printed by this method have been made
unique. So if you have 2 edit boxes, they won't both have "Edit"
listed in their identifiers. In fact the first one can be
referred to as "Edit", "Edit0", "Edit1" and the 2nd should be
referred to as "Edit2".
"""
if depth is None:
depth = sys.maxsize
# Wrap this control
this_ctrl = self.__resolve_control(self.criteria)[-1]
# Create a list of this control and all its descendants
all_ctrls = [this_ctrl, ] + this_ctrl.descendants()
# Create a list of all visible text controls
txt_ctrls = [ctrl for ctrl in all_ctrls if ctrl.can_be_label and ctrl.is_visible() and ctrl.window_text()]
# Build a dictionary of disambiguated list of control names
name_ctrl_id_map = findbestmatch.UniqueDict()
for index, ctrl in enumerate(all_ctrls):
ctrl_names = findbestmatch.get_control_names(ctrl, all_ctrls, txt_ctrls)
for name in ctrl_names:
name_ctrl_id_map[name] = index
# Swap it around so that we are mapped off the control indices
ctrl_id_name_map = {}
for name, index in name_ctrl_id_map.items():
ctrl_id_name_map.setdefault(index, []).append(name)
这说明.descendants()
不是Application
class的一个方法,而是属于控件的。看来我错了。是否可以创建我自己的 print_control-identifiers()
版本,其中 returns 可以迭代的控制对象列表?
列出顶级 windows 的正确方法是 application.windows()
。然后你可以为每个列出的 window 调用 .descendants()
。在大多数情况下,应用程序只有一个顶级 window。特别是对于 backend="uia"
,甚至新对话框都是主要 window 的子对话框(对于 backend="win32"
,每个对话框都是顶级 window)。