pywinauto: перебирать все элементы управления в окне
Я пытаюсь написать общий тестовый скрипт, чтобы найти ошибки в новых сборках программного обеспечения. Моя идея состоит в том, чтобы перебирать элементы управления в окне и взаимодействовать с каждым из них, регистрируя любые ошибки и перезапускать программное обеспечение в случае его сбоя.
Я ищу способ динамического поиска идентификаторов управления, немного похоже print_control_identifiers()
но с выводом, являющимся списком или подобной структурой, через которую я могу перебирать.
На GitHub вопрос об идентификаторах управления это было упомянуто:
можно пройти иерархию с помощью
.children()
(только непосредственные дети) и.descendants()
(все поддерево в виде простого списка)
Я предположил, что мог бы просто перебрать мой 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 (обертка), ни метод-обертка "потомки" не найдены (опечатка?)
РЕДАКТИРОВАТЬ
Я прибег к просмотру кода и нашел 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
класс, но принадлежит контролю. Кажется, я ошибался там. Можно ли создать собственную версию print_control-identifiers()
который возвращает список объектов управления, через которые можно выполнить итерацию?
1 ответ
Правильный метод для перечисления окон верхнего уровня application.windows()
, Тогда вы можете позвонить .descendants()
для каждого перечисленного окна. В большинстве случаев приложение имеет только одно окно верхнего уровня. Особенно для backend="uia"
даже новые диалоги являются дочерними элементами главного окна (для backend="win32"
каждый диалог является окном верхнего уровня).