Download ui.py from Bobss/WatcherRobot-hello_watcher: direct link, hf CLI and curl.
- Browser
- Download file 5.51 kB
-
https://huggingface.co/spaces/Bobss/WatcherRobot-hello_watcher/resolve/main/ui.py
- Command line
-
hf download hf://spaces/Bobss/WatcherRobot-hello_watcher/ui.py
-
curl -L -o ui.py https://huggingface.co/spaces/Bobss/WatcherRobot-hello_watcher/resolve/main/ui.py
5.51 kB
| """Native control-panel UI for a WatcheRobot Application.""" | |
| from __future__ import annotations | |
| import tkinter as tk | |
| from tkinter import scrolledtext, ttk | |
| # 机器人动作清单:动作ID -> 按钮显示名(与桌面端 desktop-expression-resources 一致) | |
| ACTIONS: list[tuple[str, str]] = [ | |
| ("happy", "开心"), | |
| ("smile", "微笑"), | |
| ("get", "打招呼"), | |
| ("sunglasses", "得意"), | |
| ("thinking", "思考"), | |
| ("listening", "聆听"), | |
| ("speaking", "说话"), | |
| ("focus", "专注"), | |
| ("sad", "难过"), | |
| ("shock", "惊讶"), | |
| ("fondle_love", "摸摸头"), | |
| ("fondle_anger", "生气"), | |
| ] | |
| # 灯光控制:颜色 -> 显示名(关灯用 lights.off) | |
| LIGHTS: list[tuple[str, str]] = [ | |
| ("#ff4d4f", "红"), | |
| ("#52c41a", "绿"), | |
| ("#4096ff", "蓝"), | |
| ("#ffd666", "黄"), | |
| ] | |
| FONT_MAIN = ("PingFang SC", 13) | |
| FONT_TITLE = ("PingFang SC", 18, "bold") | |
| FONT_SMALL = ("PingFang SC", 11) | |
| class ControlPanel: | |
| """Button control panel: robot actions, lights, stop, and a live log.""" | |
| def __init__( | |
| self, | |
| *, | |
| on_action=None, | |
| on_light=None, | |
| on_lights_off=None, | |
| on_stop=None, | |
| ) -> None: | |
| self._is_open = True | |
| self._on_action = on_action | |
| self._on_light = on_light | |
| self._on_lights_off = on_lights_off | |
| self._on_stop = on_stop | |
| self._root = tk.Tk() | |
| self._root.title("机器人控制台 - WatcheRobot") | |
| self._root.geometry("680x560") | |
| self._root.minsize(600, 480) | |
| self._root.protocol("WM_DELETE_WINDOW", self.close) | |
| container = ttk.Frame(self._root, padding=20) | |
| container.pack(fill="both", expand=True) | |
| # 标题与连接状态 | |
| header = ttk.Frame(container) | |
| header.pack(fill="x") | |
| ttk.Label(header, text="机器人控制台", font=FONT_TITLE).pack(side="left") | |
| self._status_var = tk.StringVar(value="正在连接机器人…") | |
| ttk.Label(header, textvariable=self._status_var, font=FONT_SMALL).pack( | |
| side="right", pady=(6, 0) | |
| ) | |
| ttk.Separator(container).pack(fill="x", pady=(12, 10)) | |
| # 动作按钮区 | |
| ttk.Label(container, text="表情 / 动作", font=FONT_MAIN).pack(anchor="w") | |
| grid = ttk.Frame(container) | |
| grid.pack(fill="x", pady=(6, 10)) | |
| for index, (action_id, label) in enumerate(ACTIONS): | |
| row, col = divmod(index, 4) | |
| button = ttk.Button( | |
| grid, | |
| text=f"{label}\n{action_id}", | |
| command=lambda aid=action_id, name=label: self._fire_action(aid, name), | |
| ) | |
| button.grid(row=row, column=col, sticky="nsew", padx=5, pady=5, ipady=6) | |
| for col in range(4): | |
| grid.columnconfigure(col, weight=1) | |
| # 灯光按钮区 | |
| ttk.Label(container, text="灯光", font=FONT_MAIN).pack(anchor="w") | |
| light_row = ttk.Frame(container) | |
| light_row.pack(fill="x", pady=(6, 10)) | |
| for color, label in LIGHTS: | |
| ttk.Button( | |
| light_row, | |
| text=label, | |
| command=lambda c=color, name=label: self._fire_light(c, name), | |
| ).pack(side="left", padx=5, ipady=4, expand=True, fill="x") | |
| ttk.Button( | |
| light_row, text="关灯", command=self._fire_lights_off | |
| ).pack(side="left", padx=5, ipady=4, expand=True, fill="x") | |
| # 停止按钮 | |
| ttk.Button( | |
| container, text="■ 立即停止所有动作", command=self._fire_stop | |
| ).pack(fill="x", ipady=6, pady=(4, 10)) | |
| # 日志区 | |
| ttk.Label(container, text="运行日志", font=FONT_MAIN).pack(anchor="w") | |
| self._log = scrolledtext.ScrolledText( | |
| container, height=8, font=("Menlo", 11), state="disabled" | |
| ) | |
| self._log.pack(fill="both", expand=True) | |
| # ---- 对外接口 ---- | |
| def set_status(self, text: str) -> None: | |
| self._status_var.set(text) | |
| def log(self, text: str) -> None: | |
| self._log.configure(state="normal") | |
| self._log.insert("end", text + "\n") | |
| self._log.see("end") | |
| self._log.configure(state="disabled") | |
| def is_open(self) -> bool: | |
| return self._is_open | |
| def pump(self) -> None: | |
| """Process one native UI event cycle without blocking asyncio.""" | |
| if not self._is_open: | |
| return | |
| try: | |
| self._root.update_idletasks() | |
| self._root.update() | |
| except tk.TclError: | |
| self._is_open = False | |
| def close(self) -> None: | |
| if not self._is_open: | |
| return | |
| self._is_open = False | |
| try: | |
| self._root.destroy() | |
| except tk.TclError: | |
| pass | |
| # ---- 按钮事件转发(回调由 app.py 注入) ---- | |
| def _fire_action(self, action_id: str, name: str) -> None: | |
| self.log(f"▶ 请求动作:{name}({action_id})") | |
| if self._on_action: | |
| self._on_action(action_id, name) | |
| def _fire_light(self, color: str, name: str) -> None: | |
| self.log(f"◆ 请求灯光:{name}({color})") | |
| if self._on_light: | |
| self._on_light(color, name) | |
| def _fire_lights_off(self) -> None: | |
| self.log("◇ 请求关灯") | |
| if self._on_lights_off: | |
| self._on_lights_off() | |
| def _fire_stop(self) -> None: | |
| self.log("■ 请求停止所有动作") | |
| if self._on_stop: | |
| self._on_stop() | |