import copy import random from typing import TypedDict from textual.containers import ScrollableContainer from textual.widget import Widget from textual.widgets import Button, Label, Markdown from textual.events import Key import heurams.kernel.particles as pt import heurams.kernel.puzzles as pz from heurams.services.hasher import hash from heurams.services.logger import get_logger from .base_puzzle_widget import BasePuzzleWidget logger = get_logger(__name__) class Setting(TypedDict): __origin__: str __hint__: str text: str delimiter: str min_denominator: str class ClozePuzzle(BasePuzzleWidget): def __init__( self, *children: Widget, atom: pt.Atom, alia: str = "", name: str | None = None, id: str | None = None, classes: str | None = None, disabled: bool = False, markup: bool = True, ) -> None: super().__init__( *children, atom=atom, name=name, id=id, classes=classes, disabled=disabled, markup=markup, ) self.inputlist = list() self.hashtable = {} self.alia = alia self._load() self.btn_shortcuts = {} self.hashmap = dict() def _load(self): setting = self.atom.registry["nucleon"]["puzzles"][self.alia] self.puzzle = pz.ClozePuzzle( text=setting["text"], delimiter=setting["delimiter"], min_denominator=int(setting["min_denominator"]), ) self.puzzle.refresh() self.ans = copy.copy(self.puzzle.answer) # 乱序 random.shuffle(self.ans) def compose(self): yield Label(self.puzzle.wording, id="sentence") yield Markdown(f"> {self.listprint(self.inputlist)}", id="inputpreview") # 渲染当前问题的选项 with ScrollableContainer(id="btn-container") as s: c = 0 for i in self.ans: h = str(hash(i)) if hash(i) in self.hashmap.keys(): continue c += 1 self.hashmap[h] = i btnid = f"sel000-{h}" logger.debug(f"建立按钮 {btnid}") self.btn_shortcuts[f"{c}"] = btnid yield Button(f"[{c}] {i}", id=f"{btnid}") s.focus() yield Button("退格", id="delete") self.btn_shortcuts[f"0"] = "delete" self.btn_shortcuts[f"backspace"] = "delete" self.btn_shortcuts[f"delete"] = "delete" def listprint(self, lst): s = "" if lst: lastone = lst[-1] for i in lst[:-1]: s += i + " " s += f" `{lastone}`" return s def update_display(self): preview = self.query_one("#inputpreview") preview.update(f"> {self.listprint(self.inputlist)}") # type: ignore def on_button_pressed(self, event: Button.Pressed) -> None: button_id = event.button.id if button_id == "delete": if len(self.inputlist) > 0: self.inputlist.pop() self.update_display() else: answer_text = self.hashmap[button_id[7:]] # type: ignore self.inputlist.append(answer_text) self.update_display() if len(self.inputlist) >= len(self.puzzle.answer): is_correct = self.inputlist == self.puzzle.answer rating = 4 if is_correct else 2 self.handler(rating) self.screen.rating = rating # type: ignore if not is_correct: self.inputlist = [] self.update_display() def handler(self, rating): if self.atom.lock(): pass else: self.atom.minimize(rating) def on_key(self, event: Key) -> None: self.notify(event.key) if event.key in self.btn_shortcuts: btn_id = self.btn_shortcuts.get(event.key) btn_id = "#" + btn_id self.query_one(btn_id, Button).press()