Расширенные операции с интерфейсом¶
В этой главе представлены более глубокие API для автоматизации, с помощью которых вы можете выполнять различные детализированные операции. Содержание этой главы довольно обширно, и если вы сталкиваетесь с этим впервые, мы рекомендуем вам терпеливо прочитать каждый раздел.
Совет
Получение элемента¶
Возможно, вы уже немного знакомы с этим из базовых знаний или предыдущих разделов. Вам необходимо найти соответствующие элементы с помощью селекторов, чтобы выполнять с ними операции. Вы также, вероятно, видели, где можно получить параметры селектора. Далее мы будем подробно рассматривать этот элемент. На изображении справа вы можете видеть информацию об элементе "同意" (Согласиться).

Внимание
Для получения вышеуказанного элемента мы обычно используем 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")
Клик по элементу¶
Вызовите следующий API для выполнения обычного клика по элементу. В данном контексте это будет имитировать ручное нажатие на кнопку "Согласиться".
element.click()
Если вам нужно указать конкретное место для клика на элементе, вы можете указать corner. Corner.COR_CENTER означает клик по центру элемента. Вы также можете кликнуть по его верхнему левому или правому нижнему углу (COR_BOTTOMRIGHT).
element.click_exists(corner=Corner.COR_TOPLEFT)
Выполняет длительное нажатие на элементе. Если элемент не существует, выбрасывается исключение. Этот API также поддерживает corner, но не позволяет указать продолжительность длительного нажатия.
element.long_click()
Кликает по элементу, если он существует. Если элемент не существует, вызов этого API не вызовет исключения. Этот API также поддерживает corner.
element.click_exists()
>>> element.click_exists()
True
Проверка существования¶
Во многих случаях перед выполнением дальнейших действий необходимо проверить существование элемента, иначе последующие шаги могут вызвать ошибку или привести к неверным действиям на неправильном экране. В некоторых ситуациях вам может понадобиться использовать следующий API для проверки существования.
element.exists()
Информация об элементе¶
В некоторых случаях вам может понадобиться получить частичную информацию об элементе, например, его координаты, область, текст или описание. Вы можете использовать следующий API для чтения информации об элементе.
element.info()
Для нашего тестового элемента выше, этот API выведет следующую информацию:
>>> 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 { ... }
Подсказка
Как видите, эта информация выглядит довольно сложной. Это стандартный формат вывода protobuf. Вы можете напрямую получить доступ к соответствующим свойствам, чтобы вывести их фактические значения. Например, чтобы прочитать значение text элемента, вы можете сделать это следующим образом:
>>> info = element.info()
>>> print (info.text)
同意
Конечно, здесь также есть информация о координатах области элемента, к которой вы также можете получить доступ. Например, если вы хотите получить информацию об области элемента, вы можете использовать следующий код для ее вывода или сохранить ее в переменную для дальнейших операций:
>>> info = element.info()
>>> print (info.bounds)
Выводимое или возвращаемое значение — это информация об области (Bound). Позже вы обнаружите, что это тот же параметр, который используется в некоторых API для создания скриншотов. Да, вы можете передать этот параметр в API скриншота, чтобы сделать снимок только этого элемента, но мы уже сделали для вас удобную обертку.
Возможно, вы также захотите получить ширину и высоту элемента для расчета некоторых смещений, например, для расчета смещения относительно других элементов. Вы можете сделать это так:
>>> info = element.info()
>>> print (info.bounds.width, info.bounds.height)
484 138
Или получить центральную точку элемента, или угловые точки, такие как верхний левый и правый нижний углы. Конечно, следующие API обычно возвращают информацию о точке (Point), и вы можете получить из объекта 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)
Или, если вы знаете, что существует несколько совпадающих элементов и хотите получить N-й из них, вы можете использовать следующий API.
element_3rd = element.get(3)
Подсчет элементов¶
Обычно вы не будете использовать этот API напрямую. Следующий вызов позволяет получить количество элементов, соответствующих вашему текущему селектору.
>>> 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 секунд.
Подсказка
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
Ввод текста¶
Ввод текста — это аспект, требующий особого внимания. Мы не можем вводить текст в кнопку "Согласиться", потому что это кнопка. Теперь давайте рассмотрим элемент поля ввода. Основная информация об этом элементе показана ниже.

Внимание
Подсказка
Для вышеуказанного поля ввода мы можем вызвать следующий API, чтобы ввести строку "你好世界" (Привет, мир). Конечно, поддерживается ввод английских или других unicode-строк. Вам просто нужно использовать его, как показано ниже, чтобы ввести текст в поле.
>>> element = d(text="搜索感兴趣的内容")
>>> element.set_text("你好世界")
True
Или, если вам вдруг захотелось получить текущий текст, отображаемый в этом поле ввода, вы можете вызвать его так.
Внимание
>>> element = d(className="android.widget.EditText")
>>> element.get_text()
'你好世界'
Или же очистить текущее введенное содержимое. Обычно ввод текста автоматически очищает предыдущий текст, но вы также можете сделать это вручную.
Подсказка
>>> element = d(className="android.widget.EditText")
>>> element.clear_text_field( )
True
Примечание
Обычный свайп¶
Используйте следующий API для выполнения свайпа по экрану, например, для прокрутки списков вверх и вниз. Следующий вызов выполняет свайп вверх. Параметр step можно настроить самостоятельно; чем он больше, тем медленнее будет свайп, что подходит для свайпов, требующих высокой точности.
Внимание
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
Быстрый свайп (Fling)¶
Быстрый свайп имитирует быстрое движение пальцем по экрану. Эта операция быстро прокручивает экран и подходит для симуляции действий, таких как быстрый просмотр. В следующем примере экран прокручивается сверху вниз. Селектор в примере пуст, но вам все равно нужно решить, заполнять ли его в зависимости от конкретной ситуации.
d().fling_from_top_to_bottom()
Быстрый свайп снизу вверх
d().fling_from_bottom_to_top()
Быстрый свайп слева направо
d().fling_from_left_to_right()
Быстрый свайп справа налево
d().fling_from_right_to_left()
Внимание
>>> element = d(resourceId="com.tencent.news:id/important_list_content")
>>> element.fling_from_bottom_to_top()
True
В процессе обновления...
Другие операции¶
# Перетащить это приложение в папку "Покупки" (измените в соответствии с вашей ситуацией)
element.drag_to(Selector(text="购物"))
#########
# Поиск дочерних и соседних элементов
#########
# Иногда встречаются повторяющиеся элементы или элементы без явных признаков, которые трудно найти
# В этом случае можно сузить область поиска, используя методы поиска дочерних/соседних элементов
# Дочерний элемент, например: в окне входа в чат поля ввода являются дочерними элементами этого окна
# Соседние элементы, например: в окне входа в чат поля для имени пользователя и пароля являются соседними элементами (в обычной ситуации)
form = d(resourceId="login_form")
form.child(index=1)
# Это получит дочерний элемент login_form с индексом 1
form.child(index=1).sibling()
# Таким же образом можно найти соседнюю кнопку "Восстановить пароль"
# (На самом деле, ее можно найти по тексту, так что это необязательно, здесь просто для демонстрации)
form.sibling(textContains="找回密码")
# Они сами являются элементами (element), и вы можете выполнять с ними любые операции для element
# Другое: свайп вниз/влево/вправо/вверх до самого конца
# Поскольку не всегда возможно прокрутить до конца или определить, что конец достигнут
# поэтому параметр max_swipes является обязательным
d().fling_from_top_to_bottom_to_end(max_swipes=32)
d().fling_from_bottom_to_top_to_end(max_swipes=32)
d().fling_from_left_to_right_to_end(max_swipes=32)
d().fling_from_right_to_left_to_end(max_swipes=32)
#########
# scroll: более "механическая" прокрутка
#########
step = 60
max_swipes = 32
# Прокрутить сверху вниз на step шагов
d().scroll_from_top_to_bottom(step)
# Прокрутить снизу вверх на step шагов
d().scroll_from_bottom_to_top(step)
# Прокрутить слева направо на step шагов
d().scroll_from_left_to_right(step)
# Прокрутить справа налево на step шагов
d().scroll_from_right_to_left(step)
# Другое: свайп вниз/влево/вправо/вверх до самого конца
# Аналогично описанию fling выше
d().scroll_from_top_to_bottom_to_end(max_swipes, step)
d().scroll_from_bottom_to_top_to_end(max_swipes, step)
d().scroll_from_left_to_right_to_end(max_swipes, step)
d().scroll_from_right_to_left_to_end(max_swipes, step)