13.1.13. 設定編輯器#
許多相機應用程式需要少數幾項非程式設計人員也能變更的設定 -- 例如閾值、模式、Wi-Fi 密碼 -- 而不必動到腳本。OpenMV Cam Settings Editor 就是 IDE 為此提供的工具:一個 JSON 檔案描述這些控制項,編輯器將該描述轉換成表單,你的腳本則以 json.load() 讀回已儲存的值。此檔案既是組態,也是其自身編輯器的定義,因此儲存數值的同一份組態也會安排出用於編輯這些數值的 GUI。
工具 → OpenMV Cam Settings Editor 子選單包含其三項動作。
Open OpenMV Cam Settings Config File 會從已連線相機的磁碟讀取組態並在編輯器中開啟;儲存時會將其寫回相機。
Open Config File 會對你電腦上的
.json檔案執行相同操作,用於離線準備組態。Create Default Config 會將一份入門設定寫入磁碟並開啟它 -- 這是一個演練了每一種控制項類型的範例,可隨時精簡為你自己應用程式所需的設定。

設定編輯器已開啟預設組態 -- JSON 檔案的各項控制項以表單形式呈現(分頁、滑桿、下拉選單、核取方塊),底部為 Save 與 Cancel。#
13.1.13.1. 設定檔的結構剖析#
設定檔是一個包含兩個鍵的單一 JSON 物件:
title(字串,選用)-- 編輯器視窗的標題。預設為
OpenMV Cam Settings Editor。controls(陣列,必填)-- 編輯器依序渲染為表單各列的控制項物件清單。這就是整個 GUI。
{
"title": "Camera Settings",
"controls": [
{ "type": "slider", "name": "threshold", "label": "Threshold", "value": 50 },
{ "type": "checkbox", "name": "draw_overlays", "label": "Draw Overlays", "value": true }
]
}
controls 中的每一個項目都是一個控制項 -- 文字欄位、數字、核取方塊、下拉選單、slider、group、分頁列、靜態 label 等等。其 type 決定要使用的小工具;物件的其餘部分則用來設定它。當檔案儲存時,編輯器無法辨識的任何鍵都會保持原封不動,因此你可以在檔案中將自己的中繼資料與控制項一併保留。
13.1.13.3. 儲存與讀取值#
會儲存值的控制項 -- 核取方塊、slider、文字欄位 -- 會將該值放在 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,你的腳本中沒有任何部分依賴於編輯器 -- 編輯器只是寫入你的腳本本來就會讀取的檔案。裝置可以同時攜帶它的設定以及一個易於變更設定的方式,而腳本則始終只是單純的值讀取者。
有一種控制項類型會在 Save 時強制驗證其輸入:帶有 mask 或 regex 的 lineedit 會在其文字無效或不完整時拒絕讓編輯器儲存,並指明需要修正的欄位。其他每一種控制項都會在你編輯時就約束輸入 -- slider 無法超出其範圍,spinbox 會夾限在其界限之內 -- 因此已儲存的檔案永遠是有效的。
13.1.13.4. 設定檔參考#
各控制項類型記載於下方頁面中,並依其功能分組。