Última versión: agosto de 2021 para NVDA 2021.2


ÍNDICE

Nota para usuarios de lectores de pantalla: para volver a este índice, usa tu orden del modo exploración para moverte al encabezado de nivel 1 en dirección contraria.


Esta guía fue escrita originalmente y es mantenida principalmente por Joseph Lee (@josephsl), y está diseñada por la comunidad de usuarios y desarrolladores de NVDA. Luke Davis (@XLTechie) a veces ejerce como editor.

Las valiosas contribuciones y correcciones de la comunidad son bienvenidas.

NVDA es copyright 2006-2021 NV Access Limited. Microsoft Windows, Microsoft Office, Win32 API y otros productos de Microsoft son propiedad de Microsoft Corporation. el paquete IAccessible es propiedad intelectual de IBM y la Fundación Linux. Python es propiedad intelectual de la Python Software Foundation. Otros productos mencionados son propiedad intelectual de sus respectivos propietarios.

Introducción

Bienvenido a la Guía de desarrollo de complementos para NVDA. Esta es la guía completa sobre cómo desarrollar complementos de NVDA. Esta guía también explica algún segmento de código útil del código fuente del núcleo de NVDA, que resalta conceptos para ti mientras aprendes a escribir complementos.

Para obtener más información sobre el desarrollo de NVDA, por favor visita [la Página de GitHub de NVDA]. Asegúrate de revisar la Guía del desarrollador de NVDA para familiarizarte con los términos clave y los conceptos básicos para comenzar con el desarrollo de complementos.

Audiencia

Esta guía se ha diseñado para:

  • aprendices de Python
  • principiantes en el desarrollo de NVDA
  • desarrolladores Expertos de Python
  • desarrolladores Expertos de NVDA
  • Gente familiarizada con otros lenguajes de programación distintos a Python.
  • Desarrolladores de scripts para otros lectores de pantalla.

Si eres nuevo en el desarrollo de complementos o del núcleo de NVDA, te recomendamos que conozcas Python primero, ya que proporciona los conocimientos de programación necesarios para entender el resto de la guía. Si eres programador de Python pero eres nuevo en el desarrollo de NVDA, por favor, consulta la guía del desarrollador de NVDA y el documento descripción del diseño.

Nota especial sobre la versión de Python

Hasta 2019, NVDA y los complementos estaban escritos principalmente en Python 2, específicamente 2.7. A partir de julio de 2019, NVDA ha sido migrado a Python 3.7, con algunos desarrolladores de complementos modificando su código fuente para que se ejecute en Python 2 y 3. Con la publicación de NVDA 2019.3 en febrero de 2020, la transición a Python 3 se dio por completada. Asegúrate de estar atento al desarrollo de NVDA y a las listas de correo de complementos, así como a la documentación de desarrollo relevante y a los avisos relacionados con las noticias sobre Python 3 y NVDA. Para más información sobre las diferencias entre Python 2 y 3, consulta el apéndice E.

Esta guía usará estrictamente código de Python 3.

Nota especial sobre la compatibilidad hacia atrás de NVDA

Para modernizar el código fuente de NVDA y reaccionar a los cambios del uso que se hace del lector de pantalla, NV Access adoptó una política anual de compatibilidad hacia atrás en 2020. Para cada año del calendario, se designa la primera versión principal (año.1) como versión «incompatible hacia atrás», en la que se incorporan cambios que afectarán a los complementos. Se incluyen cambios en los nombres de funciones y clases, así como la eliminación de código obsoleto. Como estos cambios afectarán a muchos complementos, los desarrolladores de complementos deben probar su compatibilidad tan pronto como se libere la primera beta de la versión principal de NVDA incompatible hacia atrás.

Lista de versiones de NVDA incompatibles hacia atrás y cambios destacables:

  • 2019.3: Python 3
  • 2021.1: actualización de dependencias
  • 2022.1: refactorización de tipos de controles

A menos que se indique lo contrario, esta guía asumirá la versión de NVDA más reciente incompatible hacia atrás al proporcionar ejemplos de código (a fecha de agosto de 2021, se asume 2021.1). Se documentarán las excepciones en los sitios adecuados.

Una nota especial para creadores de scripts de otros lectores de pantalla

Algunos de los conceptos descritos en este documento son los mismos para diferentes lectores de pantalla, tales como objetos, ventanas, eventos, APIs de accesibilidad y así sucesivamente. Sin embargo, hay cosas importantes que hay que tener en cuenta al escribir o portar scripts:

  • A diferencia de algunos lectores de pantalla, NVDA no tiene una especificación formal o un modelo de objeto definido por la documentación en otros lectores de pantalla.
  • El código que escribas se ejecutará dentro del mismo entorno de ejecución que el propio lector de pantalla, por lo que puedes realizar cosas como obtener directamente objetos enfocados, modificar la funcionalidad de NVDA e incluso reemplazar las funciones y clases de NVDA con las tuyas propias.
  • Python, y consecuentemente NVDA, es un sistema orientado a objetos. En otras palabras, la mayor parte de su código consistirá en definir clases y objetos que NVDA recogerá en tiempo de ejecución.
  • A diferencia de los motores de scripting para algunos lectores de pantalla, no hay ningún hack especial involucrado cuando se desee proporcionar una característica que funcione en todas las aplicaciones.
  • A diferencia de los motores de scripting para algunos lectores de pantalla, no estás limitado a bibliotecas que vengan con los lectores de pantalla; como un programa basado en Python, puedes usar cualquier módulo (s) de python que se adapte a tus necesidades, incluyendo módulos externos. Por ejemplo, un módulo popular utilizado para interactuar con aplicaciones web es el módulo JSON (JavaScript Object Notation), que no está incluido en las versiones de NVDA anteriores a 2017.3. Necesitas incorporar las bibliotecas externas de Python tú mismo si deseas usar estos paquetes.

Nota especial sobre la versión de NVDA para la tienda de Windows

A partir de NVDA 2018.1, se han establecido cambios para permitir a NVDA ejecutarse como una aplicación de la tienda Windows. Una vez se publique en la tienda la versión de NVDA para la tienda de Windows, los usuarios de Windows 10 podrán ir a la tienda y obtener NVDA. Sin embargo, hay restricciones que vienen con esta versión de NVDA, la más notable es que los complementos no pueden ejecutarse en este entorno. Por lo tanto, si necesitas ejecutar o escribir complementos de NVDA, deberás utilizar la versión clásica de escritorio de este lector, disponible en la sección de descargas de nuestro sitio web.

Nota muy importante sobre la migración de código personalizado de extensión a development scratchpad

Si vienes de NVDA 2018.4 o versiones anteriores, recordarás que se podían ejecutar módulos de extensión (a veces llamados «extensiones») desde el interior de carpetas situadas en el directorio de configuración de usuario (por ejemplo, appModules). Esta funcionalidad se ha rediseñado en NVDA 2019.1 de la siguiente manera:

  1. NVDA ya no cargará código de extensiones alojado en los siguientes subdirectorios de la carpeta de configuración de usuario: appModules, brailleDisplayDrivers, globalPlugins y synthDrivers.
    • Si descubres que el código de estas carpetas no está funcionando desde que tienes NVDA 2019.1, la razón anterior explica por qué.
  2. Debes activar la funcionalidad development scratchpad (reservada para desarrolladores) si quieres habilitar la carga de código de extensión personalizado. Para hacerlo:
    1. Teniendo NVDA 2019.1 (o posterior) en ejecución, ve al menú NVDA / Preferencias / Opciones / Avanzado.
    2. Debes marcar la casilla «Comprendo que cambiar estas opciones puede hacer que NVDA funcione incorrectamente».
    3. Debes marcar la casilla «Habilitar la carga de código personalizado desde el directorio Developer Scratchpad».
    4. Pulsa Aceptar.
  3. Debes almacenar el código que estaba previamente alojado en la lista anterior de subcarpetas dentro de los directorios correspondientes de la carpeta scratchpad.
  4. Si necesitas usar NVDA 2018.4 y quisieras utilizar código personalizado, no elimines las subcarpetas del directorio de configuración de usuario. En cualquier otro caso, elimina las carpetas que se mencionan en el elemento 1 de esta lista.

Nota muy importante sobre los cambios del módulo controlTypes en NVDA 2021.2

NVDA 2021.2 introduce la refactorización de tipos de controles, que cambia cómo se indican los roles y estados de los controles. En versiones de NVDA más antiguas, los roles de controles se escriben como controlTypes.ROLE_, y los estados se escriben como controlTypes.STATE_. Con la refactorización de tipos de controles, se deben escribir los roles y los estados como controlTypes.Role. y controlTypes.State., respectivamente. Por ejemplo:

  • Rol de texto editable:
    • 2021.1 y anteriores: controlTypes.ROLE_EDITABLETEXT
    • 2021.2 y posteriores: controlTypes.Role.EDITABLETEXT
  • Estado verificable:
    • 2021.1 y anteriores: controlTypes.STATE_CHECKABLE
    • 2021.2 y posteriores: controlTypes.State.CHECKABLE

La forma anterior de indicar roles y estados de controles se eliminará en NVDA 2022.1. A menos que se indique lo contrario, esta guía empleará el nuevo estilo.

Conceptos básicos sobre complementos

¿Qué son los Complementos?

Los complementos son pequeños programas adicionales que extienden la funcionalidad de NVDA o su soporte para aplicaciones. Esto puede incluir la adición de características globales, la mejora
del soporte para una aplicación, o la adición de soporte para pantallas Braille nuevas o sintetizadores de voz, o la mejora del uso visual de NVDA mediante potenciadores de la visión.

Un complemento completamente construido constará del propio código Python del complemento, contenido en uno o más módulos (más sobre esto en la próxima sección), y
normalmente algo de documentación, y otros archivos de soporte. Si esto suena desalentador, no te preocupes: empezaremos con muy poco, ejemplos, y sólo con código
Python. El resto de estructura de soporte de los complementos vendrá más adelante.

Nota: en ocasiones a los complementos también se los conoce como «extensiones», especialmente en la guía del desarrollador de NVDA. Aunque parece que son lo mismo, estos términos son diferentes:

  • Complemento: uno o más módulos o componentes empaquetados en un archivo para facilitar su instalación.
  • Extensión: módulo o colección de módulos diseñados para modificar el comportamiento de NVDA de diversas formas.

A lo largo de esta guía, cuando hagamos referencia a «complementos», puedes asumir que contienen al menos un componente o extensión.

¿Qué son los módulos de un complemento?

Los complementos pueden actuar globalmente (a través de todo NVDA), en un programa o aplicación específica, tras el escenario (a nivel del hardware), o mejorar la experiencia con NVDA para público con visión. Llamamos a estas cuatro
áreas principales «módulos». Cada complemento contiene al menos un módulo, consistente en uno o más archivos Python diseñados para actuar en una de estas cuatro áreas
específicas. Además, si tiene sentido para el complemento que estás desarrollando, tu complemento puede incluir más de un módulo. Por ejemplo, si tu complemento
proporciona accesibilidad mejorada en una aplicación específica, pero también proporciona órdenes globales que funcionan en cualquier parte con NVDA, estarías utilizando dos módulos.

Actualmente, NVDA soporta estos tipos de módulos adicionales:

  • Extensión global: una extensión global añade características para NVDA que se pueden utilizar en cualquier lugar, como la función de OCR.
  • Módulo de aplicación: Un módulo de aplicación permite un soporte mejorado para un programa específico. Los módulos de aplicación sólo se ejecutan mientras el programa se ejecute. Cambian la forma en que NVDA reacciona a las ventanas y controles de la aplicación en ejecución.
  • Controlador: Un controlador permite a un programa comunicarse con el hardware, y en algunas ocasiones, con otro software. Actualmente puedes escribir controladores para nuevas pantallas braille o sintetizadores de voz.
  • Potenciador: un potenciador se usa para mejorar la experiencia con NVDA a distintos grupos de usuarios que utilizan ordenadores. Actualmente sólo se soporta un tipo de potenciador, «proveedor de mejoras de visión».

¿Qué son los paquetes de complementos?

Un paquete es el archivo único que contiene el código, la documentación, y otros elementos que componen un complemento totalmente operativo, que está
pensado para ser suficientemente robusto para distribuirse al público. Cada paquete de complementos de NVDA es un archivo zip normal con una extensión de archivo .nvda-addon en lugar de .zip.

Si hacer un paquete de complemento te suena a un montón de trabajo: no te preocupes, no tienes que hacer un paquete simplemente para empezar a escribir y probar tu primer
complemento.

Instalación de Complementos de NVDA

Puedes instalar paquetes de complemento de NVDA mediante el Administrador de complementos, situado en el menú Herramientas de NVDA. De forma alternativa, puedes abrir cualquier archivo .nvda-addon
que hayas creado o descargado seleccionándolo en tu gestor de archivos de Windows, y debería lanzarse el proceso de instalación del complemento.

Configuración del entorno de desarrollo de complementos

Sigue estos pasos para preparar tu ordenador para escribir complementos de NVDA.

Requisitos del Sistema

Para crear un complemento para NVDA, asegúrate de que tu sistema cumple los siguientes requisitos:

  • NVDA:
    • Una versión de NVDA está disponible en tu ordenador (funcionará ya sea una versión portable o instalada, pero te recomendamos encarecidamente que instales una copia de NVDA en tu ordenador de desarrollo). Descarga NVDA desde la página de descargas de NV Access.
    • O incluso mejor: recomendamos instalar la última versión master (Alpha) o beta de desarrollo para mantenerse al día con los cambios en la API del núcleo. Puedes descargar las últimas versiones de desarrollo en https://www.nvaccess.org/files/nvda/snapshots/.
  • Python:
    • Series de Python 3.7, versión 3.7.9 de 32 bits para Windows: https://www.python.org/downloads/release/python-379/
    • Aunque la comunidad de complementos entiende que Python 2 podría ser necesario para dar compatibilidad hacia atrás, no recomendamos usarlo en entornos de producción.
  • SCons 3, versión 3.1.2 o posterior para generar paquetes de complementos: http://www.scons.org/
  • Markdown 2.0.1 o posterior para generar la documentación del complemento: https://pypi.python.org/pypi/Markdown/2.0.1
  • El paquete Gettext de GNU para Windows para soporte de traducción de mensajes. La compilación se puede encontrar en: http://gnuwin32.sourceforge.net/downlinks/gettext.php
    • Una vez descargados, copia ambos archivos exe en la carpeta de desarrollo del complemento. Consulta la siguiente sección para obtener una descripción de la estructura de carpetas del complemento.
      • msgfmt.exe
      • xgettext.exe
  • Si estás desarrollando soporte para un programa, sintetizador de voz o pantalla braille, instala el software y el hardware necesario.
  • Elementos opcionales:

Nota: si utilizas Windows 10 Anniversary Update o posterior y deseas usar Ubuntu en Windows (también conocido como Windows Subsystem for Linux), Python ya viene instalado. Puedes utilizar Advanced Packaging Tool (APT) para obtener SCons y Gettext. A continuación, puedes utilizar pip para descargar e instalar Markdown.

Estructura de Carpetas de Desarrollo del Complemento

Cuando escribas complementos, se recomienda que guardes el código del complemento en carpetas separadas, una por complemento. Si decides descargar la plantilla del complemento, la estructura de carpetas se creará automáticamente.

Una vez que instales las dependencias necesarias (mira arriba), pega los ejecutables del paquete Gettext en la carpeta del complemento. Sáltate este paso si planeas construir tu complemento usando el Bash de WSL.

Estructura de carpetas del complemento

Cada carpeta de complementos, como mínimo, debe contener los siguientes archivos y carpetas:

  • manifest.ini para almacenar la información de manifiesto del complemento tal como el nombre, el autor y el rango de compatibilidad (versión mínima, última versión probada).
  • Una subcarpeta «addon» con el directorio de módulos del complemento bajo esta subcarpeta (appModules, globalPlugins, synthDrivers, brailleDisplayDriverss, visionEnhancementProviders). Se pueden especificar una o más carpetas de módulos.

Si estás utilizando la plantilla de complementos, la estructura de carpetas se creará automáticamente, por lo que sólo necesitas crear la subcarpeta addon y las carpetas de módulos adicionales y el código dentro de estas carpetas. Consulta el archivo readme en la carpeta de la plantilla para obtener más información sobre cómo personalizar tu manifiesto de complemento mediante los archivos de la plantilla.

Enpaquetado de complementos

Hay dos modos de empaquetar complementos:

  1. Para empaquetar el complemento manualmente, comprime (en zip) la carpeta del complemento como un archivo .zip, y luego cambia el nombre de la extensión del archivo a .nvda-addon.
  2. Para utilizar la plantilla de complemento con SCons, abre el símbolo del sistema en el modo Administrador (Windows Vista o posterior) o Bash en Ubuntu en Windows (Windows 10 Anniversary Update con el WSL habilitado), cambie a la carpeta de complemento y escribe scons.

Para más información sobre gestión de complementos, consulta el capítulo gestión en esta guía.

Comenzando: Ejemplos prácticos

Entonces, ¿estás listo para comenzar tu aventura con los complementos, pero no estás seguro de cómo darle vida? Si ese es tu caso, vé por favor a través de este capítulo, pues te da la información básica para conseguir comenzar con los complementos y te da consejos para escribir código.

Nota: para este capítulo, no usaremos paquetes de complementos reales. En su lugar, usaremos carpetas de extensiones de scratchpad – una serie de subdirectorios situados en una carpeta llamada «scratchpad», que es una subcarpeta en tu carpeta de configuración de usuario de NVDA (disponible desde el menú de inicio/pantalla si está instalado NVDA) para almacenar nuestros ficheros Python de ejemplo.

Para editar archivos .py, necesitas un procesador de textos que pueda manejar archivos .py. El mejor que recomendamos es Notepad++.

Cómo está organizado el código de los complementos

El código de tu complemento se almacena en uno o más ficheros Python (fichero .py). A pesar de los diferentes tipos de complementos que hay por ahí, todos ellos tienen un diseño similar.

Primero, comienza por escribir una cabecera opcional para el complemento, tal como tu nombre, una o dos frases breves sobre para qué es el complemento y así sucesivamente. Aunque esto es opcional, Se recomienda que escribas el encabezado como un recordatorio para realizar un seguimiento de lo que estás haciendo con tu complemento. Si planeas distribuir tu complemento, debes escribir una cabecera con avisos de copyright y licencia.

A continuación, le dices a NVDA los módulos que necesitas para el complemento en el archivo. Esto se hace escribiendo import module donde module es el nombre del módulo que desees utilizar en tu código. Por ejemplo, si quieres escuchar tonos mientras escribes tu complemento, escribe import tones. Normalmente será posible que tengas que importar dos o más módulos para tu complemento específico(véase más abajo en la lista de módulos que necesitas para el tipo de módulo del complemento que estés escribiendo).

después de declarar los módulos necesarios o de importar, escribe el código de tu complemento (definiendo clases, variables, métodos y así sucesivamente). La sección más importante es el código de la clase del complemento, que determinará el tipo de módulo de complemento al que se asignará tu código.

Por ejemplo, si deseas agregar soporte para un programa, después de importar appModuleHandler y otros módulos necesarios, podrías escribir:

class appModule(appModuleHandler.AppModule):

Después de eso, todo lo que se escribe es código Python (consulta la documentación de Python sobre cómo escribir programas en Python).

Ejecutar tu complemento de ejemplo en este capítulo

Antes de que puedas ejecutar complementos de prueba, debes habilitar el development scratchpad desde las opciones avanzadas de NVDA. Después de hacerlo, aparecerá una nueva carpeta llamada «scratchpad» en la carpeta de configuración del usuario. Lee la sección anterior sobre scratchpad para más detalles sobre cómo hacerlo.

Para ejecutar tu complemento de ejemplo desde este capítulo, abre el directorio de configuración de usuario de NVDA(desde Menú o pantalla Inicio, busca el elemento NVDA » Explorar carpeta de configuración del usuario»). Luego busca la carpeta «scratchpad» (si está activada), y pega tu fichero .py en la carpeta correspondiente dentro de esta subcarpeta: carpeta appModules para ejemplos de módulos de aplicación, y carpeta globalPlugins para extensiones globales.

Ejemplo 1: escuchar un tono al pulsar NVDA+A

Comencemos con un ejemplo sencillo: Si pulsamos NVDA+A, se oye un tono durante 1 segundo desde cualquier programa. Puesto que queremos usar esto en todas partes, debe ser una extensión global.

En primer lugar, si todavía no lo has hecho, habilita development scratchpad. Después abre la carpeta de configuración de usuario, abre la carpeta scratchpad (si existe–créala si no es así), a continuación, abre la carpeta globalPlugins. Crea un nuevo fichero .py y dale un nombre descriptivo, como example1.py (se recomienda encarecidamente que cuando le dés el nombre del fichero de la extensión global, este sea corto). A continuación, abre el archivo .py recién creado en el procesador de textos.

El código siguiente implementa nuestro ejemplo. Pon esto en tu .py exactamente como se muestra.:

# Primer ejemplo de desarrollo de complemento

import globalPluginHandler
import tones # Queremos escuchar pitidos.

class GlobalPlugin(globalPluginHandler.GlobalPlugin):

    def script_doBeep(self, gesture):
        tones.beep(440, 1000)  # pita un Lá estándar de la quinta octava durante 1 segundo.

    __gestures={
        "kb:NVDA+A": "doBeep"
    }

En Python, para poner comentarios se pone el símbolo número (#) al principio de la línea de comentario.

Explicación del código del ejemplo 1

Nuestro primer ejemplo nos permite hacer sonar un pitido durante un segundo cuando pulsemos NVDA+A. pero podrías preguntarte sobre el significado del código de más arriba, así que vamos a dar unos pasos a través del código, uno por vez.

  1. En la parte superior del archivo, escribimos una cabecera que nos dice que este es un ejemplo de complemento.
  2. Dado que esta es una extensión global, tenemos que importar un módulo fundamental: global plugin handler, por lo que escribimosimport globalPluginHandler.
  3. Entonces escribimosimport tones para importar (cargar, o incluir) el módulo de tonos, un módulo integrado de NVDA. Cada vez que desees utilizar un método de un módulo dado, importa el o los módulos necesarios.
  4. A continuación, definimos una clase llamada GlobalPlugin. El texto dentro de los paréntesis nos dice de dónde proviene esta clase (más sobre este concepto en un segundo). Una clase, en programación, describe un objeto, tal como una persona, un escritorio, un programa y otros.
  5. Dentro de la clase, escribimos un método (función) llamado script_doBeep. Esto es un ejemplo de un script, un método que va a ejecutarse al pulsar una orden. Dentro de este script, escribimos tones.beep(440, 1000) para decir a NVDA que haga sonar un tono durante 1 segundo. En programación, una funcción puede tomar argumentos, o un conjunto de parámetros que dice a la función qué hacer basándose en los valores dados(nos los encontraremos más adelante). De hecho, muchos métodos que escribirás, incluyendo nuestro script doBeep toma uno o más argumentos. Más sobre los scripts más tarde en el viaje a través de la guía.
  6. Por último, escribimos un diccionario simple (una colección) para almacenar enlaces a nuestra orden (script) para nuestro script doBeep. Aquí, le dijimos a NVDA que asignase la orden NVDA+A para el script doBeep. Más tarde aprenderás un truco útil que te permitirá especificar los gestos del script y otra información al mismo tiempo que el propio script.

Guarda este fichero, después reinicia NVDA. Ahora, cada vez que se pulse NVDA+A, se escucha un tono durante 1 segundo. Una vez que te sientas cómodo con el código del complemento y de cómo se presenta, Puedes eliminar el fichero .py recién creado.

No entiendo los términos anteriores

Para algunos, los términos «clase», «método» y demás podrían ser nuevos. Vamos a repasar lo que son estos términos, ya que son fundamentales para el desarrollo de complementos:

  • Clase: una clase describe un objeto. Podría describir cualquier cosa, tal como una persona, una mesa, un complemento de NVDA y otros. Las clases son fundamentales para NVDA y otros programas – de hecho, un buen número de programadores son expertos por conocer las clases.
  • Método: un método es un pequeño programa o una rutina que ejecuta un programa para hacer algo, tal como generar tonos, calcular números grandes, cargar complementos de NVDA y así sucesivamente. Algunas personas los llaman «funciones.»
  • Script: un script es un método que se ejecuta cuando el usuario realiza órdenes tales como pulsar ciertas teclas en un teclado. Por ejemplo, cuando pulsas NVDA+F12, NVDA ejecuta el script dateTime, localizado en uno de los módulos principales de NVDA llamado Global Commands. Un script toma dos argumentos: dónde se debería ejecutar el script (normalmente «self»; lo veremos más adelante) y el gesto para el script (véase más adelante).
  • Variable: una variable es algo que puede cambiar, tal como el nombre de una persona, un nombre del complemento de NVDA en ejecución, la versión de NVDA que estás utilizando y así sucesivamente. En un archivo de complemento pueden definirse una o más variables(Por ejemplo, para almacenar constantes comunes, tales como cadenas).
  • Módulo: un módulo es una colección de métodos y variables en un fichero. Cuando escribimos complementos, de hecho, estamos escribiendo módulos adicionales que NVDA puede utilizar mientras se está ejecutando.

Hay otros términos que vamos a llegar a conocer en breve.

Ejemplo 2: generar un tono cuando se cambie al Bloc de notas

La mayor parte del código de abajo viene de la Guía del desarrollador de NVDA.

NVDA no sólo te permite agregar comandos globales, sino que también permite la escritura de código para mejorar el uso de los programas a través de módulos de aplicación. Un módulo de aplicación es también un archivo Python, excepto que, esta vez, el nombre del archivo .PY Es el nombre del ejecutable de un programa. Por ejemplo, un módulo de aplicación para el Bloc de notas se llamaría notepad.py.

El código de abajo, de la Guía del desarrollador de NVDA, da un pequeño ejemplo de un módulo de aplicación típica: reproducir un pitido corto al cambiar al Bloc de notas. Pon el código de abajo en notepad.py, que a su vez se debe colocar en la carpeta appModules del directorio scratchpad (si está activado) en la carpeta de configuración del usuario con el fin de que se ejecute.

# Un ejemplo de módulo de aplicación.

import appModuleHandler
import tones

class AppModule(appModuleHandler.AppModule):

    def event_gainFocus(self, obj, nextHandler):
        tones.beep(256, 200)
        nextHandler()

Explicación del código del ejemplo 2

Estamos viendo más código nuevo aquí. Vamos a repasar esto, de nuevo pieza por pieza:

  1. A diferencia del primer ejemplo, el módulo fundamental que necesitamos es appModuleHandler.
  2. La clase que estamos utilizando es appModule.
  3. A diferencia de la última vez, estamos utilizando eventos, un método que se ejecuta cuando se producen determinados eventos, como cuando los nombres de los controles cambian. Los eventos reciben un objeto como uno de sus argumentos, el objeto para el que el evento debe ser tratado, o, como mucha gente dice, «disparado.»
  4. Dentro del método de evento, también estamos viendo una llamada anextHandler. Este método se utiliza en métodos de evento para decirle a NVDA que pase el evento para que pueda ser atendido, tal como decir el nombre de un control después de sonar.

Más términos nuevos por favor

Otros términos que puedes ver son:

  • Evento: un evento es un método que va a ser ejecutado cuando sucedan algunos hechos, tales como cuando un programa esté enfocado, cuando cambien los nombres de un control y así.
  • Llamada: decimos que una función llama a algún otro método cuando corremos el segundo método desde otro método. Por ejemplo, en nuestro primer ejemplo, llamábamos al método tones.beep desde nuestro método de script.
  • Objeto: un objeto es una instancia de una clase- es decir, una clase que se crea cuando se ejecuta un programa. A lo largo de tus complementos, a medida que escribes las clases y al ejecutar tus complementos, tus clases cobran vida como objetos, comúnmente abreviados como obj. En NVDA, un objeto puede referirse a los controles o partes de un programa.
  • Self: en Python, la palabra»self» significa clase actual(si estamos definiendo una, como por ejemplo al escribir complementos), o significa la clase para la que se define un método. Por ejemplo, en una clase llamada numbers, el método añadido tendría a self como su primer argumento, recordándonos que el método añadido es parte de la clase numbers. En el mundo de desarrollo de NVDA, self por lo general significa el objeto actual de NVDA (véase más adelante), o en el desarrollo de complementos, la instancia de un complemento. Muchos de tus métodos tendrán a self como el primer argumento.

Al igual que en el ejemplo 1, una vez te sientas cómodo con el código del módulo de aplicación, es posible que desees eliminar este código para el Bloc de notas, a menos que desees mantener el oír pitidos cuando se cambie al Bloc de notas. Las diferencias reales entre extensiones globales y módulos de aplicación se harán más claras cuando hablemos de ellos con más detalle en esta guía.

Algunos consejos para principiantes

Aquí van algunos consejos útiles transmitidos por escritores de complementos:

  • Comienza con complementos sencillos, como decir un mensaje, tonos, etc.
  • Escribe y prueba un método por vez.
  • Si estás escribiendo módulos de aplicación o controladores, familiarízate con los programas, sintetizadores o pantallas braille a las que desees dar soporte (por ejemplo lee la documentación, trata de usarlos, etc.).
  • En la definición de las órdenes (especialmente en extensiones globales), primero consulta las órdenes utilizadas en NVDA y otros complementos antes de asignar una nueva orden en el complemento para evitar conflictos de órdenes.

Módulos útiles del núcleo de NVDA

A lo largo de la vida del desarrollo de un complemento, te encontrarás con algunos módulos útiles del núcleo de NVDA que serían útiles en el código para tu complemento. Esta sección te los explica y también algunas funciones.

Nota: para lectores que sean creadores de scripts para otros lectores de pantalla, consultad el Apéndice D sobre las funciones equivalentes entre las herramientas de creación de scripts de lectores de pantalla.

Lista de módulos y métodos básicos útiles de NVDA

A continuación se enumeran los módulos disponibles del núcleo de NVDA y algunos métodos útiles que se encuentran en los módulos:

  • Addon Handler (addonHandler.py): El módulo que implementa el subsistema de complementos. El método addonHandler.initTranslation() se utiliza para inicializar el soporte de internacionalización para tu complemento.
  • API básica de NVDA (api.py): una colección de los métodos básicos utilizados en NVDA, como la obtención del foco y el navegador de objetos, ajustar el foco, etc. Algunas de las funciones más útiles son:
    • api.getFocusObject(): recupera el control enfocado (devuelve el objeto con el foco).
    • api.getNavigatorObject(): obtiene el navegador de objetos actual. Si NVDA está configurado para seguir al foco del sistema, el foco y el navegador de objetos serán lo mismo, de otro modo se devuelve un objeto diferente.
    • api.getForegroundObject(): devuelve la ventana en primer plano de la aplicación actual (el padre de este objeto es la aplicación misma).
    • Estos tienen un método correspondiente para establecer un objeto determinado como el foco o el navegador de objetos. Ten en cuenta que esto permite a NVDA consultar el foco o el navegador de objetos nuevo pero no cambia el foco actual del sistema.
    • api.getDesktopObject(): devuelve la shell (objeto del nivel más alto).
    • api.copyToClip(texto a copiar, notificar éxito o fallo opcionalmente): copia texto al portapapeles y, opcionalmente, pide a NVDA que avise al usuario sobre esta operación.
  • Subsistema de módulos de aplicación (appModuleHandler.py, appModules): El subsistema encargado de manejar los módulos de aplicación (véase el capítulo sobre los módulos de aplicación para obtener más información).
  • Soporte de ARIA (aria.py): implementa el soporte para aplicaciones de Internet enriquecidas accesibles (ARIA).
  • Colección de objetos base (baseObject.py): contiene objetos base útiles tales como objetos scriptables (consulta el capítulo sobre objetos de NVDA y superposición de objetos para más información).
  • Subsistema de entrada y salida braille (braille.py, brailleInput.py): controla la salida y la entrada braille de las pantallas braille, necesario para complementos de controladores de pantallas braille.
  • Módulos integrados (builtin.py): permite el acceso a módulos integrados cuando se trabaja con complementos.
  • Configuración (config): gestiona la configuración y los perfiles (los perfiles están disponibles en 2013.3 o posterior).
  • Motores de reconocimiento de contenido (paquetes contentRecog): añade la capacidad de utilizar OCR y otros métodos para reconocer contenido en diversos escenarios. NVDA incorpora un motor de OCR para Windows 10.
  • Colección de controles y estados (controlTypes.py): incluye diccionarios sobre los tipos de control (roles) y posibles estados.
  • Eventos (eventHandler.py): maneja varios eventos como la optención del foco. Una función en particular es útil en los módulos de aplicación:
    • eventHandler.requestEvents(evento a solicitar, id del proceso, nombre de clase de ventana del control): Permite a NVDA escuchar eventos específicos para ciertos controles mientras se utiliza otra aplicación.
  • Puntos de extensión (extensionPoints): proporciona una forma de permitir que los complementos y otros módulos definan y respondan a acciones específicas como cambios de perfil, acciones en un complemento, etc. Se definen los siguientes puntos de extensión:
    • extensionPoints.Action; notifica cuando algo ocurre, por ejemplo: el perfil cambia.
    • extensionPoints.Decider: decide si procesar algo más, por ejemplo: procesar la entrada del teclado desde un sistema remoto.
    • extensionPoints.Filter: modifica un texto determinado para su procesamiento posterior, por ejemplo, secuencias avanzadas de voz.
  • Colección de Comandos globales (globalCommands.py): Una lista de las órdenes globales disponibles durante el uso de NVDA (véase la sección sobre el alcance de un script para más información).
  • Subsistema de extensiones globales (globalPluginHandler.py): El módulo necesario para controlar las extensiones globales.
  • GUI de NVDA (gui): Una colección de clases utilizadas por NVDA para mostrar sus mensajes de forma gráfica. Incluye la interfaz gráfica de usuario para el menú de NVDA, el administrador de complementos y otros.
  • Utilidades de los puertos Hardware y gestión de la entrada y la salida (hwPortUtils.py, hwIo.py): Un conjunto de utilidades para la comunicación a través de los puertos serie de hardware y otros, útil durante el desarrollo de un complemento de controlador.
  • Soporte para IAccessible (IAccessibleHandler, objetos IAccessible): Se utilizan para dar soporte a los controles Microsoft Active Accessibility (MSAA) / IAccessible.
  • Gestión de entrada (inputCore.py): Gestiona la entrada del usuario.
  • Soporte Java (JABHandler.py, objetos JAB): Una colección de los métodos utilizados para soportar el subsistema JAB (Java Access Bridge) utilizado por las aplicaciones Java.
  • Entrada de teclado (keyboardHandler.py): Soporta la introducción de órdenes desde el teclado.
  • Función de registro (logHandler.py): Permite a un módulo escribir registros para ser vistos por un desarrollador o por un usuario a través del visualizador de registro. Incluye la siguiente clase:
    • logHandler.Log: la clase que implementa la utilidad de registro.
  • Presentación de contenido matemático (paquetes de MathPress): permite a NVDA reconocer e interactuar con diversos contenidos matemáticos y lenguajes de marcado. NVDA incluye el paquete de soporte MathML y el soporte para Math Player, que viene incorporado en 2015.2 o posterior.
  • Soporte de ratón (mouseHandler.py): Soporta órdenes de ratón.
  • Colección de objetos de NVDA (NVDAObjects): Una colección de objetos o controles de NVDA que se utilizan en muchas aplicaciones y estándares como la UIA (User Interface Automation). Algunos objetos requieren que se realicen acciones especiales y éstas se especifican en el módulo de comportamientos del paquete de objetos NVDA. Entre los más comunes se incluyen:
    • NVDAObjects.NVDAObject: la clase base para objetos de NVDA donde se definen eventos, propiedades, etcétera.
    • NVDAObjects.behaviors: una colección de comportamientos para controles específicos, tales como editar campos con o sin detección de selección, terminales, consejos, globos de ayuda, una forma de simular órdenes de tabla en varios controles y otros.
    • NVDAObjects.IAccessible: una colección de objetos MSAA/IAccessible, tales como la que funciona con vistas de listas SysListView32 y otros.
    • NVDAObjects.JAB: una colección de clases utilizadas al interactuar con aplicaciones Java y Java Access Bridge.
    • NVDAObjects.UIA: diversas clases para objetos impulsados por UI Automation. Desde 2016, también es el sitio de una colección de controles usados en el antiguo Microsoft Edge (no Chromium) y objetos promovidos por el motor de renderizado EdgeHTML.
    • NVDAObjects.Window: ventanas genéricas y otros objetos personalizados como los que se encuentran en Microsoft Excel.
  • Facilidad de revisión (review.py): ayuda a trabajar con el cursor de revisión.
  • Soporte de Scripts (scriptHandler.py): Maneja scripts, métodos ejecutados debido a la pulsación de las órdenes de teclado y otras entradas.
    • scriptHandler.script: un decorador que permite definir información sobre el script asociado mientras se define el propio script, incluyendo descripción (mensaje de ayuda de entrada), gesto o gestos (una orden o lista de órdenes), y la categoría del script.
  • Salida de voz(speech): Controla la salida de voz.
  • Soporte para controladores de Sintetizador (synthDriverHandler.py): Este es el módulo principal necesario para complementos de sintetizador de voz.
  • Acceso al texto de los Widgets (textInfos): Permite el acceso al texto de widgets y documentos.
  • Soporte de pantalla táctil (touchHandler.py): Proporciona soporte para la entrada de la pantalla táctil (sólo versiones instaladas).
    • touchHandler.touchSupported(): indica si el sistema soporta interacción táctil.
  • Salida de tonos (tones.py): Permite al usuario escuchar tonos. Se definió la siguiente función:
    • tones.beep(tono en hercios, duración en milésimas de segundo, volumen del canal izquierdo, volumen del canal derecho): Reproduce un sonido de un tono específico durante un tiempo determinado. Los dos primeros argumentos son obligatorios, mientras que los otros dos son opcionales.
  • Mensajes de la interfaz de usuario (ui.py): Incluye varias funciones para la salida de voz y/o braille, incluyendo:
    • ui.message(mensaje que se verbaliza o se muestra en Braille, prioridad de la voz, mensaje Braille opcional): verbaliza o muestra en Braille el mensaje (una cadena encerrada entre comillas). Opcionalmente, se puede especificar la prioridad de la voz para interrumpir lo que verbaliza el sintetizador de voz cuando se anuncia el mensaje, así como emitir un mensaje diferente en las pantallas Braille.
    • ui.browseableMessage(mensaje a ser mostrado, título, HTML o no): muestra algún texto y un título opcional en una ventana de navegador web. Si deseas utilizar el marcado HTML, define el argumento isHTML como True.
  • Soporte UIA (UIAHandler.py, objetos UIA): Usado para soportar controles UIA (User Interface Automation) (Windows 7 y posteriores).
  • Mmodos virtuales(virtualBuffers): Maneja documentos del modo virtual, tales como sitios web.
  • Especificación de la versión de Windows y comprobaciones (winVersion.py): proporciona constantes que representan y comparan versiones de Windows.
    • winVersion.getWinVer(): devuelve la versión actual de Windows (nombre de la versión, principal.menor.revisión, tipo de instalación, y service pack si lo hay). Estos datos se pueden comparar con otras versiones de Windows, por ejemplo para detectar una versión de Windows 10 concreta como la actualización de octubre de 2018. Ejemplo: winVersion.getWinVer() >= winVersion.WIN10_1809.
  • Capas de la API de Windows: los siguientes módulos son capas delgadas de las bibliotecas de la API de Windows. Puedes utilizar los módulos siguientes o llamar a la API de Windows directamente a través de ctypes.windll.dllname (por ejemplo, ctypes.windll.user32):
    • winKernel: abarca algunas constantes, estructuras y funciones de kernel32.dll que se encuentran comúnmente en NVDA.
    • winUser: abarca las constantes, estructuras y funciones definidas en user32.dll que utiliza NVDA.

Los módulos sin extensión .py son directorios que contienen módulos especializados. Hay otros métodos útiles por ahí, además de la lista anterior, pero los anteriores son los más útiles. Consulta la documentación del código fuente de NVDA para ver otros métodos, o mira los ejemplos que aparecen a continuación para ver cómo se utilizan estos métodos y otros durante la vida de un complemento.

Algunos ejemplos de la vida real

Veamos algunos ejemplos simplificados de la vida real que demuestran cómo se utilizan los componentes listados más arriba en escenarios comunes de desarrollo de complementos.

Ejemplo 1: ¿Estoy en la aplicación correcta donde se encuentra el foco?

El siguiente código verifica si el navegador de objetos e encuentra en algún lugar de la misma aplicación o no.

import api

def sameApp(obj=None):
    if obj is None:
        obj = api.getNavigatorObject()
    return api.getFocusObject().appModule == obj.appModule

La función api.getNavigatorObject () devuelve el objeto del navegador actual, el objeto que te interesa en lugar del objeto enfocado. Cada objeto de NVDA incluye un miembro appModule que registra en qué aplicación se encuentra un objeto.

Ejemplo 2: Mostrar un mensaje en un documento en modo exploración

Desde NVDA 2015.2 y posterior, fue posible mostrar un mensaje en una ventana del modo de exploración para que la gente pueda utilizar las órdenes del modo de exploración para revisar el contenido del mensaje. El siguiente código muestra»Hola mundo» en la ventana de un documento.

import ui

ui.browseableMessage("¡Hola Mundo!", isHtml=False)

La bandera isHtml indica a NVDA que trate el mensaje como un texto HTML.

Ejemplo 3: Anunciar el ID de automatización de un objeto UIA

En UI Automation, el ID de automatización se utiliza para identificar diferentes elementos de pantalla. El siguiente código muestra esta información en una ventana navegable.

import ui
from NVDAObjects.UIA import UIA

def announceUIAId():
    obj = api.getFocusObject()
    if isinstance(obj, UIA):
        ui.browseableMessage(obj.UIAAutomationId, isHtml=True)

Ejemplo 4: Enviar pulsaciones de teclas

Puedes pedirle a NVDA que envíe pulsaciones de teclas específicas instanciando un objeto de gesto de teclado.

import keyboardHandler

def sendApplicationsKey():
    keyboardHandler.KeyboardInputGesture.fromName("applications").send()

Ejemplo 5: Detener la voz cada vez que cambie el contenido de la pantalla si el anuncio de cambio de contenido dinámico está desactivado

El código que aparece a continuación es un controlador para un evento de cambio de nombre que detiene la voz siempre que el contenido de la pantalla cambie si el anuncio de cambio de contenido dinámico está desactivado.

import appModuleHandler
import config
import speech

class AppModule(appModuleHandler.AppModule):

    def event_nameChange(self, obj, nextHandler):
        if not config.conf["presentation"]["reportDynamicContentChanges"]:
            speech.cancelSpeech()

Ejemplo 6: uso del decorador script

Un decorador es una función que envuelve y devuelve otra función tras realizar operaciones internas. Por ejemplo, un decorador puede hacer cambios en el interior de una función o comprobar algo en nombre de otra función sin que la función envuelta sea consciente de lo que está ocurriendo.

En NVDA 2018.3, se introdujo un decorador especial llamado scriptHandler.script para facilitar la definición de información de un script. Mientras se define el script, se pueden asignar su descripción (mensaje en el modo Ayuda de entrada), gesto o gestos (órdenes asociadas al script), y categoría.

Recuerda el primer ejemplo, donde se oía un pitido cada vez que se pulsaba NVDA+a. La desventaja es que el script real y su orden (gesto) asociada se encuentran definidos en lugares distintos. Se pueden agrupar utilizando el decorador script (scriptHandler.script) como se muestra a continuación.

# Primer ejemplo de extensión global en el desarrollo de complementos, ahora editado para usar el decorador script

import globalPluginHandler
import scriptHandler
import tones

class GlobalPlugin(globalPluginHandler.GlobalPlugin):

    @scriptHandler.script(gesture="kb:NVDA+A")
    def script_doBeep(self, gesture):
        tones.beep(440, 1000)  # pita un Lá estándar de la quinta octava durante 1 segundo.

A lo largo de esta guía, cada vez que se muestren scripts de ejemplo, se utilizará el decorador script a menos que se indique lo contrario.

Esto es sólo una muestra de lo que te puedes encontrar al escribir complementos y de cómo puedes usar diversos componentes de NVDA para lograr lo que quieras. Recorreremos los componentes de un complemento y conoceremos más funciones a lo largo de esta guía.

Componentes del módulo de un complemento y consejos de desarrollo

Un módulo de complemento se compone de una serie de componentes. Esto incluye el manejo de la entrada y de la salida, el trabajo con diferentes objetos de NVDA, la reacción a eventos, el almacenamiento de configuración y más.

Este capítulo presenta los componentes y conceptos clave que se utilizan en el desarrollo de complementos, tales como objetos de NVDA, scripts, gestión de eventos y temas adicionales con ejemplos.

Ten en cuenta que la guía de desarrollo básico de NVDA presenta los siguientes conceptos. Este capítulo pretende ser una extensión de ese documento. Consulta la guía de desarrollo de NVDA para una breve introducción.

Nota para creadores de scripts de otros lectores de pantalla: es posible que estés familiarizado con algunos de los conceptos introducidos en esta sección. Por favor, lee esta sección si deseas conocer mejor cómo se utilizan algunos de ellos en NVDA.

La consola Python

Esta guía sugiere que en ocasiones utilices la consola Python. La consola es una característica de NVDA que puede ser muy útil para los desarrolladores. Se describe con más detalle en el capítulo 5 de la guía del desarrollador de NVDA.
Puedes activarla yendo al menú Herramientas de NVDA, o pulsando NVDA+ctrl+z.

Trabajando con objetos en pantalla

Un objeto es una instancia de una clase – es decir, una clase activa mientras un programa se está ejecutando. Por ejemplo, si se ha definido una clase llamada botón, el botón en una pantalla es el objeto de esta clase botón.

En NVDA, un objeto es la representación de un control o partes de un programa. Esto incluye botones, casillas de verificación, campos de edición, barras de herramientas, deslizadores e incluso la ventana de la aplicación. Estos se organizan en jerarquías, o relación padre-hijo donde un objeto podrá contener objetos hijos – por ejemplo, un objeto lista en Windows Explorer podrá contener uno o más elementos de lista, y el padre de esta lista podría ser la ventana de Windows Explorer. El objeto que estás examinando en este momento se denomina «navegador de objetos.»

Los objetos de NVDA (o simplemente llamados objetos) contienen una serie de propiedades o atributos útiles. Estos incluyen el nombre del objeto, su valor (seleccionado, el texto de la ventana de edición, etc), rol (casilla de verificación, ventana, objeto incrustado, etc.), posición (coordenadas de la pantalla) y más. Los objetos de NVDA también contienen métodos útiles para su manipulación, tales como cambiar el valor del objeto, reaccionar a los eventos para el objeto (recepción del foco, el valor ha cambiado, etc) y demás.

En muchas situaciones, un objeto NVDA puede pertenecer a una clase de objetos relacionados. Para cada una de las clases de objetos, NVDA proporciona formas de manipularlos. Estas clases incluyen IAccessible, JAB, UIA y así. Estas clases y comportamientos para cada clase de objetos se definen en el directorio NVDAObjects en el código fuente de NVDA, y para utilizarlos en tu complemento, importa el controlador de clase de objeto adecuado para el objeto que estás utilizando(por ejemplo si estás trabajando con un objeto IAccessible, importa NVDAObjects.IAccessible.).

Dos de estas clases de objetos merecen especial mención: los modos virtuales y los interceptores de árbol. Un interceptor de árbol permite a NVDA funcionar con un «árbol» de objetos como si fuesen sólo un objeto. un caso especial de interceptor de árbol es el modo virtual, que permite a NVDA trabajar con documentos complejos, como documentos PDF. Estos objetos contienen un mecanismo especial para determinar si una orden de teclado dada será pasada a la aplicación o manejada por NVDA (por ejemplo, el modo de exploración donde se usa la primera letra de navegación para moverse entre elementos).

Examinando la jerarquía de objetos

Hay varias alternativas que puedes utilizar para ver la jerarquía de un objeto de un determinado programa:

  1. utilizando las órdenes de navegación de objetos(NVDA+2/4/5/6/8 del teclado numérico) con el modo simple de revisión desactivado.
  2. Utilizando la consola Python, usa los atributos obj.next/previous/parent/firstChild/lastChild. Si quieres ver todas las propiedades disponibles, desde la consola Python , escribe dir(obj).

Si deseas ver una descripción más detallada sobre el navegador de objetos, mientras que el navegador de objetos se encuentre en el objeto en que estés interesado, pulsa NVDA+F1 para lanzar el visualizador del registro. La raíz de todos los objetos de Windows es el escritorio, objeto shell.

Foco vs. navegador de objetos

En tu complemento, podrías querer trabajar con varios objetos y manipularlos. Esto puede incluír cambiar el objeto enfocado, sincronizar el navegador de objetos y el foco, cambiar la función de un objeto y así sucesivamente.

Un objeto en el foco es el control enfocado actualmente. Este está relacionado con el foco del teclado- es decir, sigue al control resaltado. En contraste, un navegador de objetos es el objeto de tu interés. Dado que el navegador de objetos se puede mover en cualquier lugar, puedes examinar dos objetos a la vez: el objeto enfocado y el navegador de objetos. Por ejemplo, podrías enfocar sobre un campo de edición mientras examinas la barra de título con el navegador de objetos. Ya hemos visto un ejemplo más arriba donde podemos comprobar si estamos en dos aplicaciones diferentes mirando los módulos de aplicación del navegador de objetos y del objeto con el foco.

En tu complemento, para obtener el objeto con el foco, escribe someObj = api.getFocusObject(). el someObj puede ser llamado de forma diferente- la convención es utilizar el nombre «obj». Para obtener el navegador de objetos (que podría ser diferente del del objeto enfocado), utiliza obj = api.getNavigatorObject().

Otras golosinas útiles relacionadas con objetos

Aquí van algunos otros métodos que funcionan con objetos de NVDA, todos ubicados en el módulo api.py:

  • Si deseas obtener el objeto en primer plano (útil si se desea ver algún objeto secundario de la ventana en primer plano), utiliza obj = api.getForegroundObject().
  • Desde la consola Python, para ver el número de objetos secundarios que contiene un objeto (por ejemplo, el hijo, o los widgets de una ventana en primer plano), teclea obj.childCount. El valor0 significa que no hay más objetos secundarios.
  • Para definir un objeto como el foco nuevo o el navegador de objetos, utiliza api.setFocusObject(obj) o api.setNavigatorObject(obj). Esto no cambia la forma que tiene Windows de ver el objeto enfocado, simplemente hace creer a NVDA que el nuevo objeto es el que tiene el foco o el navegador de objetos.
  • Puedes obtener varias propiedades de un objeto especificando obj.property donde property es el atributo que deseas ver (por ejemplo obj.value). Entre las propiedades más comunes se incluyen name, value, states, role, appModule, windowClassName, etc.

Ejemplo 1: encontrar el valor de un deslizador en un programa

Supongamos que un usuario te pide que le des el valor de un deslizador en un programa usando un app module. Después de buscar en la jerarquía de objetos y otras propiedades, sabes que la barra de herramientas es el último hijo del objeto en primer plano.

Aquí está el código para implementar esta función:

# Ejemplo de objeto 1

import api
import appModuleHandler

class AppModule(appModuleHandler.AppModule):

    sliderChildIndex = -1 # La variable para almacenar el índice del hijo.

    def getSliderValue(self):
        fg = api.getForegroundObject()
        sliderVal = fg.children[self.sliderChildIndex].value
        return sliderVal

En este código, el método fg.children[index] se utiliza para recuperar al hijo con el índice dado (aquí, ya dijimos que la barra de herramientas es el último hijo, el índice sería menos 1, o el último hijo; podríamos haber utilizado fg.lastChild). Alternativamente, puedes utilizar `fg.getChild (-1) en ciertas situaciones (IAccesible, por ejemplo).

Sin embargo, este código tiene un problema: ¿y si el valor del deslizador está en realidad dentro del primer elemento secundario del control deslizador real? Una forma de solucionar este problema es comprobar el rol del objeto. El código modificado quedaría así:

    def getSliderValue(self):
        from controlTypes import Role # es posible importar desde dentro de un método.
        fg = api.getForegroundObject()
        slider = fg.lastChild
        if slider.role == Role.SLIDER: return slider.firstChild.value

Así, cuando estemos seguros de que estamos tratando con el control deslizador, el método devuelve el valor del primer hijo del deslizador (Si ese es el caso). Presta atención a los dos signos de igual para la comparación de igualdad, en lugar de sólo uno que es el signo igual para la asignación.

Hay otros ejemplos que puedes probar para familiarizarte con la navegación y manipulación de objetos:

  • Obtener el nombre de un objeto que se encuentre en otro lugar en el programa.
  • Mover el navegador de objetos al objeto en primer plano.
  • Poner el foco en otro programa.
  • Localizar la primera barra de estado en un programa con varias barras de estado.

Para ver ejemplos de la vida real sobre los objetos de NVDA, consulta el código fuente de NVDA o los códigos fuente de diversos complementos de la comunidad.

Objetos Especiales y redefinición de las propiedades de objetos en tiempo de ejecución

A veces, no es suficiente con trabajar con el comportamiento predeterminado de un control. Por ejemplo, algunas partes de un programa pueden necesitar gestos personalizados, o puede que uno tenga que cambiar el rol de una ventana a la de un botón.

NVDA proporciona dos métodos para crear objetos especiales o superpuestos (o clases), cada uno adaptado para diferentes necesidades:

  • event_NVDAObject_init(self, objeto con el que trabajamos): Si deseas sobreescribir ciertos atributos de un control, como su rol o la etiqueta(nombre), puedes utilizar este método para pedirle a NVDA que tome en cuenta tu»entrada» cuando se encuentran los objetos por primera vez (o inicializados). Por ejemplo, si el control tiene el nombre de clase de ventana TForm (visto en muchas aplicaciones Delphi), puedes pedir a NVDA que trate a este control como una ventana estándar asignando obj.role = controlTypes.Role.WINDOW (ver módulo controlTypes para lista de roles disponibles).
  • chooseNVDAObjectOverlayClasses(self, object, lista de clases): Esto permite a NVDA que utilices tu propia lógica al tratar con ciertos objetos. Por ejemplo, esto es útil si deseas asignar gestos personalizados para determinadas partes de un programa en tu módulo de aplicación (De hecho, muchos módulos de aplicación crean objetos para tratar con determinadas partes de un programa, luego usan chooseNVDAObjectOverlayClasses para seleccionar el objeto correcto cuando se cumplan ciertas condiciones). Estos objetos personalizados se deben basar en un objeto sólido con el que deseamos trabajar (la mayoría de las veces IAccessible es suficiente, por lo tanto los demás objetos superppuestos heredan de, o son los hijos o clases especializadas de los objetos IAccessible). En ciertas situaciones, puedes deshacerte de una propiedad de un objeto usando este método, por ejemplo diciendo a NVDA que no trate a una barra de progreso como tal, eliminando el comportamiento de barra de progreso del objeto en cuestión.

Ten en cuenta que en el caso del segundo método, la clase(s) con el nombre dado debe estar presente en el fichero, que es/son heredados de un objeto base conocido (en Python, la sintaxis para la herencia es claseHija(claseBase), y es por lo general leída como, «esta clase hija hereda de esta clase base». Veremos un código como éste más tarde).

Ejemplos de sobreescritura de clases y de modificación de roles

A continuación los ejemplos que ilustran el uso de los dos métodos de modificación de sobreescritura y de atributos que hemos comentado anteriormente:

Un ejemplo del primer caso: la modificación de un atributo.

    # Reasignar algunos formularios Delphi como ventana.
    import controlTypes

    def event_NVDAObject_init(self, obj):
        if obj.windowClassName == "TForm": obj.role = controlTypes.Role.WINDOW

Esto significa que cada vez que nos encontremos con una ventana con la clase llamada «TForm», NVDA la tratará como una ventana normal.

El ejemplo 2 trata de un módulo de aplicación que tiene dos objetos para tratar con partes específicas de un programa, luego usa chooseNVDAObjectOverlayClasses para asignar la lógica para cada control.

#Un ejemplo de superposición de clases

class enhancedEdit(IAccessible):
    # Algún código para ejecutarse cuando el nombre de la clase de la ventana es MyEdit.

class MainWindow(IAccessible):
    # Otro código, esta vez añadiendo gestos personalizados para la ventana principal del programa.

# En el módulo de aplicación:

def chooseNVDAObjectOverlayClasses(self, obj, clsList):
    if obj.windowClassName == "myEdit": clsList.insert(0, enhancedEdit)
    elif obj.windowClassName == "TWindow": clsList.insert(0, mainWindow)

En ambos casos, el objeto que se desea comprobar debe insertarse como el primer elemento de la clsList. El efecto es que estos objetos personalizados tendrán prioridad cuando se estén buscando gestos o código (comportamiento) para el objeto, y en la información del desarrollador, estos objetos personalizados vendrán por primera vez cuando el MRO (orden de Métodos de Resolución) muestre el navegador de objetos.

Nota: es posible que tengas que afinar estos dos métodos para proporcionar clases de superposición correctas para controles muy específicos (tales como comprobación de nombres, roles específicos, etc.); de lo contrario, es posible que descubras que se asignan dos o más controles de apariencia idéntica a tu objeto personalizado cuando en realidad son muy diferentes. Además, el event_NVDAObject_init sólo está disponible en módulos de aplicación.

Entrada y salida: scripts y mensajes IU

Otro componente crucial de los complementos es manejar órdenes del usuario y mostrar qué está haciendo el complemento. Esto se realiza a través de scripts (en la entrada) y mensajes IU (en la salida).

Un script es un método que se ejecuta cuando el usuario lleva a cabo ciertas órdenes. Por ejemplo, cuando pulsas NVDA+T, NVDA ejecuta un script en el módulo de órdenes globales llamado SayTitle. En Poedit, por ejemplo, cuando un traductor pulsa Control+Shift+A, NVDA leerá los comentarios para el traductor añadidos por el programador para ayudar a clarificar una cadena traducible dada. Esta orden no es una orden nativa de NVDA, pero se definió en el módulo de aplicación de Poedit para llevar a cabo esta función.

Normalmente, un complemento que acepte scripts tendrá un mapa de lista orden:función en algún lugar en el módulo. El más simple es un diccionario de Gestos (órdenes), un diccionario python (llamado normalmente __gestures) que posee órdenes como claves y scripts como valores para estas claves (más de una tecla, u orden que puede vincularse a los scripts). De forma alternativa, la información de cada script, como su descripción o gestos asociados, se puede especificar mediante el decorador script. Estos diccionarios se cargan cuando el complemento se carga y se borran cuando se sale de NVDA o cuando la aplicación para la que es el módulo de aplicación pierde el foco(es decir, el usuario ha cambiado a otro programa).

Otro modo de enlazar scripts es a través de inserción en tiempo de ejecución. Esto se hace creando otro diccionario de gestos diferente del diccionario __gestures que posea gestos sensibles al contexto tales como la manipulación de un sólo control. Entonces el desarrollador podría utilizar inputCore.bindGesture (o inputCore.bindGestures si se ha definido más de un gesto/script) para definir ciertos gestos a la vez, entonces utilizar inputCore.clearGestures luego inputCore.bindGestures(__gestures) para eliminar los gestos añadidos. Un modo más elegante, que consiste en scripts para objetos específicos, se tratará cuando hablemos sobre módulos de aplicación y cómo asignar gestos a partes específicas de un programa.

Para la mayoría de los scripts, no tienes que preocuparte por enviar el comando a la aplicación. Sin embargo, en caso de que el script realice tareas adicionales mientras envía la tecla a la aplicación activa, puedes utilizar gesture.send() para enviar el comando primero antes de realizar trabajo adicional.

De manera similar a los scripts, el módulo UI permite verbalizar o braillificar lo que tu complemento está haciendo. Esto se hace usando el mensaje ui.message(algo que decir) donde algo que decir se reemplaza por una cadena para que NVDA la diga. Alternativamente, puedes llamar a los métodos de manejo del habla y del braille directamente si deseas que el habla diga una cosa y la pantalla braille muestre otra. No repasaremos `ui.message’ aquí (verás ejemplos de esos), pero lo más importante son los scripts, así que nos enfocaremos en eso en esta sección.

En el momento de escritura de esta guía, NVDA soporta entrada desde el teclado, pantallas braille con o sin teclado braille y pantallas táctiles. Estos tipos de entrada tienen un prefijo de gesto correspondiente (kb para teclado, br para braille y ts para pantalla táctil) que identifica el tipo de gesto. La salida puede enviarse vía voz y/o braille.

Ejemplo 2: Un diccionario de scripts básico y un mensaje de salida

En este ejemplo, definiremos dos scripts llamados «sayHello» y «sayGoodBye», luego los vincularemos a dos gestos separados.

    # Un fragmento de ejemplo de asignación de un script desde una extensión global.
    import ui

    def script_sayHello(self, gesture):
        ui.message("Hello!")

    def script_sayGoodBye(self, gesture):
        ui.message("Good Bye!")

    __gestures={
        "kb:control+NVDA+1":"sayHello",
        "kb:Control+NVDA+2":"sayGoodBye"
    }

Ahora cuando pulses Control+NVDA+1, NVDA dirá, «Hello», y cuando pulses Control+NVDA+2, NVDA dirá, «Good bye.» Este es el código básico al recibir comandos y enviar mensajes.

Ejemplo 3: información del script usando el decorador script

Como hemos visto en el ejemplo de más arriba, el decorador script se puede usar para asignar gestos a los scripts fácilmente. Sin embargo, el decorador script puede hacer algo más que asignar gestos: se puede usar para proporcionar información adicional, como el mensaje de ayuda de entrada del script.

Además del ejemplo 2 modificado, el ejemplo que se muestra a continuación añadirá un mensaje de ayuda de entrada a ambos scripts, junto con la posibilidad de que NVDA diga «Adiós» al pulsar NVDA+control+3.

    # Un fragmento de ejemplo del uso del decorador script en una extensión global.
    import ui
    from scriptHandler import script

    @script(
        description="Dice hola",
        gesture="kb:control+NVDA+1"
    )
    def script_sayHello(self, gesture):
        ui.message("Hello!")

    @script(
        description="Dice adiós",
        gestures=["kb:Control+NVDA+2", "kb:Control+NVDA+3"]
    )
    def script_sayGoodBye(self, gesture):
        ui.message("Good Bye!")

Argumentos del decorador script

Puedes pasar la siguiente información al decorador script sobre un script:

  • description: descripción corta de un script. Se presentará en el modo Ayuda de entrada y en el diálogo Gestos de entrada.
  • category: la categoría asociada con el script, usada para agrupar el script bajo la categoría adecuada en el diálogo Gestos de entrada.
  • gesture: un único gesto asociado al script.
  • gestures: una lista de gestos asociados al script.

Ejemplo 4: Scripts para objetos específicos

Al igual que en los objetos especializados anteriores, los scripts se pueden asignar a ciertos objetos especificando el diccionario de gestos para este objeto en particular. Aquí hay un ejemplo de un módulo de aplicación que define scripts para la ventana principal de un programa reproductor multimedia, utilizando el decorador script:

# Scripts para objetos para un programa.
from NVDAObjects.IAccessible import IAccessible
from scriptHandler import script

class Player(IAccessible):

    @script(gesture="kb:NVDA+T")
    def script_saySongName(self, gesture):
        ui.message(self.songTitle_) #Supongamos que se haya definido esa variable.

    # Y en el módulo de aplicación principal:
    def chooseNVDAObjectOverlayClasses(self, obj, clsList):
        if obj.windowClassName == "PlayerWindow": clsList.insert(0, Player)

Algo extraño ocurre con este ejemplo: normalmente, cuando se pulsa NVDA+T, NVDA dice el título de la ventana actual, pero en este ejemplo, anuncia el nombre de la canción. Este es el resultado de la búsqueda del script (ver abajo) donde el script para el objeto actual se ejecuta en lugar del script de título de comandos globales. Esta es una forma común de vincular nuevos scripts en tiempo de ejecución.

Orden de búsqueda de scripts y conflictos de órdenes

Según escribas tus complementos con scripts, necesitas recordar el siguiente orden de búsqueda de script cuando intentes asignar órdenes a scripts:

  1. Extensiones globales.
  2. Módulos de aplicación para el programa actualmente enfocado.
  3. Objetos de NVDA con los que estemos tratando.
  4. Órdenes globales.

Por ejemplo, si asignas la orden NVDA+Shift+Y a un script de módulo de aplicación, NVDA ejecutará ese script desde ese programa ya que ninguna extensión global está usando esta orden. Sin embargo, si se instala una extensión global que use esa orden, el script de la extensión global se ejecutará en lugar del script del módulo de aplicación. Del mismo modo, desde el ejemplo anterior, cuando se utilicen programas distintos a ese reproductor multimedia, NVDA ejecutará una orden desde la colección de órdenes globales cuando se pulse NVDA+T; pero mientras utilicemos este reproductor multimedia, NVDA+T anunciará el nombre de la canción (se da prioridad a los objetos de NVDA en los módulos de aplicación).

Debido a la regla anterior, se debe tener cuidado al definir un script para un complemento. Para ayudarte con esto, mantén las siguientes directrices a mano:

  1. En primer lugar, consulta la referencia rápida de órdenes de NVDA para ver si la orden que deseas utilizar se ha definido en las órdenes globales. Deberías tratar de minimizar los conflictos con las órdenes de NVDA incorporadas. Una excepción son las órdenes para los módulos de aplicación, donde una misma orden puede ser utilizada de forma diferente de un programa a otro.
  2. Lee la documentación de los complementos (especialmente las extensiones globales) para ver si algún complemento está utilizando esta orden y, en caso afirmativo, ponte en contacto con el autor del complemento para obtener un vínculo alternativo.

Otros comentarios sobre scripts

  • Puedes utilizar cualquier modificador como parte de las órdenes para scripts (por ejemplo, Alt+NVDA+letra). Sin embargo, para evitar conflictos de órdenes, intenta reducir al mínimo el uso de órdenes que los programas puedan utilizar como Control+letra, Alt+Shift+letra, etcétera.
  • Al asignar órdenes de teclado, las etiquetas de las teclas del teclado no distinguen entre mayúsculas y minúsculas.
  • Puedes definir una categoría de script para mostrar al usuario dónde se utilizará el script de tu complemento (mostrado en el cuadro de diálogo Gestos de entrada en NVDA 2013.3 o posterior). Hay dos maneras de hacerlo: a nivel de módulo mediante el atributo scriptCategory del módulo del complemento, o bien designando la categoría para cada script mediante el atributo script_name.category o como parte del decorador script definiendo el argumento category. Se recomienda nombrar la categoría de script del mismo modo que el nombre del complemento.
  • Puedes definir el mensaje del modo de ayuda de entrada para un script utilizando el atributo __doc__ (comúnmente conocido como docstrings) o pasando una breve descripción al argumento description del decorador script. La descripción del script también se utiliza en el diálogo de Gestos de entrada para mostrar información del mismo.
  • Si necesitas dejar uno o más scripts sin asignar (por ejemplo, si un gesto entra en conflicto con una orden global), no incluyas el vínculo del gesto para el script en el diccionario de gestos, o no definas los argumentos gesture o gestures en el decorador script. Esto ayuda a minimizar los conflictos de gestos y permite a los usuarios asignar gestos personalizados para los scripts.
  • Si hay dos objetos, A y B, y si B hereda de A y ambos contienen la misma orden para un script, puedes asignar «None» al nombre del script en el objeto B (subclase) para evitar una orden cuando se trate de órdenes del objeto B. Por ejemplo, si F10 está definido para ambos objetos y F10 no se utiliza en el objeto B, puedes asignar el comando F10 del objeto B a»None» para que F10 pueda enviarse al sistema operativo. Esto se implementa en algunos módulos del núcleo de NVDA y en el complemento StationPlaylist Studio.

Eventos

Puedes pedirle a NVDA que haga algo si ocurre algo. Por ejemplo, puedes pedirle a NVDA que diga el nuevo nombre de un objeto cuando cambie su nombre, o decir el valor del nuevo elemento cuando se enfoque. Estas condiciones o acciones se denominan eventos.

Cuando se produce un evento, NVDA hace lo siguiente:

  1. Encuentra qué fue el evento (por ejemplo, una casilla de verificación obtiene el foco).
  2. Realiza acciones para el evento (por ejemplo, indica el nombre y el estado marcado de esta casilla de verificación).
  3. Pasa el evento por la cadena en caso de que otros objetos puedan tener acciones asociadas con el evento.

Dependiendo de dónde se defina el evento, necesitarás dos o cuatro cosas al definirlo. Si se declara desde el módulo de un complemento, las partes requeridas son el nombre del evento, el módulo en cuestión (self), el objeto y el siguiente manejador en caso de que el objeto tenga otros eventos asociados. Si se define como parte de un objeto, se requiere el nombre del evento y el objeto (self).

Una rutina típica de eventos se ve así:

    def event_eventName(self, obj, nextHandler):
        # Hace alguna acción.
        nextHandler()

Para eventos de objeto, usa:

    def event_eventName(self):
        # Rutina de evento.

De hecho, hemos conocido un»evento» real antes: event_NVDAObject-Init. Se trata de un evento especial (uno de los muchos eventos definidos en NVDA) disparado cuando NVDA se encuentra con un nuevo objeto y lo inicializa de acuerdo con tu entrada (consulta la sección sobre sobreescritura de propiedades de un objeto para obtener más información). Veamos otros eventos que puedes encontrar mientras escribes tu complemento.

Ejemplo 5: Anunciar el nombre cambiado de un control

El siguiente código procede de uno de los complementos de módulos de aplicación.

A continuación se muestra una rutina para un evento que le indica el nombre de algún texto en la pantalla cuando el texto cambie.

    def event_nameChange(self, obj, nextHandler):
        if obj.windowClassName == "TStaticText": ui.message(obj.name)
        nextHandler()

Como puedes ver, siempre que el nombre del objeto de texto cambie, NVDA anunciará el nuevo nombre al usuario. El evento»cambio de nombre» es uno de los muchos eventos para los que puedes definir acciones personalizadas en tu complemento (la lista completa va a continuación).

Nota: puedes definir eventos para cualquier objeto de tu elección, especialmente controles en un programa (donde puedes definir acciones personalizadas para eventos en tu módulo de aplicación). Si este es el caso, debes asegurarte de que el control cumpla ciertas condiciones establecidas, como el nombre, la función, etc., para permitir que NVDA mantenga un»ojo» en ese objeto específico.

Lista de posibles eventos

Esta es una lista de eventos comunes para los que puedes definir acciones personalizadas en tu complemento:

  • gainFocus: el usuario ha movido el foco a un control específico, o el usuario acaba de cambiar a un programa.
  • loseFocus: el contrario a gainFocus.
  • nameChange: el nombre de un control ha cambiado (mira arriba para ver un ejemplo).
  • valueChange: El valor del control, como por ejemplo el texto de un campo de texto, ha cambiado.
  • stateChange: Útil para realizar un seguimiento de si las casillas de verificación, los botones y el estado de otros controles (marcados, seleccionados, etc.) han cambiado.
  • Foreground: el objeto que nos interesa se ha convertido en la ventana de primer plano del programa.

Los eventos menos comunes que se utilizan en situaciones específicas incluyen:

  • typedCharacter: el usuario ha introducido algo en un teclado.
  • appModule_gainFocus: el usuario ha cambiado a la aplicación donde se define un módulo de aplicación. Los usos comunes incluyen añadir órdenes de pantalla táctil para aplicaciones específicas, anunciar información adicional sobre el estado actual de la aplicación y así sucesivamente.
  • appModule_loseFocus: el contrario a appModule_gainFocus.
  • descriptionChange: la descripción de un control ofrecida por la API de accesibilidad ha cambiado.
  • suggestionsOpened: se utiliza en los controles donde se puede escribir algo y las sugerencias se mostrarán en base al texto introducido. La implementación predeterminada en NVDA reproduce un sonido para indicar la aparición de sugerencias.
  • suggestionsClosed: el contrario a suggestionsOpened. Los eventos de sugerencias requieren NVDA 2017.3 y el objeto debe heredar de la clase NVDAObjects.behaviors.editableTextWithSuggestions.

Eventos dentro de objetos

La sección anterior describía las rutinas de eventos desde la perspectiva de un complemento. Esta es sólo una manera de definir los eventos. La otra manera es definir eventos desde dentro de los objetos, y es la misma que la anterior excepto que sólo toma un argumento (self).

Otros componentes

Además de objetos, scripts y eventos, puedes añadir otros componentes en tu complemento para trabajar con controles específicos. Por ejemplo, puedes utilizar un módulo textInfo (como NVDAObjects.NVDAObjectTextInfo) para trabajar con offsets de texto y texto en campos de edición y otros controles, o utilizar módulos externos de desarrolladores externos para tareas especializadas como el acceso al registro de Windows (_winreg) y otras. También puedes utilizar los módulos integrados de Python (time, functools, etc.) para operaciones avanzadas.

Un componente notable es text infos, una forma de permitir que los complementos tengan acceso al texto ubicado en los objetos. Puedes acceder al texto, desplazarte por él (por ejemplo, mediante líneas y palabras) y manipular partes mediante intervalos de marcación y desplazamientos. Para la mayoría de los complementos, no es necesario definir un text info personalizado, ya que la API de accesibilidad subyacente proporcionará la información adecuada.

Otro componente útil es la biblioteca de presentación de texto matemático (mathPres). Esto se utiliza para permitir que NVDA acceda al contenido matemático en lugares como navegadores web, así como para que los complementos definan capas personalizadas de presentación matemática y formas de acceder al contenido matemático en varias aplicaciones. Hasta ahora, la recuperación de contenido matemático se hace en texto marcado en MathML con MathPlayer instalado.

Si deseas almacenar los ajustes para tu complemento, utiliza ConfigObj o el gestor de configuración integrado de NVDA para almacenar los archivos de configuración y los ajustes. ConfigObj define los ajustes como una colección de diccionarios que se pueden actualizar en tiempo real. También puedes pasar un mapa de validación para permitir al gestor de configuración (config.conf) de NVDA validar los ajustes para tu complemento y dejar que los ajustes adicionales se conviertan en ajustes específicos del perfil.

Finalmente, puedes pedirle a NVDA que realice algunas rutinas mientras el complemento se esté cargando o finalizando. Esto se hace definiendo el método __init__ y terminate para el complemento. Dependiendo del tipo de extensión, utiliza:

  • Para extensiones globales:
    python
    def __init__(self):
    # La rutina a hacer cuando la extensión global se cargue.
    # ¡Precaución! siempre deberías llamar al método super primero para inicializar la clase padre correctamente.
    super(GlobalPlugin, self).__init__()
  • Para módulos de aplicación:
    def __init__(self, *args, **kwargs):
    super(AppModule, self).__init__(*args, **kwargs)
    # Lo que debería hacer NVDA cuando el módulo de aplicación se cargue.
  • Para terminar, independientemente del tipo de complemento:
    def terminate(self):
    # Hacer algo cuando el complemento termine.
    # ¡Precaución! nunca inicializes cualquier módulo del núcleo tal como GUI en el método terminate, ya que al hacerlo evitarás que NVDA salga correctamente.

Construyamos un complemento

Ahora que tenemos una visión general básica de los componentes de los complementos, estamos listos para crear algunos complementos sencillos. Pero primero, repasemos el proceso real de desarrollo de complementos, consejos de depuración, lo que se debe hacer y lo que no se debe hacer y otras cosas.

Consejos para planificación y desarrollo de complementos

A lo largo de los años, la comunidad de NVDA ha creado una serie de potentes complementos para los usuarios. Durante estos años, los redactores de complementos recopilaron algunos consejos útiles cuando se trata de escribir sus propios complementos. He aquí algunos de ellos:

  • Conocer a NVDA: es importante que te familiarices con las órdenes, conceptos y consejos de NVDA. Suscríbete a los grupos de usuarios de NVDA para aprender más sobre NVDA y conocer cómo funciona, ya que lo ampliarás a través de tus complementos.
  • Conocer el producto que tienes a mano: como se mencionó anteriormente, es importante que conozcas el software para el que estás escribiendo el módulo de aplicación, los sintetizadores y las pantallas braille para los que vas a escribir el controlador, etcétera.
  • Planificar con anticipación: si sabes que mantendrás tu complemento durante varios meses o años, es útil tener un plan y escribir el código del complemento para prepararte para futuras extensiones. Por ejemplo, trabajando en las características que necesitas implementar ahora, dividiendo partes de un programa en objetos y así sucesivamente.
  • Listo para depurar y probar tu complemento: escribir tu código de complemento es sólo una parte del desarrollo global del complemento. La otra parte es probar y depurar tu complemento para asegurarte de que los usuarios lo utilizan con errores mínimos. A medida que escribas tus complementos, asegúrate de probar tu código con regularidad.
  • Lo más importante, diviértete.

Qué hacer y qué no

Éstas son algunas cosas que deberías hacer y no hacer durante el desarrollo de un complemento:

  1. Habla con los usuarios: es importante recordar que tus complementos serán utilizados por los usuarios de NVDA de todo el mundo, por lo que es importante mantenerte en contacto con ellos para recopilar informes de errores y sugerencias.
  2. Pide ayuda si es necesario: Si estás atascado, puedes pedir ayuda a otros desarrolladores de complementos en cualquier momento para obtener soluciones o consejos, o si lo necesitas, solicita la colaboración de otros desarrolladores de complementos.
  3. Prueba tu complemento en más de un equipo: a veces, un error en un equipo puede ayudarte a resolver problemas en tu complemento en el equipo más adelante.
  4. No utilices código imaginativo sin entender tus intenciones: un error tipográfico o una indentación olvidada puede convertirse en un problema cuando depuras un complemento.
  5. Mantente al día con los cambios básicos de NVDA: a veces, puede que descubras que tu complemento no funcione debido a los cambios en el código del núcleo de NVDA. Asegúrate de leer la sección «Cambios para desarrolladores» en el documento Novedades de NVDA para mantenerte al día con los cambios de código que puedan afectar a tu complemento.

Preguntas frecuentes sobre componentes de complementos y desarrollo

P. Cuando trato de obtener un objeto usando un índice, éste obtiene uno después del índice que escribí.

Este es el efecto colateral de la indexación basada en cero (el recuento comienza en 0).

P. Al importar un módulo, NVDA dice que no puede localizar el módulo.

¿Escribiste el nombre correcto del módulo? ¿Extrajiste los archivos del módulo en la ubicación correcta? Intenta corregir el error tipográfico, observa la ruta de importación y vuelve a intentar importar.

P. ¿Cuál es la diferencia entre la revisión simple y la revisión normal y cuál debería usar?

La revisión simple excluye los objetos de diseño como ventanas, agrupaciones, etc. que se colocan para propósitos de diseño. La revisión normal también incluye esto. La elección de usar la revisión simple versus la revisión normal depende de tu situación.

P. La orden para mi app module no funciona en mi app module, sino que NVDA hace otra cosa.

Comprueba si está instalada una extensión global que utilice la orden. Primero, quita la extensión global e inténtalo de nuevo.

P. ¿Cómo puedo utilizar Win32 API en mi complemento u objeto?

Existe un documento escrito por un desarrollador de complementos que habla sobre el uso de Win32 API en su complemento. Selecciona [este enlace][Using Win32 API] para ver este documento.

P. ¿Cómo puedo crear diálogos en mi complemento?

Consulta la sección Diálogos interactivos.

P. ¿Puedo crear funciones y asignar variables fuera de las clases de módulo?

Sí. Esto es útil si necesitas hacer referencia a ellos desde dentro de la clase del complemento. Por ejemplo, es posible que tengas una función definida fuera de tu clase que necesitarás utilizar desde más de un método en una clase de extensión global.

P. Quiero guardar la configuración de usuario para mi complemento. ¿Se puede hacer esto?

Sí. Deberás utilizar una biblioteca que permita persistencia, como la biblioteca ConfigObj(configObj), JSON o Pickle para administrar la configuración. Algunos complementos (como OCR) que utilizan archivos de configuración almacenan su configuración como un archivo ini en la carpeta de configuración de usuario de NVDA. En el caso de las extensiones globales, puedes cargar y guardar la configuración de usuario desde el complemento cuando se crea el complemento (init) o se finaliza (terminate), respectivamente. Esto no se puede hacer fácilmente con los módulos de aplicación. Además, tendrás que proporcionar una alternativa (órdenes, diálogos, etc.) en la que los usuarios puedan configurar las opciones del complemento.

P. Tengo un script que llama a una función que se ejecuta durante mucho tiempo, y no puedo ejecutar órdenes de NVDA cuando se ejecuta mi script.

Una forma de solucionar esto es usando hilos (operaciones separadas e independientes en un programa) a través del módulo de hilos de Python. Para ello, crea un método que sepas que se ejecutará durante mucho tiempo y, a partir del script que llama a este método, crea un nuevo hilo (ver la documentación del módulo de hilos de Python) que se encargará de ejecutarlo. De esta forma, se pueden ejecutar otras órdenes de NVDA mientras el método adicional realiza su trabajo (consulta el módulo de reconocimiento de voz de Google para obtener un código de ejemplo).

P. Me gustaría portar un módulo escrito en la sintaxis de Python 2 para utilizarlo como un complemento de NVDA.

Esto no se puede hacer fácilmente. Un módulo práctico para este propósito es six, que permite ejecutar el código Python 2 y 3. NVDA 2019.3 y las versiones posteriores utilizan estrictamente Python 3.

P. Mi complemento usa componentes de interfaz gráfica, y después de instalar NVDA 2018.3, obtengo errores relacionados con WXPython.

NVDA 2018.3 usa WXPython 4, mientras que las versiones anteriores usan una versión más antigua de WXPython. Si quieres dar soporte a versiones anteriores de NVDA, debes emplear sentencias condicionales (if/else) y comprobaciones de versión, de tal forma que se elija el camino correcto al ejecutar el código.

P. Tras instalar NVDA 2019.1, los usuarios dicen que mis complementos no son compatibles.

NVDA 2019.1 introduce indicadores de compatibilidad (en ocasiones llamados rango de compatibilidad) de complementos que le dan la siguiente información:

  • Versión mínima de NVDA (minimumNVDAVersion): un complemento puede especificar la versión mínima de NVDA necesaria para funcionar. Esto viene bien si necesitas utilizar funciones introducidas o modificadas en una versión dada de NVDA sin dar soporte a versiones de NVDA más antiguas.
  • Última versión de NVDA donde se ha probado (lastTestedNVDAVersion): indica a NVDA la versión más alta probada con el complemento. Si no se establece este indicador, NVDA tratará tus complementos como incompatibles con la versión más reciente.

Las palabras entre paréntesis son claves de manifest. Con la llegada de NVDA 2019.1, estos indicadores de compatibilidad son obligatorios en todos los complementos.

P. Mi módulo de aplicación que estaba alojado en la carpeta appModules de la configuración de usuario no funciona en NVDA 2019.1.

Esto se debe a que NVDA 2019.1 ya no carga código de extensión personalizado situado en carpetas del directorio de configuración de usuario. Lee la sección de scratchpad para más detalles.

P. ¿Debería sustituir el diccionario de gestos y el atributo docstring de los scripts por el decorador script?

Es decisión tuya utilizar el antiguo diccionario de gestos y el docstring del script o el nuevo decorador script para definir su información. Se recomienda el decorador script para los complementos nuevos, ya que permite definir la información fácilmente sobre el terreno.

P. ¿Cuál es el estilo de código recomendado para los complementos?

El siguiente es el estilo de código para los complementos, que deriva del estilo de código usado en el lector de pantalla NVDA:

  • Usa tabuladores para la sangría.
  • Usa el estilo camel case para nombres de variable y función. Por ejemplo, unaFunción.

P. Me he dado cuenta de que NVDA no viene con todas las bibliotecas de Python.

En particular, NVDA no viene con asyncio. Debes incluir bibliotecas adicionales en la carpeta de componentes de tu complemento.

P. Necesito manipular variables de entorno, como las rutas del sistema.

Una forma eficaz de hacerlo es anexar la cadena deseada (tal como la ruta a un ejecutable que necesitas arrancar) al valor existente de la variable de entorno. Esto permite que tu complemento funcione con los valores modificados sin romper NVDA y/o los componentes de Windows mientras NVDA está en ejecución.

No incluimos preguntas frecuentes de programación o relacionadas con Python, ya que hay sitios que responden a preguntas sobre Python, tales como las relacionadas con el estilo de codificación. Consulta estos documentos si tienes problemas con el código Python.

Ahora que hemos cubierto los componentes básicos de un complemento, vamos a aprender acerca de cómo empaquetar lo que sabes en tus propios módulos de complemento: extensiones globales, módulos de aplicación, controladores y potenciadores.

Introducción a las extensiones globales

Una extensión global añade características disponibles en todas partes. Por ejemplo, si hay un control que se utilizará en muchas aplicaciones, puedes escribir una extensión global para manejarlas en NVDA. Otro ejemplo es la adición de funciones adicionales a NVDA que se puedan utilizar en todos los programas, como la capacidad de OCR, la gestión de marcadores de ubicación, etc.

Una extensión global es un archivo de código fuente de Python (.py) con el nombre de tu plugin. Por ejemplo, si estás añadiendo soporte para campos de edición enriquecidos en muchas aplicaciones, puedes nombrar la extensión como richEditSupport.py. Cuando los nombres, trata de ser breve para que puedas ver lo que hace tu extensión.

Importante: aunque los potenciadores puedan parecer idénticos a las extensiones globales (y se crearon potenciadores como extensiones globales en el pasado), no son lo mismo. Mira la sección de potenciadores para más detalles.

Plan de desarrollo típico para extensiones globales

Normalmente, una extensión global se desarrolla así:

  1. Tú o alguien sugiere una característica o soporte para un control en particular a través de diferentes programas.
  2. Planificas tu extensión global (consulta la sección sobre cuándo escribir o no escribir extensiones globales).
  3. escribes tu extensión global y la pruebas. Una vez hecha y probada, liberas la extensión.

Dado que las extensiones globales son archivos Python, puedes utilizar toda la potencia de python en el código de tu complemento. Además, dado que las extensiones globales tienen acceso a toda la potencia del código NVDA, como eventos, scripts y objetos, puedes utilizar los conceptos aprendidos en las secciones anteriores.

El código de la extensión global

Como se ha mostrado anteriormente, el procedimiento para escribir extensiones globales es el mismo que para escribir cualquier programa Python, excepto que importas globalPluginHandler y colocas tu código de complemento en una clase llamada GlobalPlugin que hereda de globalPluginHandler.GlobalPlugin (consulta el ejemplo en el capítulo introductorio). Si necesitas utilizar módulos de terceros, debes colocar el paquete en la misma carpeta que el archivo de extensión global e importar los módulos externos. A continuación, define los objetos (normalmente objetos de superposición), métodos, etc. en tu código.

Cuándo escribir o no escribir extensiones globales

Dado que las extensiones globales se utilizan en todas partes, es posible que tengas la tentación de escribir soporte para una sola aplicación utilizando solamente una extensión global. Sin embargo, este no es el caso. Existen otras pautas a tener en cuenta a la hora de decidir si escribir una extensión global o no:

Podrías considerar la posibilidad de escribir una extensión global si:

  1. Tú o un usuario deseais utilizar una cierta característica en todas partes.
  2. Necesitas soportar los mismos controles en diferentes aplicaciones, siempre que el control se comporte igual en estos programas.

No deberías escribir una extensión global si:

  1. Si deseas mejorar el soporte para una sola aplicación.
  2. Estás escribiendo soporte para sintetizadores de voz o pantallas braille.

Algunas cosas más que recordar sobre las extensiones globales

  • Cuando escribes scripts en tu extensión global, las órdenes que les asignes tendrán prioridad (buscadas primero). Por lo tanto, es importante consultar la guía del usuario de NVDA y la ayuda de otros complementos para minimizar los conflictos de órdenes.
  • Cada extensión global debe estar ubicado en el directorio globalPlugins en la estructura de carpetas del complemento.
  • Es posible usar más de un archivo Python en tu extensión global. Si este es el caso, necesitas ponerlos en una carpeta (el nombre debe ser el nombre de la extensión) dentro de la carpeta globalPlugins, con el archivo principal de la extensión llamado init.py.
  • Si necesitas hacer algo cuando la extensión global esté cargada (como por ejemplo, cargar la configuración de usuario), debes escribir un método init en tu clase de extensión. En este método, debes llamar primero al método init en super (globalPluginHandler.GlobalPlugin) antes de hacer otro trabajo de inicialización. Además, si necesitas hacer algo cuando la extensión global termine, define el método terminate.

Repasemos algunos ejemplos y ejercicios.

Ejemplo 1: Escribir braille computerizado utilizando el teclado QWERTY

Te vas a reunir con un cliente que utiliza el traductor braille de Duxbury (un popular programa de producción de documentos braille). Este cliente está trabajando con otro usuario de NVDA que desea escribir braille desde el teclado de su computadora en cualquier lugar. Basado en esto, decides escribir una extensión global, y encontraste un módulo que permite al teclado del ordenador actuar como un teclado braille usando una función.

La extensión global, llamada brailleWrite.py, se vería así:

# Un ejemplo de extensión global.

import qtbrl # El módulo de entrada braille.
import globalPluginHandler
from scriptHandler import script

class GlobalPlugin(globalPluginHandler.GlobalPlugin):
    brlentry = False # La entrada braille no está activa.

    @script(
        description="Enciende o apaga la entrada Braille.",
        gesture="kb:NVDA+X"
    )
    def script_toggleBrailleEntry(self, gesture):
        self.brlentry = True if not self.brlentry else False # alternar modo de entrada Braille.

Con estos antecedentes en mente, prueba algunos de los ejercicios cortos que se presentan a continuación.

Ejercicios

  1. Escribe una extensión global llamada nvdaVersion.py para decir la versión actual de NVDA cuando se pulse NVDA+Shift+V.
  2. Un usuario quiere oír la hora anunciada cada minuto. Usando el reloj en la bandeja del sistema, escribe una extensión global para anunciar cuándo cambia la hora (pista: es necesario usar un evento y comprobar la función del objeto reloj).

Introdución a los módulos de aplicación

Un módulo de aplicación mejora el soporte para un programa en particular. Por ejemplo, puedes escribir un módulo de aplicación que agregue órdenes de conveniencia para leer varias partes de la pantalla, o puedes definir cómo debe comportarse un control particular en un programa.

Un módulo de aplicación es un archivo Python (.py) con el nombre correspondiente al nombre ejecutable de un programa. Por ejemplo, un módulo de aplicación para Winamp se llama winamp.py ya que el nombre ejecutable de Winamp es winamp.exe.

El mismo NVDA viene con varios app modules, como Winamp, Adobe Reader, programas de Microsoft Office y así sucesivamente.

Diferencias entre módulos de aplicación y extensiones globales

A primera vista, los módulos de aplicación pueden tener el mismo aspecto que cualquier extensión global. Sin embargo, los módulos de aplicación tienen propiedades adicionales de las que carecen las extensiones globales, incluyendo:

  • En lugar de globalPluginHandler, necesitas importar appModuleHandler. La clase a implementar es AppModule(appModuleHandler.AppModule).
  • Los módulos de aplicación se almacenan en la carpeta appModules de la estructura de directorios del complemento y reciben el mismo nombre que el nombre ejecutable del programa.
  • Puedes pedir a NVDA que entre en el modo de suspensión en un programa donde NVDA no hable o no tenga nada en braille mientras estés utilizando el programa, y cualquier orden de teclado que pulses será manejado directamente por el programa. Esto se hace estableciendo el atributo sleepMode en la clase AppModule a True.
  • Algunas aplicaciones presentan información en forma de página web, y si esto ocurre, se puede usar el modo exploración. Sin embargo, en NVDA 2019.2 y versiones posteriores, esta capacidad se desactiva en aplicaciones como Skype. Para restaurar la funcionalidad del modo exploración, es necesario establecer disableBrowseModeByDefault a False.
  • La rutina event_NVDAObject_init sólo está disponible en los módulos de aplicación.
  • Puedes pedir a NVDA que vigile un objeto para manejar eventos aunque el usuario esté usando otra aplicación.

Proceso y estrategias de desarrollo de módulos de aplicación

Así se desarrolla un módulo de aplicación típico:

  1. Tú o un usuario solicitais soporte mejorado para un programa.
  2. Si es posible, ponte en contacto con el proveedor de la aplicación (programador) para solicitar mejoras de accesibilidad para el programa por su parte.
  3. Con o sin la cooperación del proveedor de aplicaciones, examinarías cómo funciona el programa y las áreas de la pantalla que necesitan ser leídas.
  4. Escribe y prueba el módulo de aplicación (con usuarios) hasta que esté listo para su lanzamiento.

A medida que escribas app modules, prueba estos consejos:

  1. Utiliza objetos para representar partes de un programa. Esto se hace en dos pasos: define el control para las partes de un programa a través de objetos (heredando de algún objeto como IAccessible), luego utiliza la rutina chooseNVDAObjectOverlayClasses para decirle a NVDA que trabaje con tu objeto personalizado cuando trabaje con ese control. Mira la sección de clases de superposición para consejos.
  2. Si es posible, prueba tu app module utilizando dos o más versiones del programa para asegurarte de que tu módulo de aplicación funciona con esas versiones.
  3. No deberías incorporar todas las características deseadas en la versión 1.0 – deja algunas de ellas para una futura versión.

Ejemplo 2: Módulo de aplicación simple en el Bloc de notas

Supongamos que deseas averiguar qué línea estás editando en el Bloc de notas. Asumiendo que el Bloc de notas mostrará la barra de estado en todo momento, deseas asignar una combinación de teclas para leer el número de línea actual.

El módulo de aplicación para el Bloc de notas se vería así:

# Módulo de aplicación de ejemplo para bloc de notas, notepad.py.
import appModuleHandler
import api
import ui
from scriptHandler import script

class AppModule(appModuleHandler.AppModule):

    @script(gesture="kb:NVDA+S")
    def script_sayLineNumber(self, gesture):
        # Supongamos que el número de línea sigue el formato "  ln 1".
        lineNumList = api.getStatusBar().getChild(1).name.split()
        lineNum = lineNumList[0]+lineNumList[1]
        ui.message(lineNum)

Por lo tanto, cada vez que ejecutes el Bloc de notas, cuando pulses NVDA+S, NVDA dirá el número de línea.

Ejemplo 3: Silenciar NVDA en Openbook

Openbook es un programa de lectura y escaneo de Freedom scientific. Como Openbook proporciona voz, puedes decir a NVDA que entre en modo latente mientras Openbook (openbook.exe) se ejecute usando el módulo de aplicación siguiente:

# Silenciar a NVDA en openbook, openbook.py.
import appModuleHandler

class AppModule(appModuleHandler.AppModule):

    sleepMode = True

Con esa única línea de código, NVDA entrará en modo latente en ese programa (sólo debes hacerlo si el programa proporciona soporte de voz y/o braille por sí solo).

Ejemplo 4: Anunciar cambios de propiedad de control mientras se usa otra aplicación

Puedes pedir a NVDA que gestione eventos específicos mientras se concentra en otra aplicación. Esto se hace llamando a eventHandler.requestEvents en el método init del módulo de aplicación. Para poder invocar esto, necesitas el ID de proceso (PID) para la aplicación, el nombre de clase de ventana para el objeto y el nombre del evento a tratar.

El siguiente código permite a NVDA anunciar los cambios de valor mientras se centra en otra aplicación.

# Ejemplo de módulo de aplicación para un programa de mensajería.
# El objeto que deseamos rastrear tiene el nombre de la clase de ventana "MessengerWindow".

import appModuleHandler
import eventHandler

class AppModule(appModuleHandler.AppModule):

    def __init__(self, *args, **kwargs):
        super(AppModule, self).__init__(*args, **kwards)
        eventHandler.requestEvents("valueChange", self.processID, "MessengerWindow")

Una vez definidos, aunque estén enfocados en otra aplicación, se anunciarán nuevos mensajes (valores).

Propiedades y métodos útiles de los módulos de aplicación

sleepMode y processID son sólo dos de los muchos atributos que tienen los módulos de aplicación. Otras propiedades y métodos útiles utilizados en los módulos de aplicación incluyen los siguientes:

  • appName: el nombre de la aplicación (normalmente el nombre del ejecutable).
  • productName: Registra el nombre real del producto para la aplicación.
  • productVersion: Registra la versión de la aplicación.
  • is64BitProcess: si es true, la aplicación es un proceso de 64 bits (sólo true si se está usando una aplicación de 64 bits en versiones Windows de 64 bits).
  • dumpOnCrash: si estás depurando aplicaciones que se bloquean con frecuencia, puedes llamar a esta función para permitir que NVDA guarde un volcado de esta aplicación en el directorio de archivos temporales para que puedas recuperarlo más tarde.
  • disableBrowseModeByDefault: algunas aplicaciones son, en esencia, documentos web y, como tales, harán que se active el modo exploración. Debes configurar este valor a True si quieres forzar a NVDA a que trate la aplicación adecuadamente como una aplicación. En otras palabras, esto desactiva el modo exploración.
  • appPath: registra la ruta al ejecutable de la aplicación.
  • appArchitecture: la arquitectura de procesador prevista para la aplicación. Por ejemplo, x86, AMD64, ARM64.
  • isWindowsStoreApp: aplicable en Windows 8, 8.1 y 10. Determina si la aplicación está alojada en un contenedor de aplicaciones, tal y como hacen las aplicaciones de la tienda.

Y otras propiedades. Escribe dir(obj.appModule) desde la consola Python para obtener la lista completa.

Otros comentarios sobre los módulos de aplicación

Aquí hay otras observaciones sobre los módulos de aplicación:

  • Si encuentras que las diferentes versiones del programa están dispuestas de forma diferente, por ejemplo, las ubicaciones para los controles son diferentes, entonces puedes escribir código que pueda manejar estos casos. Hay una serie de opciones entre las que puedes elegir: añadir algunas constantes en tu módulo de aplicación para manejar diferentes ubicaciones de objetos, escribir código para estos controles (uno por versión) en objetos personalizados que se seleccionarán en el método de clases de superposición, etc.
  • Si es posible, intenta trabajar con los servicios que proporciona la aplicación, como los métodos COM (Component Object Model) (por ejemplo, el módulo de aplicación de Outlook), API’s que proporciona la aplicación (como Winamp) y así sucesivamente.
  • Para soportar una aplicación que funcione igual que otro programa (especialmente si estás escribiendo un módulo de aplicación para una versión de 64 bits de un programa de 32 bits para el que has escrito un módulo de aplicación), utiliza el siguiente fragmento de código (llamado aliasing):\
    from appName import *\
    donde appName es el nombre del módulo de aplicación y * (asterisco o estrella) significa importarlo todo. Para un ejemplo de esto, mira los módulos de aplicación de NVDA para Miranda32 y Miranda64.
  • Si deseas ampliar un módulo de aplicación que venga con NVDA, utiliza el siguiente fragmento de código (llamado sobreescritura del módulo incorporado):\
    from nvdaBuiltin.appModules.appName import *\
    Donde appName es el módulo de aplicación que deseas ampliar. Por ejemplo, si deseas admitir diferentes controles en la calculadora de Windows (calc.py), utiliza:\
    from nvdaBuiltin.appModules.calc import *\
  • Muchos módulos de aplicación (tanto integrados como de terceros) utilizan nombres de applicación como parte del nombre de una constante (un valor que no cambia). Por ejemplo, en el módulo Powerpoint de NVDA (powerpnt.py), muchas constantes comienzan con «PP». Del mismo modo, en el módulo de aplicación de Station Playlist Studio, muchas constantes en el archivo del módulo de aplicación (splstudio.py) comienzan con «SPL». Esto se usa para recordarte dónde se utilizan estas constantes.

Controladores

Un controlador permite que un software como NVDA se comunique con el hardware o utilice la funcionalidad proporcionada por otro software. Normalmente, cuando la gente habla de controladores, usualmente se refiere a un programa instalado en un ordenador que permite que el software se comunique con un componente hardware específico, como tarjetas de vídeo, teclados y demás.

En NVDA, los controladores se refieren a módulos que NVDA puede utilizar para comunicarse con un sintetizador de voz o una pantalla braille. Por ejemplo, puedes escribir un controlador de pantalla braille que envíe una salida braille a la pantalla braille, o pedirle a tu sintetizador que cambie de idioma y proporcione opciones configurables.

Componentes del controlador

Todos los controladores (independientemente del dispositivo de destino o software que se admita) importan los módulos apropiados, como synthDriverHandler. En muchos controladores, la mayoría del código del controlador trata sobre la comunicación con el dispositivo o software de destino, y todos los controladores deben definir la clase de controlador (synthDriverHandler.SynthDriver o braille.BrailleDisplayDriver).

Todas las clases de controladores, como mínimo, deben contener:

  • Identificador del controlador: una cadena de caracteres como «oneCore» que identifique de forma unívoca a un controlador determinado.
  • Nombre amigable: el nombre que aparecerá en los cuadros de diálogo de sintetizador o de configuración braille.
  • Indicador de disponibilidad: un método de clase llamado check que indica a NVDA que el controlador está listo para su uso.
  • Gestor de conexiones: conjunto de rutinas que indican a NVDA cómo localizar un determinado sintetizador o una pantalla braille.
  • Manejador de salida: una función que realiza el procesamiento de salida real. Para los sintetizadores, el método speak debe estar presente; para las pantallas braille, se necesita el método display.

Para los sintetizadores de voz, es necesario tener:

  • Opciones de timbre de configuración del sintetizador: una lista de ajustes de los sintetizadores que los usuarios pueden ajustar mediante el timbre de configuración del sintetizador.

Para las pantallas braille:

  • Manejadores de entrada: si se desea la entrada de la pantalla braille, el autor del controlador debe implementar detectores para órdenes tales como teclas braille, botones de enrutamiento y hardware adicional.
  • conjunto de órdenes: un mapa que identifique las asignaciones de órdenes de NVDA para varios botones de hardware de la pantalla.

Unas pocas cosas importantes que recordar antes, durante y después del desarrollo de controladores

  • Antes de escribir un controlador, asegúrate de tener el software y/o hardware necesarios.
  • Asegúrate de estudiar los protocolos y las API’s utilizadas por un sintetizador de voz o una pantalla braille (esto es más importante para las pantallas braille, que pueden implementar diferentes protocolos).
  • Asegúrate de saber cómo comunicarte con el equipo o software: puertos, IDs USB, direcciones Bluetooth, configuración del puerto serie, dlls, etc.
  • Trabaja con otra persona que utilice el equipo o el software para el que estás escribiendo el (los) controlador (es).

Pasos típicos de desarrollo de un controlador

Cuando escribas controladores, es posible que desees seguir los pasos recomendados para el desarrollo de módulos de aplicación (planificación, comunicación con los proveedores, pruebas de usuario, etc.). Sin embargo, dado que los controladores requieren un conocimiento íntimo del hardware y/o del software, deberías dedicar más tiempo a probar tu controlador. Esto es más importante si estás escribiendo un controlador para una pantalla braille que puede enviar órdenes arbitrarias (órdenes braille, botones de enrutamiento, etc.).

Potenciadores

Un potenciador es un módulo que ayuda a ciertos grupos de usuarios a utilizar ordenadores de una forma más eficiente. Aquí se pueden incluir seguimiento del cursor, ampliación y otras mejoras.

Actualmente NVDA puede proporcionar asistencia mediante proveedores de mejoras de visión. Un proveedor de mejoras de visión es un potenciador que permite a personas con vista o baja visión usar ordenadores de manera efectiva trabajando en tándem con NVDA. Los potenciadores pueden incluir resaltado del cursor, efecto de cortina de pantalla y ampliación de ciertas zonas de la pantalla. Estos potenciadores se alojan bajo la carpeta «visionEnhancementProviders»y se definen con la clase «VisionEnhancementProvider», que hereda de «vision.providerBase.VisionEnhancementProvider».

Nota: como de momento sólo se soporta un potenciador, nos referiremos al proveedor de mejoras de visión en las siguientes secciones.

Componentes potenciadores

Un potenciador como el proveedor de mejoras de visión se parecerá a una combinación de extensión global y controlador (mira las secciones anteriores para conocer más sobre ellos). En la mayoría de los casos, se pueden aplicar los componentes usados en un controlador. Estos incluyen:

  • Identificador del potenciador: una cadena de caracteres como «screenCurtain» que identifique de forma unívoca a este potenciador.
  • Nombre amigable: el nombre que aparecerá en el diálogo Visión.
  • Mejoras soportadas: un conjunto congelado de roles de mejora que introducirá este proveedor.
  • Comprobación de inicio: un procedimiento que se aseugrará de que NVDA se ejecuta en un entorno específico donde el potenciador sería más útil. Por ejemplo, se puede buscar una versión de Windows específica.
  • Inicio y apagado: un constructor de clase que indica al potenciador que se ponga en línea y un método «terminate» que lo desactiva.
  • Reacción a eventos: si un potenciador quisiera responder a diversas acciones realizadas por los usuarios, puede especificar acciones como consecuencia.

Unas pocas cosas importantes que recordar antes, durante y después del desarrollo de potenciadores

  • Asegúrate de hablar con los usuarios que planean utilizar tu potenciador. Para los proveedores de mejoras de visión, comprueba que las mejoras son las que los usuarios quieren.
  • Prueba tus potenciadores con muchos usuarios para asegurarte de que funcionan correctamente.

Pasos típicos de desarrollo de un potenciador

Cuando escribas potenciadores como los proveedores de mejoras de la visión, puedes querer seguir los pasos recomendados para el desarrollo de controladores y extensiones globales (planear, hablar con usuarios, pruebas con usuarios, etc.). Ya que estos módulos afectarán globalmente a la experiencia con NVDA (incluso al cambiar entre perfiles de configuración), asegúrate de que el potenciador no degrada la experiencia de usuario de la gente que no lo necesite.

Compartiendo tu complemento y experiencia con otros

Una vez que hayas terminado de desarrollar tus complementos, es posible que desees compartir tu código con otros. A lo largo del camino, podrías aportar tu conocimiento para que otros puedan beneficiarse de tus experiencias.

Este capítulo está diseñado para proporcionar orientación sobre la liberación y el mantenimiento de complementos, así como para conectarte con los usuarios de complementos y otros desarrolladores del núcleo y de complementos de NVDA de todo el mundo.

La lista de complementos de NVDA

Si deseas mantenerte en contacto con los usuarios de tu complemento o deseas aprender o contribuir con tu complemento a otros, suscríbete a[la lista de complementos de NVDA][4]. Esta es una lista de poco tráfico dedicada a discutir los complementos actuales y futuros, así como a revisar otros complementos creados por miembros de la comunidad o hacer que otros desarrolladores de complementos revisen sus complementos en todo el mundo.

El sitio web y el repositorio de código de los complementos de la comunidad de NVDA

Para descargar u obtener más información sobre los diversos complementos creados por los usuarios de NVDA, visita el [sitio web de los complementos de la comunidad de NVDA]. Puedes examinar los complementos actualmente disponibles, ver los complementos en desarrollo y leer las directrices de desarrollo de complementos.

Para los desarrolladores que deseen leer el código que impulsa a varios complementos, puedes buscar repositorios de complementos almacenados en GitHub. Los desarrolladores de complementos de la comunidad de NVDA utilizan Git para el control de versiones.

Algunos de los repositorios útiles y educativos son:

  • [add-on template]: este es el repositorio de código fuente para la plantilla de complementos de la comunidad.
  • Place markers de Noelia Martínez: agrega funcionalidad de marcadores de posición y proporciona un buen ejemplo sobre el uso de text infos.
  • Windows 10 App Essentials de Joseph Lee: proporciona soporte mejorado para Windows 10 y varias aplicaciones universales, considerado un clásico en la forma en que las extensiones globales y módulos de aplicaciones trabajan juntos e incluye ejemplos de clases de superposición y comportamientos de control que se derivan de los objetos UI Automation.
  • Read Feeds de Noelia Martínez: facilita el descubrimiento de feeds en varios sitios web e incluye un ejemplo sencillo de almacenar y validar configuraciones adicionales.
  • NVDA Remote Support de Christopher Toth y Tyler Spivey: un popular complemento utilizado para la solución remota de problemas y soporte técnico, proporciona ejemplos de cómo se utilizan varias bibliotecas externas de Python.
  • StationPlaylist de Joseph Lee: mejora el uso de StationPlaylist Studio, proporciona ejemplos interesantes sobre las clases de superposición y la API de aplicaciones, el uso de hilos y diálogos en un complemento y otras interfaces de usuario.

Envío de tu complemento a revisión

Si quieres enviar tu complemento para que se distribuya en el sitio web de complementos, debes solicitar la revisión por parte de un miembro de la comunidad de complementos como se describe:

  1. Si no lo has hecho, suscríbete a la lista de correo de complementos de NVDA.
  2. Solicita a la comunidad que revise tu complemento. Asegúrate de indicar su nombre, autor, una descripción breve del complemento y el repositorio público con el código si es posible.
  3. Los miembros de la comunidad revisarán y harán comentarios sobre tu complemento, incluyendo aspectos como licencia y copyright, experiencia de usuario, documentación y seguridad.
  4. Si tu complemento es aceptado para su distribución en el sitio web de la comunidad de complementos, un responsable de mantenimiento del sitio web de complementos de la comunidad lo registrará. Esto incluye la creación de una nueva entrada para tu complemento y la preparación del soporte de traducción.
  5. Opcionalmente, después de que se acepte tu complemento, puedes solicitar a la comunidad que haga revisiones más detalladas. Aquí se pueden incluir la mejora de mensajes, disposición de la interfaz gráfica, auditorías de seguridad avanzadas, pruebas de compatibilidad y migración a versiones de Python más recientes.

Lista de comprobación de revisión de complementos

Tu complemento:

  1. Debe estar liberado bajo la licencia GNU General Public License (GPL), versión 2 o posterior, o una equivalente que permita a un software GPL, como NVDA, incorporar tu código mientras se ejecuta.
  2. Debe estar escrito en Python 3. La compatibilidad con Python 2 es opcional, pero no se recomienda a partir de la API base de NVDA 2021.1.
  3. Debe ser compatible con la última liberación de la API base (en mayo de 2021, la API base es 2021.1).
  4. Los mensajes que se presenten a los usuarios deberían hacerse traducibles (usa la función de Gettext _() para hacer que los mensajes sean traducibles). Asegúrate de acompañar los mensajes traducibles con comentarios para traductores.

Ejemplos avanzados de código y características

Este capítulo está en construcción. Si hay algo que desees ver aquí, o tienes un comentario o corrección, contacta con alguno de los autores o solicítalo en la lista de correo de complementos de NVDA.

Diálogos interactivos

Introducción

Para presentar información a tus usuarios, ui.message y ui.browseableMessage normalmente son suficientes. Sin embargo, si el usuario necesita enviar información de vuelta a tu complemento, no te van a ayudar. Por este motivo necesitas los diálogos. La creación de diálogos y muchas consideraciones sobre su uso se encuentran fuera del ámbito de este documento, pero podemos ayudarte en tus primeros pasos.

Para crear y usar diálogos, debes importar dos módulos: gui (import gui) y WXPython (import wx). Lee su documentación para conocer la impresionante cantidad de opciones a tu disposición.

Para abreviar:
* Gui proporciona métodos para construir y mostrar algunos diálogos estándar.
* Wx proporciona la implementación real de esos diálogos, y proporciona muchas de las constantes y opciones extendidas que necesitas realmente para usar de forma eficaz los diálogos.

Ejemplo 1: un diálogo básico

El siguiente código creará un diálogo básico, proporcionando al usuario dos opciones muy familiares. Mira las notas que hay después del código para entender qué ocurre.

import wx  # Lo necesitamos para trabajar con diálogos y ventanas

import gui  # Lo necesitamos para trabajar con diálogos y ventanas
import globalPluginHandler
from scriptHandler import script

class GlobalPlugin(globalPluginHandler.GlobalPlugin):

    @script(
        gesture="kb:nvda+shift+t",  # Configura el atajo
        description="Ejecuta un ejemplo de la guía de complementos"  # Ayuda de entrada de NVDA
    )
    def script_makeSillyWindow(self, gesture):  # Un método script normal de la clase GlobalPlugin
        def showSillyWindow():          # Se define una función interna
            gui.messageBox("¡Aviso! Estás a punto de no hacer nada. Pero estarás haciéndolo con una ventana de diálogo muy importante. ¿Deseas continuar?",
                "Ventana de pregunta tonta", wx.OK | wx.CANCEL | wx.ICON_WARNING)
        wx.CallAfter(showSillyWindow)

Si guardas lo anterior como extensión global y la cargas en NVDA, y a continuación pulsas NVDA+shift+t, deberías encontrarte en una nueva ventana.
La ventana es generada por la llamada a gui.messageBox. El primer parámetro es el texto de la ventana, el segundo parámetro es su título, y el tercero contiene una lista de indicadores constantes que wx usa para generar los botones y otros atributos del diálogo.
* wx.ICON_WARNING, hace que la ventana se comporte como una advertencia de Windows.
* wx.OK, hace que la ventana muestre el botón Aceptar estándar.
* Y wx.CANCEL hace lo mismo con Cancelar.

Por desgracia, si llamamos a gui.messageBox directamente, generalmente hará que NVDA se cuelgue, a menos que se ejecute desde el hilo principal. Para resolver ese problema, usamos wx.CallAfter, que mete en cola el diálogo para que se muestre desde el hilo principal. Sin embargo, wx.CallAfter no puede llamar a gui.messageBox directamente: debe hacerlo a través de otro método o función. En este ejemplo lo solucionamos mediante una función anidada cuyo único propósito es generar una sola vez ese diálogo.

Ejemplo 2: un diálogo con tres caminos

A continuación se encuentra un ejemplo más complejo, que muestra cómo devolver información de un diálogo en función del botón que se ha pulsado. Ten en cuenta que existen muchas más constantes que puedes utilizar, y tipos de diálogos distintos a messageBox. Esta sección es sólo una introducción al tema para dar los primeros pasos.

import wx

import gui
import globalPluginHandler
from scriptHandler import script

class GlobalPlugin(globalPluginHandler.GlobalPlugin):

    @script(
        gesture="kb:nvda+shift+t",  # Configura el atajo
        description="Ejecuta un ejemplo de la guía de complementos"  # Ayuda de entrada de NVDA
    )

    @script(gesture="kb:nvda+shift+t")      # Configura la tecla
    def script_askPointlessQuestion(self, gesture):
        def askTheQuestion():           # Definir una función interna
            result = gui.messageBox("""¡Aviso! Estás a punto de responder una pregunta tonta.
¡Por suerte tienes esta fantástica ventana para hacerlo!
¿Deseas continuar?""",
                "Pregunta Tonta", wx.YES_NO | wx.NO_DEFAULT | wx.CANCEL)
            if result == wx.YES: answer = "sí"
            elif result == wx.NO: answer = "no"
            else: return
            gui.messageBox("Has respondido %s a la pregunta tonta." %(answer), "Respuesta Tonta", wx.OK)
        wx.CallAfter(askTheQuestion)

Usar el registro

Hay más cosas en profundidad que puedes hacer con el registro, y que se mostrarán aquí.
Sin embargo, la mayor parte del tiempo, todo lo que querrás hacer es escribir un mensaje básico en el registro. Para hacerlo, puedes importar el objeto instanciado de NVDA log, y llamar a sus métodos como haces con cualquier otro.

import globalPluginHandler
from scriptHandler import script
from logHandler import log  # Esto es lo que necesitas para registrar
from datetime import date

class GlobalPlugin(globalPluginHandler.GlobalPlugin):

    @script(
        gesture="kb:nvda+shift+l",  # Configura el atajo
        description="Ejecuta un ejemplo de la guía de complementos"  # Ayuda de entrada de NVDA
    )
    def script_captainsLog(self, gesture):
        today = date.today().strftime("%Y.%m.%d")
        log.info(f"Registro de NVDA. Fecha en la Tierra, {today}.")
        log.warning("Estos son los complementos del lector de pantalla NVDA.")
        log.debugWarning("Su misión continua. ¡Buscar nuevas oportunidades para mejorar vidas!")
        log.debug("¡Empoderar a los usuarios!")
        log.error("¡Y acceder a software que ningún otro lector de pantalla había hecho accesible antes!")

Si ejecutas lo anterior en el scratchpad de NVDA o como extensión global, y lo invocas pulsando la secuencia de teclas NVDA+shift+l, recibirás entre 0 y cinco entradas en el registro, dependiendo de la configuración del nivel de registro que tengas en las opciones generales. Por ejemplo, si tu nivel de registro está configurado en «Depuración», encontrarás todas, pero sólo cuatro de ellas si el nivel de registro está configurado en «Aviso de depuración».

Nota: mientras desarrollas complementos, normalmente es deseable disponer de la máxima depuración posible, de tal forma que puedas recopilar información cuando las cosas vayan mal. Por este motivo, puedes querer configurar tu nivel de registro en «Depuración».

Concurrencia

Introducción

Si un procedimiento en un complemento se ejecuta durante un largo periodo de tiempo, NVDA podría parecer congelado o dejar de responder. Aquí es donde entran en escena los hilos – ejecutando una tarea de larga duración en un hilo diferente, permitiendo que NVDA siga respondiendo.

Aunque Python soporta hilos, sólo puede ejecutar una tarea a la vez debido al bloqueo global del intérprete (GIL). Por tanto, en ordenadores con varios núcleos de procesador, se aconseja el uso de procesos (mediante el módulo multiprocessing) para permitir que los intérpretes de Python se ejecuten en todos los núcleos. Sin embargo, esta solución introduce latencia y sobrecarga, por lo que en muchos escenarios se prefieren los hilos.

Escenarios de concurrencia

Los hilos son útiles si

  • Necesitas trabajar en algo mientras esperas un resultado. Por ejemplo, si una extensión global necesita acceder a Internet para diversas tareas, se puede usar un hilo separado para obtener información en línea mientras NVDA está ocupado con algo más.
  • Monitorizar tareas en segundo plano sin interrumpir a NVDA. Por ejemplo, una clase superpuesta definida en un módulo de aplicación puede usar un hilo para anunciar información de la pantalla mientras esta cambia en segundo plano.
  • Ejecutar tareas periódicamente. Por ejemplo, un sintetizador de voz puede usar un hilo temporizador para determinar si un sintetizador hardware está listo o no.

Ejemplos de concurrencia

Todos los ejemplos usan el módulo threading a no ser que se indique lo contrario.

Descargar contenido de un sitio web mediante una extensión global:

import threading
import urllib
# Otras partes de la extensión global.

def downloadContent(address):
    return urllib.urlopen(address)

class GlobalPlugin(globalPluginHandler.GlobalPlugin):

    def script_downloadContent(self, gesture):
        downloadThread = threading.Thread(target=downloadContent, args=(someWebsite,))
        downloadThread.start()
        downloadThread.join()

Necesitamos usar un hilo separado para acceder al contenido web porque urllib (urllib.request en Python 3) bloquea, haciendo que NVDA parezca colgarse.

Anunciar un mensaje diez segundos después de pulsar una tecla en una aplicación:

Hay dos temporizadores que puedes usar: threading.Timer o wx.Timer. El siguiente ejemplo de módulo de aplicación usa threading.Timer.

import threading
import ui
# Otras partes de un módulo de aplicación.

class AppModule(appModuleHandler.AppModule):

    def script_saySomething(self, gesture):
        messageTimer = threading.Timer(10.0, ui.message, args=("este es un mensaje de temporizador", ))
        messageTimer.start()

Una limitación de threading.Timer es que no soporta tareas repetitivas, para ello deberás usar wx.Timer.

Almacenar y usar información persistente más allá de tu complemento

Próximamente

Información miscelánea

Por favor, añade material adicional a esta guía. En el Equipo de Complementos de NVDA agradecemos las contribuciones de otros desarrolladores y usuarios de complementos por todo el mundo.

Apéndices

Apéndice A: Diccionario de términos de complemento

Los siguientes términos se utilizan a lo largo de esta guía de desarrollo, así como en la comunidad de complementos para referirse a complementos, procesos de desarrollo, etcétera.

  • Complemento: una extensión para un programa. En el mundo de NVDA, un complemento se refiere a paquetes adicionales que los usuarios pueden instalar para ampliar la funcionalidad de NVDA, mejorar la compatibilidad con una aplicación o añadir nuevos sintetizadores de voz o pantallas braille.
  • Aplicación: sinónimo de programa.
  • Módulo de aplicación: un módulo que añade o mejora el soporte para un programa.
  • API: interfaz de programación de aplicaciones. En inglés, Application Programming Interface.
  • Clase base: clase padre de un objeto.
  • Pantalla Braille: un hardware o software que emite texto a través de una salida braille táctil y/o permite a los usuarios ingresar texto a través de un teclado braille u otros mecanismos de entrada.
  • Módulo integrado: un módulo que viene con NVDA y que puede ser sobreescrito o ampliado por los complementos opcionalmente.
  • Caret: cursor que se muestra en pantalla, generalmente visto al editar texto o al navegar por documentos.
  • clase: definición de un objeto.
  • Controlador: programa que permite que otro programa se comunique con cierto software o hardware.
  • Potenciador: módulo que añade mejoras de usabilidad en tándem con NVDA.
  • Evento: una rutina llamada cuando suceden ciertas cosas, como la introducción de caracteres, cambios en el texto de la pantalla, una casilla de verificación marcada y así sucesivamente.
  • Función: un pedazo de código que realiza algo con uno o más parámetros de entrada y opcionalmente devuelve algo.
  • Gesto: una entrada como pulsaciones de teclas, gestos en la pantalla táctil, teclas braille, etcétera.
  • Extensión global: un módulo que añade características en todas partes.
  • Módulo: conjunto de variables, funciones, clases y otros dentro de un fichero.
  • Objeto: una definición de clase con vida propia.
  • Script: una función que se adjunta (asigna) a un gesto.
  • Sintetizador de voz: un software o hardware que convierte texto y varias órdenes de voz enviados a la salida de voz.
  • Variable: un marcador de posición temporal para algunos datos.

Apéndice B: Programación y conceptos de Python que todo desarrollador de complementos necesita saber

La siguiente lista resume los conceptos que todos los desarrolladores de complementos necesitarán conocer cuando escriban complementos.

  • API: un conjunto de documentación y muestras de código utilizado para permitir que un programa o hardware interactúe con otro software o hardware.
  • Clase base: una clase que proporciona métodos base, atributos y propiedades para que otros objetos hereden y amplíen. Sinónimo de superclase y clase padre.
  • Clase hija: una clase que deriva su poder de una o más clases básicas.
  • Clase: definición de objetos, incluyendo métodos, atributos y comportamientos esperados. Todas las clases y atributos de Python son públicos; pueden «llegar a ser privados» mediante el uso de convenciones de nomenclatura como prefijar un nombre de variable con subrayados (_).
  • Bloque de código: colección de código.
  • Compilación: traducción de un lenguaje de programación de alto nivel a un lenguaje de programación de bajo nivel apto para la ejecución en máquina.
  • Programación dirigida por eventos: un paradigma de programación basado en el seguimiento, la reacción y el manejo de eventos.
  • Excepción: una o más circunstancias en tiempo de ejecución que impiden el funcionamiento normal de un programa, como negarle el acceso a un recurso, problemas de uso del nombre en el código y otros casos.
  • GUI: Interfaz gráfica de usuario.
  • Manejador: una referencia opaca a un recurso tal como un archivo, socket TCP, ventana, etcétera.
  • Relación tiene vs. es: el primero se refiere a atributos de una sola clase, y el segundo a clases heredadas.
  • Sintaxis basada en la indentación: uso de indentaciones tales como tabulaciones para denotar bloques de código.
  • Herencia: habilidad para que una o más clases padres provean métodos y atributos básicos para que las clases hijas sobreescriban o amplíen según surja la necesidad.
  • Interpretar: ejecutar un programa escrito en un lenguaje de alto nivel sin compilarlo primero.
  • Objeto: instancia de ejecución de una o más clases.
  • Jerarquía de objetos: cómo se organizan los elementos de pantalla mediante la relación padre-hijo (contenedor – contenido).
  • Programación orientada a objetos: un paradigma de programación que define soluciones a problemas o representa cosas reales a través de clases y objetos.
  • Alcance: dónde las variables, funciones, clases y objetos se definen en código.

Apéndice C: Comparación de tipos de complementos

La siguiente tabla compara varios tipos de complementos y cuándo utilizarlos.

Tarea o característica Extensión global Módulo de aplicación Controlador Potenciador
Puede usarse en todas partes No
Restricciones de nombrado No (limitado por las convenciones de nombrado de ficheros de Windows) Debe llamarse como el ejecutable No (limitado por las convenciones de nombrado de ficheros de Windows) No (limitado por las convenciones de nombrado de ficheros de Windows)
Recupera varios controles, incluyendo el control enfocado No
Las órdenes pueden utilizarse en cualquier lugar No Sólo los controladores de pantalla braille si se definió
Manejar eventos tales como cambios del foco No
Definir objetos personalizados para representar controles No No
Definir acciones personalizadas al realizarse cuando el módulo se cargue y se descargue
Definir acciones personalizadas a realizarse cuando ocurran cambios de perfil y otros eventos
Puede modificar atributos de objeto en tiempo de ejecución No No No
Modifica rutinas de salida de voz y otras y la experiencia de presentación (Por ejemplo speech.cancelSpeech, braille.handler.update, etc.) No Depende del potenciador
Incluir opciones personalizadas
Puede parchear funciones de NVDA, classes y módulos a voluntad No se aconseja No No se aconseja
Sujeto a cambios de perfil de configuración No
Puede llamar a bibliotecas externas escritas en C y otros lenguajes y empaquetadas como DLLs

Apéndice D: notas y referencias para creadores de scripts de otros lectores de pantalla

Si escribes scripts para lectores de pantalla como JAWS para Windows o Window-Eyes, asegúrate de repasar esta sección ya que te presenta las tareas comunes que puedes realizar con NVDA y otras notas.

Tarea función /clase/módulo de NVDA o de Python Notas
Cancelar voz speech.cancelSpeech() Detiene la voz.
Verbalizar algo speech.speakMessage(mensaje) o como parte de ui.message(mensaje) la función ui.message también realiza la salida braille.
Braillificar algo braille.handler.message(mensaje) o ui.message(mensaje) Igual que arriba.
Mostrar una ventana HTML ui.browseableMessage(mensaje, título, isHTML = True) Imita el visor virtual excepto a la hora de mostrarse en pantalla.
Recuperar objeto con el foco algo = api.getFocusObject()
Recuperar ventana en primer plano primerplano = api.getForegroundObject()
Título de la ventana activa (si está presente) foreground.name
Averiguar qué objeto es el navegador de objetos obj = api.getNavigatorObject() Este podrá ser o no ser el control enfocado.
Recuperar el manejador de ventana para un objeto dado hwnd = obj.windowHandle Obtiene el objeto a través de las funciones api.get*Object().
Comprobar el nombre de la clase de ventana obj.windowClassName == algo
Asegurarse de que el objeto padre tiene el ID de control de ventana correcto if obj.parent.windowControlID == algo: instrucciones
El objeto padre no proporciona lo que yo quiero, pero el abuelo lo hace algo = obj.parent.parent.atributo
La etiqueta de un elemento de lista es el nombre de su primer objeto hijo. obj.name = obj.firstChild.name
Deseo que la etiqueta de control del objeto enfocado y la descripción del objeto anterior se anuncien cuando pulso NVDA+Tab En reportFocus(self): obj.name += » » + obj.previous.description
Anunciar cambios de estado si y sólo si el siguiente objeto es la barra de herramientas que estoy buscando. En event_stateChange(self): toolbar = obj.next; if toolbar.role == controlTypes.Role.TOOLBAR and toolbar.attribute = loqueestoybuscando y condiciones adicionales …: instrucciones Para una mejor legibilidad, coloca cada declaración en líneas separadas aplicando indentaciones correctas.
Anunciar el nombre de un objeto en pantalla (siempre que se pueda utilizar la navegación por objeto) obj = api.getForegroundObject().ruta…; ui.message(obj.name) Trata de colocar cada sentencia en su propia línea con las indentaciones correctas aplicadas. La ruta se refiere a obj.next/previous/parent/firstChild/lastChild/children[index]/getChild(index) y así sucesivamente.
Comprueba si el rol del objeto es el que deseas obj.role == controlTypes.Role.* Role.* puede ser cualquier rol que estés buscando.
Búsqueda de un texto específico en el nombre del objeto textobuscado in obj.name Esta es una tarea típica de los miembros de una cadena de texto.
¿Empieza la etiqueta del control con un texto específico? obj.name.startswith(textobuscado)
Longitud de un campo de texto con valor fácilmente recuperable len(obj.value) Esto funciona si se puede encontrar el valor del campo.
Es una casilla marcada controlTypes.State.CHECKED in obj.states obj.states es un conjunto. En primer lugar, verifica que el rol es de una casilla de verificación.
Cuántos elementos hay en una lista algunaLista.childCount Siempre que la lista proporcione una correcta implementación subyacente para obtener el recuento de elementos.
Dónde se encuentra el objeto en la pantalla obj.location Esto devuelve una tupla de cuatro elementos, es decir, coordenadas x e y de la esquina superior izquierda del objeto, así como longitud y anchura. Por ejemplo, en el objeto Shell (escritorio) con una resolución de pantalla de 1920 por 1080 píxeles, el valor de retorno será (0,0,1920,1080).
¿Es este un control MSAA? isinstance(obj, NVDAObjects.IAccessible.IAccessible) Una implementación típica es importar IAccessible desde NVDAObjects.IAccessible y hacer isinstance(obj, IAccessible).
Posición de un elemento de lista MSAA item.IAccessibleChildID Siempre y cuando esto se implemente correctamente. El valor predeterminado para los controles que no sean elementos de lista o elementos de vista de árbol es 0.
Necesito trabajar con métodos de un objeto IAccessible directamente obj.IAccessibleObject.method primero, descubre cómo usar el método MSAA dado para un control, y después recupera el propio objeto IAccessible y llama al método.
Dame el elemento UIA que proporciona un control UIA obj.UIAElement útil si quieres realizar operaciones de cliente UIA en este elemento.
ID de automatización para un elemento UIA obj.UIAAutomationID Primero, comprueba si el objeto es un control UIA.
Framework utilizado para generar este objeto UIA obj.UIAElement.cachedFrameworkID El framework GUI utilizado para programar este objeto. Los frameworks más comunes son Direct UI, Windows Presentation Foundation (WPF) con controles UIA habilitados, XAML (lenguaje extensible de programación de aplicaciones) y Microsoft Edge.
Quiero pedirle a UIA los valores de una propiedad específica obj._getUIACacheablePropertyValue(propertyID) asumiendo que el objeto es un control UIA, pásale el identificador de la propiedad que quieres conocer como argumento de esta función. Si la propiedad está soportada, se devolverá un valor válido. Si no, se lanzará un error COM.
Nombre ejecutable de cualquier objeto obj.appModule.appName appModule es el atributo de cualquier objeto que se pueda representar dentro de una aplicación como el control enfocado.
Ruta al ejecutable del objeto en primer plano api.getForegroundObject().appModule.appPath Obtiene la ruta al ejecutable de cualquier objeto.
Proporcionar información sobre un script en un solo paso scriptHandler.script(descripción, uno o más gestos, categoría) Este es un decorador. Normalmente escribirías: @scriptHandler.script seguido de la información del script. Gestos puede ser un único gesto (gesture=cadena) o una lista (gestures=[gestos]).
Asignar un atajo de teclado a un script @scriptHandler.script(gesture=atajoDeTeclado) Los atajos de teclado empiezan por «kb:».
Enviar combinaciones de teclas gesture.send() Se debe llamar desde un script asociado a la combinación de teclas deseada.
Manejo de múltiples pulsaciones de teclas scriptHandler.getLastScriptRepeatCount() 0 significa que la orden se ha pulsado una vez.
Quiero asignar control+alt+fila numérica a un script @scriptHandler.script(gestures=[f»kb:control+alt+{i}» for i in range(10)]) Se utiliza una combinación de decorador y literales en cadena con formato (cadenas f) definidos dentro de una lista comprimida.
proporcionar un mensaje de ayuda de entrada @scriptHandler.script(description=mensajeDeAyudaDeEntrada) Efectivamente, la descripción del script (asignada a su cadena de documentación) se trata como su mensaje de ayuda de entrada.
Manejar los cambios de nombre event_nameChange(self, obj, nextHandler) El cuerpo debe consistir en lo que se debe hacer, terminando con una llamada a la función nextHandler ().
Anuncios de cambio de región viva event_liveRegionChange(self, obj, nextHandler) De forma predeterminada, el nuevo texto será hablado y/o braillificado.
Transformar instantáneamente una ventana en un diálogo En chooseNVDAObjectOverlayClasses(self, obj, clsList): si encuentras la ventana que quieres: clsList.insert(0, NVDAObjects.Behaviors.Dialog) Asegúrate de identificar que esta ventana es realmente un diálogo. Si se hace correctamente, los contenidos de este»diálogo» se anunciarán automáticamente.
Estoy trabajando con una ventana de terminal Hereda de NVDAObjects.behaviors.Terminal
Deseo añadir órdenes de navegación de tabla para un objeto que todavía no se muestra como tabla Hereda de NVDAObjects.behaviors.RowWithFakeNavigation Esta clase define el mensaje del modo de ayuda de entrada y una implementación básica para las órdenes de navegación de tabla (Control+Alt+flechas).
Necesito punteros para proporcionar soporte mejorado para una aplicación Java NVDAObjects.JAB y módulo JABHandler Java Access Bridge (de 32 y 64 bits) debería instalarse (instalado en 2019.3).
Añadir soporte para una aplicación con funcionalidad similar a la de otra aplicación Importar contenidos del módulo de aplicación fuente a través de from nombremódulo import * Se conoce con el nombre de «Aliasing».
Reproducir un tono tones.beep(hertz, duration) Duración en milésimas de segundo.
Reproducir un tono en el altavoz izquierdo tones.beep(hertz, duration, leftVolume=100, rightVolume=0)
Reproducir un fichero de sonido wav nvwave.playWaveFile(path) Por ejemplo, nvwave.playWaveFile(r»test.wav»)
Obtener información de texto para un objeto determinado obj.TextInfo Observa la «T» mayúscula.
Comprobar si el eco de teclado (caracteres tecleados) está activado config.conf[«keyboard»][«speakTypedCharacters»]
activar la verbalización de las teclas de órdenes sin abrir el cuadro de diálogo de configuración config.conf[«keyboard»][«speakCommandKeys»] = True El usuario debe activar esto a través del diálogo de opciones de teclado.
El Modo foco/modo formularios está activo obj.treeInterceptor.passThrough Si es true, el modo foco/modo formularios está activado mientras se utilizan los documentos del modo exploración.
Dispone de soporte para pantalla táctil touchHandler.touchSupported() Si devuelve True, el soporte táctil está activo y disponible.
Obtener la versión de NVDA versionInfo.version
Quiero hacer algo cada vez que cambien los perfiles de configuración config.post_configProfileSwitch debes registrar una función que escuche esta acción, y hacer que haga algo cada vez que los perfiles cambien.
Hazme saber si esta es una versión de desarrollo debug Si esta variable es True, la versión actual es de desarrollo. En cualquier otro caso, es una liberación oficial.
Necesito ciertas características para que mi código funcione mejor hasattr(módulo, algo) esto te permite comprobar la existencia de una característica o atributo que puedas necesitar, de tal forma que puedas dar soporte a versiones antiguas y nuevas del código.
Versión de Windows winVersion.getWinVer() Devuelve la versión actual de Windows (nombre de la versión, principal.menor.revisión, tipo de instalación (estación de trabajo, servidor o controlador de dominio) y service pack si lo hay). Se puede comparar la versión devuelta con una versión de Windows concreta del módulo winVersion. Por ejemplo, winVersion.getWinVer() >= winVersion.WIN81.
¿Es Windows de 64 bits? os.environ(«PROCESSOR_ARCHITEW6432») in [«AMD64», «ARM64″] o también os.path.exists(r»C:\Program Files (X86)») El método variable de entorno es más fiable. A partir de 2017.4, se debe comprobar tanto AMD64 como ARM64, especialmente cuando se admite Windows 10 en ARM.
Acceso al registro Módulo winreg «_winreg» en Python 2.
Abrir un sitio web con el navegador web predeterminado os.startfile(URL)
Descargar cabeceras para un archivo en la web resource = urllib.urlopen(URL)
Trabajar con datos JSON Módulo json
Reservar algo de memoria en algún lugar ctypes.windll.kernel32.VirtualAllocEx() La forma más rápida de hacerlo es la función winKernel.virtualAllocEx.
Enviar un mensaje a otro proceso ctypes.windll.user32.SendMessageW() El camino más corto es la función winUser.sendMessage().
Hora actual en segundos time.time() Devuelve segundos transcurridos desde el 1 de enero de 1970 a medianoche.
Crear un cuadro de mensaje gui.messageBox Una fina envoltura alrededor de la clase wx.MessageBox.
Crear un diálogo personalizado wx.Dialog
Ejecutar varias tareas en segundo plano a la vez threading.Thread En realidad, debido a problemas internos, Python ejecutará un hilo tras otro. Este enfoque es útil si deseas ejecutar una tarea en segundo plano mientras haces que NVDA siga respondiendo.
Ejecutar algo periódicamente wx.PyTimer o threading.Timer
Ocurrencia del recuento de texto en un documento collections.Counter Asegúrate de tener una lista de palabras de un archivo de texto antes de ejecutar una cuenta en él.
Crear una matriz dinámica Objeto list El objeto de lista de Python ([]) es una matriz dinámica.
Trabajar con matrices asociativas Objeto dict El objeto diccionario de Python ({}) es otro nombre para la matriz asociativa, a veces llamada mapa.
Abrir, analizar y guardar archivos de configuración módulo config o módulo configobj
Quiero que mi código se ejecute más rápido y sin errores ¡No lo hagas a menos que sea necesario! Citando a un conocido programador, «No optimices si no quieres tener dolores de cabeza».
Quiero liberar la versión 1.0 de mi código con todo incluido ¡Nunca lo hagas a menos que sepas por qué, sepas qué estás haciendo o lo especifique un contrato que hayas firmado!
Me gustaría traer una función de otro lector de pantalla a NVDA Justifica por qué y planéalo adecuadamente
Quiero integrar funciones de mi complemento en el lector de pantalla NVDA Envía una solicitud de cambios y prepárate para responder a las preguntas de los revisores A veces, una o dos características de un complemento aterrizan en el lector de pantalla NVDA, pero después de pasar por un proceso de revisión en una solicitud de cambios. Para más información, mira las pautas de contribución de NV Access.

Apéndice E: Python 2 frente a 3

La siguiente tabla describe cambios notables entre Python 2 y 3 y da consejos para hacer que tu complemento sea compatible con ambas versiones.

Concepto o tarea Python 2 Python 3 Consejos de compatibilidad
Imprimir texto print texto print(texto) Esta última también funciona en Python 2.
Formato predeterminado del texto ANSI Unicode Se prefiere Unicode. Para usarlo, prefija el texto con una «u».
Rango xrange range Si no te importa el rendimiento, usa range.
Reducir/Acumular reduce functools.reduce
Map y Filter devuelven lista iterador Si quieres compatibilidad máxima, envuelve la llamada a map o filter en una llamada a la función list.
Alias de excepciones exception, e exception as e La última también funciona en Python 2.
Importación relativa import module from . import module La última también funciona en Python 2.
Acceso al registro _winreg winreg Para dar máxima compatibilidad usa winreg, y en Python 2 import _winreg as winreg.
Iteración por elementos/claves/valores en un diccionario dict.iteritems iterkeys itervalues
División estrictamente entera / // El primero ahora hace una división normal en Python 3.
Definición de clases class algunaclase: contenido class algunaclase(object): contenido El último también funciona en Python 2.