Продвинутый интерфейс¶
В этом разделе представлены более продвинутые интерфейсы автоматизации. С их помощью вы можете выполнять разнообразные точные операции. Раздел объёмный, и если вы знакомитесь с ним впервые, рекомендуем внимательно изучить каждую часть.
Совет
При написании кода автоматизации вы можете прямо в терминале справа от удалённого рабочего стола ввести команду lamda и выполнять в нём приведённые ниже тестовые примеры или самостоятельно выбирать элементы, проверять нажатия и т. п. Это ускоряет написание и проверку.
Получение элемента¶
Вы, вероятно, уже познакомились с этим в базовых материалах или ранее. Чтобы выполнить действие, необходимо найти нужный элемент через селектор. Вы также уже видели, где можно получить параметры селектора. Дальнейшее описание построено вокруг этого элемента. Справа на изображении показана информация об элементе «同意».

Внимание
Элемент, по которому вы щёлкаете непосредственно в левой части интерфейса, может не быть фактическим элементом, поскольку он может перекрываться по размеру и положению с другими элементами. Обычно элементы, пересекающиеся по положению или размеру, отображаются в информационной панели справа; вы можете прокручивать список, чтобы понять, какой из них действительно нужен. Вы также можете вручную перебрать все элементы, нажимая клавишу TAB в левой части экрана выбора.
Для приведённого выше элемента обычно используется text. Условие применения text — на текущем экране нет другого элемента с тем же текстом «同意»; это самый простой способ. Можно также использовать resourceId, но учтите, что resourceId здесь не является уникальным идентификатором: это идентификатор ресурса, и на одном экране может быть много элементов с одинаковым resourceId. Другие поля, такие как packageName, checkable и т. п., применяются реже, но если нет text, resourceId, description и других, можно попробовать их. Получить этот элемент можно несколькими способами.
element = d(text="同意")
element = d(text="同意", resourceId="com.tencent.news:id/btm_first_agree")
element = d(resourceId="com.tencent.news:id/btm_first_agree")
Нажатие на элемент¶
Вызов следующего метода выполняет обычное нажатие на элемент; в данном контексте это даёт тот же эффект, что и ручное нажатие «同意».
element.click()
Если нужно указать точку нажатия внутри элемента, передайте параметр corner при вызове метода нажатия. Например, Corner.COR_CENTER означает нажатие по центру элемента; также можно нажать по левому верхнему или правому нижнему углу (Corner.COR_BOTTOMRIGHT).
element.click_exists(corner=Corner.COR_TOPLEFT)
Выполняет долгое нажатие на элемент; если элемент не существует, возникает исключение. Метод также поддерживает corner и позволяет указать длительность нажатия.
element.long_click(timeout=0) # миллисекунды
Нажимает на элемент, если он существует; если элемента нет, вызов не вызывает исключение. Метод также поддерживает corner.
element.click_exists()
>>> element.click_exists()
True
Проверка существования¶
Во многих случаях перед дальнейшими действиями необходимо проверить существование элемента. Иначе последующий процесс может завершиться ошибкой или даже выполнить неверное действие на неправильном экране. Для проверки существования используйте следующий метод.
element.exists()
Информация об элементе¶
В некоторых случаях может понадобиться получить часть информации об элементе: координаты, область, а также текстовые данные, такие как текст или описание. Прочитать информацию об элементе можно следующим методом.
element.info()
Для нашего тестового элемента выводится следующая информация.
>>> info = element.info()
>>> print (info)
bounds { ... }
className: "android.widget.TextView"
clickable: true
enabled: true
focusable: true
packageName: "com.tencent.news"
resourceName: "com.tencent.news:id/btn_first_agree"
text: "\345\220\214\346\204\217"
visibleBounds { ... }
Подсказка
Вы можете заметить, что в выведенной информации отсутствуют некоторые поля, например description. Обычно это означает, что значение поля пустое или равно false; вы по-прежнему можете обращаться к соответствующему атрибуту и получать его значение.
Как видите, эта информация довольно громоздкая — это стандартный формат вывода protobuf. Можно напрямую обращаться к нужному атрибуту и выводить его фактическое значение. Например, чтобы прочитать текст элемента, используйте следующий код.
>>> info = element.info()
>>> print (info.text)
同意
Конечно, в этой информации есть и данные о координатах области элемента, к которым также можно обращаться. Например, если нужно получить область, соответствующую элементу, можно вывести её следующим образом или сохранить в переменную для дальнейшего использования.
>>> info = element.info()
>>> print (info.bounds)
top: 947
left: 338
bottom: 997
right: 743
Выводимое или возвращаемое значение — это информация об области (Bounds). Вы увидите, что этот параметр также используется некоторыми методами создания снимков экрана. Его можно передать в метод снятия скриншота, чтобы сделать отдельный снимок элемента. Однако мы уже подготовили более простой способ.
Возможно, вам также понадобится получить ширину и высоту элемента для расчёта смещений, например относительного смещения других элементов. Можно использовать:
>>> info = element.info()
>>> print (info.bounds.width, info.bounds.height)
484 138
Или получить центр элемента или его углы, например левый верхний, правый нижний и т. д. Следующие методы обычно возвращают объект Point, из которого можно получить соответствующие координаты экрана устройства X и Y.
>>> info = element.info()
>>> print (info.bounds.center())
x: 792
y: 1908
>>> print (info.bounds.center().x)
792
Следующий вызов возвращает координаты угла элемента. В примере получены координаты левого верхнего угла; кроме того, поддерживается получение четырёх углов: bottom-right, top-right, bottom-left и т. д.
>>> info = element.info()
>>> print (info.bounds.corner("top-left"))
x: 550
y: 1839
>>> print (info.bounds.corner("top-left").x)
550
Перебор элементов¶
Вы также можете перебрать все элементы, найденные селектором. Обычно в текущем контексте такой селектор может соответствовать только одному элементу. Если нужно проверить перебор, выберите селектор, соответствующий нескольким элементам. Можно использовать цикл for или другой способ непосредственно на селекторе.
for i in element: print (i.info())
Если вы знаете, что есть несколько подходящих элементов, и хотите получить N-й найденный элемент, используйте следующий способ.
element_3rd = element.get(3)
Подсчёт элементов¶
Обычно этот метод не используется напрямую. Следующий вызов возвращает количество элементов, соответствующих текущему селектору.
>>> element.count()
1
Снимок элемента¶
Поддерживается создание снимков на уровне элемента: можно отдельно захватить изображение элемента без необходимости делать снимок всего экрана и затем обрезать его.
element.screenshot(quality=60)
После создания снимка можно сразу получить его бинарные данные методом getvalue() или передать его непосредственно в PIL.Image.
>>> element.screenshot(quality=60).getvalue()
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xe2\x02(ICC_PROFILE\x00\x01\x01\x00\x00\x02\x18\x00\x00\x00\x00\x02\x10\x00\x00mntrRGB XYZ \x00\x00...
Если дальнейшая обработка не нужна, можно сразу сохранить снимок в локальный файл.
>>> element.screenshot(quality=60).save("image.png")
Ожидание элемента¶
В некоторых случаях нужно определить, завершилась ли загрузка текущей страницы. Обычно это можно сделать, проверив появление нужного элемента. В следующем примере ожидается появление элемента «同意», максимальное время ожидания — 10 секунд.
Подсказка
Здесь время ожидания указывается в миллисекундах, поэтому 10 секунд нужно умножить на 1000: 10 секунд = 10000 миллисекунд.
element.wait_for_exists(10*1000)
>>> element.wait_for_exists(10*1000)
True
Кроме того, поддерживается ожидание исчезновения элемента, то есть ожидание, пока элемент пропадёт с экрана.
element.wait_until_gone(10*1000)
>>> element.wait_until_gone(10*1000)
False
Ввод текста¶
Ввод текста требует внимания. Нельзя ввести текст в кнопку, потому что это кнопка. Теперь выберем элемент поля ввода; его основные данные показаны ниже.

Внимание
При получении элемента поля ввода есть несколько важных моментов. Обратите внимание: при поиске элемента поля ввода клавиатура должна быть открыта, только после этого ищите нужный элемент. Рекомендуется искать внимательно, иначе вы можете получить не настоящее поле ввода.
Подсказка
В автоматизированном сценарии, чтобы клавиатура была открыта, достаточно сначала программно нажать на поле ввода, отображаемое в родительском контейнере.
Для поля ввода выше можно вызвать следующий метод, чтобы ввести строку «你好世界». Можно также ввести английский или любую другую строку Unicode — просто используйте приведённый ниже способ, чтобы ввести текст в поле.
>>> element = d(text="搜索感兴趣的内容")
>>> element.set_text("你好世界")
True
Если нужно получить текст, который сейчас отображается в поле ввода, можно вызвать следующий метод.
Внимание
Обратите внимание: здесь мы изменили селектор. Исходный селектор использовал атрибут text, но после ввода текста содержимое элемента изменилось, и прежний селектор перестал совпадать, поэтому мы взяли другой селектор. Выбор подходящего селектора очень важен, но этот пример приведён лишь для демонстрации.
>>> element = d(className="android.widget.EditText")
>>> element.get_text()
'你好世界'
Также можно очистить текущее содержимое поля. Обычно при вводе текста прежнее содержимое очищается автоматически, но можно очистить его и вручную.
Подсказка
Аналогичного эффекта можно добиться, циклически нажимая клавишу BACKSPACE через интерфейс нажатия клавиш.
>>> element = d(className="android.widget.EditText")
>>> element.clear_text_field( )
True
Примечание
В крайне редких случаях в некоторых местах этот метод ввода текста может работать некорректно; мы работаем над улучшением поддержки.
Обычное смахивание¶
Следующий метод выполняет свайп по экрану, например прокрутку списка вверх и вниз. Приведённый ниже вызов выполняет смахивание вверх. Чем больше значение step, тем медленнее движение; это лучше подходит для свайпов, требующих высокой точности.
Внимание
В простых случаях для этой операции не нужно передавать параметры селектора. Если смахивание не работает, задайте селектор для подходящего элемента, например элемента с атрибутом scrollable или контейнера первого уровня списка.
d().swipe(direction=Direction.DIR_UP, step=32)
>>> element = d(resourceId="com.tencent.news:id/important_list_content")
>>> element.swipe(direction=Direction.DIR_UP, step=32)
True
Быстрое смахивание¶
Быстрое смахивание похоже на быстрое движение пальцем; эта операция быстро прокручивает экран и подходит для имитации быстрого просмотра. В следующем примере экран смахивается сверху вниз. В примере селектор пустой, но вам всё равно нужно решать по ситуации, указывать ли селектор.
d().fling_from_top_to_bottom()
Быстрое смахивание снизу вверх:
d().fling_from_bottom_to_top()
Быстрое смахивание слева направо:
d().fling_from_left_to_right()
Быстрое смахивание справа налево:
d().fling_from_right_to_left()
Внимание
В простых случаях для этой операции не нужно передавать параметры селектора. Если смахивание не работает, задайте селектор для подходящего элемента, например элемента с атрибутом scrollable или контейнера первого уровня списка.
>>> element = d(resourceId="com.tencent.news:id/important_list_content")
>>> element.fling_from_bottom_to_top()
True
Перетаскивание элемента¶
Перетаскивает элемент в позицию другого элемента (например, перемещает значок приложения в папку).
element.drag_to(Selector(text="购物")) # перетащить в позицию целевого элемента
Запросы к дочерним и соседним элементам¶
Для повторяющихся элементов или элементов без явных признаков можно сначала найти родительский контейнер, а затем с помощью child получить дочерний элемент или с помощью sibling — соседний элемент того же уровня, чтобы сузить область поиска.
form = d(resourceId="login_form") # найти родительский контейнер
form.child().get(1) # получить первый дочерний элемент внутри form
form.sibling(textContains="找回密码") # получить соседний с form элемент, текст которого содержит "找回密码"
# Ниже приведён более сложный запрос
# Получить первый результат, соответствующий resourceId=com.example.com:id/resource, выбрать его дочерний узел resourceId=com.example.com:id/abc, затем внутри этого дочернего узла найти элемент, description которого содержит "一天内", и вывести его информацию.
d(resourceId="com.example.com:id/resource").get(0).child(resourceId="com.example.com:id/abc").child(descriptionContains="一天内").info()
Для примера раскладки ниже можно использовать следующий запрос, чтобы точно выбрать этот элемент.
d(resourceId="com.zhiliaoapp.musically:id/bxa").child().get(3).child().get(1).info()
Подсказка
В большинстве случаев такие точные запросы с child и sibling не нужны: достаточно одного вызова d(text="Continue with Google"), за исключением случаев, когда элемент действительно невозможно найти по тексту.
Равномерное смахивание¶
Смахивание с фиксированным шагом step, более механическое, чем swipe; подходит для сценариев, где требуется стабильное пошаговое перемещение.
d().scroll_from_top_to_bottom(step=60) # вниз
d().scroll_from_bottom_to_top(step=60) # вверх
d().scroll_from_left_to_right(step=60) # вправо
d().scroll_from_right_to_left(step=60) # влево