---
# System prepended metadata

title: 編輯器介面功能

---

# 光舞燈效編輯器 ‑ `editor.py` 詳解筆記



## 開頭：用 Qt Designer 設計介面 UI → 轉換成 `ui.py`

> 下面這段是我實際操作的流程，給還沒用過 Qt Designer 的同學參考。

1. **開啟 Qt Designer**

   * Windows 在 Start Menu 找 *Qt Designer*；macOS 用 `Qt Creator` 內建的 Designer；Linux 直接執行 `designer` 指令。
2. **建立或載入 `.ui` 檔**

   * 我通常選 *Main Window* 模板。
   * 拖拉元件（`QPushButton`、`QComboBox`、`QWebEngineView`…）到視窗，並調整 `objectName`，這名字就是未來在 Python 裡呼叫的屬性名。
3. **設定版面配置（Layouts）**

   * 使用水平、垂直、網格布局，確保視窗放大縮小時元件不會跑位。
4. **存檔**

   * 存成 `UI.ui`（或你自訂檔名），並把檔案一併放進 Git 版本控制。
5. **命令列執行 `pyuic6` 轉 Python**

   ```bash
   pyuic6 UI.ui -o UI.py
   ```

   * `pyuic6` 會解析 `.ui` 的 XML，輸出一支純 Python 檔案（大約數百行）。
6. **在主程式匯入 `Ui_MainWindow`**

   ```python
   from UI import Ui_MainWindow

   class MainWindow_controller(QMainWindow):
       def __init__(self):
           super().__init__()
           self.ui = Ui_MainWindow()
           self.ui.setupUi(self)
   ```
7. **串接事件與邏輯**

   * 之後就能寫 `self.ui.pushButton.clicked.connect(self.do_something)` 來綁定功能。
8. **反覆迭代**

   * UI 改版 → 回 Designer 編輯 → 重新 `pyuic6` → 覆蓋 `UI.py` → Git commit。

> **小提醒**：請避免手動修改 `UI.py`，因為下次重新產生會被覆蓋。若要擴充 UI 行為，應在 Controller 類別中操作，而非直接動 `UI.py`。
---

## 1 程式的大鋼

1. **PyQt6 主框架** ‑ 產生整體視窗，並匯入 Qt Designer 匯出的 `UI.py`。
2. **`QWebEngineView` 嵌網頁** ‑ 中央區塊載入 `index.html`，裡面跑時間軸、WaveSurfer 音波、Canvas 動畫。
3. **前後端橋梁 – `QtWebChannel`** ‑ 透過 `CallHandler` 暴露 Python API，讓 JavaScript 能呼叫（例如：更新顏色、設定關鍵幀）。
4. **JSON 資料倉庫** ‑ `setting.json`（舞者設定）、`data.json`（顏色幀）、`pos.json`（走位幀）。
5. **UDP 即時同步** ‑ 每當跳到新幀，就把幀號用 UDP 廣播出去，實體 LED 服裝即時跟隨。

掌握這 5 點後，再深入細節就不會迷路。

---

## 2 檔案開頭：常數與工具函式

### 2‑1 路徑常數與 UDP socket

```python
# editor.py 行25–44
settingjson_path = r"data/setting.json"
...
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
```

* 我把所有 JSON 路徑集中在檔頭，未來搬資料夾只改這裡。
* `UDP()`（行45–51）把幀號打包成 4 bytes，廣播到 `255.255.255.255:12345`。

### 2‑2 `loadjson()` / `savejson()`

```python
# editor.py 行53–78
def loadjson(path):
    ...

def savejson(path, data):
    ...
```

* `loadjson()`：若檔案不存在就自動產生預設結構，避免程式直接崩潰。
* `savejson()`：除了寫檔，還把資料同步到 Firebase（行68 之後），多台電腦協作靠它。

---

## 3 `ColorDelegate`（行86–92）

這個自訂 `QStyledItemDelegate` 讓 `QComboBox` 的每個選項直接顯示背景色，用肉眼挑色最直觀。

---

## 4 `CallHandler`（行94–113）

前端透過 `QtWebChannel` 呼叫的插槽集中在這裡，例如：

| 函式                  | 作用                                    |
| ------------------- | ------------------------------------- |
| `receiveTime(sec)`  | 前端每 1/60 秒回報時間，Python 端據此更新 UI 與 UDP。 |
| `updateframe(obj)`  | 拖移時間軸的顏色幀標記後呼叫，重排陣列並寫檔。               |
| `updatepos(obj)`    | 拖舞者位置時即時更新 `pos.json`。                |
| `selectdancer(idx)` | 前端點人物縮圖 → 切換下方 ComboBox 顏色區。          |

---

## 5 `MainWindow_controller` 核心（行115–683）

我把它拆成七段說明。

### 5‑1 建構子 `__init__`（行116–149）

1. `Ui_MainWindow()` 初始化版面。
2. 建立 `QWebEngineView` → 指向 `simulator/index.html`。
3. 啟動 Firebase 監聽，檔案一變就 `async_reload()`。
4. 呼叫 `setup_control()` 把按鈕、快捷鍵、ComboBox 通通綁好。

### 5‑2 `setup_control()`（行150–214）

* 註冊 `CallHandler` → 前端能呼叫 Python。
* 初始化全域狀態：`self.time`、`self.nowframe`、`self.nowPos`、`self.partcolors`…
* 預設選到第一位舞者：`dancerselected()`。

### 5‑3 UI 事件綁定

* `setup_btn()`（行215–242）— 把所有 `QPushButton` 對應到函式。
* `setup_shortcuts()`（行243–272）— 快捷鍵一覽：`1` 存顏色、`G` 播放/暫停、`Z` 新增走位幀…。

> **小技巧**：元件超過十顆就用 dict + 迴圈批次綁定，程式乾淨很多。

### 5‑4 時間軸互動核心

| 函式                          | 行數      | 職責                                  | 備註                          |
| --------------------------- | ------- | ----------------------------------- | --------------------------- |
| `receivetime`               | 304–339 | 前端回報時間，計算 `nowframe/nowPos` 並廣播 UDP | 用 `get_time_index()` 二分搜尋定位 |
| `set_frame_start_bynowtime` | 515–533 | 把目前時間標記為「此幀開始」                      | 須檢查與前一幀衝突                   |
| `set_frame_end_bynowtime`   | 534–550 | 把目前時間標記為「此幀結束」                      | 同理檢查下一幀                     |

### 5‑5 顏色編輯流程

1. **載入顏色表** – `setcomboboxcolor()`（361–392）
2. **顯示當前顏色** – `loadcolor()`（394–400）
3. **使用者改色** – `colorchanged()`（420–427）→ 寫回 `data.json` → 前端自動刷新。

### 5‑6 走位 (`pos.json`) 流程

* `newpos()` / `delpos()`：插入或刪除走位關鍵幀。
* `updatepos()`：拖曳人物時即時寫座標。
* 完成後呼叫 `reloadDataAndRedraw()` 讓網頁重新繪製。

### 5‑7 除錯 & UDP 小工具

* `debug_btn()` + `debug()`：0.5 秒來回第一、最後一幀，現場排線超好用。
* `UDP_now()`：手動重送幀號，裝置斷訊時救援。

---

## 6 輔助函式速查表

| 函式                           | 行數      | 一句話說明                            |
| ---------------------------- | ------- | -------------------------------- |
| `get_time_index`             | 78–93   | 二分搜尋：給定時間 → 回傳它屬於哪段 `time` array |
| `settime`                    | 272–279 | 把文字框秒數塞回前端 `setTime()`           |
| `zoomin` / `zoomout`         | 668–672 | 呼叫前端縮放 WaveSurfer 顯示             |
| `scrollleft` / `scrollright` | 673–677 | 時間軸橫向捲動 10 px                    |

更多細節請自行翻 `editor.py`，函式名稱都很直白。

---

## 7 技術關鍵

1. **PyQt × Web 混搭** ‑ 用桌面程式包裹 Web 互動，得兩邊優勢。
2. **資料 ↔ UI ↔ 存檔 三向同步** ‑ 保證多端一致性。
3. **快捷功能** ‑ UDP 重廣播、快捷鍵、Debug 閃爍… 緊急狀況救場。

---

## 8 動手試試看

1. 把 `setting.json` 的舞者名字改掉，再跑程式，看左下角是否跟著變。
2. 在時間軸新增顏色幀，改幾個部位顏色，按 `1` 儲存，重開程式驗證。
3. 換另一首歌路徑，重新載入，WaveSurfer 波形若正常顯示，即設定成功。

完成三步，就能正式使用這套編輯器！

---

> 若有任何段落不清楚，歡迎問我或直接用除錯器一步步跑最有效！
