13.1.13. Редактор настроек#

Многим приложениям для камеры нужна горстка настроек, которые непрограммист может изменить – порог, режим, пароль Wi-Fi – не касаясь скрипта. OpenMV Cam Settings Editor — инструмент IDE для этого: файл JSON описывает элементы управления, редактор превращает это описание в форму, а ваш скрипт считывает сохранённые значения обратно с помощью json.load(). Файл является одновременно и конфигурацией, и определением собственного редактора, поэтому та же конфигурация, что хранит значения, также формирует GUI для их редактирования.

Подменю Tools → OpenMV Cam Settings Editor содержит три его действия.

  • Open OpenMV Cam Settings Config File считывает конфигурацию с диска подключённой камеры и открывает её в редакторе; сохранение записывает её обратно на камеру.

  • Open Config File делает то же самое для файла .json на вашем компьютере, для подготовки конфигурации в автономном режиме.

  • Create Default Config записывает стартовую конфигурацию на диск и открывает её – пример, задействующий каждый тип элемента управления, готовый к сокращению до настроек, которые нужны вашему собственному приложению.

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

Редактор настроек с открытой конфигурацией по умолчанию – элементы управления из JSON-файла представлены в виде формы (вкладки, ползунки, раскрывающиеся списки, флажки), с кнопками Save и Cancel внизу.#

13.1.13.1. Структура файла конфигурации#

Файл конфигурации – это единый JSON-объект с двумя ключами:

  • title (строка, необязательный) – заголовок окна редактора. По умолчанию OpenMV Cam Settings Editor.

  • controls (массив, обязательный) – список объектов элементов управления, которые редактор отображает по порядку в виде строк формы. Это весь графический интерфейс.

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

Каждая запись в controls – это один элемент управления: текстовое поле, число, флажок, выпадающий список, ползунок, группа, полоса вкладок, статическая метка и так далее. Его type выбирает виджет; остальная часть объекта настраивает его. Любые ключи, которые редактор не распознаёт, остаются нетронутыми при сохранении файла, поэтому вы можете хранить в файле собственные метаданные рядом с элементами управления.

13.1.13.2. Ключи, общие для всех элементов управления#

Каждый объект элемента управления принимает эти ключи, каким бы ни был его type:

  • type (строка, обязательный) – какой виджет строить, например "slider" или "combobox". Отсутствующий или нераспознанный type отображает отключённый заполнитель Unknown control вместо провала всего файла, поэтому опечатка обходится вам в один элемент управления, а не во всю форму.

  • name (строка) – идентификатор для вашего использования. Редактор никогда его не читает; он лишь сохраняет его. Дайте каждому элементу управления уникальное name, и ваш сценарий использует его, чтобы найти сохранённое значение этого элемента управления.

  • label (строка) – текст, отображаемый рядом с элементом управления или на нём. Для большинства элементов управления это форматированный текст – работают HTML-теги и ссылки <a href>.

  • tooltip (строка) – всплывающий текст для элемента управления.

  • enabled (логическое, по умолчанию true) – установите в false, чтобы элемент управления отображался серым и был доступен только для чтения.

Контейнерные элементы управления (group, tabs) используют title вместо label для своего заголовка, а tabs задаёт всплывающий текст для каждой вкладки; различия отмечены на странице каждого элемента управления.

13.1.13.3. Сохранение и чтение значений#

Элемент управления, который хранит значение – флажок, ползунок, текстовое поле – несёт его в ключе value. Когда вы нажимаете Save, редактор записывает текущее value каждого элемента управления прямо обратно в тот же объект элемента управления, на месте, и перезаписывает файл. Файл сохраняет свою структуру: ничего не перемещается, меняются только ключи value. Нет отдельного плоского отображения name в значение – значения живут внутри элементов управления, прямо там, где они определены.

Вот почему компоновка важна для вашего сценария. Массивы controls и tabs сохраняют порядок, в котором вы их построили, поэтому к файлу нельзя обращаться по имени, как к словарю – config["controls"]["threshold"] не работает. Эта небольшая вспомогательная функция устраняет разрыв: передайте ей путь из имён до нужного вам значения, и она сама обойдёт дерево.

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

Каждый шаг в пути – это name элемента управления или title вкладки (вкладка не имеет собственного name, хотя вы можете его добавить, и оно будет сохранено). Полоса вкладок и любая group, которой вы не дали имени, прозрачны – find проходит сквозь них – поэтому вы перечисляете только вкладки и элементы управления, которым вы дали метки, близко к вложенному поиску config[...][...]. Именованная group является отдельным шагом, поэтому find может также вернуть значение её флажка. Дайте каждому элементу управления уникальное name, чтобы путь никогда не был неоднозначным. Полный пример читает значения из вложенной демонстрационной компоновки.

Поскольку конфигурация – это обычный JSON, ничто в вашем сценарии не зависит от редактора – редактор лишь записывает файл, который ваш сценарий уже читает. Устройство может нести свою конфигурацию и удобный способ её изменения, в то время как сценарий остаётся простым читателем значений.

Один тип элемента управления проверяет свой ввод при сохранении: lineedit с mask или regex не позволяет редактору сохранить, пока его текст недопустим или неполон, и называет поля, которые нужно исправить. Все остальные элементы управления ограничивают ввод по мере редактирования – ползунок не может выйти за свой диапазон, spinbox зажимается в своих границах – поэтому сохранённый файл всегда допустим.

13.1.13.4. Справочник по файлу конфигурации#

Типы элементов управления описаны на страницах ниже, сгруппированные по тому, что они делают.