# -*- coding: utf-8 -*- """给所有按钮加回退(撤销)。 用户原话:"你直接给所有按钮都做一个回退功能不就行了吗" 设计 ---- `run()` 执行完之后,**自动判断这一步能不能退、怎么退**,能退就记一条。 * 开关类(15 个) -> 翻回原值 * 画面类(15 个) -> 关掉画面(复用已有的 close_visuals) * 撤不回来的 -> **不记** 最后一条很重要:关机、重启、删文件、结束进程这类记了只会给用户 "我还能退回去"的错觉,反而危险。所以专门列了个 IRREVERSIBLE 集合。 新增三个功能: undo_last 回退上一步 undo_all 全部回退(倒序一条条撤) undo_list 看还能回退什么 """ from __future__ import annotations import sys from pathlib import Path FEATURES = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\windows" r"\lanremote\features.py") # ---------------- 1. run() 记回退 ---------------- RUN_OLD = ''' def run(self, name: str, params: dict) -> str: handler = getattr(self, f"_do_{name}", None) if handler is None: raise ValueError(f"没有这个功能: {name}") return handler(params or {})''' RUN_NEW = ''' def run(self, name: str, params: dict) -> str: handler = getattr(self, f"_do_{name}", None) if handler is None: raise ValueError(f"没有这个功能: {name}") result = handler(params or {}) # 执行完顺手记一条"怎么退回去"。 # 判断逻辑集中在 _remember_undo 里,各个功能自己不用管。 try: self._remember_undo(name, params or {}, result) except Exception: # noqa: BLE001 pass return result # ================================================================== # 回退(撤销) # ================================================================== # 这些**撤不回来**。记了只会给用户"还能退回去"的错觉,反而更危险。 IRREVERSIBLE = { # 电源 "shutdown", "restart", "sleep", "hibernate", "power", "lock_now", "screen_off_lock", "shutdown_timer", "reboot_now", "lock_and_off", "class_end_cleanup", # 破坏性 "delete_file", "empty_recycle", "clean_temp", "clean_desktop", "kill_process", "kill_games", "kill_by_name", "quit_agent", "net_reset", "restart_audio", "usb_check", # 一次性只读 "netcheck", "selfcheck", "site_list", "restrict_status", } # 这些会在电脑上开一个全屏窗口 —— 回退就是把它关掉 VISUAL_FEATURES = { "big_text", "big_text_quiet", "class_attention", "class_start", "bell", "countdown_screen", "class_timer", "whiteboard", "stopwatch", "danmaku", "marquee", "lottery", "dice", "coin_flip", "countdown_321", "cinema_mode", "cheer", "matrix_rain", "starfield", "dashboard", "roll_group", "roll_call_no_repeat", "hand_count", "class_schedule", "countdown_day", "sticky_note", } def _spec_map(self) -> dict: """name -> 功能声明。用来查 kind / label。""" cached = getattr(self, "_spec_cache", None) if isinstance(cached, dict): return cached out: dict = {} try: for item in self.defs(): key = item.get("name") if key: out[key] = item except Exception: # noqa: BLE001 pass self._spec_cache = out return out def _undo_stack(self) -> list: stack = getattr(self, "_undo", None) if stack is None: stack = [] self._undo = stack return stack def _remember_undo(self, name: str, params: dict, result) -> None: if not result or name in self.IRREVERSIBLE: return if name in ("undo_last", "undo_all", "undo_list"): return spec = self._spec_map().get(name) if spec is None: return label = str(spec.get("label") or name) entry = None if spec.get("kind") == "toggle": back = not self._truthy(params.get("on")) entry = { "name": name, "params": {"on": back}, "label": label, "what": f"把「{label}」{'打开' if back else '关掉'}回去", } elif name in self.VISUAL_FEATURES: entry = { "name": "close_visuals", "params": {}, "label": label, "what": f"关掉「{label}」显示的画面", } if entry is None: # 其它功能(打开程序、改设置…)暂时不记, # 免得给用户"什么都能退"的错误预期 return entry["at"] = time.strftime("%H:%M:%S") stack = self._undo_stack() stack.append(entry) # 只留最近 20 条,别让内存涨(这台机器还要长时间跑) if len(stack) > 20: del stack[:-20] def _apply_undo(self, entry: dict) -> str: """执行一条回退。 注意:这里**直接调 _do_xxx,不走 run()** —— 走 run 的话又会记一条新的回退,点一下就来回横跳。 """ target = str(entry.get("name") or "") what = str(entry.get("what") or target) if not target: return "这条回退记录不完整" handler = getattr(self, f"_do_{target}", None) if handler is None: return f"回退失败:找不到 {target}" try: detail = handler(dict(entry.get("params") or {})) except Exception as exc: # noqa: BLE001 return f"回退失败:{what} —— {exc}" return f"已回退:{what}" + (f"({detail})" if detail else "") def _do_undo_last(self, params) -> str: stack = self._undo_stack() if not stack: return "没有可以回退的操作" entry = stack.pop() return self._apply_undo(entry) def _do_undo_all(self, params) -> str: stack = self._undo_stack() if not stack: return "没有可以回退的操作" count = len(stack) lines = [] # 倒序:后做的先撤 for entry in reversed(stack): lines.append(self._apply_undo(entry)) stack.clear() return f"已回退 {count} 步:\\n" + "\\n".join(lines) def _do_undo_list(self, params) -> str: stack = self._undo_stack() if not stack: return "没有可以回退的操作" lines = [f"{i + 1}. [{e.get('at', '')}] {e.get('what', '')}" for i, e in enumerate(reversed(stack))] return (f"可以回退 {len(stack)} 步(最近的在前):\\n" + "\\n".join(lines))''' # ---------------- 2. 功能声明 ---------------- DEFS_OLD = ''' add("restore_all", "一键恢复全部限制", "action", "教室")''' DEFS_NEW = ''' add("restore_all", "一键恢复全部限制", "action", "教室") add("undo_last", "回退上一步", "action", "系统") add("undo_all", "回退全部", "action", "系统") add("undo_list", "看能回退什么", "action", "系统")''' def main() -> int: text = FEATURES.read_text(encoding="utf-8") ok = True if "_remember_undo" in text: print(" 已经加过回退,跳过") return 0 for old, new, tag in ((RUN_OLD, RUN_NEW, "run() 记回退 + 三个回退功能"), (DEFS_OLD, DEFS_NEW, "功能声明")): if old not in text: print(f" [失败] {tag} —— 锚点没找到") ok = False continue text = text.replace(old, new, 1) print(f" {tag}") FEATURES.write_text(text, encoding="utf-8") import ast try: ast.parse(text) print(" 语法 OK") except SyntaxError as exc: print(f" 语法错误: {exc}") # 打印出错位置 for j, line in enumerate(text.splitlines()): if j + 1 == (exc.lineno or 0): print(f" >>> {j + 1}: {line}") break ok = False return 0 if ok else 1 if __name__ == "__main__": sys.exit(main())