Files
HeurAMS/compositions.py
2025-08-28 13:08:37 +08:00

298 lines
9.5 KiB
Python

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()