13.1.13. El editor de ajustes#

Muchas aplicaciones de cámara necesitan un puñado de ajustes que una persona no programadora pueda cambiar – un umbral, un modo, una contraseña de Wi-Fi – sin tocar el script. El OpenMV Cam Settings Editor es la herramienta del IDE para ello: un archivo JSON describe los controles, el editor convierte esa descripción en un formulario, y tu script vuelve a leer los valores guardados con json.load(). El archivo es a la vez la configuración y la definición de su propio editor, de modo que la misma configuración que almacena los valores también dispone la interfaz gráfica para editarlos.

El submenú Tools → OpenMV Cam Settings Editor contiene sus tres acciones.

  • Open OpenMV Cam Settings Config File lee la configuración desde la unidad de la cámara conectada y la abre en el editor; al guardar se vuelve a escribir en la cámara.

  • Open Config File hace lo mismo para un archivo .json de tu ordenador, para preparar una configuración sin conexión.

  • Create Default Config escribe una configuración inicial en disco y la abre – un ejemplo que ejercita todos los tipos de control, listo para reducirse a los ajustes que necesita tu propia aplicación.

The OpenMV Cam Settings Editor showing the default demo configuration -- Camera, Processing, Network, and System tabs, with the Camera tab's exposure and gain sliders, resolution and pixel-format dropdowns, mirror and flip checkboxes, and a digital-zoom slider, above the Save and Cancel buttons

El editor de configuración con la configuración predeterminada abierta – los controles del archivo JSON dispuestos como un formulario (pestañas, deslizadores, menús desplegables, casillas de verificación), con Save y Cancel en la parte inferior.#

13.1.13.1. Anatomía de un archivo de configuración#

Un archivo de configuración es un único objeto JSON con dos claves:

  • title (cadena, opcional) – el título de la ventana del editor. El valor predeterminado es OpenMV Cam Settings Editor.

  • controls (arreglo, obligatorio) – la lista de objetos de control que el editor representa, en orden, como filas de un formulario. Esta es toda la interfaz gráfica.

{
  "title": "Camera Settings",
  "controls": [
    { "type": "slider", "name": "threshold", "label": "Threshold", "value": 50 },
    { "type": "checkbox", "name": "draw_overlays", "label": "Draw Overlays", "value": true }
  ]
}

Cada entrada de controls es un control – un campo de texto, un número, una casilla de verificación, un menú desplegable, un deslizador, un grupo, una tira de pestañas, una etiqueta estática, etc. Su type selecciona el widget; el resto del objeto lo configura. Cualquier clave que el editor no reconozca se deja intacta cuando se guarda el archivo, de modo que puedes conservar tus propios metadatos en el archivo junto a los controles.

13.1.13.2. Claves que comparte todo control#

Todo objeto de control acepta estas claves, sea cual sea su type:

  • type (cadena, obligatorio) – qué widget construir, p. ej. "slider" o "combobox". Un type ausente o no reconocido representa un marcador de posición deshabilitado Unknown control en lugar de invalidar todo el archivo, así que un error tipográfico te cuesta un control, no el formulario.

  • name (cadena) – un identificador para tu uso. El editor nunca lo lee; solo lo conserva. Asigna a cada control un name único y tu script lo usa para encontrar el valor guardado de ese control.

  • label (cadena) – el texto mostrado junto al control o sobre él. Para la mayoría de los controles es texto enriquecido – funcionan las etiquetas HTML y los enlaces <a href>.

  • tooltip (cadena) – texto emergente para el control.

  • enabled (booleano, predeterminado true) – establécelo en false para mostrar el control atenuado y de solo lectura.

Los controles de contenedor (group, tabs) usan title en lugar de label para su encabezado, y tabs toma su texto emergente por pestaña; las diferencias se indican en la página de cada control.

13.1.13.3. Guardar y leer valores#

Un control que contiene un valor – una casilla de verificación, un deslizador, un campo de texto – lo lleva en una clave value. Cuando haces clic en Guardar, el editor escribe el value actual de cada control de vuelta en ese mismo objeto de control, en su sitio, y reescribe el archivo. El archivo mantiene su estructura: nada se mueve, solo cambian las claves value. No hay un mapa plano aparte de name a valor – los valores viven dentro de los controles, justo donde se definen.

Por eso la disposición le importa a tu script. Los arreglos controls y tabs mantienen el orden en que los construiste, de modo que el archivo no puede indexarse por nombre como un diccionario – config["controls"]["threshold"] no funciona. Esta pequeña función auxiliar salva esa distancia: dale la ruta de nombres hasta el valor que quieres, y recorre el árbol por ti.

import json


def find(node, *path):
    """Return the control at a path of names; read its value with
    ["value"]. The tab strip and any unnamed group are transparent,
    so you list only the tabs and controls you gave a name."""
    for key in path:
        match = _child(node, key)
        if match is None:
            raise KeyError(key)
        node = match
    return node


def _child(node, key):
    for member in node.get("controls", []) + node.get("tabs", []):
        if member.get("name") == key or member.get("title") == key:
            return member
        if "name" not in member and ("controls" in member or "tabs" in member):
            match = _child(member, key)   # see through an unnamed group / the tab strip
            if match is not None:
                return match
    return None


with open("config.json") as file:
    config = json.load(file)

threshold = find(config, "threshold")["value"]

Cada paso de la ruta es el name de un control o el title de una pestaña (una pestaña no tiene un name propio, aunque puedes añadir uno y se conserva). La tira de pestañas y cualquier group al que no diste nombre son transparentes – find los recorre – de modo que enumeras solo las pestañas y los controles que etiquetaste, muy cerca de una consulta anidada config[...][...]. Un group con nombre es un paso propio, por lo que find también puede devolver el valor de su casilla de verificación. Asigna a cada control un name único para que una ruta nunca sea ambigua. El Ejemplo completo lee valores de la disposición de demostración anidada.

Como la configuración es JSON simple, nada en tu script depende del editor – el editor solo escribe el archivo que tu script ya lee. Un dispositivo puede llevar su configuración y una forma cómoda de cambiarla mientras el script sigue siendo un simple lector de valores.

Un tipo de control impone su entrada al guardar: un lineedit con una mask o regex impide que el editor guarde mientras su texto sea inválido o incompleto, y nombra los campos que hay que corregir. Todos los demás controles restringen la entrada mientras editas – un deslizador no puede salir de su rango, un spinbox se ajusta a sus límites – de modo que un archivo guardado siempre es válido.

13.1.13.4. Referencia del archivo de configuración#

Los tipos de control se documentan en las páginas siguientes, agrupados según lo que hacen.