Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 235 additions & 0 deletions README.es-ES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@


# LabelAutofinderCore

Este es un módulo para NVDA. Útil en otros complementos, no hace nada por sí solo.

El módulo implementa varias técnicas para asociar a un objeto elegido la etiqueta (u otra información) en pantalla, según la posición visual más cercana.

Los escenarios admitidos son:

* páginas web, por ejemplo, los campos en un formulario que no están etiquetados correctamente;
* programas (incluidas aplicaciones UWP) con etiquetas como objetos (recuperables con la revisión de objetos), a veces asociados a elementos de interfaz incorrectos;
* programas con etiquetas como texto (recuperables con la revisión de pantalla), donde la asociación con objetos podría haberse perdido por completo.

¿TL;DR? Salta a la sección "Script para pruebas".

## Uso

El método principal para importar y llamar es `getLabel`, generalmente desde dentro de `event_*` o `chooseNVDAObjectOverlayClasses`.

En el mejor de los casos, con la configuración predeterminada, simplemente puedes hacer algo como:

```
import appModuleHandler
from .labelAutofinderCore import getLabel

class AppModule(appModuleHandler.AppModule):

def event_gainFocus(self, obj, nextHandler):
if not obj.name: # and other checks you want
obj.name = getLabel(obj)
nextHandler()
```

Nota: todos los ejemplos aquí se presentarán como `AppModule`, es decir, un contexto donde este módulo puede tener comportamientos más predecibles. Pero nada te impide usarlo en un `GlobalPlugin`, si restringes adecuadamente su acción (por ejemplo: solo en campos de formulario en páginas web).

## Configuración predeterminada y cómo personalizarla

A veces, la búsqueda de etiquetas con la configuración predeterminada falla, pero puedes personalizarla para capturar mejor la etiqueta correcta.

Puedes proporcionar los siguientes parámetros a `SearchConfig`:

* `obj`: objeto a etiquetar (si deseas pasar solo la configuración a `getLabel`);\
Predeterminado: objeto con foco (u objeto navegador para web) recuperado a través de `api`; debido al procesamiento de eventos o la construcción de objetos, se recomienda encarecidamente pasar el objeto (vía `getLabel` o `config`);
* `strategy`: puede ser "auto", "obj", "text", "uwp" o "web" (pero especificarlo debería tener un impacto mínimo en el rendimiento, es principalmente para comportamientos internos);\
Predeterminado: "auto";
* `labelContainer`: el objeto que contiene la etiqueta (solo para estrategias de texto y web);\
Predeterminado: None (el algoritmo sube en el árbol de ancestros, en orden de abajo hacia arriba);
* `maxParent`: el límite superior debajo del cual recuperar las etiquetas;\
Predeterminado: objeto en primer plano (o None para web, lo que prácticamente significa el primer objeto con el rol DOCUMENT);
* `directions`: constantes de la clase `SearchDirections` (LEFT, TOP, RIGHT, BOTTOM, HORIZONTAL, VERTICAL, LEFT_TOP, ALL), o cualquier tupla definida como `(*SearchDirections.LEFT, *SearchDirections.TOP, *SearchDirections.BOTTOM)`;\
Predeterminado: `SearchDirections.LEFT_TOP`;
* `maxHorizontalDistance`: distancia horizontal máxima entre el punto izquierdo/derecho del objeto a etiquetar y el punto relativo de la etiqueta;\
Predeterminado: 150 para uwp, 100 para obj y web, 8 para la estrategia de texto; si se establece en `sys.maxsize`, será 10000 para la estrategia de texto, el ancho del objeto en primer plano en caso contrario;
* `maxVerticalDistance`: distancia vertical máxima entre el punto superior/inferior del objeto a etiquetar y el punto relativo de la etiqueta;\
Predeterminado: 150 para uwp, 100 para obj y web, None para la estrategia de texto (fuerza a usar la altura del carácter); si se establece en `sys.maxsize`, será 10000 para la estrategia de texto, la altura del objeto en primer plano en caso contrario.

Además, también puedes derivar una configuración a partir de una configuración anterior, construyéndola como `SearchConfig(oldConfig=prevConfig)`.

Así, si tu etiqueta está en la parte inferior, en lugar de la izquierda o superior predeterminadas, puedes hacer:

```
import appModuleHandler
from .labelAutofinderCore import getLabel, SearchConfig, SearchDirections

class AppModule(appModuleHandler.AppModule):

def event_gainFocus(self, obj, nextHandler):
if not obj.name: # and other checks you want
config = SearchConfig(directions=SearchDirections.BOTTOM)
obj.name = getLabel(obj, config)
nextHandler()
```

Ten en cuenta que, con `LEFT_TOP` predeterminado o múltiples direcciones, el módulo siempre devuelve una etiqueta, es decir, la etiqueta con la distancia mínima al objeto pasado entre las encontradas en las direcciones especificadas.

## Script para pruebas

Para comprender y explorar mejor tu situación, puede resultar útil usar un script como este:

```
import api
import globalPluginHandler
import ui
from scriptHandler import script
from .labelAutofinderCore import getLabel, SearchConfig, SearchDirections

class GlobalPlugin(globalPluginHandler.GlobalPlugin):

scriptCategory = "Testing LabelAutofinder module"

@script(
description=_("tries to find and reports a label for current focused object")
)
def script_findLabel(self, gesture):
tempObj = api.getNavigatorObject()
if tempObj.treeInterceptor:
obj = tempObj
else:
obj = api.getFocusObject()
labelTuples = []
baseConfig = SearchConfig(obj=obj)
for direction in ("left", "top", "right", "bottom"):
searchDirection = getattr(SearchDirections, direction.upper())
directionConfig = SearchConfig(oldConfig=baseConfig, directions=searchDirection)
# overview=True to get distance, in addition to label
distanceAndLabel = getLabel(config=directionConfig, overview=True)
if distanceAndLabel:
labelTuples.append((direction, *distanceAndLabel,))
if not labelTuples:
ui.message(_("Unable to find any label"))
return
# sort for distance
labelTuples.sort(key=lambda i: i[1])
labelMsgs = []
for direction, distance, label in labelTuples:
labelMsg = "{distance} on {direction}: {label}".format(distance=distance, direction=direction, label=label)
labelMsgs.append(labelMsg)
msg = '; '.join(labelMsgs)
ui.message(msg)
```

## ...¡y otra información!

Aunque nació para etiquetas, al desarrollar este módulo me alegró descubrir que puede usarse en una pequeña cantidad de otros casos.

Uno de ellos son los deslizadores (sliders). Usualmente van del 0 al 100%, en pantalla, de hecho, podrían presentarse con un rango completamente diferente, por ejemplo, de KB/s, Decibelios, y así sucesivamente.

Ahora puedes hacer algo como:

```
import appModuleHandler
from controlTypes import Role as roles
from NVDAObjects.IAccessible import IAccessible
from .labelAutofinderCore import getLabel, SearchConfig, SearchDirections

class AppModule(appModuleHandler.AppModule):

def chooseNVDAObjectOverlayClasses(self, obj, clsList):
if not obj.name and obj.role == roles.SLIDER:
clsList.insert(0, SliderWithUnit)

class SliderWithUnit(IAccessible):

def _get_name(self):
name = getLabel(self)
return name

def _get_value(self):
config = SearchConfig(directions=SearchDirections.RIGHT) # or any other direction in your situation
value = getLabel(self, config)
return value
```

## Notas y sugerencias

### Incluir como submódulo de Git

Si incluyes este módulo en tu complemento, la mejor forma es probablemente agregarlo como un submódulo de Git, bajo la ruta apropiada. Por ejemplo:

```
>git submodule add https://github.com/ABuffEr/labelAutofinderCore addon/appModules/labelAutofinderCore
>git submodule init
>git submodule update
>git commit -m "Added labelAutofinderCore as submodule"
>git push --all
```

Si tienes problemas al ejecutar `git submodule update` las siguientes veces, intenta agregar la opción "--remote" o consulta [aquí.](https://stackoverflow.com/questions/3336995/git-will-not-init-sync-update-new-submodules)

Independientemente de la ruta, mantén el nombre de la última carpeta como "labelAutofinderCore", para garantizar una "comprobación de compatibilidad" por parte de otros complementos (especialmente plugins globales).

Además, ¡me hará mucho gusto que cites este trabajo en tu readme!

### Cuando el texto desaparece

Cuando se requiere la estrategia de texto (encuentras etiquetas solo con la revisión de pantalla), es posible que notes un comportamiento extraño al reiniciar NVDA y en otras situaciones: el texto desaparece por completo, para volver a aparecer si minimizas o cierras y vuelves a abrir el programa/ventana.

No es causado por este módulo, que sin embargo proporciona una solución.

Usa algo como esto:

```
import appModuleHandler
from controlTypes import Role as roles
from .labelAutofinderCore import refreshTextContent

class AppModule(appModuleHandler.AppModule):

def event_foreground(self, obj, nextHandler):
# to fix text disappearing
if obj.role == roles.PANE: # or similar, but anyway the role of object containing text
refreshTextContent(obj)
nextHandler()
```

Para conocer la razón y un método alternativo (pero menos confiable en mi experiencia), consulta [este mensaje de Emil Hesmyr.](https://nvda-addons.groups.io/g/nvda-addons/message/25970)

### Combobox y combobox editable

Puedes encontrarte con situaciones con combobox y combobox editables sin etiquetar (me refiero, con otro objeto secundario como campo editable).

Sugiero distinguir mediante `event_gainFocus` y `event_focusEntered` para evitar una doble etiquetado.

Algo como esto:

```
import appModuleHandler
from controlTypes import Role as roles
from .labelAutofinderCore import getLabel

class AppModule(appModuleHandler.AppModule):

def event_gainFocus(self, obj, nextHandler):
# to label simple edit and combo boxes
if (
(not obj.name)
and
(obj.role == roles.COMBOBOX or (obj.role == roles.EDITABLETEXT and obj.simpleParent.role != roles.COMBOBOX))
):
obj.name = getLabel(obj)
nextHandler()

def event_focusEntered(self, obj, nextHandler):
# to label combo with edit boxes
if not obj.name and obj.role == roles.COMBOBOX:
obj.name = getLabel(obj)
nextHandler()
```

### Evitar cajas de edición "grandes"

Existen cajas de edición anónimas que tienen toda la razón para serlo, como informes de registro, ventana principal de un editor de texto, y así sucesivamente.

Una forma rápida de excluir estos objetos puede ser referirse a `obj.location.width`, estableciendo un límite superior o inferior razonable (que puede variar según la resolución de pantalla, aunque), o a la presencia de MULTILINE en los estados.