13.1.13. O editor de definições#

Muitas aplicações de câmara precisam de um punhado de definições que um não-programador possa alterar – um limiar, um modo, uma palavra-passe de Wi-Fi – sem tocar no script. O OpenMV Cam Settings Editor é a ferramenta do IDE para isso: um ficheiro JSON descreve os controlos, o editor transforma essa descrição num formulário, e o seu script lê de volta os valores guardados com json.load(). O ficheiro é simultaneamente a configuração e a definição do seu próprio editor, pelo que a mesma configuração que armazena os valores também dispõe a interface gráfica para os editar.

O submenu Tools → OpenMV Cam Settings Editor contém as suas três ações.

  • Open OpenMV Cam Settings Config File lê a configuração da unidade da câmara ligada e abre-a no editor; ao guardar, escreve-a de volta na câmara.

  • Open Config File faz o mesmo para um ficheiro .json no seu computador, para preparar uma configuração offline.

  • Create Default Config escreve uma configuração inicial no disco e abre-a – um exemplo que exercita todos os tipos de controlo, pronto a ser reduzido às definições de que a sua própria aplicação necessita.

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

O editor de definições com a configuração predefinida aberta – os controlos do ficheiro JSON dispostos como um formulário (separadores, controlos deslizantes, listas pendentes, caixas de seleção), com Save e Cancel na parte inferior.#

13.1.13.1. Anatomia de um ficheiro de configuração#

Um ficheiro de configuração é um único objeto JSON com duas chaves:

  • title (string, opcional) – o título da janela do editor. A predefinição é OpenMV Cam Settings Editor.

  • controls (array, obrigatório) – a lista de objetos de controlo que o editor apresenta, por ordem, como linhas de um formulário. É toda a interface 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 em controls é um controlo – um campo de texto, um número, uma checkbox, uma lista pendente, um slider, um group, uma barra de separadores, um label estático, e assim por diante. O seu type seleciona o widget; o resto do objeto configura-o. Quaisquer chaves que o editor não reconheça ficam intactas quando o ficheiro é guardado, pelo que pode manter os seus próprios metadados no ficheiro juntamente com os controlos.

13.1.13.2. Chaves comuns a todos os controlos#

Todos os objetos de controlo aceitam estas chaves, seja qual for o seu type:

  • type (string, obrigatório) – que widget construir, por exemplo "slider" ou "combobox". Um type ausente ou não reconhecido apresenta um marcador desativado Unknown control em vez de fazer falhar todo o ficheiro, pelo que um erro de escrita lhe custa um controlo, não o formulário.

  • name (string) – um identificador para seu uso. O editor nunca o lê; limita-se a preservá-lo. Atribua a cada controlo um name único e o seu script utiliza-o para encontrar o valor guardado desse controlo.

  • label (string) – o texto mostrado ao lado ou sobre o controlo. Para a maioria dos controlos é texto formatado – etiquetas HTML e ligações <a href> funcionam.

  • tooltip (string) – texto de ajuda ao passar o rato sobre o controlo.

  • enabled (boolean, predefinição true) – defina como false para mostrar o controlo esbatido e apenas de leitura.

Os controlos de contentor (group, tabs) usam title em vez de label para o seu título, e tabs recebe o seu tooltip por separador; as diferenças são indicadas na página de cada controlo.

13.1.13.3. Guardar e ler valores#

Um controlo que guarda um valor – uma checkbox, um slider, um campo de texto – transporta-o numa chave value. Quando clica em Guardar, o editor escreve o value atual de cada controlo de volta nesse mesmo objeto de controlo, no lugar, e reescreve o ficheiro. O ficheiro mantém a sua estrutura: nada se move, apenas as chaves value mudam. Não existe um mapa plano separado de name para valor – os valores residem dentro dos controlos, precisamente onde são definidos.

É por isso que a disposição é importante para o seu script. Os arrays controls e tabs mantêm a ordem em que os criou, pelo que o ficheiro não pode ser indexado por nome como um dicionário – config["controls"]["threshold"] não funciona. Este pequeno auxiliar preenche essa lacuna: dê-lhe o caminho de nomes até ao valor que pretende, e ele percorre a árvore por si.

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 passo do caminho é o name de um controlo, ou o title de um separador (um separador não tem name próprio, embora possa adicionar um e este seja mantido). A barra de separadores e qualquer group que não tenha nomeado são transparentes – o find percorre-os – pelo que lista apenas os separadores e os controlos que rotulou, próximo de uma consulta aninhada config[...][...]. Um group nomeado é um passo próprio, pelo que o find pode também devolver o valor da sua caixa de verificação. Atribua a cada controlo um name único para que um caminho nunca seja ambíguo. O Exemplo completo lê valores da disposição de demonstração aninhada.

Como a configuração é simplesmente JSON, nada no seu script depende do editor – o editor limita-se a escrever o ficheiro que o seu script já lê. Um dispositivo pode transportar a sua configuração e uma forma acessível de a alterar, enquanto o script continua a ser um simples leitor de valores.

Um tipo de controlo impõe a sua entrada ao Guardar: um lineedit com uma mask ou regex recusa deixar o editor guardar enquanto o seu texto for inválido ou incompleto, e indica os campos a corrigir. Todos os outros controlos restringem a entrada à medida que edita – um slider não pode sair do seu intervalo, um spinbox limita-se aos seus limites – pelo que um ficheiro guardado é sempre válido.

13.1.13.4. Referência do ficheiro de configuração#

Os tipos de controlo estão documentados nas páginas abaixo, agrupados pelo que fazem.