298 lines
9.5 KiB
Python
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()
|