# Operaciones Avanzadas de la Interfaz

Este capítulo le presenta APIs de automatización más profundas, a través de las cuales puede realizar diversas operaciones detalladas. El contenido de este capítulo es extenso; si es la primera vez que lo aborda, le recomendamos que lea cada sección con paciencia.

```{tip}
Al escribir código de automatización, puede introducir directamente el comando `lamda` en el terminal del lado derecho del escritorio remoto y ejecutar el siguiente código de prueba o realizar pruebas de selección o clic de elementos por su cuenta. Esto puede acelerar su proceso de escritura y validación.
```

## Obtener Elemento

Es posible que ya tenga algún conocimiento sobre esto a partir de los conceptos básicos o de secciones anteriores. Necesita encontrar los elementos relevantes a través de un selector para poder operarlos. Probablemente también haya visto dónde obtener los parámetros del selector. Las siguientes introducciones girarán en torno a este elemento. En el lado derecho de esta imagen, puede ver la información relevante del elemento "同意" (Aceptar).

![Elemento de muestra](/assets/images/auto-eyeselect.png)

```{attention}
El elemento en el que hace clic directamente en la interfaz de la izquierda puede no ser el elemento real, ya que podría superponerse en tamaño y posición con otros elementos. Generalmente, cuando varios elementos se superponen en posición y tamaño, también los mostraremos en la barra de información de la derecha. Puede desplazarse hacia arriba y hacia abajo para ver cuál es el que realmente necesita. También puede recorrer manualmente todos los elementos presionando la tecla TAB en la interfaz de selección de la izquierda.
```

Para el elemento anterior, generalmente lo obtenemos a través de `text`. La condición para usar `text` es que no haya otro elemento en la interfaz actual cuyo `text` también sea "同意". Este es el método más simple. En segundo lugar, también puede usar `resourceId`, pero debe tener en cuenta que el `resourceId` aquí no representa un ID único, sino un ID de recurso, y una interfaz puede contener muchos elementos con el mismo ID de recurso. Otros como `packageName`, `checkable`, etc., no se usan comúnmente, pero si `text`, `resourceId`, `description`, etc., no están disponibles, puede intentar usar estos campos. Podemos obtener este elemento de las siguientes maneras.

```python
element = d(text="同意")
element = d(text="同意", resourceId="com.tencent.news:id/btm_first_agree")
element = d(resourceId="com.tencent.news:id/btm_first_agree")
```

## Clic en Elemento

Llame a la siguiente API para realizar una operación de clic normal en un elemento. En el contexto, esto logrará el efecto de hacer clic manualmente en "Aceptar".

```python
element.click()
```

Si necesita especificar la posición del clic en el elemento, puede especificar `corner`. `Corner.COR_CENTER` representa hacer clic en el punto central del elemento. También puede hacer clic en su esquina superior izquierda o inferior derecha (`COR_BOTTOMRIGHT`).

```python
element.click_exists(corner=Corner.COR_TOPLEFT)
```

Realiza una operación de clic largo en este elemento. Si no existe, se lanza una excepción. Esta API también admite `corner`, pero no se puede especificar la duración del clic largo.

```python
element.long_click()
```

Hace clic si el elemento existe. Si el elemento no existe, llamar a esta API no provocará una excepción. Esta API también admite `corner`.

```python
element.click_exists()
```

```python
>>> element.click_exists()
True
```

## Verificar Existencia

En muchas situaciones, antes de realizar operaciones adicionales, es necesario verificar la existencia de un elemento. De lo contrario, los procesos posteriores podrían encontrar excepciones o realizar operaciones incorrectas en la interfaz equivocada. Es posible que necesite usar la siguiente API para verificar la existencia en ciertas situaciones.

```python
element.exists()
```

## Información del Elemento

En algunos casos, es posible que desee obtener parte de la información de un elemento, como sus coordenadas, área, o cadenas de texto como el texto o la descripción que contiene. Puede leer la información del elemento a través de la siguiente API.

```python
element.info()
```

En nuestro elemento de prueba anterior, la información de salida para este elemento de prueba es:

```python
>>> 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 { ... }
```

```{hint}
Es posible que note que a la información impresa como la anterior le faltan algunos campos, como `description`, etc. Esto generalmente significa que el campo tiene un valor vacío o es `false`. Aún puede acceder a los campos correspondientes a través de sus propiedades para obtener su valor.
```

Como puede ver, esta información es relativamente compleja. Este es el formato de impresión predeterminado de protobuf. Puede acceder directamente a las propiedades correspondientes para imprimir su valor real. Por ejemplo, si desea leer el valor del `text` del elemento, puede usarlo como se muestra a continuación.

```python
>>> info = element.info()
>>> print (info.text)
同意
```

Por supuesto, también hay información relacionada con las coordenadas del área del elemento, a la que también puede acceder. Por ejemplo, si ahora desea obtener la información del área correspondiente a este elemento, puede usarlo como se muestra a continuación para imprimir la información del área. También puede obtenerla como una variable para operaciones posteriores.

```python
>>> info = element.info()
>>> print (info.bounds)
```

El valor de salida o devuelto es una información de área (Bound). Más adelante descubrirá que este es un parámetro que también utiliza una API de captura de pantalla. Sí, puede pasar este parámetro a la API de captura de pantalla para tomar una captura de pantalla solo de este elemento, pero ya lo hemos encapsulado para usted.

También es posible que desee obtener el ancho y el alto del elemento para calcular ciertos desplazamientos, como el desplazamiento de otros elementos relativos. Puede hacerlo así:
```python
>>> info = element.info()
>>> print (info.bounds.width, info.bounds.height)
484 138
```

O bien, obtener el punto central del elemento, o puntos de esquina como la esquina superior izquierda o inferior derecha. Por supuesto, las siguientes APIs suelen devolver información de `Point`, y también puede obtener las coordenadas de pantalla del dispositivo en los ejes X e Y correspondientes del objeto `Point`.
```python
>>> info = element.info()
>>> print (info.bounds.center())
x: 792
y: 1908
>>> print (info.bounds.center().x)
792
```

La siguiente llamada se utiliza para obtener las coordenadas de las esquinas del elemento. El ejemplo obtiene las coordenadas de la esquina superior izquierda del elemento. Además, admite la obtención de las coordenadas de las cuatro esquinas, como `bottom-right`, `top-right`, `bottom-left`, etc.

```python
>>> info = element.info()
>>> print (info.bounds.corner("top-left"))
x: 550
y: 1839
>>> print (info.bounds.corner("top-left").x)
550
```

## Recorrer Elementos

También puede recorrer todos los elementos seleccionados por el selector. Normalmente, este elemento puede ser único en el contexto, por lo que si está probando, elija otros selectores para la prueba. Simplemente use un bucle `for` u otro método en el selector para recorrerlos.

```python
for i in element: print (i)
```

O si sabe que existen múltiples elementos coincidentes y desea obtener el N-ésimo elemento coincidente específico, puede usar la siguiente API para obtenerlo.

```python
element_3rd = element.get(3)
```

## Contar Elementos

Normalmente no usará esta API directamente. La siguiente llamada puede obtener el número de elementos que coinciden con su selector actual.

```python
>>> element.count()
1
```

## Captura de Pantalla de Elemento

Admitimos la captura de pantalla a nivel de elemento, lo que le permite capturar la imagen de un elemento individualmente sin necesidad de una captura de pantalla completa y posterior recorte.

```python
element.screenshot(quality=60)
```

Después de tomar la captura de pantalla, puede usar directamente `getvalue` para obtener los datos binarios de la captura, o pasarla directamente a una `PIL Image`.

```python
>>> 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...
```
O si no necesita procesarla más, también puede optar por guardar la captura de pantalla directamente en un archivo local.
```python
>>> element.screenshot(quality=60).save("image.png")
```

## Esperar por Elemento

En algunos casos, es posible que necesite determinar si la página actual ha terminado de cargarse. Generalmente, puede determinar si la página actual ha terminado de cargarse verificando si un elemento relevante ya se muestra. El siguiente ejemplo esperará a que aparezca el elemento "同意", con un tiempo de espera máximo de 10 segundos.

```{hint}
La duración de la espera aquí está en milisegundos, por lo que 10 segundos deben multiplicarse por 1000. 10 segundos = 10000 milisegundos.
```

```python
element.wait_for_exists(10*1000)
```

```python
>>> element.wait_for_exists(10*1000)
True
```

Por supuesto, no solo admitimos la espera a que aparezca un elemento, sino también la espera a que un elemento desaparezca, es decir, hasta que el elemento desaparezca de la interfaz.

```python
element.wait_until_gone(10*1000)
```

```python
>>> element.wait_until_gone(10*1000)
False
```

## Entrada de Texto

La entrada de texto es un área que requiere una atención relativamente especial. No podemos introducir texto en un botón de "Aceptar" porque es un botón. Ahora, tomemos un elemento de campo de entrada para la introducción. La información básica de este elemento es la siguiente.

![Entrada de texto](/assets/images/input-text.png)

```{attention}
Obtener elementos de campo de entrada puede requerir cierta atención. Asegúrese de que, al obtener un elemento de campo de entrada, su método de entrada debe estar en estado **desplegado** antes de buscar el elemento relevante, y se recomienda buscar con cuidado, de lo contrario, lo que obtenga podría no ser el campo de entrada real.
```

```{hint}
En el flujo de un código de automatización, para que el método de entrada esté en estado desplegado, solo necesita escribir código para hacer clic primero en el campo de entrada que se muestra en el nivel superior.
```

Para el campo de entrada anterior, podemos llamar a la siguiente API para introducir la cadena "你好世界" (Hola Mundo) en el campo de entrada. Por supuesto, también admite la entrada de cadenas en inglés u otras cadenas unicode. Solo necesita usarlo como se muestra a continuación para introducir texto en el cuadro.

```python
>>> element = d(text="搜索感兴趣的内容")
>>> element.set_text("你好世界")
True
```

O, si de repente se le ocurre que desea obtener el texto que se muestra actualmente en este campo de entrada, puede llamarlo de esta manera.

```{attention}
Puede ver que aquí hemos cambiado el selector. Nuestro selector inicial se basaba en `text`, pero después de introducir texto, el contenido cambió, por lo que ese elemento ya no existe, y cambiamos a otro selector. Usar el selector adecuado es muy importante, pero como ya lo hemos escrito, lo dejaremos así.
```

```python
>>> element = d(className="android.widget.EditText")
>>> element.get_text()
'你好世界'
```

O para borrar el contenido introducido actualmente. Normalmente, introducir texto borra automáticamente el texto anterior, pero también puede borrarlo manualmente.

```{hint}
De hecho, usar la API de pulsación de teclas en un bucle para presionar la tecla BACKSPACE continuamente también puede lograr un efecto similar.
```

```python
>>> element = d(className="android.widget.EditText")
>>> element.clear_text_field( )
True
```

```{note}
En casos extremos, algunos lugares no pueden usar esta API para introducir texto normalmente. Estamos trabajando para dar soporte a esto.
```

## Deslizamiento Normal

Use la siguiente API para realizar operaciones de deslizamiento en la interfaz, como pasar de página hacia arriba y hacia abajo en una lista. La siguiente llamada implementa un deslizamiento hacia arriba. Ajuste `step` según sea necesario; cuanto mayor sea, más lenta será la velocidad de deslizamiento, lo que es más adecuado para deslizamientos que requieren alta precisión.

```{attention}
Por comodidad, esta operación no requiere proporcionar un parámetro de selector, pero si encuentra que no se puede deslizar, establezca las condiciones del selector en un elemento adecuado, como un elemento con la propiedad `scrollable` o el contenedor de primer nivel de una lista.
```

```python
d().swipe(direction=Direction.DIR_UP, step=32)
```

```python
>>> element = d(resourceId="com.tencent.news:id/important_list_content")
>>> element.swipe(direction=Direction.DIR_UP, step=32)
True
```

| Indicador de Dirección | Descripción                |
|------------------------|----------------------------|
| Direction.DIR_UP       | Deslizar hacia arriba      |
| Direction.DIR_LEFT     | Deslizar hacia la izquierda|
| Direction.DIR_DOWN     | Deslizar hacia abajo       |
| Direction.DIR_RIGHT    | Deslizar hacia la derecha  |

## Deslizamiento Rápido (Fling)

El deslizamiento rápido es similar al comportamiento humano de deslizar rápidamente la pantalla. Esta operación deslizará la pantalla rápidamente, adecuada para simular operaciones como la navegación rápida. El siguiente ejemplo desliza la pantalla de arriba hacia abajo. En el ejemplo, el selector está vacío; aún necesita decidir si completar el selector según la situación real.

```python
d().fling_from_top_to_bottom()
```

Deslizamiento rápido de abajo hacia arriba

```python
d().fling_from_bottom_to_top()
```
Deslizamiento rápido de izquierda a derecha
```python
d().fling_from_left_to_right()
```
Deslizamiento rápido de derecha a izquierda

```python
d().fling_from_right_to_left()
```

```{attention}
Por comodidad, esta operación no requiere proporcionar un parámetro de selector, pero si encuentra que no se puede deslizar, establezca las condiciones del selector en un elemento adecuado, como un elemento con la propiedad `scrollable` o el contenedor de primer nivel de una lista.
```

```python
>>> element = d(resourceId="com.tencent.news:id/important_list_content")
>>> element.fling_from_bottom_to_top()
True
```

Actualizando...

## Otras Operaciones

```python
# Arrastra esta APP a la carpeta "Compras" (modificar según la situación real)
element.drag_to(Selector(text="购物"))

#########
# Encontrar elementos hermanos o hijos
#########
# A veces hay elementos duplicados o sin características distintivas, lo que dificulta su localización
# En estos casos, puedes reducir el rango de búsqueda encontrando elementos hijos/hermanos
# Elemento hijo, por ejemplo: en un cuadro de diálogo de inicio de sesión de chat, los campos de entrada son elementos hijos del cuadro de diálogo.
# Elemento hermano, por ejemplo: los campos de nombre de usuario y contraseña dentro de un formulario de inicio de sesión son elementos hermanos (normalmente).
form = d(resourceId="login_form")
form.child(index=1)
# Esto obtendrá el elemento con índice 1 dentro de login_form
form.child(index=1).sibling()
# También puedes encontrar el botón "Recuperar contraseña", que es hermano de login_form, de esta manera
# (En realidad, ya se puede identificar por el texto, por lo que no es necesario hacerlo así, esto es solo una demostración)
form.sibling(textContains="找回密码")
# Son elementos en sí mismos, puedes realizar cualquier operación de elemento sobre ellos


# Otros, deslizar continuamente hacia abajo/izquierda/derecha/arriba hasta llegar al final
# Como no siempre es posible deslizar hasta el final o detectar que se ha llegado al final
# el parámetro max_swipes es obligatorio
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: un deslizamiento más mecánico
#########
step = 60
max_swipes = 32
# Deslizar hacia abajo step pasos
d().scroll_from_top_to_bottom(step)
# Deslizar hacia arriba step pasos
d().scroll_from_bottom_to_top(step)
# Deslizar hacia la derecha step pasos
d().scroll_from_left_to_right(step)
# Deslizar hacia la izquierda step pasos
d().scroll_from_right_to_left(step)

# Otros, deslizar continuamente hacia abajo/izquierda/derecha/arriba hasta llegar al final
# Igual que en la descripción de fling anterior
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)
```