13.1.13. L’editor delle impostazioni#

Molte applicazioni per camere necessitano di una manciata di impostazioni che un non programmatore possa modificare – una soglia, una modalità, una password Wi-Fi – senza toccare lo script. L’OpenMV Cam Settings Editor è lo strumento dell’IDE per questo: un file JSON descrive i controlli, l’editor trasforma quella descrizione in un modulo e il tuo script rilegge i valori salvati con json.load(). Il file è sia la configurazione sia la definizione del proprio editor, così la stessa configurazione che memorizza i valori dispone anche la GUI per modificarli.

Il sottomenu Tools → OpenMV Cam Settings Editor contiene le sue tre azioni.

  • Open OpenMV Cam Settings Config File legge la configurazione dall’unità della camera connessa e la apre nell’editor; il salvataggio la riscrive sulla camera.

  • Open Config File fa lo stesso per un file .json sul tuo computer, per preparare una configurazione offline.

  • Create Default Config scrive una configurazione iniziale su disco e la apre – un esempio che mette alla prova ogni tipo di controllo, pronto per essere ridotto alle impostazioni di cui la tua applicazione ha bisogno.

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

L’editor delle impostazioni con la configurazione predefinita aperta – i controlli del file JSON disposti come un modulo (schede, cursori, menu a discesa, caselle di controllo), con Save e Cancel in fondo.#

13.1.13.1. Anatomia di un file di configurazione#

Un file di configurazione è un singolo oggetto JSON con due chiavi:

  • title (string, facoltativo) – il titolo della finestra dell’editor. Il valore predefinito è OpenMV Cam Settings Editor.

  • controls (array, obbligatorio) – l’elenco degli oggetti di controllo che l’editor visualizza, in ordine, come righe di un modulo. Questa è l’intera GUI.

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

Ogni voce in controls è un controllo – un campo di testo, un numero, una casella di spunta, un menù a discesa, uno slider, un gruppo, una barra di schede, un’etichetta statica e così via. Il suo type seleziona il widget; il resto dell’oggetto lo configura. Qualsiasi chiave che l’editor non riconosce viene lasciata intatta al salvataggio del file, quindi puoi conservare i tuoi metadati nel file insieme ai controlli.

13.1.13.2. Chiavi comuni a ogni controllo#

Ogni oggetto di controllo accetta queste chiavi, qualunque sia il suo type:

  • type (string, obbligatorio) – quale widget costruire, ad esempio "slider" o "combobox". Un type mancante o non riconosciuto produce un segnaposto Unknown control disabilitato invece di far fallire l’intero file, quindi un errore di battitura ti costa un controllo, non il modulo.

  • name (string) – un identificatore per il tuo uso. L’editor non lo legge mai; si limita a conservarlo. Assegna a ogni controllo un name univoco e il tuo script lo usa per trovare il valore salvato di quel controllo.

  • label (string) – il testo mostrato accanto o sul controllo. Per la maggior parte dei controlli si tratta di rich text – funzionano i tag HTML e i collegamenti <a href>.

  • tooltip (string) – il testo mostrato al passaggio del mouse sul controllo.

  • enabled (boolean, predefinito true) – imposta a false per mostrare il controllo in grigio e in sola lettura.

I controlli contenitore (group, tabs) usano title invece di label per la loro intestazione, e tabs accetta il suo tooltip per ciascuna scheda; le differenze sono indicate nella pagina di ciascun controllo.

13.1.13.3. Salvataggio e lettura dei valori#

Un controllo che contiene un valore – una casella di spunta, uno slider, un campo di testo – lo porta in una chiave value. Quando fai clic su Save, l’editor scrive il value corrente di ciascun controllo direttamente nello stesso oggetto di controllo, sul posto, e riscrive il file. Il file mantiene la sua struttura: nulla si sposta, cambiano solo le chiavi value. Non esiste una mappa piatta separata da name a valore – i valori risiedono all’interno dei controlli, esattamente dove sono definiti.

Ecco perché il layout è importante per il tuo script. Gli array controls e tabs mantengono l’ordine in cui li hai costruiti, quindi il file non può essere indicizzato per nome come un dizionario – config["controls"]["threshold"] non funziona. Questa piccola funzione ausiliaria colma il divario: forniscile il percorso di nomi fino al valore che desideri, e percorrerà l’albero al posto tuo.

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"]

Ogni passo del percorso è il name di un controllo, oppure il title di una scheda (una scheda non ha un name proprio, anche se puoi aggiungerne uno e viene conservato). La barra delle schede e ogni group a cui non hai dato un nome sono trasparenti – find li attraversa – quindi elenchi solo le schede e i controlli che hai etichettato, in modo simile a una ricerca annidata config[...][...]. Un group con nome è un passo a sé, quindi find può anche restituire il valore della sua casella di controllo. Assegna a ogni controllo un name univoco affinché un percorso non sia mai ambiguo. La Esempio completo legge i valori dal layout dimostrativo annidato.

Poiché la configurazione è semplice JSON, nulla nel tuo script dipende dall’editor – l’editor si limita a scrivere il file che il tuo script già legge. Un dispositivo può portare con sé la propria configurazione e un modo semplice per modificarla, mentre lo script resta un semplice lettore di valori.

Un tipo di controllo impone il proprio input al salvataggio: una lineedit con una mask o una regex impedisce all’editor di salvare finché il suo testo è non valido o incompleto, e indica i campi da correggere. Ogni altro controllo vincola l’input durante la modifica – uno slider non può uscire dal suo intervallo, uno spinbox si limita ai suoi confini – quindi un file salvato è sempre valido.

13.1.13.4. Riferimento del file di configurazione#

I tipi di controllo sono documentati nelle pagine seguenti, raggruppati in base a ciò che fanno.