Aquí te presentamos un videotutorial sobre el navegador de objetos, modos de revisión, OCR (el complemento) con el cursor de revisión y el complemento Obj Pad, que fazilita la navegación por objetos con modos para las teclas de flechas.
Haz clic en este enlace para ver el tutorial.
Notas:
Este tutorial fue realizado en marzo del 2017. Por lo tanto, NVDA todabía no funcionava con el OCR de Windows 10, así que aquí te mostramos de forma vrebe cómo utilizarlo. Si tienes una versión inferior a Windows 10, utiliza el complemento OCR que se usa en el tutorial.
Pasos:

  • Coloca el nabegador de objetos sobre la imagen que deseas reconocer.
  • Pulsa NVDA+R. Si el ocr de Windows 10 está disponible y si el objeto se puede reconocer, NVDA dirá reconociendo y mostrará el resultado en un documento con modo exploración, de tal forma que puedes leer el documento como si de cualquier documento en modo exploración se tratase y utilizar espacio o enter para activar (si es posible) un clic en alguna parte del texto (ejemplo, activar una opción en una aplicación).

En este tutorial, se trabaja con los comandos de la distrivución de teclado sobremesa o escritorio. Te recomendamos ver la Referencia rápida de órdenes para ver los jestos de la distrivución portátil, así como los jestos táctiles para equipos con pantalla táctil y Windows 8 o superior.
Este tutorial ha sido realizado por un colaborador de la comunidad de tiflojuegos.com conocido como Civerbadnnes, ya que esto llega a servir en algunos videojuegos para algo de accesibilidad.
¡Esperamos que sea útil!

En esta entrada te presentamos Tifloaudio, un podcast creado por Manolo Álvarez. En él, entre muchas otras curiosidades y tecnologías específicas para personas ciegas, se habla de NVDA. Escuchando en orden los episodios que te mostramos a continuación, harás un recorrido histórico por este lector de pantalla, desde que comenzó a ser un poco más conocido allá por el 2009 hasta la actualidad. Podrás ver su evolución, así como conocer en profundidad cómo funcionan algunas de sus características. Pulsando cada enlace, se abrirá la entrada correspondiente al episodio seleccionado en la web de Tifloaudio. Si estás preparado, ¡allá vamos!
Tiflo Audio 7: Demostración de NVDA
Tiflo Audio 55: Cambiar voces en el lector de pantalla NVDA
Tiflo Audio 70 – Utilizar Windows 10 con el lector de pantalla NVDA y una línea braille
Tiflo Audio 82 – Demostración novedades NVDA 2017.3: Nuevas voces Windows OneCore, Reconocimiento de textos OCR y entradas con líneas braille
Tiflo Audio 94 – Nuevos comandos para líneas braille con el lector de pantalla NVDA 2018
¡Esperamos que los disfrutes!

En este episodio, que ya tiene algún tiempo, se hace una introducción y se resuelven dudas comunes a la hora de dar el paso de Jaws a NVDA. No ha cambiado gran cosa desde que se publicó, salvo el nuevo diálogo de opciones. En él participan José María Ortiz, Daniel Montalvo, Juan Buño y José Manuel Delicado.
Visita la entrada con el episodio en la web de José María Ortiz
¡Que lo disfrutes!

A la hora de programar un complemento que tiene algún tipo de interfaz gráfica, o hacer modificaciones a la propia interfaz de NVDA, todos los desarrolladores ciegos nos hacemos la misma pregunta tarde o temprano: ¿cómo se ve en pantalla esto que estoy programando? Podemos probar distintas combinaciones de constantes, posiciones y tamaños para los controles, pero al no poder verlos, probablemente el resultado no sea el que esperamos. En la entrada de hoy vamos a hablar del módulo guiHelper, disponible desde la versión 2016.4 de NVDA. La misión de este módulo es abstraernos un poco de los detalles más visuales del diseño de nuestra interfaz, de tal forma que sólo debamos preocuparnos de añadir controles y funcionalidad. El módulo no hace magia, pero nuestras interfaces quedarán probablemente un poquito mejor que antes. Si quieres conocer este módulo un poco más, sigue leyendo. ¡Allá vamos!
Nota: en este artículo se asume que el lector tiene conocimientos sobre la programación de complementos con interfaz gráfica.

Importar el módulo

El módulo guiHelper se encuentra en el paquete gui. Si nuestro complemento tiene interfaz gráfica, en algún momento seguramente habremos importado los módulos gui y wx, por lo que sólo haría falta escribir esto:
from gui import guiHelper
Una vez hecho esto, ya podremos acceder a sus constantes, funciones y clases.

Constantes de guiHelper

Estas constantes vienen incluidas directamente en el módulo.

  • BORDER_FOR_DIALOGS: es el borde que se encuentra alrededor de cada control dentro de un diálogo.
  • SPACE_BETWEEN_VERTICAL_DIALOG_ITEMS: cuando los elementos de un diálogo se disponen de manera vertical, esta constante se puede emplear para indicar cuánto espacio hay entre cada elemento.
  • SPACE_BETWEEN_BUTTONS_HORIZONTAL: especifica el espacio que hay entre botones dispuestos de manera horizontal.
  • SPACE_BETWEEN_BUTTONS_VERTICAL: igual que en el caso anterior, pero se aplica cuando los botones están dispuestos verticalmente unos sobre otros.
  • SPACE_BETWEEN_ASSOCIATED_CONTROL_HORIZONTAL: es el espacio que separa a dos controles relacionados dispuestos en horizontal.
  • SPACE_BETWEEN_ASSOCIATED_CONTROL_VERTICAL: lo mismo que en el caso anterior, pero en disposiciones verticales.

Funciones de guiHelper

A día de hoy, guiHelper sólo dispone de una función a nivel de módulo: associateElements. Esta función introduce dos controles en un objeto Sizer de wx, situándolos en horizontal o vertical y separándolos adecuadamente según su tipo. Su cabecera es la siguiente:
def associateElements( firstElement, secondElement)
Los controles deben pasarse en el orden en que queremos que se asocien. Las posibles asociaciones son:

  • wx.StaticText y wx.Choice, wx.TextCtrl o wx.Button: asocia una etiqueta con un cuadro combinado, un cuadro de edición o un botón, en disposición horizontal.
  • wx.StaticText y wx.ListCtrl o wx.ListBox o wx.TreeCtrl: asocia una etiqueta con una presentación en lista, un cuadro de lista o una vista en árbol, en disposición vertical.
  • wx.Button y wx.CheckBox: asocia un botón y una casilla de verificación, disposición horizontal.
  • wx.TextCtrl y wx.Button: asocia un cuadro de texto y un botón en posición horizontal.

En todos los casos, los controles deben ser objetos ya instanciados. La función devuelve el objeto Sizer que los contiene, listo para añadirlo a un diálogo u otro Sizer.

Clases de guiHelper

El módulo guiHelper dispone de cuatro clases que nos ayudan a construir nuestra interfaz. Aunque vamos a verlas todas, la que más se usa es BoxSizerHelper. Todas ellas tienen el atributo sizer, empleado al construir el resto de la interfaz.

La clase ButtonHelper

Esta clase sirve para añadir el espacio adecuado entre botones, tanto en horizontal como en vertical. Al instanciar objetos a partir de ella, se debe especificar la orientación del Sizer que va a generar, ya sea wx.VERTICAL o wx.HORIZONTAL. Por ejemplo:
bh=guiHelper.ButtonHelper(wx.VERTICAL)
Una vez construido el objeto, podemos agregar botones llamando al método addButton:
bh.addButton(mydialog, label=_("Test button 1"))
El método addButton construye un nuevo botón con los parámetros que le pasemos, o también puede añadir un botón previamente creado. Es imprescindible indicar en el primer caso el diálogo o ventana padre que contendrá al botón, y una etiqueta por razones de accesibilidad con el parámetro label. Al acabar, se devuelve el botón creado para que podamos hacer cosas con él, como asociarle acciones.

La clase LabeledControlHelper

Esta clase permite crear controles etiquetados y asociar etiqueta y control entre sí en un Sizer. Se apoya en la función associateElements que vimos más arriba. La cabecera de su constructor es:
def __init__(self, parent, labelText, wxCtrlClass, **kwargs)
Por lo tanto, para construir un objeto de tipo LabeledControlHelper necesitamos pasar como parámetros el diálogo o ventana padre, el texto de la etiqueta que acompañará al control, la clase del control (cuidado, aquí no valen controles previamente creados), y cualquier argumento que necesite el control para construirse. Veámoslo de forma más clara con un ejemplo:
lch=guiHelper.LabeledControlHelper(mydialog, "Selecciona una fruta", wx.Choice, choices=['pera', 'manzana', 'fresa'])
En el ejemplo anterior hemos pasado como clase wx.Choice para crear un cuadro combinado, y como parámetros adicionales las opciones que presentará.
La clase LabeledControlHelper dispone de dos atributos para que podamos trabajar cómodamente: sizer, igual que en el resto de clases, y control, que hace referencia al control de wx que hemos creado.

La clase PathSelectionHelper

Esta clase nos permite crear de una forma rápida, sencilla y abstrayéndonos de todos los detalles, un cuadro de edición y un botón “Examinar…” como los que aparecen en muchos programas, con el objetivo de que podamos fabricar un selector de rutas. En el constructor le pasamos el padre, como siempre, el texto del botón Examinar, y el título del diálogo que se desplegará para seleccionar una ruta cuando pulsemos dicho botón. Por ejemplo:
psh=guiHelper.PathSelectionHelper(mydialog, "Examinar...", "Selecciona la carpeta que contiene los documentos")
Como en los casos anteriores, tenemos la propiedad sizer, pero además aquí nos acompaña otra: pathControl, que contiene una referencia al cuadro de texto donde está escrita la ruta.
En caso de que sea necesario, se podría crear una clase que herede de esta y sobreescribir o extender los métodos getDefaultBrowseForDirectoryPath y onBrowseForDirectory. El primero devuelve la ruta por defecto que muestra el diálogo de seleccionar carpeta, y el segundo muestra este diálogo y almacena el resultado en el cuadro de edición.

La clase BoxSizerHelper

Llegamos a la clase más importante de este módulo, la más usada, y la que emplea la funcionalidad expuesta en casi todas las otras. BoxSizerHelper permite configurar la disposición interna de un objeto Sizer. La cabecera de su constructor es:
def __init__(self, parent, orientation=None, sizer=None)
Como siempre, el primer argumento que espera recibir es la ventana padre. Después, podemos pasar una orientación (wx.VERTICAL o wx.HORIZONTAL), en cuyo caso se creará internamente un objeto Sizer, o un Sizer ya creado, a ser posible vacío. Si pasamos ambos parámetros, se produce un error.
Al igual que sucede en los casos anteriores, tenemos un atributo sizer. Esta clase, además, presenta los siguientes métodos:

  • addItem(self, item, **keywordArgs): añade un control wx ya creado y lo sitúa adecuadamente. El último argumento es opcional y sólo se usa en raras ocasiones. El método devuelve el elemento que se pasa como parámetro, para que podamos asociarle acciones y hacer otras cosas con él.
  • addLabeledControl(self, labelText, wxCtrlClass, **kwargs): añade un control etiquetado. El primer parámetro que recibe es el texto con la etiqueta del control, seguido de la clase del control y los parámetros adicionales que se quieran pasar. Fíjate en el ejemplo anterior del cuadro combinado. Este método devuelve el control que se acaba de agregar.
  • addDialogDismissButtons(self, buttons): agrega y alinea los botones Aceptar, Cancelar y Aplicar de los diálogos estándar. Este método debería llamarse cuando ya se han añadido todos los demás controles. Buttons puede ser un objeto wx.Button, un objeto wx.Sizer o un objeto guiHelper.ButtonHelper. Devuelve el mismo objeto que se le pasa como parámetro.

Aplicaciones prácticas

Al igual que en una entrada anterior, vamos a poner como ejemplo el complemento Enhanced Aria (aunque esta vez no vendrá solo), para demostrar lo fácil que es adaptar nuestros complementos para que se apoyen en guiHelper. En la versión 1.3, para agregar la primera casilla de verificación del diálogo de ajustes de aria mejorada, se hacía esto:
self.bannerenabled=wx.CheckBox(self, wx.NewId(), label=(u"Report banners")) # Como self es una clase que hereda de wx.Dialog, se pasa como padre
self.bannerenabled.SetValue(config.conf['aria']['reportBanner']) # Se establece el valor de la casilla

sizer.Add(self.bannerenabled,border=10,flag=wx.BOTTOM) # se añade la casilla sizer
Como se puede comprobar, hay que crear la casilla con su etiqueta correspondiente, asignarle el valor adecuado y añadirla al objeto que va a contenerla, con indicadores de borde y alineación. Sin embargo, en la versión 2.0 basta con hacer esto:
def createSettings(obj, sizer):
helper=guiHelper.BoxSizerHelper(obj, sizer=sizer)
obj.bannerenabled=helper.addItem(wx.CheckBox(obj, wx.NewId(), label=
("Report banners")))
obj.bannerenabled.SetValue(config.conf['aria']['reportBanner'])

Como se puede ver, creamos un BoxSizerHelper pasando como parámetro el Sizer que nos dan en el método makeSettings, y que en el caso particular de esta implementación se pasa a createSettings. A continuación, agregamos una nueva casilla de verificación llamando a addItem, y luego establecemos su valor.
Veamos el diálogo de Windows 10 App Essentials, que también se apoya en este módulo. Lo podemos encontrar en el archivo w10config.py. Se trata de un diálogo completo, una clase que hereda de wx.Dialog. Por lo tanto, construirlo implica más esfuerzo, al no tener a nuestra disposición la clase SettingsDialog de NVDA o la clase SettingsPanel. En lugar del método makeSettings, que ya nos da un Sizer, las operaciones de construcción se hacen en el constructor del diálogo.
En primer lugar, se crea un objeto de tipo BoxSizer:
mainSizer = wx.BoxSizer(wx.VERTICAL)
Y a continuación, se crea un BoxSizerHelper con un Sizer nuevo, que será hijo del que se acaba de crear:
w10Helper = gui.guiHelper.BoxSizerHelper(self, orientation=wx.VERTICAL)
Teniendo el BoxSizerHelper, ya se pueden añadir controles. Vamos a ver dos de ellos, la casilla de verificación y el cuadro combinado.
self.autoUpdateCheckbox=w10Helper.addItem(wx.CheckBox(self,label=_("Automatically check for add-on &updates"))) # Se crea la casilla
self.autoUpdateCheckbox.SetValue(config.conf["wintenApps"]["autoUpdateCheck"]) # Se establece su valor
labelText = _("&Add-on update channel:") # Etiqueta del cuadro combinado
self.channels=w10Helper.addLabeledControl(labelText, wx.Choice, choices=["development", "stable"]) # Cuadro combinado

Ya para acabar, se añaden los botones Aceptar y cancelar, y se hace de una forma muy particular, ya predefinida en wx:
w10Helper.addDialogDismissButtons(self.CreateButtonSizer(wx.OK | wx.CANCEL))
En este caso no existe acceso directo a los botones, como sí pasa con el resto de los controles, así que las asociaciones se hacen de esta manera:
self.Bind(wx.EVT_BUTTON, self.onOk, id=wx.ID_OK)
self.Bind(wx.EVT_BUTTON, self.onCancel, id=wx.ID_CANCEL)

El BoxSizerHelper ya está completo, hay que añadirlo al Sizer principal. Una vez más, se pide ayuda a guiHelper, y al propio motor wx para centrarlo y posicionarlo:
mainSizer.Add(w10Helper.sizer, border=gui.guiHelper.BORDER_FOR_DIALOGS, flag=wx.ALL)
mainSizer.Fit(self)
self.Center(wx.BOTH | (wx.CENTER_ON_SCREEN if hasattr(wx, "CENTER_ON_SCREEN") else 2))

Conclusión

Es importante que nuestros complementos e interfaces gráficas en general sean visualmente usables. Aunque NVDA es un lector de pantalla empleado mayoritariamente por personas con discapacidad visual, estas personas suelen estar acompañadas por otras que ven perfectamente, especialmente cuando se introducen en el mundo de la informática. Por tanto, debemos tener en cuenta a todos los posibles usuarios.
En esta entrada hemos conocido el módulo guiHelper, que nos ayuda precisamente en esta tarea. GuiHelper no hace magia, no tiene ningún tipo de “inteligencia artificial” que coloque nuestros controles exactamente como deberían estar para que nuestra interfaz sea atractiva. Sin embargo, nos ayuda un poco a conseguirlo, simplifica nuestro código y por lo tanto facilita el mantenimiento.
Si tus complementos no se apoyan todavía en este módulo, no esperes más para adaptarlos. Tus usuarios lo agradecerán, ¡y tú también!

¡Hola!
Como dijimos el otro día en una entrada anterior, estamos organizando el primer encuentro de la comunidad de NVDA en español. Si quieres participar hablando de algún tema, ¡te esperamos!
La entrada de hoy vamos a dedicarla a Teamtalk, un conocido sistema de conferencias por chat, voz y vídeo, ya que lo utilizaremos para realizar el encuentro de forma remota. Daremos una breve descripción del programa, un resumen de sus características, y los primeros pasos que se deben llevar a cabo para conectarse al servidor que ya tenemos preparado para la ocasión. ¡Comencemos!

Descripción de Teamtalk

Teamtalk es un software multiplataforma que permite comunicarse a través de Internet mediante chat, voz y vídeo. Mucha gente lo compara con Skype, e incluso llega a utilizarlo como alternativa a este. Sin embargo, sus diferencias son tantas que aquí sólo podremos citar unas pocas:

  • En vez de llamadas de usuario a usuario o grupales, en Teamtalk existen canales. Los usuarios no llaman o contestan una llamada. Simplemente buscan un canal donde haya otro usuario y se unen a él.
  • Teamtalk es totalmente descentralizado. No hay un servidor único, tú puedes montar tu propio servidor.
  • La calidad de sonido es constante, con todo lo que ello implica. Si la conexión a Internet es mala, la calidad no se rebaja automáticamente, sino que el sonido se entrecorta.

Algunas de sus características más destacables son:

  • Canales: aunque la calidad sea constante, esto no significa que no se pueda regular. Cada servidor puede tener distintos canales, cada uno con unas características distintas (velocidad en bits del audio, códec utilizado, tasa de muestreo, etc.)
  • Capacidad para regular el sonido: se pueden configurar aspectos tales como la reducción de ruido, posicionamiento de los usuarios en el canal (escuchar algunos a la izquierda y otros a la derecha, por ejemplo), o el volumen de los altavoces y el micrófono sin tocar los ajustes del sistema. Además se puede elegir el motor de sonido, el nivel de activación del micrófono, y muchas más cosas
  • Capacidad para transmitir y recibir vídeo utilizando una WebCam
  • Capacidad para compartir el escritorio o controlar el escritorio compartido de otro usuario
  • Capacidad de emitir archivos multimedia
  • Opciones para compartir archivos subiéndolos al canal
  • Mensajes de chat tanto en el canal como privados con otros usuarios
  • Un cliente específico para usuarios de lectores de pantalla, que se mantiene al día con los últimos avances. Y por si fuera poco, hay un complemento para hacerlo un poco más accesible con NVDA!
  • Muchas otras funciones: grabación de conversaciones, posibilidad de gestionar las suscripciones a lo que envía un usuario concreto, etc.

Descarga e instalación de Teamtalk

Ahora que ya sabemos qué es, ¡toca pasar a la acción! La web oficial de Teamtalk es bearware.dk. Para ahorrar tiempo, vamos a poner los enlaces directos de la última versión, que en el momento en que se escribe esta entrada es la 5.3.2.

La instalación de Teamtalk en Android, iOs y Windows es relativamente sencilla. En este último caso, tras abrir el instalador, aparecerá el asistente de instalación en inglés y nos irá guiando por el proceso. Podemos pulsar el botón next en la mayoría de las pantallas, pero es importante que en una de ellas, que contiene un cuadro combinado, seleccionemos la opción TeamTalk Classic for Accessibility. De lo contrario se instalará la versión no accesible del programa, que resulta más difícil de manejar.

Configuración inicial en Windows

Si no desmarcamos la casilla correspondiente, el instalador abrirá Teamtalk al finalizar. En ese momento se nos preguntará si queremos agregar una excepción al firewall de Windows. Debemos responder que sí, y aceptar los avisos del control de cuentas de usuario, si aparecen. Al hacerlo, llegaremos por fin a la ventana principal de la aplicación.
La ventana de Teamtalk está formada por los siguientes elementos, presentados como aparecen al tabular por ellos:

  • Un árbol, en el que aparecerán tanto los canales como los usuarios cuando estemos conectados a un servidor.
  • Una barra deslizante para ajustar el volumen de los altavoces.
  • Una barra deslizante para ajustar la ganancia del micrófono. Se recomienda que su valor no supere el 50%, de lo contrario el ruido generado puede ser catastrófico para el resto de los usuarios.
  • Una barra deslizante para ajustar el nivel de activación del micrófono. Se recomienda ponerla a 0, de lo contrario hay que hacer mucho ruido para que Teamtalk decida transmitir.
  • Un control de pestañas, que permite seleccionar entre chat o archivos. Por el momento, no nos interesa la carga y descarga de archivos, por lo que ignoraremos la segunda pestaña.
  • Un cuadro de edición con el historial de mensajes.
  • Un cuadro de edición para escribir mensajes.
  • Una barra de menús en la que se encuentran muchísimas opciones que podrás utilizar más adelante.

Lo primero que haremos ahora que tenemos Teamtalk abierto es ir a sus preferencias y modificar algunos ajustes. Para ello, pulsaremos f4 desde la ventana de la aplicación.
En la pestaña general podemos escribir nuestro nombre, seleccionar nuestro sexo y las opciones de activación de voz. Se recomienda activar ambas, tanto la de activación por voz como la de pulsar para hablar (push to talk). En este último caso habrá que configurar un atajo de teclado, que deberemos mantener pulsado mientras hablemos.
La pestaña Display nos permite cambiar el idioma de Teamtalk y ponerlo en español, elegir si queremos minimizarlo a la bandeja y si queremos que se inicie minimizado, entre otras opciones. Se recomienda desmarcar una casilla llamada “Enable VU-meter updates” para evitar que se muestre una molesta barra de progreso.
La pestaña Sound System nos permite configurar los dispositivos de sonido. En la documentación de Teamtalk nos recomiendan que nos quedemos con Windows Audio Session como motor de sonido. Podemos elegir otros que nos dan más opciones, como Direct Sound, pero hay que tener en cuenta que si lo hacemos se deben elegir los dispositivos de sonido en los cuadros combinados que hay después, no se pueden dejar por defecto.
El resto de pestañas contienen valores que pueden dejarse como están, no es necesario modificarlos. Una vez hayamos configurado Teamtalk a nuestro gusto, podemos pulsar en Aceptar y conectarnos al servidor.

Conexión al servidor

Para abrir el diálogo de conexión puedes ir a la barra de menús, o directamente pulsar f2. Al hacerlo aparecerás en una lista. Pulsa tabulador hasta llegar a un cuadro de edición llamado Dirección del host. Escribe teamtalk.nvda.es y pulsa intro. ¡Tan sencillo como eso!
Al conectarte, verás mensajes en el cuadro de edición del historial del chat, y algunos elementos en el árbol. El primero de ellos es el canal raíz. Si pulsas flecha abajo, verás a los usuarios conectados, incluido tú. Si sigues bajando, verás el resto de canales. Pulsa ctrl+j o intro para unirte a ellos, o accede al menú de contexto para ver las opciones disponibles.

Los canales del servidor de Teamtalk de NVDA.es

Además del canal raíz, hay otros tres canales, y es importante que conozcas algunas particularidades de ellos. Eso sí, ten en cuenta que el chat está disponible en todos.

  • Canal raíz: tiene el códec de audio desactivado por defecto. Esto significa que en este canal sólo se puede usar el chat. Debes verlo como una “entrada” o zona de paso, ideal para permanecer mientras seleccionas otro canal.
  • Foro: es un canal libre para debatir sobre NVDA. Sin embargo, sólo se puede hablar utilizando el método pulsar para hablar (recuerda el atajo que configuraste antes), y no permite que ningún usuario grabe la conversación. Deberás desactivar “Usar activación por voz” desde el menú “yo” para poder hablar, de lo contrario los otros usuarios no te escucharán. Esto se hace para garantizar cierto orden, seguridad y privacidad.
  • Pruebas de sonido: este canal sólo permite tres usuarios conectados a la vez, y ha sido diseñado especialmente para los ponentes, aunque cualquiera puede entrar. Lo ideal es que entres a este canal, busques tu usuario en el árbol, pulses ctrl+4 para suscribirte a tu propia voz y te escuches. En cuanto hayas ajustado los parámetros de volumen, pulsa de nuevo ctrl+4 y únete a otro canal.
  • Sala de conferencias: en este canal hablarán los ponentes del encuentro de NVDA en español. Nadie más puede hablar, a no ser que obtenga permiso explícito del operador del canal, situación que se dará a la hora de hacer preguntas.

Algunos atajos de teclado útiles

  • f2: conecta y desconecta del servidor.
  • f4: abre las preferencias.
  • ctrl+shift+a: activa o desactiva la transmisión de voz. Imprescindible tenerla desactivada en el canal foro.
  • f5: permite cambiar rápidamente el nick.
  • f6: cambiar estado entre en línea o ausente, con mensaje opcional si fuera necesario.
  • ctrl+4: suscribirte o anular suscripción a tu propia voz.
  • ctrl+m: silenciar a todos los usuarios.
  • ctrl+j: unirse a un canal.
  • ctrl+l: abandonar el canal sin unirse a otro.

Conclusión

Después de hacer este breve recorrido por Teamtalk, toca despedir la entrada. Te recordamos que también buscamos a alguien que quiera transmitir el encuentro en su emisora de radio, ya que Teamtalk puede ser algo complejo para bastantes usuarios. Para cualquier pregunta o sugerencia, no dudes en escribirnos un comentario debajo, o contactar con nosotros en la lista de correo, las redes sociales o el formulario de contacto de esta web.
¡Hasta la próxima!

¡Hola a todos!
Faltan un par de semanas para que NVDA 2018.2 vea la luz, y sin embargo nadie ha parado de hablar de los cambios que trae desde hace ya un tiempo. Uno de los más importantes es el nuevo diálogo de preferencias organizado en categorías, al que hemos hecho referencia ya en varias entradas al hablar de complementos que se actualizan. En este tutorial veremos qué hay que hacer para que el diálogo de preferencias de nuestro complemento se integre en el nuevo diálogo de opciones de NVDA. Nos apoyaremos en el complemento Enhanced Aria, ya que su código es bastante simple, está en un único módulo y los cambios realizados son relativamente sencillos de entender. ¡Comencemos!
Nota: este es un artículo avanzado de desarrollo. Se asume que el lector sabe hacer complementos sencillos, y conoce el lenguaje Python con todo lo que ello conlleva.

Creación de ajustes sensibles a cambios de perfil

NVDA nos da mucha flexibilidad a la hora de crear diálogos para nuestros complementos. De hecho, nos da tanta que nos podemos perder, hacernos un lío, y programar todo con wx para luego darnos cuenta de que existen módulos que nos abstraen de un montón de detalles complejos. Con la configuración pasa exactamente lo mismo, podemos saber que existe configobj, pero ignorar que hay una capa por encima que nos facilita la vida. Definir una especificación de configuración es tan sencillo como crear una variable global en el módulo de nuestro complemento, indicando de qué tipo son los valores y el valor por defecto de cada uno. Esta es la configuración de Enhanced Aria:
confspec={
 "reportBanner":"boolean(default=true)",
 "reportMain":"boolean(default=true)",
 "reportContentinfo":"boolean(default=true)",
 "reportNavigation":"boolean(default=true)",
 "reportComplementary":"boolean(default=true)",
...}
En el caso de Enhanced Aria, todo lo que se configura es booleano, casillas que se marcan o desmarcan. Una vez definida la especificación, hay que decirle a NVDA que la añada al conjunto de especificaciones. Una variable global definida por sí sola en un módulo no va a hacer nada: config.conf.spec['aria']=confspec
Después de este inciso, breve pero necesario para entender todo lo demás, vamos a los diálogos en sí.

El diálogo tradicional de ajustes

Un diálogo tradicional de ajustes es una clase que hereda de gui.settingsDialogs.SettingsDialog. ¡Cuidado con las mayúsculas al programar, nos pueden hacer pasar un mal rato! Todos los diálogos de preferencias de NVDA heredan de esta clase, y los complementos también suelen hacerlo. La clase SettingsDialog nos ofrece varios métodos y propiedades que podemos redefinir:

  • title: propiedad que define el título del diálogo. Debe ser una cadena de caracteres.
  • makeSettings(self, sizer): este método se usa para rellenar el sizer que se recibe como parámetro con los controles que queramos. Enseguida vemos un ejemplo.
  • postInit(self): se ejecuta tras crear y mostrar el diálogo en pantalla. Sirve para llevar el foco por defecto a un control que nos interese, por ejemplo.
  • onOk(self, evt): NVDA llama a este método cuando se pulse el botón aceptar. Los botones Aceptar y Cancelar vienen ya en la clase padre, no tenemos que añadirlos en makeSettings. Aquí se hacen cosas tales como guardar la configuración. Importante llamar al método de la clase padre cuando terminemos.
  • onCancel(self, evt): método que se ejecuta al pulsar el botón cancelar. Importante llamar al método de la clase padre si lo redefinimos.

NVDA 2018.2 extiende este diálogo añadiendo un botón de aplicar, el evento asociado (onApply), y la posibilidad de que el diálogo pueda abrirse varias veces simultáneamente. Todas estas novedades son opcionales para conservar la compatibilidad con complementos que no se adapten a ellas.
Veamos algunos ejemplos. El título de Enhanced Aria es traducible a otros idiomas, y quedaría así: title=(u"Enhanced Aria settings")
El método makeSettings sería el siguiente. Para no hacer muy pesada la lectura, se pondrá sólo una parte:
 def makeSettings(self, sizer):
  #TRANSLATORS: report banners checkbox
  self.bannerenabled=wx.CheckBox(self, wx.NewId(), label=
(u"Report banners"))
  self.bannerenabled.SetValue(config.conf['aria']['reportBanner'])
  sizer.Add(self.bannerenabled,border=10,flag=wx.BOTTOM)
  #TRANSLATORS: report main content checkbox
  self.mainenabled=wx.CheckBox(self, wx.NewId(), label=(u"Report main content"))
  self.mainenabled.SetValue(config.conf['aria']['reportMain'])
  sizer.Add(self.mainenabled,border=10,flag=wx.BOTTOM)
#TRANSLATORS: report search forms checkbox
  self.searchenabled=wx.CheckBox(self, wx.NewId(), label=
(u"Report search forms"))
  self.searchenabled.SetValue(config.conf['aria']['reportSearch'])
  sizer.Add(self.searchenabled,border=10,flag=wx.BOTTOM)
...

Como se puede ver, no sólo creamos, etiquetamos y añadimos casillas de verificación, sino que establecemos su valor basándonos en la configuración actual. Una vez creadas, postInit hace lo siguiente:
 def postInit(self):
  self.bannerenabled.SetFocus()

Nuestro diálogo ya está listo. Como se puede intuir, no se ha redefinido el método onCancel, que simplemente descarta todo y no tiene que hacer nada más. Sin embargo, el método onOk sí tiene trabajo.
def onOk(self, evt):
  config.conf['aria']['reportBanner']=self.bannerenabled.GetValue()
  config.conf['aria']['reportMain']=self.mainenabled.GetValue()
  config.conf['aria']['reportSearch']=self.searchenabled.GetValue()
  config.conf['aria']['reportForm']=self.formenabled.GetValue()
  ...
  applyConfig()
  super(enhancedAriaSettings, self).onOk(evt) # Importante, llamada al padre!

Y ahora, ¿qué hacemos para poder llegar a este diálogo?
Como bien se ha dicho antes, la magia no existe. En el método init de la clase GlobalPlugin, hay que crear un elemento de menú y asociarlo al submenú preferencias de NVDA.
  self.prefsMenu = gui.mainFrame.sysTrayIcon.menu.GetMenuItems()[0]
  #TRANSLATORS: The configuration option in NVDA Preferences menu
  self.enhancedAriaSettingsItem = self.prefsMenu.Append(wx.ID_ANY, _(u"Enhanced aria settings..."), _(u"Change enhanced Aria settings"))
  gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onEnhancedAriaMenu, self.enhancedAriaSettingsItem) # Asociamos abrir el menú a activar el diálogo

No debemos olvidarnos de eliminar el elemento de menú en el método terminate, de lo contrario aparecerá duplicado al recargar las extensiones: self.prefsMenu.RemoveItem(self.enhancedAriaSettingsItem)
Para activar el diálogo, hacemos algo como esto: gui.mainFrame._popupSettingsDialog(enhancedAriaSettings)
Esta es la clase que contiene nuestro diálogo, enhancedAriaSettings. Pero lo hará por poco tiempo, ¡es hora de dar el salto al diálogo de categorías!

El panel de configuración en el diálogo de categorías

Para añadir paneles de configuración al diálogo de opciones, simplemente debemos crear una clase que herede de gui.settingsDialogs.SettingsPanel, y añadir nuestro panel al diálogo de opciones de NVDA, que está definido como variable global en el módulo gui. Esta clase nos ofrece los siguientes métodos y propiedades:

  • title: el título del panel de configuración.
  • makeSettings(self, sizer): al igual que antes, se nos pasa un sizer que rellenaremos con controles.
  • onSave(self): código que se ejecuta al pulsar los botones aceptar o aplicar del diálogo. Ya no es necesario llamar al método padre, como sí pasaba en onOk.
  • onPanelActivated(self): código que se ejecuta cuando se selecciona este panel. Aquí sí se debería llamar al método padre. Sin embargo, no deberíamos mover el foco como se hacía en postInit.
  • onPanelDeactivated(self): código que se ejecuta al abandonar el panel. Se debe llamar al método padre si lo redefinimos.
  • onDiscard(self): método que se llama al salir del diálogo de opciones sin guardar los cambios.

Una vez definido nuestro panel, debemos agregarlo al diálogo de opciones de NVDA. Por ejemplo, en el método init de la clase GlobalPlugin: NVDASettingsDialog.categoryClasses.append(enhancedAriaPanel)
Y por supuesto, eliminarlo en el método terminate, de lo contrario nos arriesgamos a que se duplique: NVDASettingsDialog.categoryClasses.remove(enhancedAriaPanel)
El código queda más simple, ¿verdad? Es muy sencillo hacer un complemento con preferencias que se adapte a NVDA 2018.2. Y como a mí no me gusta lo fácil, vamos a complicarlo un poco: vamos a añadir compatibilidad entre versiones. Puede haber gente que no quiera actualizar NVDA, y no debemos olvidarnos de ellos.

Soporte para todas las versiones de NVDA posibles

Llegados a este punto, podríamos tener dos clases: una con el diálogo y otra con el panel. Podríamos añadir el panel al diálogo de opciones y crear un elemento de menú en el submenú preferencias, y no pasaría nada. Como ya se ha dicho, NVDA nos da flexibilidad. ¿Permitiría esto que nuestro complemento adaptado funcionase con versiones antiguas? La respuesta ya te la puedes imaginar, si fuera afirmativa no existiría este apartado en el tutorial.
Por un lado, tal y como está, nuestro complemento muere estrepitosamente en NVDA 2018.1.1, diciendo que hay módulos que no encuentra. Por otro, queremos que el elemento de menú aparezca en versiones antiguas, y el panel del diálogo de opciones en las nuevas. Para lo último, en los métodos init y terminate de GlobalPlugin, podemos hacer algo como esto.
Método init:
  if hasattr(settingsDialogs, 'SettingsPanel'): # Es el nuevo NVDA
   NVDASettingsDialog.categoryClasses.append(enhancedAriaPanel)
  else:
   self.prefsMenu = gui.mainFrame.sysTrayIcon.menu.GetMenuItems()[0].GetSubMenu()
   #TRANSLATORS: The configuration option in NVDA Preferences menu
   self.enhancedAriaSettingsItem = self.prefsMenu.Append(wx.ID_ANY, _(u"Enhanced aria settings..."), _(u"Change enhanced Aria settings"))
   gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onEnhancedAriaMenu, self.enhancedAriaSettingsItem)

Método terminate:
   if hasattr(settingsDialogs, 'SettingsPanel'):
    NVDASettingsDialog.categoryClasses.remove(enhancedAriaPanel)
 else: # Es el antiguo
    self.prefsMenu.RemoveItem(self.enhancedAriaSettingsItem)

Como se puede comprobar, miramos si existen atributos que sólo podrían existir en NVDA 2018.2.
De cara a la importación de módulos, podríamos hacer algo como esto:
import gui
from gui import settingsDialogs
try:
 from gui import NVDASettingsDialog
 from gui.settingsDialogs import SettingsPanel
except:
 pass # No hacer nada si falla

El planteamiento está muy bien. Si falla al importar las clases es porque no existen, ya que estamos en un NVDA antiguo. Por lo tanto, nos olvidamos del panel y usamos el elemento de menú y el diálogo clásico. ¿Todos estamos deacuerdo? Sí, excepto el intérprete Python.
Cuando Python lea nuestro archivo de complemento, verá que intentamos definir la clase del panel, y que indicamos un padre que no existe porque no lo hemos importado. Por suerte, Python es tan flexible como NVDA: cualquier variable se puede asignar a cualquier cosa, sin importar el tipo e incluso sin que importe que sea una clase o función. Lo único que nos pide es algo de lo que pueda heredar el panel, así que vamos a darle el objeto base del lenguaje: object. En la cláusula except, en vez de poner pass para que no haga nada, hacemos: SettingsPanel=object

Optimización de código

Es tentador copiar y pegar código de unos métodos a otros. En el caso de EnhancedAria, siempre define las mismas casillas, y siempre guarda la misma configuración. Sin embargo, no es una buena práctica, ya que si tenemos un fallo este también se duplica, y hay que corregirlo tantas veces como se haya hecho el copia-pega. Por lo tanto, se ha sacado el código repetido y se ha metido en funciones globales del módulo:

  • addonSettingsTitle: título del diálogo y del panel. Es el mismo en ambos casos. Si lo dejamos duplicado, un traductor tendría que modificarlo dos veces.
  • createSettings(obj, sizer): obj es una referencia al objeto que llama a la función. Es como el self que vimos antes. Esta función crea las casillas.
  • storeSettings(obj): almacena los ajustes al pulsar el botón aceptar.

Haciendo estos cambios, el tamaño de las clases se reduce. Así quedaría el método onSave:
 def onSave(self):
  storeSettings(self)

Conclusión

Existen muchas formas de adaptar nuestros complementos para que aprovechen las nuevas características introducidas en NVDA 2018.2. Podemos modificarlos, o podemos dejarlos como están. Sin embargo, para proporcionar una experiencia de usuario consistente, se recomienda lo primero más que lo segundo. En este tutorial se ha visto sólo una de las muchísimas alternativas existentes. Tanto Python como NVDA son flexibles, permitiendo que cada desarrollador haga las cosas a su manera y optimice el código como le parezca oportuno. Por ejemplo, la simplificación de código vista anteriormente podría haberse hecho definiendo una clase y aplicando herencia múltiple, si bien es cierto que este enfoque podría complicar un poco las cosas. Espero que hayáis disfrutado leyendo este tutorial tanto como yo al escribirlo, y que os sirva para poner vuestros complementos al día. Para cualquier pregunta o sugerencia, no dudéis en dejarnos un comentario debajo.
¡Hasta la próxima!

Aunque hace unos días os hablábamos de que no es conveniente utilizar portables modificados de NVDA, sí es cierto que tener copias portables creadas bajo nuestra propia responsabilidad y en principio para nuestro uso o de personas cercanas en las que confiemos, puede resultar muy útil en determinados momentos, como tener acceso a un PC en el que ayudar a alguien, en lugares públicos como bibliotecas o centros de trabajo, etc. Lo malo es que NVDA ejecutándose de manera portable no tendrá todos los privilegios necesarios para acceder al sistema entero, pero lo bueno es que al menos sí podremos hacerlo.

¿Qué es un portable?

Una copia portable de NVDA es aquella que podemos llevar en un Pen drive o cualquier otro soporte de almacenamiento que se nos ocurra, con nuestras configuraciones (o no), que nos permite ejecutar NVDA en cualquier equipo donde podamos conectar un USB y que cumpla los requisitos mínimos para ejecutar NVDA. Esto resulta especialmente útil a la hora de trabajar en un equipo que no es nuestro o en momentos similares.

¿Es recomendable? Depende de para qué. Aunque desde nvda.es no lo recomendamos para el uso habitual del equipo, nadie te impide no instalar NVDA y ejecutarlo desde donde más te gusta, al igual que nadie te asegura su óptimo funcionamiento, y esto es porque probablemente no lo tendrás. Al ser una copia portable, a la que no permitimos privilegios para acceder a determinadas partes del sistema, como las ventanas del control de usuarios, por ejemplo, habrá partes del sistema con las que nos será imposible trabajar con NVDA portable.

¿Cómo se crea?

Tenemos dos formas de crearlo, las dos igual de sencillas.

  • Desde el instalador de NVDA.
    1. Abrimos el instalador de NVDA.
    2. Nos movemos con Tab hasta el botón crear una copia portable.
    3. Seleccionamos donde queremos que se guarde.
  • Desde nuestro NVDA ya instalado.
    1. Abrimos el menú de NVDA.
    2. Bajamos hasta herramientas y pulsamos crear copia portable.
    3. Pulsamos el botón de examinar, y seleccionamos en la ventana de Explorador de Archivos que se nos abre el directorio donde queremos que se guarde nuestra copia portable.
    4. Si queremos que lo haga, marcamos la casilla correspondiente para que se copien la configuración y los complementos de nuestra copia instalada al portable que estamos creando. Esto copiará todos los complementos instalados, los sintetizadores y sus licencias y todo cuanto tengamos personalizado (salvo la aplicación de los ajustes de NVDA a ventanas seguras).
    5. Pulsamos continuar y después aceptar cuando se nos haya creado.

Es recomendable crear el portable en una carpeta vacía, ya que genera más de 50 archivos.

¿Cómo se ejecuta?

Para ejecutar NVDA portable, será tan sencillo como navegar al directorio donde se haya guardado y ejecutar el archivo nvda.exe. Si queremos instalarlo en nuestro equipo, será tan sencillo como navegar de nuevo al menú herramientas, donde la opción de crear copia portable se habrá sustituido por la de instalar. Al instalarlo, podemos aplicar (o no) los complementos y configuraciones que teníamos en el portable.

Conclusiones

La creación de este artículo viene motivada por la grabación de un vídeo de Félix Couce, donde explica de forma más detallada el uso y creación de las copias portables de NVDA que se puede encontrar en nuestro canal de YouTube y que insertamos aquí.

En definitiva. ¿Conviene crear copias portables de NVDA? Depende del uso que vayamos a darle, pero desde luego es algo que se le ha reprochado en multitud de ocasiones a JAWS y que incluso se ha tratado de emular para este lector de pantalla en muchas ocasiones. No obstante, todas estas iniciativas han resultado fallidas. No creemos que sea conveniente para el uso continuo del lector, ya que se le puede sacar todo el potencial instalado, pero es la mejor opción para utilizar equipos ajenos.

Referencias

NVDA tiene una opción que, aunque desconocida para mucha gente, lo hace ser muy versátil a la hora de trabajar en entornos concretos con programas aún más concretos. La opción de la que hablamos son los perfiles, que podemos configurar según nuestras necesidades.

Contenido

¿Qué son?. 1

El menú de perfiles. 1

Creando un nuevo perfil. 1

Notas adicionales. 2

Referencias. 2

 

 

¿Qué son?

Son configuraciones concretas que podemos hacer a NVDA en escenarios concretos según las necesidades que tengamos al trabajar en determinada aplicación. Estos perfiles se lanzarán de dos maneras, o bien cuando los lancemos de forma manual o bien al configurarlos en determinados entornos, al estar usando una aplicación, por ejemplo. Realmente es mucho más sencillo de lo que parece.

Por ejemplo, puede que, al estar editando en Word, necesitemos que NVDA nos lea los atributos de la letra, los errores de ortografía, los ajustes de párrafo y las sangrías, pero esto nos incomode a la hora de navegar por internet con Firefox. En lugar de activarlo y desactivarlo manualmente, NVDA puede hacerlo por nosotros. La mayoría de las opciones de NVDA, menos las opciones generales, que se aplican a todo el programa, pueden aplicarse en un perfil de configuración cuando lo estamos editando. Incluso podríamos configurar que NVDA cambiara de sintetizador y/o velocidad de lectura al abrir un lector de e-books, por ejemplo.

 

El menú de perfiles

Accedemos a él desde el menú principal de NVDA, perfiles de configuración, o con NVDA+Control+P. Por defecto, solo tendremos el perfil de la configuración normal, que estamos editando. Si por ejemplo tenemos el complemento para Lambda instalado, veremos que tenemos un perfil para Lambda que este nos crea para aplicar sus configuraciones.

  • Al entrar en el menú, lo primero que tenemos es esta lista de perfiles mencionada más arriba.
  • Al movernos con tab, encontramos una opción para crear un nuevo perfil.
  • Al colocarnos en un perfil adicional de NVDA, podemos eliminarlo o editarlo desde este menú.
  • Si continuamos, podemos desplegar una lista de disparadores.
  • Si continuamos, podemos deshabilitar la actuación de los disparadores.

 

Creando un nuevo perfil.

                Para crear un nuevo perfil, pulsamos el botón nuevo. Podemos ponerle un nombre, que nos ayudará a identificarlo, y al pulsar tab seleccionaremos qué actuará como disparador, es decir, en qué situación se activará este perfil. Lo ideal es que, si queremos que se ejecute en una aplicación, Word, por ejemplo, lo creemos con una ventana de Word abierta. Será entonces cuando podremos seleccionar aplicación actual (Winword) y solo se lanzará al abrir Word. Esto, por supuesto, podríamos aplicarlo a la mayoría de las aplicaciones para las que consideremos que necesitamos una configuración diferente.

                Es importante reseñar que, si no rellenamos el nombre, al seleccionar que se ejecute en una aplicación, se asignará un nombre automáticamente que nos permitirá identificar el perfil.

 

 

Notas adicionales

  • Se puede activar un perfil manual mediante su opción correspondiente, pero esto no evitará que los lanzadores activen otros. Las opciones del primer perfil, el manual, primarán sobre las que active el lanzador. Por ejemplo, si en el perfil activado manualmente no tenemos activado el aviso de errores de ortografía, pero sí lo tenemos en el automático activado por un lanzador, al no estar activado en el primero no se leerán. No obstante, sin en el del disparador se ha cambiado la voz y esto no se ha hecho en el manual, se utilizará la voz del perfil disparado y las configuraciones modificadas se guardarán en el manual.
  • Desde la opción de disparadores podemos ver que disparador está asociado a cada perfil y editarlo.
  • Si se ha activado manualmente un perfil, las opciones que se modifiquen se guardarán en él. Si no se ha hecho, las opciones modificadas se guardarán en el perfil que más recientemente se haya lanzado. Si no hay perfil, asociado o manual, las opciones modificadas se guardarán en el perfil normal.
  • Para editar el perfil asociado a verbalizar todo, este se debe activar de forma manual.
  • Se pueden desactivar todos los disparadores marcando la casilla de verificación correspondiente. Esto puede ser útil para hacer determinadas configuraciones sin que interfieran los perfiles.

 

Referencias

 

Nota: Esta guía fue publicada en el año 2015 en la antigua web de NVDA en español. La versión que se ofrece a continuación ha sido actualizada solucionando errores producidos por la conversión entre lenguajes de marcado, eliminando algunos fallos ortográficos y gramaticales del autor y cambiando la referencia al apartado de la guía de usuario que habla de los diccionarios. Si detectas algún error, déjanos un comentario más abajo.

Como se explica en la sección 11.1.14 de Diccionarios del Habla en la Guía de Usuario, gracias a la creación o modificación de los diccionarios NVDA permite personalizar la forma en que el sintetizador lee ciertas palabras, símbolos o dígitos.

En esta pequeña guía vamos a ver como utilizar Expresiones regulares en los diccionarios del habla del lector de pantalla NVDA, ya que en la guía de usuario no se explican con detalle.

A continuación se muestra una breve introducción a las expresiones regulares, seguida de una lista de caracteres y secuencias especiales. No trates de memorizar estas listas, por ahora solo debes tener en cuenta que cada carácter y secuencia especial tiene su propio significado y función.

Con los ejemplos aprenderás a entender el funcionamiento y a crear tu propio diccionario con expresiones regulares.

¿Estás listo? ¡Vamos!

Expresiones regulares: conceptos básicos

En esta sección se presentan conceptos de expresiones regulares, y se explica cómo se crean y se utilizan en los diccionarios de voz de NVDA.

Una expresión regular, llamada también regex, es una secuencia de caracteres que forma un patrón de búsqueda, principalmente utilizada para la búsqueda de cadenas de caracteres y operaciones de sustitución.

Específicamente, NVDA va a aplicar Expresiones regulares para reemplazar patrones cada vez que se encuentre una coincidencia en la lectura.

Podemos decir que es una operación de sustitución cuando el texto escrito se reemplaza antes de ser procesado por el sintetizador de voz.

Las expresiones regulares pueden contener caracteres ordinarios y caracteres especiales.

Los caracteres ordinarios son las letras de la a a la z, los números, el signo subrayado y otros signos de puntuación que no están incluidos en los caracteres especiales.

La mayoría de los caracteres ordinarios, como “a A b 9 0”, son las expresiones regulares más simples, simplemente coinciden consigo mismos.

Por ejemplo, se pueden concatenar caracteres ordinarios así:

la expresión “Odissea2001” coincide con “Odissea2001”.

Caracteres Especiales

Lista completa de caracteres especiales y su comportamiento en el contexto de las expresiones regulares:

  • “\”. Marca el siguiente caracter como un caracter especial, un literal, una referencia inversa. Por ejemplo, “r” coincide con el caracter “r”, mientras que “\r” coincide con un caracter de retorno de carro. Cuando precede a caracteres que normalmente se tratan de forma especial, indica que el siguiente caracter no es especial y debe ser interpretado literalmente. Por ejemplo, la secuencia \” coincide con ” y ( coincide con (.
  • “.”. Coincide con cualquier caracter excepto con “\n”.
  • “^”. Coincide con la posición al principio de la cadena de entrada. La propiedad Multiline está establecida para coincidir también con la siguiente posición después de “\n” o “\r”. Es decir, este caracter corresponde también a la posición inmediatamente después de una nueva línea o un retorno de carro.
  • “$”. Coincide con la posición al final de la cadena de entrada. La propiedad Multiline está establecida para que $ coincida también con la posición anterior a “\n” o “\r”.
  • “?”. Cuando este caracter va inmediatamente después de cualquier otro cuantificador (se mencionan a continuación), el modelo coincidente es no expansivo. Un modelo no expansivo coincide lo menos posible con la cadena buscada, mientras que el modelo expansivo predeterminado coincide tanto como sea posible con la cadena buscada.
  • “?”. Un cuantificador,coincide una vez o ninguna con el caracter o subexpresión anterior. Equivale a {0,1}
  • “*”. Un cuantificador, coincide cero o más veces con el caracter o subexpresión anterior. Equivale a {0,N}.
  • “+”. Un cuantificador, coincide una o más veces con el caracter o subexpresión anterior. Equivale a {1,N}.
  • “{n}”. Un cuantificador: coincide exactamente n veces, donde n es un entero no negativo. Por ejemplo, “o{2}” no coincide con la ‘o’ de “soy”, pero coincide con las dos oes de “Cooperar”.
  • “{n,}”. Un cuantificador: coincide como mínimo n veces, donde n es un entero no negativo. Por ejemplo, “o{2,}” coincide con todas las oes de “nooooo”, pero no coincide con la “o” de “sol”.
  • “{n,m}”. Un cuantificador. M y n son enteros no negativos donde n es menor o igual de m. Coincide n veces como mínimo y m veces como máximo. Tener en cuenta que no se puede incluir un espacio entre la coma y los números.
  • “[xy]”. Un juego de caracteres, coincide con cualquiera de los caracteres incluidos entre corchetes. Por ejemplo, “[aeu]” coincide con “a” en “Madrid”.
  • “[^xy]”. Un juego de caracteres negativo. Coincide con cualquier caracter no incluido entre corchetes. Por ejemplo, “[^aeu]” coincide con la “M”, la”d”, la “r”, la “d” de “Madrid”, pero no coincide con la “a”.
  • “[a-z]”. Un intervalo de caracteres, coincide con cualquier caracter del intervalo especificado. Por ejemplo, “[a-z]” coincide con cualquier caracter alfabético en minúsculas dentro del intervalo de la ‘a’ a la ‘z’.
  • “[^a-z]”. Caracteres de un intervalo negativo. Es decir, coincide con cualquier caracter que no esté incluido en el intervalo especificado. Por ejemplo, “[^a-z]” coincide con cualquier caracter que no esté incluido en el intervalo de la ‘a’ a la ‘z’.
  • “|” Es un carácter disyuntivo: x|y, coincide con x o con y. Por ejemplo, la expresión “Juli(a|o)” coincide con “Julia” o con “Julio”.
  • “(…)”. Una subexpresión que coincide con el modelo entre paréntesis y captura la coincidencia. La coincidencia capturada se puede recuperar en el remplazo mediante las secuencias especiales “\1” “\2” etc. Para que coincidan los paréntesis ( ), utilice “(” o “)”.
  • “(?:…)”. Una subexpresión que coincide con el modelo entre los paréntesis, pero que no captura la coincidencia, es decir, la coincidencia no se almacena para un posible uso posterior. La expresión “(?:\d+)(Salir)” coincide con la palabra “Salir” después de un conjunto de números en una cadena como por ejemplo “123456Salir Gráfico Enlace”. Nota, los números no se almacenan y además no serán procesados por el sintetizador. El sintetizador simplemente ignora el contenido de esta Afirmación. Esta subexpresión se considera como grupo de cadena irregular, No se utiliza mucho en los diccionarios.
  • “(?=…)”. Una subexpresión que realiza una búsqueda anticipada positiva, la cual coincide con la cadena en cualquier punto donde empiece una cadena que coincida con el modelo entre los paréntesis. Por ejemplo, “Juli(?=a|o)” coincide con “Juli” en “Julia” pero no con “Juli” en “Juliu”. Ésta es una coincidencia sin captura, es decir, la coincidencia no se captura para un posible uso posterior. Las búsquedas anticipadas no consumen caracteres, es decir, después de que se produce una coincidencia, la búsqueda de la siguiente comienza inmediatamente después de la última coincidencia, no después de los caracteres que componen la cadena de búsqueda anticipada.
  • “(?!…)”. Una subexpresión que realiza una búsqueda anticipada negativa, la cual coincide con la cadena de búsqueda en cualquier punto donde empiece una cadena que no coincida con el modelo. Por ejemplo, “Juli(?!e|u)” coincide con “Juli” en “Julia” pero no coincide con “Juli” en “Julie”. Ésta es una coincidencia sin captura, es decir, la coincidencia no se captura para un posible uso posterior. Las búsquedas anticipadas no consumen caracteres, es decir, después de que se produce una coincidencia, la búsqueda de la siguiente comienza inmediatamente después de la última coincidencia, no después de los caracteres que componen la cadena de búsqueda anticipada.
  • “(?<=…)”. Afirmación look-behind positiva, es decir, es válida la siguiente expresión sólo si la condición del modelo entre paréntesis se verifica. Por ejemplo, “(?<=0)\d” coincide con cualquier número precedido por cero. Las afirmaciones son sin captura, es decir, no se captura para un posible uso posterior.
  • “(?<!…)”. Afirmación look-behind no positiva, es decir, es válida la siguiente expresión sólo si la condición del modelo entre paréntesis no se verifica. Por ejemplo, “(?<![1-9])\d” coincide con cualquier número que no está precedido por un número comprendido entre 1 y 9.

Secuencias Especiales

Las secuencias especiales consisten en “\” y un caracter ordinario como en la lista de abajo. Si el caracter ordinario no está en la lista, entonces la expresión resultante coincidirá con el segundo caracter.

  • “\A”. Coincide sólo en el inicio de la cadena.
  • “\Z”. Coincide sólo con el final de la cadena.
  • “\b”. Coincide con el límite de una palabra. Es decir, la posición entre una palabra y un espacio. Por ejemplo, “ir\b” coincide con ‘ir’ en “mentir”, pero no coincide con ‘ir’ en “mentira”.
  • “\B”. Coincide con una posición que no es un límite de palabra. La expresión “ir\B” coincide con “ir” en “mentiras”, pero no coincide con “ir” en “mentir”.
  • “\d”. Coincide con un dígito. Equivale a “[0-9]”.
  • “\D”. Coincide con un caracter que no sea un dígito. Equivale a “[^0-9]”.
  • “\s”. Coincide con cualquier caracter de espacio en blanco incluidos espacio, tabulación, salto de página, etc. Equivale a “[\f\n\r\t\v]”.
  • “\S”. Coincide con cualquier caracter que no sea un espacio en blanco. Equivale a “[^\f\n\r\t\v]”.
  • “\w”. Coincide con cualquier caracter alfanumérico incluido el subrayado. Equivale a “[A-Za-z0-9_]”.
  • “\W”. Coincide con cualquier caracter que no pertenezca a los caracteres alfanuméricos. Equivale a “[^A-Za-z0-9_]”.

Ejemplos

Finalmente llegamos a la parte práctica con los ejemplos.

Por conveniencia usaremos un diccionario temporál en los ejemplos. Para ello, necesitamos ajustar la opción para las expresiones regulares desde el diálogo correspondiente.

Ejemplo 1

El sintetizador eSpeak en español pronuncia con C suave palabras extranjeras que comienzan con “ch”, como Christ, christmass, Christofer o Christopher, Christian, etc. Si deseamos escuchar una pronunciación mejor, podemos usar la siguiente expresión regular, muy sencilla.

Patrón: “\bChri\B”

Reemplazar: “Cri”

Explicación: la secuencia especial “\b” marca el inicio de la palabra, mientras que la secuencia “\B” (la B mayúscula) dice que en esta posición no hay limite de palabra.

Cada vez que se encuentra el prefijo “Chri” en el texto, el sintetizador lee el texto indicado en el Reemplazo “Cri”.

Tener en cuenta que las secuencias especiales \b y \B son marcadores de posición, no necesitan una recuperación en el reemplazo.

La mayoría de las secuencias especiales, como por ejemplo \s, \w, \d, necesitan ser recuperadas en el reemplazo, si no vamos a recuperarlas no se envierán al sintetizador.

Ejemplo 2

A veces puede ser útil leer el nombre de unidades de medida cuando en el texto aparecen como abreviaturas.

Por ejemplo, 1ml 12 ml. ¿no sería fantástico que el sintetizador las leyera así?

Un mililitro y doce mililitros.

Necesitamos dos expresiones regulares para distinguir el plural y el singular.

Primera expresión regular.

Patrón: “(^|\s)1[ ]?ml\b”

Reemplazar: “\1un mililitro”

Segunda expresión:

Patrón: “(?<=\d)[ ]?ml\b”.

Reemplazar: ” Mililitros”.

Explicación: la primera subexpresión “(^|\s)” tiene en cuenta si la posición está al principio o si hay un espacio antes del número 1.

Esta subexpresión se puede recuperar en el remplazo mediante la secuencia especial \1.

Recuerda que las subexpresiones regulares entre paréntesis, llamadas también grupos de cadena, se pueden recuperar en el remplazo con la secuencia especial correspondiente. Es decir, el primer grupo de cadena de izquierda a derecha se recupera con \1, el segundo con \2, etcétera. Después del grupo está el número 1, ya que estamos buscando en el texto 1ml o 1 ml, puede ser que haya o no un espacio entre el número y la abreviatura.

Entre corchetes se ha puesto un espacio, se pueden añadir distintos caracteres de espacio. Luego hay un cuantificador, cerrar interrogación, que indica que este espacio puede aparecer una vez o ninguna.

Finalmente, nuestra abreviatura ml y una secuencia especial “\b” para cerrar la expresión regular.

La secuencia al fin indica un límite de palabra, es decir que “ml\b” no puede coincidir por ejemplo con “mls” o “mla”.

La segunda expresión regular se abre con una aserción “(?<=\d), significa que nuestra expresión Es válida solo si hay un dígito antes.

La aserción usa una secuencia especial “\d” que coincide con cualquier número.

Entonces, ¿por qué no funciona si antes está solo el número 1?

Te toca adivinar la respuesta…

Ahora, gracias a las dos expresiones regulares, cuando “ml” es precedido por 1 o ningún caracter alfanumérico, el sintetizador lee “mililitro”. En los demás casos el sintetizador lee “mililitros”.

El orden es importante, las expresiones regulares más arriba Tienen prioridad y pueden cancelar las expresiones posteriores. Por eso la segunda expresiónSe inserta a continuación de la primera.

¿A qué estás esperando? Ahora trata de crear reglas para leer otras unidades como Kg, MB, m² o cm³.

Ejemplo 3

Todavía es común separar los millares, millones, etc., mediante un espacio Para facilitar la lectura de números cuando constan de más de cuatro cifras:

12 354 o 1 303 404.

Algunos sintetizadores no leen correctamente esta forma de escribir los números enteros, más adelante se explica cómo configurar una expresión regular para leerlos correctamente.

Abrimos un diálogo para crear nuestro diccionario del habla desde el menú Preferencias.

Necesitamos ajustar el patrón para comparar como expresión regular desde el botón de opción correspondiente, y rellenar el campo Patrón y el campo de reemplazar.

Patrón: “(?<=\d)([  ]?)(\d{3})”

reemplazar: “\2”

Vamos a explicar.

La primera afirmación indica que la búsqueda es cierta si hay un dígito antes de la cadena. Sigue una subexpresión entre paréntesis con un conjunto de espacios dentro, son tres signos distintos de espacio que se pueden encontrar.

Recuerda que solo uno de los caracteres entre corchetes coincide con la búsqueda, luego hay un cuantificador “?” para indicar que se puede encontrar cero o una sola vez. Sigue a la derecha otra subexpresión entre paréntesis que indica un número de tres dígitos. El cuantificador “{3}” significa que el dígito “\d” debe repetirse tres veces, ni más ni menos.

Vamos a ver lo que tenemos que insertar en el campo reemplazar.

La primera subexpresión “(?<=\d)” en el patrón es una afirmación, no se almacena para un posible uso en el remplazo. Esto no significa que no se tengan en cuenta los caracteres de esa afirmación, el sintetizador va a leer estos dígitos junto a los otros sin recuperar la subexpresión en el campo reemplazar.

El segundo grupo o subexpresión “([ ]?)” es una subexpresión regular y se puede recuperar en el remplazo mediante la secuencia especial \1. Pero nosotros queremos excluirlo, así que no lo recuperamos.

Por el contrario, vamos a recuperar la tercera subexpresión “(\d{3})” mediante la secuencia especial \2.

De esta manera enviamos al sintetizador un número sin espacios entre los dígitos.

Ejemplo 4

Cuando consultamos una lista de números de teléfono o algo similar, puede ser útil leer los números dígito a dígito.

Por ejemplo, si encontramos 3332211444 podríamos leerlo como si estuviera escrito “3 3 3 2 2 1 1 4 4 4”.

Necesitamos una secuencia especial en el patrón de búsqueda que encuentre o coincida con cualquier número, y luego añadir un espacio después de cada dígito sólo cuando es seguido por otro dígito.

Patrón: “(\d)(?=\d)”

Reemplazar: “\1 “.

En el reemplazo recuperamos el primer grupo “(\d)” con “\1”, e inmediatamente después hay un espacio. El patrón de búsqueda encuentra cualquier dígito solo si va seguido de otro dígito.

Este es un ejemplo de una expresión regular que se puede utilizar en determinados casos, pero la mayoría de las veces dá como resultado una lectura tediosa. Está bien para leer números de teléfono, pero no sería agradable leer por ejemplo “Son las 13:26”.

Como hemos visto, las expresiones regulares permiten encontrar un texto específico y reemplazarlo a nuestro gusto antes de enviarlo al sintetizador.

La mayoría de las veces es suficiente utilizar expresiones regulares simples, aunque debemos tener en cuenta que cuanto más simple sea la expresión regular, menos específica será la búsqueda.

Una sugerencia antes de concluir: no se deberían crear diccionarios del habla muy largos. Si el diccionario es demasiado grande puede ralentizar mucho el lector de pantalla. (1500 expresiones o más).

Además, no hay necesidad de corregir los errores de ortografía, o tratar de simular la pronunciación extranjera cuando el sintetizador no tiene los fonemas necesarios.

¡Hola a todos!
En este videotutorial, que ya tiene algún tiempo, Felipe Ibarra nos explica cómo instalar complementos en NVDA de una forma clara y sencilla. El vídeo dura aproximadamente 24 minutos, y ha sido grabado teniendo en cuenta también a personas sin discapacidad visual, de tal forma que todo el mundo pueda aprender de un modo guiado cómo se instalan complementos en este lector de pantalla. ¡Que lo disfrutes! Y como siempre, si tienes cualquier inquietud, no dudes en compartirla con nosotros más abajo, en los comentarios.