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 (string, ไม่บังคับ) -- ชื่อหน้าต่างของเอดิเตอร์ ค่าเริ่มต้นคือ OpenMV Cam Settings Editor

  • controls (array, จำเป็น) -- รายการออบเจกต์คอนโทรลที่เอดิเตอร์เรนเดอร์ ตามลำดับ เป็นแถวของฟอร์ม นี่คือ 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.2. คีย์ที่คอนโทรลทุกตัวมีร่วมกัน#

ออบเจกต์คอนโทรลทุกตัวรองรับคีย์เหล่านี้ ไม่ว่า type ของมันจะเป็นอะไรก็ตาม:

  • type (string, จำเป็น) -- วิดเจ็ตใดที่จะสร้าง เช่น "slider" หรือ "combobox" หาก type หายไปหรือไม่รู้จัก จะเรนเดอร์ตัวยึดตำแหน่ง Unknown control ที่ถูกปิดใช้งานแทนที่จะทำให้ทั้งไฟล์ล้มเหลว ดังนั้นการพิมพ์ผิดจึงเสียแค่คอนโทรลตัวเดียว ไม่ใช่ทั้งฟอร์ม

  • name (string) -- ตัวระบุสำหรับการใช้งานของ คุณ เอง เอดิเตอร์ไม่เคยอ่านมัน มันเพียงแค่รักษามันไว้ ให้ตั้ง name ที่ไม่ซ้ำกันให้แต่ละคอนโทรล แล้วสคริปต์ของคุณจะใช้มันเพื่อค้นหาค่าที่บันทึกไว้ของคอนโทรลนั้น

  • label (string) -- ข้อความที่แสดงข้าง ๆ หรือบนคอนโทรล สำหรับคอนโทรลส่วนใหญ่นี่คือ rich text -- แท็ก HTML และลิงก์ <a href> ใช้งานได้

  • tooltip (string) -- ข้อความที่แสดงเมื่อชี้เมาส์ที่คอนโทรล

  • enabled (boolean, ค่าเริ่มต้น true) -- ตั้งเป็น false เพื่อแสดงคอนโทรลเป็นสีเทาและอ่านได้อย่างเดียว

คอนโทรลแบบคอนเทนเนอร์ (group, tabs) ใช้ title แทน label สำหรับหัวข้อ และ tabs รับ tooltip แยกตามแต่ละแท็บ ความแตกต่างเหล่านี้ระบุไว้ในหน้าของแต่ละคอนโทรล

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: lineedit ที่มี mask หรือ regex จะไม่ยอมให้เอดิเตอร์บันทึกในขณะที่ข้อความไม่ถูกต้องหรือไม่สมบูรณ์ และจะระบุชื่อฟิลด์ที่ต้องแก้ไข คอนโทรลอื่น ๆ ทุกตัวจะจำกัดอินพุตในขณะที่คุณแก้ไข -- slider ไม่สามารถออกนอกช่วงของมันได้ spinbox จะบีบให้อยู่ในขอบเขตของมัน -- ดังนั้นไฟล์ที่บันทึกจึงถูกต้องเสมอ

13.1.13.4. การอ้างอิงไฟล์คอนฟิก#

ประเภทของคอนโทรลถูกอธิบายไว้ในหน้าด้านล่าง โดยจัดกลุ่มตามสิ่งที่มันทำ