Saltar a contenido

Automatización de Aplicaciones de Windows con BotCity Inspector

En algunos casos, podemos automatizar aplicaciones de Windows a través de las propiedades de los elementos, como alternativa a la visión por computadora.

Esto puede ser una alternativa útil cuando no queremos preocuparnos por problemas de resolución. Simplemente podemos interactuar con la estructura de la aplicación a través de los atributos de los elementos y las ventanas.

Este tutorial te guiará a través del proceso de crear una automatización simple que interactúa con los elementos de una aplicación de Windows.

Crear un nuevo proyecto

El primer paso es crear un proyecto para una automatización de desktop, puedes seguir los mismos pasos definidos en la sección anterior.

Consulta cómo crear un proyecto de Bot desktop.

Herramientas de espionaje

Los atributos y propiedades de una aplicación de Windows pueden ser visualizados usando herramientas de espionaje.

Actualmente existen diversas herramientas que permiten la inspección de aplicaciones de escritorio, como es el caso de Accessibility Insights for Windows.

Sin embargo, estas herramientas generalmente ofrecen recursos básicos en la inspección de aplicaciones y no necesariamente tienen un enfoque en el desarrollo de automatizaciones, lo que genera un trabajo adicional en la identificación de las propiedades que pueden ser utilizadas en el código al buscar los elementos.

Pensando en este escenario, BotCity ofrece una herramienta centrada en la inspección de aplicaciones Desktop y generación de código Python integrado con el framework de desarrollo, simplificando y haciendo más intuitivo el uso de estos recursos a la hora de construir las automatizaciones.

BotCity Windows Inspector

Para utilizar el BotCity Windows Inspector, solo necesitas instalar el complemento de BotCity Studio para Visual Studio Code.

Después de instalar la extensión y cargar un proyecto de Python, ya tendrás acceso a la herramienta para comenzar a inspeccionar aplicaciones en Windows.

Feature Win Inspector

A continuación verá más detalles sobre el uso de esta función y también buenas prácticas al inspeccionar aplicaciones y generar código.

Interactuando con la aplicación

Para este ejemplo simple, usaremos el Wordpad estándar de Windows.

Con la aplicación abierta y la ayuda del BotCity Windows Inspector, podemos comenzar a mapear los elementos con los que deseamos interactuar.

Start Win Inspector

Mapeando los elementos iniciales

Inicialmente, supongamos que la intención es interactuar con los botones relacionados con la configuración de la fuente del texto.

Podemos, por ejemplo, mapear los botones de Negrita, Cursiva y Subrayado del Wordpad, además del botón para centrar el texto.

Wordpad Inspection

Consejo

Para mapear cualquier elemento utilizando el Windows Inspector, basta con posicionar el mouse en la región del elemento y hacer clic con el botón izquierdo del mouse, o simplemente utilizar el atajo de teclado: Ctrl+Alt+C.

Recursos del Windows Inspector

Elementos Mapeados:

Cada elemento que se mapea en el contexto de una aplicación, se añade a la lista de elementos mapeados.

A partir de esta lista, puedes hacer clic en un elemento para visualizar sus propiedades, o eliminar un elemento con el que ya no deseas interactuar.

Win Inspector - Mapped Elements

Árbol de Inspección:

Al hacer clic en un elemento de la lista, se generará el árbol de inspección correspondiente.

De esta manera, es posible visualizar cuál es el "camino completo" desde la ventana principal de la aplicación hasta el elemento objetivo que ha sido mapeado.

Win Inspector - Inspection Tree

Tabla de Propiedades:

Además del árbol de inspección, el BotCity Windows Inspector también muestra todas las propiedades existentes de un elemento que ha sido mapeado.

Los valores de esta tabla pueden ser utilizados como selectores en el código al buscar estos elementos.

Win Inspector - Properties Table

Consejo

Es posible visualizar la tabla de propiedades de cada elemento existente en el árbol de inspección.

Solo tienes que hacer clic en el elemento deseado para que se muestre la tabla de propiedades correspondiente.

Continuando el mapeo

Antes de entrar en la etapa de generación de código, vamos a mapear algunos elementos más.

Volviendo a Wordpad, podemos hacer clic nuevamente en el botón de Start del Windows Inspector para continuar con el mapeo.

Esta vez, vamos a mapear, por ejemplo, la región del documento donde se inserta el texto y también el botón para guardar el documento.

Win Inspector - Continue Inspection

Generando código a partir de los elementos mapeados

Con los elementos iniciales mapeados, ya es posible generar el código Python que se utilizará como base de la automatización.

En esta sección, podemos definir cuál será el Backend utilizado para esta aplicación y también los fragmentos de código adecuados para el contexto actual.

Win Inspector - Code

  • Generar código para lanzar la aplicación: Genera el fragmento de código responsable de lanzar la aplicación que se está mapeando (asumiendo que cada vez que se ejecute la automatización, la aplicación se iniciará desde cero).
  • Generar código para conectarse a la aplicación: Genera el fragmento de código responsable de establecer la conexión con la instancia de la aplicación abierta. Esta opción puede desmarcarse si ya tienes ese fragmento de código en tu automatización y solo estás mapeando nuevos elementos.
  • Generar código solo para elementos mapeados: Genera el código utilizando los selectores para cada elemento existente en la lista de elementos mapeados. Esta opción no considera la referencia de los elementos padres en el código generado, por lo que puede ser necesario ajustar los parámetros de acuerdo con tu necesidad.
  • Generar código para elementos mapeados + padres: Genera el código utilizando los selectores para cada elemento existente en la lista de elementos mapeados considerando los elementos padres. Esta opción es la más indicada si deseas que el Inspector genere un código lo más cercano a la versión final.

Después de la configuración del código que será generado, solo tienes que hacer clic en el botón Generate Code para que el BotCity Windows Inspector genere el código directamente en el archivo .py.

Win Inspector - Generated Code

Información

El código Python generado por Windows Inspector se basa en Framework Desktop de BotCity.

Si está utilizando otras bibliotecas juntas, asegúrese de realizar los ajustes necesarios en su código de automatización.

Salvando el documento y finalizando el proceso

Como último paso de la automatización, vamos a mapear la ventana que se abre al hacer clic en el botón "Guardar". Podemos continuar utilizando exactamente la misma estrategia que usamos anteriormente.

En este caso, en lugar de mapear un elemento específico de la ventana de "Guardar como", podemos mapear solamente la región de la ventana principal en sí.

En el código, después utilizaremos una estrategia para insertar la ruta y guardar el documento usando esta referencia de la ventana.

Consejo

Puedes utilizar el botón Reset del Windows Inspector siempre que quieras limpiar los elementos mapeados y reiniciar la inspección.

Recuerda generar el código antes de reiniciar el Windows Inspector, para evitar que pierdas la referencia de elementos ya mapeados.

Win Inspector - Inspección Final

Como la base del código ya fue generada anteriormente, podemos dejar marcada solamente la opción para generar el código referente al nuevo elemento mapeado.

Ajustes finales y ejecución del código

Con el código base generado por el BotCity Windows Inspector, podemos refactorizar y realizar los ajustes que sean necesarios para el proceso.

En este caso, simplemente incluiremos en el código el contenido que se insertará en el documento y también la ruta para guardar el archivo al final.

Insertando un contenido de texto en el área del documento:

Podemos simplemente ajustar el método type_keys del elemento referente al documento, pasando como parámetro una cadena con el contenido.

Identifica en tu código el fragmento donde se está manipulando este elemento y haz la siguiente modificación:

...
target_element = bot.find_app_element(waiting_time=10000, from_parent_window=main_window, auto_id="59648", class_name="RICHEDIT50W", control_type="Document")
## Write content in the edit
target_element.type_keys("Hello! Welcome to BotCity!", with_spaces=True)
## Get the text of the edit
# target_element.text_block()

Pasando la ruta para guardar el archivo:

Para guardar el archivo, utilizaremos una estrategia bastante interesante usando la ventana de "Guardar como" que mapeamos anteriormente.

Con la referencia de la ventana, podemos usar nuevamente el type_keys tanto para insertar la ruta del archivo como para ejecutar una acción de Enter en la ventana.

Identifica en tu código el fragmento donde se está manipulando este elemento referente a la ventana de "Guardar como" y haz la siguiente modificación:

...
target_element = bot.find_app_element( waiting_time=10000, from_parent_window=main_window, best_match="Save As", class_name="#32770", control_type="Window")
## Set the focus to this element
target_element.set_focus()
target_element.type_keys(r"C:\Users\Administrator\Documents\document.rtf")
target_element.type_keys("{ENTER}")

Consejo

Esta estrategia para ejecutar la acción de una tecla específica o un atajo de teclado en el contexto de una ventana es un recurso de la biblioteca pywinauto y puede ser bastante útil dependiendo de tu caso de uso.

Consulta más detalles sobre algunas estrategias que se pueden utilizar y otros consejos útiles en la sección Explorando el código generado por el Inspector.

Explorando el código generado por el Inspector

Al ejecutar el código final después de los ajustes, el bot realizará automáticamente las siguientes acciones:

  • Abrir Wordpad
  • Establecer la conexión con la aplicación
  • Realizar las interacciones definidas: centrar el párrafo, hacer clic en los botones de Negrita, Cursiva y Subrayado
  • Escribir el texto en el área del documento y guardar el archivo en la ruta definida

Win Inspector - Final Code

Como se mencionó anteriormente, el código generado por el Windows Inspector utiliza el módulo Windows Applications del framework de desarrollo desktop de BotCity.

Info

El módulo Windows Applications se basa en las funcionalidades de la biblioteca pywinauto.

Consulta más detalles sobre los recursos de esta biblioteca en este enlace.

Por defecto, el BotCity Windows Inspector siempre intentará generar un código lo más completo posible. Sin embargo, pueden ser necesarios ajustes y tratamientos para garantizar que el proceso funcione como se espera.

Buscando elementos

El Windows Inspector utiliza algunos selectores clave en el código que se genera para buscar un elemento determinado.

La tabla a continuación describe mejor algunos selectores que se pueden utilizar:

Selector Descripción
class_name Elementos con esta clase de ventana
class_name_re Elementos cuya clase coincide con esta expresión regular
parent Elementos que son hijos de este elemento
process Elementos en ejecución en este proceso
title Elementos con este texto
title_re Elementos cuyo texto coincide con esta expresión regular
top_level_only Solo elementos de nivel superior (valor predeterminado=True)
visible_only Solo elementos visibles (valor predeterminado=True)
enabled_only Solo elementos habilitados (valor predeterminado=False)
best_match Elementos con un título similar a este
handle El identificador del elemento a devolver
ctrl_index El índice del elemento secundario a devolver
found_index El índice del elemento secundario filtrado a devolver
predicate_func Un gancho proporcionado por el usuario para una validación personalizada del elemento
active_only Solo elementos activos (valor predeterminado=False)
control_id Elementos con este ID de control
control_type Elementos con este tipo de control (cadena; para elementos de UIAutomation)
auto_id Elementos con este ID de automatización (para elementos de UIAutomation)
framework_id Elementos con este ID de marco (para elementos de UIAutomation)
backend Nombre del backend a utilizar durante la búsqueda (el valor predeterminado None significa el backend activo actual)

Si deseas reemplazar un parámetro que fue generado por el Inspector, o si quieres hacer una combinación más compleja, simplemente usa la tabla de propiedades como referencia para obtener la información de cada elemento.

Win Inspector - Selectores

Con estos valores a mano, solo tienes que pasarlos como parámetro al método find_app_element.

target_element = bot.find_app_element(
    waiting_time=10000,
    from_parent_window=None,
    auto_id="59648",
    class_name="RICHEDIT50W",
    control_type="Document"
)
## Reemplazando 'auto_id' por el 'title' del elemento y eliminando 'class_name'
## También sería posible utilizarlos juntos, si fuera necesario
target_element = bot.find_app_element(
    waiting_time=10000,
    from_parent_window=None,
    title="Rich Text Window",
    control_type="Document"
)

Otra alternativa interesante es acceder a un determinado elemento por su índice, en relación con su elemento padre.

Este tipo de enfoque resulta muy útil cuando no hay propiedades únicas al filtrar por un elemento, o cuando varios elementos iguales comparten las mismas propiedades.

Win Inspector - Elementos Genéricos

En el escenario anterior, tenemos varios elementos del tipo 'Edit' con propiedades compartidas y sin identificadores únicos.

Esto dificultaría la búsqueda de un elemento específico, ya que varios elementos podrían coincidir con el mismo filtro.

# Este código acabaría siendo genérico
# ya que no estaría filtrando un elemento específico
target_element = bot.find_app_element(
    waiting_time=10000,
    from_parent_window=parent_form,
    class_name="ThunderRT6TextBox",
    control_type="Edit"
)

En este escenario, podríamos usar como referencia el elemento padre y, a partir de él, acceder al elemento objetivo a través de su índice.

Win Inspector - Control Index

# Con la referencia del elemento padre,
# podemos acceder a un elemento por su índice
target_element = parent_form.Edit3
target_element.type_keys("", with_spaces=True)

Consejo

Si estás observando algún problema al encontrar un elemento, o si deseas hacer un tratamiento más elaborado, siéntete libre de personalizar los selectores y la lógica del código generado por el Windows Inspector en cualquier momento.

Visualizando las estructuras de la aplicación en el código

Si por alguna razón necesitas visualizar más a fondo la estructura de alguna ventana o elemento específico de la aplicación directamente en el código, puedes utilizar métodos específicos de pywinauto.

Con la referencia del elemento en mano, solo tienes que llamar al método dump_tree() o print_control_identifiers() para que la estructura del elemento se muestre en la terminal.

Esta alternativa es bastante interesante si estás teniendo problemas al buscar elementos específicos y quieres hacer un debug.

# Obteniendo la referencia del elemento,
# por ejemplo, la ventana principal de la aplicación
main_window = current_app.top_window()
# Mostrando la estructura del componente directamente en la terminal
main_window.dump_tree()
Ejemplo de la salida del método dump_tree()

A través del árbol generado, podrás visualizar de forma más detallada la estructura de la aplicación y los identificadores de los elementos.

Control Identifiers:

Dialog - 'Document - WordPad'    (L48, T42, R821, B918)
['Document - WordPadDialog', 'Dialog', 'Document - WordPad']
child_window(title="Document - WordPad", control_type="Window")
| 
| Pane - 'UIRibbonDockTop'    (L56, T73, R813, B189)
| ['UIRibbonDockTop', 'Pane', 'UIRibbonDockTopPane', 'Pane0', 'Pane1']
| child_window(title="UIRibbonDockTop", control_type="Pane")
|    | 
|    | Pane - 'Ribbon'    (L56, T73, R813, B189)
|    | ['RibbonPane', 'Ribbon', 'Pane2', 'RibbonPane0', 'RibbonPane1', 'Ribbon0', 'Ribbon1']
|    | child_window(title="Ribbon", control_type="Pane")
|    |    | 
|    |    | Pane - 'Ribbon'    (L56, T73, R813, B189)
|    |    | ['RibbonPane2', 'Ribbon2', 'Pane3']
|    |    | child_window(title="Ribbon", control_type="Pane")
|    |    |    | 
|    |    |    | Pane - ''    (L56, T73, R813, B220)
|    |    |    | ['Pane4']
|    |    |    |    | 
|    |    |    |    | Pane - 'Ribbon'    (L56, T42, R813, B189)
|    |    |    |    | ['RibbonPane3', 'Ribbon3', 'Pane5']
|    |    |    |    | child_window(title="Ribbon", control_type="Pane")
|    |    |    |    |    | 
|    |    |    |    |    | Toolbar - 'Quick Access'    (L94, T47, R160, B69)
|    |    |    |    |    | ['Toolbar', 'Quick AccessToolbar', 'Quick Access', 'Toolbar0', 'Toolbar1']
|    |    |    |    |    | child_window(title="Quick Access", control_type="ToolBar")
|    |    |    |    |    |    | 
|    |    |    |    |    |    | Button - 'Save'    (L94, T45, R116, B69)
|    |    |    |    |    |    | ['Save', 'Button', 'SaveButton', 'Button0', 'Button1']
|    |    |    |    |    |    | child_window(title="Save", control_type="Button")
|    |    |    |    |    |    | 
|    |    |    |    |    |    | Button - 'Undo'    (L116, T45, R138, B69)
|    |    |    |    |    |    | ['Button2', 'UndoButton', 'Undo']
|    |    |    |    |    |    | child_window(title="Undo", control_type="Button")
|    |    |    |    |    |    | 
|    |    |    |    |    |    | Button - 'Redo'    (L138, T45, R160, B69)
|    |    |    |    |    |    | ['Redo', 'Button3', 'RedoButton']
|    |    |    |    |    |    | child_window(title="Redo", control_type="Button")

...

Realizando acciones con los elementos

Para determinados tipos de elementos, como botones y campos de texto, el Windows Inspector también generará junto con el código algunas sugerencias de acciones que se pueden realizar con ese elemento.

## Realizar una acción de clic por defecto en el botón
target_element.click()
## Realizar una acción de clic con un movimiento del mouse en el botón
target_element.click_input()
## Escribir contenido en el campo de edición
target_element.type_keys("", with_spaces=True) 
## Obtener el texto del campo de edición
target_element.text_block()
## Seleccionar un ítem de la caja combinada; el ítem puede ser un índice basado en 0 o la cadena que deseas seleccionar
target_element.select()
## Devolver el texto del ítem seleccionado de la caja combinada
target_element.selected_text()
## Devolver el texto de todos los ítems en la caja combinada
target_element.texts()
## Establecer el foco en este elemento.
target_element.set_focus()
## Maximizar la ventana
target_element.maximize()
## Cerrar la ventana
target_element.close()
## Seleccionar un ítem del menú utilizando la ruta al ítem
target_element.item_by_path("{Nombre del ítem} -> {Nombre del ítem} -> ...", exact=True).select()
## Devolver todos los ítems del menú
target_element.items()

Consejo

Si no estás muy seguro del tipo de acción que necesitas realizar, o si estás tratando con elementos más complejos, consulta la documentación de pywinauto para más detalles sobre los métodos disponibles para cada tipo diferente de elemento.

Como vimos anteriormente, también es posible realizar la acción de una tecla o atajo de teclado directamente en el contexto de un elemento.

Teniendo la referencia del elemento objetivo, podemos utilizar el método type_keys o send_keys con las siguientes combinaciones:

En el contexto de pywinauto, la tecla CTRL puede ser simbolizada por: ^.

Ejemplos:

# Ejecuta el atajo Ctrl + A en el contexto del elemento
target_element.type_keys("^a")
# Ejecuta el atajo Ctrl + C en el contexto del elemento
target_element.type_keys("^c")

En el contexto de pywinauto, la tecla ALT puede ser simbolizada por: %.

Ejemplos:

# Ejecuta el atajo Alt + F4 en el contexto del elemento
target_element.type_keys('%{F4}')
# Ejecuta el atajo Alt + F + S en el contexto del elemento
target_element.type_keys("%{f}{s}")
# Presiona la tecla ENTER en el contexto del elemento
target_element.type_keys('{ENTER}')
# Presiona la tecla TAB en el contexto del elemento
target_element.type_keys('{TAB}')
# Presiona la tecla SHIFT en el contexto del elemento
target_element.type_keys('{VK_SHIFT}')

Consejo

Accede a la referencia de la documentación de pywinauto para más detalles sobre acciones con el teclado.

Buenas prácticas en el uso del BotCity Windows Inspector

A continuación, enumeramos algunos consejos y puntos importantes que pueden facilitar el uso del Windows Inspector durante el desarrollo de tus automatizaciones.

Asegúrate de que esta es la mejor aproximación para tu aplicación

El funcionamiento del BotCity Windows Inspector está directamente relacionado con el comportamiento de la aplicación de escritorio que se está automatizando.

Por lo tanto, es importante identificar si esta estrategia realmente es la mejor para tu caso de uso.

Si la aplicación que estás automatizando no muestra mucha información sobre los elementos o el comportamiento no permite este tipo de interacción, considera otras opciones, como por ejemplo el uso de visión computacional.

Haz el mapeo y prueba tu código por partes

Para garantizar que las referencias de los elementos no se pierdan al cambiar el contexto, intenta mapear pequeños conjuntos de elementos y generar el código correspondiente antes de cerrar la aplicación o avanzar a una etapa subsiguiente del proceso.

Es importante que también valides en el código si la conexión con la aplicación se está realizando de la forma adecuada (recuerda utilizar la tecnología de Backend compatible con tu aplicación) y si los elementos se están encontrando correctamente.

Haciendo esto, garantizas que al final del proceso todas las acciones se están llevando a cabo como se espera.

Valida las interacciones que los elementos mapeados aceptan

En algunos casos, la interacción con un determinado elemento puede realizarse de diversas maneras.

Dependiendo de la aplicación, la acción de un botón puede llevarse a cabo mediante un click() o incluso realizando un atajo de teclado usando type_keys().

Si estás teniendo dificultades al usar una determinada acción, recuerda probar otras opciones disponibles y validar cuál funciona mejor en tu contexto.

Si tienes dudas o quieres ver más sobre este tipo de contenido, siéntete libre de explorar los canales de la comunidad BotCity.