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" каждый диалог является окном верхнего уровня).

Другие вопросы по тегам