from textual.app import App, ComposeResult from textual.events import Event from textual.widgets import ( Collapsible, Header, Footer, Markdown, ListView, ListItem, Label, Static, Button, ) from textual.containers import Container, Horizontal, Center from textual.screen import Screen from textual.widget import Widget import uuid from typing import Tuple, Dict import particles as pt import puzzles as pz import re import random import copy class Composition: def __init__( self, screen: Screen, reactor, atom: Tuple[pt.Electron, pt.Nucleon, Dict] = pt.Atom.placeholder(), ): self.screen = screen self.atom = atom from reactor import Reactor self.reactor: Reactor = reactor self.reg = dict() def regid(self, id_): self.reg[id_] = id_ + str(uuid.uuid4()) return self.reg[id_] def getid(self, id_): if id_ not in self.reg.keys(): return "None" return self.reg[id_] def recid(self, id_): return id_[:-36] def compose(self): yield Label("示例标签", id="testlabel") yield Button("示例按钮", id="testbtn") def handler(self, event, type_): return 1 class Finished(Composition): def __init__(self, screen: Screen, reactor, atom: Tuple[pt.Electron, pt.Nucleon, Dict]): super().__init__(screen, reactor, atom) def compose(self): yield Label("本次记忆进程结束", id=self.regid("msg")) class Placeholder(Composition): def __init__(self, screen: Screen): self.screen = screen def compose(self): yield Label("示例标签", id="testlabel") yield Button("示例按钮", id="testbtn", classes="choice") def handler(self, event, type_): self.screen.query_one("#testlabel", Label).update("hi") class Recognition(Composition): def __init__(self, screen: Screen, reactor, atom: Tuple[pt.Electron, pt.Nucleon, Dict]): super().__init__(screen, reactor, atom) def compose(self): with Center(): yield Static(f"[dim]{self.atom[1]['translation']}[/]") yield Label(f"") s = str(self.atom[1]["content"]) replace_dict = { ", ": ",", ". ": ".", "; ": ";", ": ": ":", "/,": ",", "./": ".", "/;": ";", ";/": ";", ":/": ":", } for old, new in replace_dict.items(): s = s.replace(old, new) result = re.split(r"(?<=[,;:|])", s.replace("/", " ")) for i in result: with Center(): yield Label( f"[b][b]{i.replace('/', ' ')}[/][/]", id=self.regid("sentence" + str(hash(i))), ) for i in self.atom[2]["testdata"]["additional_inf"]: if self.atom[1][i]: if isinstance(self.atom[1][i], list): for j in self.atom[1][i]: yield Markdown(f"### {self.atom[2]['keydata'][i]}: {j}") continue if isinstance(self.atom[1][i], Dict): t = "" for j, k in self.atom[1][i].items(): # type: ignore # 弱智的 Pylance 类型推导 t += f"> **{j}**: {k} \n" yield Markdown(t, id=self.regid("tran")) with Center(): yield Button("我已知晓", id=self.regid("ok")) def handler(self, event, type_): if type_ == "button": if event.button.id == self.getid("ok"): self.reactor.report(self.atom, 5) return 0 return -1 class BasicEvaluation(Composition): def __init__(self, screen: Screen, reactor, atom: Tuple[pt.Electron, pt.Nucleon, Dict]): super().__init__(screen, reactor, atom) def compose(self): yield Label(self.atom[1]["content"], id="sentence") with Container(id="button_container"): btn = {} btn["5"] = Button( "完美回想", variant="success", id=self.regid("feedback5"), classes="choice" ) btn["4"] = Button( "犹豫后正确", variant="success", id=self.regid("feedback4"), classes="choice" ) btn["3"] = Button( "困难地正确", variant="warning", id=self.regid("feedback3"), classes="choice" ) btn["2"] = Button( "错误但熟悉", variant="warning", id=self.regid("feedback2"), classes="choice" ) btn["1"] = Button( "错误且不熟", variant="error", id=self.regid("feedback1"), classes="choice" ) btn["0"] = Button( "完全空白", variant="error", id=self.regid("feedback0"), classes="choice" ) yield Horizontal(btn["5"], btn["4"]) yield Horizontal(btn["3"], btn["2"]) yield Horizontal(btn["1"], btn["0"]) def handler(self, event, type_): if "feedback" in event.button.id: assess = int(self.recid(event.button.id)[8:9]) ret = self.reactor.report(self.atom, assess) return ret class FillBlank(Composition): def __init__(self, screen: Screen, reactor, atom: Tuple[pt.Electron, pt.Nucleon, Dict]): super().__init__(screen, reactor, atom) self.inputlist = [] self.hashtable = {} self._work() def _work(self): self.puzzle = pz.BlankPuzzle(self.atom[1]["content"], 2) self.puzzle.refresh() self.ans = copy.copy(self.puzzle.answer) random.shuffle(self.ans) def compose(self): yield Label(self.puzzle.wording, id=self.regid("sentence")) yield Label(f"当前输入: {self.inputlist}", id=self.regid("inputpreview")) for i in self.ans: self.hashtable[str(hash(i))] = i yield Button(i, id=self.regid(f"select{hash(i)}")) yield Button("退格", id=self.regid(f"delete")) def handler(self, event, type_): # TODO: 改动:在线错误纠正 if type_ == "button": if self.recid(event.button.id) == "delete": if len(self.inputlist) > 0: self.inputlist.pop() else: return 1 else: self.inputlist.append(self.hashtable[self.recid(event.button.id)[6:]]) if len(self.inputlist) < len(self.puzzle.answer): return 1 else: if self.inputlist == self.puzzle.answer: self.reactor.report(self.atom, 4) return 0 else: self.inputlist = [] self.reactor.report(self.atom, 2) return 1 class DrawCard(Composition): def __init__(self, screen: Screen, reactor, atom: Tuple[pt.Electron, pt.Nucleon, Dict]): super().__init__(screen, reactor, atom) self.inputlist = [] self.hashtable = {} self._work() def _work(self): self.puzzle = pz.SelectionPuzzle(self.atom[1]["keyword_note"], [], 2, "选择正确词义: ") # type: ignore self.puzzle.refresh() def compose(self): yield Label(self.atom[1].content.replace("/",""), id=self.regid("sentence")) yield Label(self.puzzle.wording[len(self.inputlist)], id=self.regid("puzzle")) yield Label(f"当前输入: {self.inputlist}", id=self.regid("inputpreview")) for i in self.puzzle.options[len(self.inputlist)]: self.hashtable[str(hash(i))] = i yield Button(i, id=self.regid(f"select{hash(i)}")) yield Button("退格", id=self.regid(f"delete")) def handler(self, event, type_): if type_ == "button": if self.recid(event.button.id) == "delete": if len(self.inputlist) > 0: self.inputlist.pop() else: return 1 else: self.inputlist.append(self.hashtable[self.recid(event.button.id)[6:]]) if len(self.inputlist) < len(self.puzzle.answer): return 1 else: if self.inputlist == self.puzzle.answer: self.reactor.report(self.atom, 4) return 0 else: self.inputlist = [] self.reactor.report(self.atom, 2) return 1 registry = { "sample": Composition, "recognition": Recognition, "fill_blank_test": FillBlank, "draw_card_test": DrawCard, "basic_evaluation": BasicEvaluation, } class TestScreen(Screen): def __init__(self): super().__init__(name=None, id=None, classes=None) self.comp = Recognition(self, None, pt.Atom.advanced_placeholder()) def compose(self) -> ComposeResult: yield Header(show_clock=True) yield from self.comp.compose() yield Footer() def on_mount(self) -> None: pass def on_button_pressed(self, event: Event) -> None: self.comp.handler(event, "button") def action_quit_app(self) -> None: self.app.exit() class AppLauncher(App): CSS_PATH = "styles.css" TITLE = "测试布局" BINDINGS = [("escape", "quit", "退出"), ("d", "toggle_dark", "改变色调")] SCREENS = { "testscreen": TestScreen, } def on_mount(self) -> None: self.action_toggle_dark() self.push_screen("testscreen") if __name__ == "__main__": app = AppLauncher() app.run()