diff --git a/src/heurams/interface/__main__.py b/src/heurams/interface/__main__.py index 6ada380..24b68a8 100644 --- a/src/heurams/interface/__main__.py +++ b/src/heurams/interface/__main__.py @@ -1,19 +1,36 @@ from textual.app import App, ComposeResult from textual.widgets import Button, Header, Label, Footer +from .screens.dashboard import DashboardScreen +from .screens.preparation import PreparationScreen +from .screens.memorizor import MemScreen +from .screens.nucleon_creator import NucleonCreatorScreen +from .screens.precache import PrecachingScreen class HeurAMSApp(App): TITLE = "潜进" SUB_TITLE = "启发式辅助记忆调度器" - BINDINGS = [("q", "quit", "退出"), ("d", "toggle_dark", "改变色调")] + BINDINGS = [("q", "quit", "退出"), + ("d", "toggle_dark", "改变色调"), + ("1", "app.push_screen('dashboard')", "仪表盘"), + ("2", "app.push_screen('precache_all')", "缓存管理"), + ("3", "app.push_screen('nucleon_creator')", "创建新单元"), + ] + SCREENS = { + "dashboard": DashboardScreen, + "nucleon_creator": NucleonCreatorScreen, + "precache_all": PrecachingScreen, + "preparation": PreparationScreen, # type: ignore + "memscreen": MemScreen, # type: ignore + } def compose(self) -> ComposeResult: yield Header(show_clock = True) - + yield Footer(show_command_palette = True) - + def on_mount(self) -> None: - pass - + self.push_screen("dashboard") + def on_button_pressed(self, event: Button.Pressed) -> None: self.exit(event.button.id) diff --git a/src/heurams/interface/screens/nucleon_editor.py b/src/heurams/interface/screens/about.py similarity index 100% rename from src/heurams/interface/screens/nucleon_editor.py rename to src/heurams/interface/screens/about.py diff --git a/src/heurams/interface/screens/dashboard.py b/src/heurams/interface/screens/dashboard.py index e69de29..fb2ceb9 100644 --- a/src/heurams/interface/screens/dashboard.py +++ b/src/heurams/interface/screens/dashboard.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +from textual.app import ComposeResult +from textual.widgets import ( + Header, + Footer, + Label, + ListView, + ListItem, + Button, + Static, +) +from textual.containers import Container +from textual.screen import Screen + +from heurams.kernel.particles import * +from heurams.context import * + +class DashboardScreen(Screen): + + def compose(self) -> ComposeResult: + yield Header(show_clock=True) + yield Container( + Label(f'欢迎使用 "潜进" 启发式辅助记忆调度器', classes="title-label"), + Label(f"当前的 UNIX 日时间戳: 0"), + Label(f'包含时间戳修正: UTC+0'), + Label("选择待学习或待修改的记忆单元集:", classes="title-label"), + ListView(id="union-list", classes="union-list-view"), + Label(f'"潜进" 开放源代码软件项目 | 版本 0.0.1 | Wang Zhiyu 2025'), + ) + yield Footer() + + def item_desc_generator(self, path) -> dict: + res = dict() + res[0] = f"{path.name}\0" + res[1] = f"" + res[1] = " 尚未激活" + return res + + def on_mount(self) -> None: + union_list_widget = self.query_one("#union-list", ListView) + probe = probe_all(0) + + if len(probe["nucleon"]): + for file in probe["nucleon"]: + text = self.item_desc_generator(file) + union_list_widget.append(ListItem( + Label(text[0] + '\n' + text[1]), + )) + else: + union_list_widget.append( + ListItem(Static("在 ./nucleon/ 中未找到任何内容源数据文件.\n请放置文件后重启应用.\n或者新建空的单元集.")) + ) + union_list_widget.disabled = True + + def on_list_view_selected(self, event) -> None: + if not isinstance(event.item, ListItem): + return + + selected_label = event.item.query_one(Label) + if "未找到任何 .toml 文件" in str(selected_label.renderable): + return + + selected_filename = str(selected_label.renderable).partition('\0')[0].replace('*', "") + # 这里需要导入相应的文件处理逻辑 + # nucleon_file = pt.NucleonUnion(pathlib.Path("./nucleon") / selected_filename) + # electron_file_path = pathlib.Path("./electron") / selected_filename + # if electron_file_path.exists(): + # pass + # else: + # electron_file_path.touch() + # electron_file = pt.ElectronUnion(pathlib.Path("./electron") / selected_filename) + # self.app.push_screen(PreparationScreen(nucleon_file, electron_file)) + + def on_button_pressed(self, event) -> None: + if event.button.id == "new_nucleon_button": + from .nucleon_creator import NucleonCreatorScreen + newscr = NucleonCreatorScreen() + self.app.push_screen(newscr) + elif event.button.id == "precache_all_button": + self.action_precache_all() + + def action_precache_all(self): + """预缓存所有单元集的音频""" + from .precache import PrecachingScreen + precache_screen = PrecachingScreen() + self.app.push_screen(precache_screen) + + def action_quit_app(self) -> None: + self.app.exit() \ No newline at end of file diff --git a/src/heurams/interface/screens/memorizor.py b/src/heurams/interface/screens/memorizor.py new file mode 100644 index 0000000..9da0f75 --- /dev/null +++ b/src/heurams/interface/screens/memorizor.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +from textual.app import ComposeResult +from textual.widgets import Header, Footer, Label, Static, Button +from textual.containers import Center +from textual.screen import Screen +from enum import Enum, auto + +from heurams.kernel.reactor import * + +class AtomState(Enum): + FAILED = auto() + NORMAL = auto() + +class MemScreen(Screen): + BINDINGS = [ + ("d", "toggle_dark", "改变色调"), + ("q", "pop_screen", "返回主菜单"), + ("v", "play_voice", "朗读"), + ] + + def __init__(self, atoms: list): + super().__init__(name=None, id=None, classes=None) + self.atoms = atoms + self.phaser = Phaser(atoms) + self.procession: Procession = self.phaser.current_procession() # type: ignore + + def compose(self) -> ComposeResult: + yield Header(show_clock=True) + with Center(): + yield Static(f"当前进度: {self.procession.process()}/{self.procession.total_length()}") + yield Label() + yield Button("重新学习此单元", id="re-recognize", variant="warning") + yield Footer() + + def on_mount(self): + pass + + def on_button_pressed(self, event): + pass + + def action_play_voice(self): + """朗读当前内容""" + pass + + def action_precache_current(self): + """预缓存当前单元集的音频""" + from .precache import PrecachingScreen + precache_screen = PrecachingScreen(self.nucleon_file) + self.app.push_screen(precache_screen) + + def action_toggle_dark(self): + self.app.action_toggle_dark() + + def action_pop_screen(self): + self.app.pop_screen() diff --git a/src/heurams/interface/screens/memory.py b/src/heurams/interface/screens/memory.py deleted file mode 100644 index 5e9c6d7..0000000 --- a/src/heurams/interface/screens/memory.py +++ /dev/null @@ -1,140 +0,0 @@ - -class MemScreen(Screen): - BINDINGS = [ - ("d", "toggle_dark", "改变色调"), - ("q", "pop_screen", "返回主菜单"), - ("v", "play_voice", "朗读"), - # ("p", "precache_current", "预缓存当前单元集"), # 新增预缓存快捷键 - ] - if config.get("quick_pass"): - BINDINGS.append(("k", "quick_pass", "快速通过[调试]")) - btn = dict() - - def __init__( - self, - nucleon_file: pt.NucleonUnion, - electron_file: pt.ElectronUnion, - tasked_num, - ): - super().__init__(name=None, id=None, classes=None) - self.nucleon_file = nucleon_file - self.electron_file = electron_file - self.reactor = Reactor(nucleon_file, electron_file, self, tasked_num) - self.stage = 1 - self.stage += self.reactor.set_round_templated(self.stage) - first_forward = self.reactor.forward() - print(first_forward) - if first_forward == -1: - self.stage = 3 - self.reactor.set_round_templated(3) - print(self.reactor.forward()) - #self._forward_judge(first_forward) - self.compo = next(self.reactor.current_appar) - self.feedback_state = 0 # 默认状态 - self.feedback_state_map = { - 0: "", - 255: "回答有误, 请重试. 或者重新学习此单元", - } - - def compose(self) -> ComposeResult: - if type(self.compo).__name__ == "Recognition": - self.action_play_voice() - yield Header(show_clock=True) - with Center(): - yield Static( - f"当前进度: {len(self.reactor.procession) - self.reactor.index}/{len(self.reactor.procession)}" - ) - yield Label(self.feedback_state_map[self.feedback_state]) - yield from self.compo.compose() - if self.feedback_state == 255: - yield Button("重新学习此单元", id="re-recognize", variant="warning") - yield Footer() - - def on_mount(self): - pass - - def on_button_pressed(self, event): - try: - if event.button.id == "re-recognize": - return - except: - pass - ret = self.compo.handler(event, "button") - self._forward_judge(ret) - def _forward_judge(self, ret): - self.feedback_state = 0 - if ret == -1: - return - if ret == 0: - try: - self.compo = next(self.reactor.current_appar) - self.refresh_ui() - except StopIteration: - nxt = self.reactor.forward(1) - try: - self.compo = next(self.reactor.current_appar) - except: - pass - if nxt == -1: - if self.reactor.round_set == 0: - if self.stage == 4: - if config.get("save"): - self.reactor.save() - self.compo = compo.Finished( - self, None, pt.Atom.placeholder() - ) - self.refresh_ui() - else: - self.reactor.set_round_templated(self.stage) - self.reactor.forward(1) - self.stage += 1 - self.compo = next(self.reactor.current_appar) - self.refresh_ui() - return - return - else: - self.refresh_ui() - return - if ret >= 1: - if ret == 2: - self.feedback_state = 255 # 表示错误 - else: - self.feedback_state = 0 - self.refresh_ui() - return - - def refresh_ui(self): - self.call_later(self.recompose) - print(type(self.compo).__name__) - - def action_play_voice(self): - def play(): - cache_dir = pathlib.Path(f"./cache/voice/") - cache_dir.mkdir(parents=True, exist_ok=True) - cache = cache_dir / f"{aux.get_md5(self.reactor.current_atom[1].content.replace('/',''))}.wav" - if not cache.exists(): - import edge_tts as tts - communicate = tts.Communicate( - self.reactor.current_atom[1].content.replace("/", ""), - "zh-CN-XiaoxiaoNeural", - ) - communicate.save_sync( - f"./cache/voice/{aux.get_md5(self.reactor.current_atom[1].content.replace('/',''))}.wav" - ) - playsound(str(cache)) - - threading.Thread(target=play).start() - - def action_precache_current(self): - """预缓存当前单元集的音频""" - precache_screen = PrecachingScreen(self.nucleon_file) - self.app.push_screen(precache_screen) - - def action_quick_pass(self): - self.reactor.report(self.reactor.current_atom, 5) - self._forward_judge(0) - def action_toggle_dark(self): - self.app.action_toggle_dark() - - def action_pop_screen(self): - self.app.pop_screen() diff --git a/src/heurams/interface/screens/nucleon_creator.py b/src/heurams/interface/screens/nucleon_creator.py new file mode 100644 index 0000000..9e9f366 --- /dev/null +++ b/src/heurams/interface/screens/nucleon_creator.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +from textual.app import ComposeResult +from textual.widgets import ( + Header, + Footer, + Label, + Input, + Select, + Button, + Markdown, +) +from textual.containers import Container +from textual.screen import Screen + +class NucleonCreatorScreen(Screen): + BINDINGS = [("q", "go_back", "返回"), ("escape", "quit_app", "退出")] + + def __init__(self) -> None: + super().__init__(name=None, id=None, classes=None) + + def compose(self) -> ComposeResult: + yield Header(show_clock=True) + with Container(id="vice_container"): + yield Label(f"[b]新建空的单元集\n") + yield Markdown("1. 键入单元集名称") + yield Input(placeholder="单元集名称") + yield Markdown("> 单元集名称不应与现有单元集重复, 新的单元集文件将创建在 ./nucleon/你输入的名称.toml") + yield Label(f"\n") + yield Markdown("2. 选择单元集类型") + LINES = """ +单一字符串 +主字符串(带有附加属性) +动态单元集(使用宏) +""".splitlines() + yield Select.from_values(LINES, prompt="选择类型") + yield Label(f"\n") + yield Markdown("3. 输入附加元数据 (可选)") + yield Input(placeholder="作者") + yield Input(placeholder="内容描述") + yield Button( + "新建空单元集", + id="submit_button", + variant="primary", + classes="start-button", + ) + yield Footer() + + def action_go_back(self): + self.app.pop_screen() + + def action_quit_app(self): + self.app.exit() + + def on_button_pressed(self, event) -> None: + pass \ No newline at end of file diff --git a/src/heurams/interface/screens/precache.py b/src/heurams/interface/screens/precache.py index 2005180..3b5ec97 100644 --- a/src/heurams/interface/screens/precache.py +++ b/src/heurams/interface/screens/precache.py @@ -1,7 +1,28 @@ +#!/usr/bin/env python3 +from textual.app import ComposeResult +from textual.widgets import ( + Header, + Footer, + Label, + Input, + Select, + Button, + Markdown, + Static, + ProgressBar, +) +from textual.containers import Container, Horizontal, Center +from textual.containers import Container +from textual.screen import Screen +import pathlib + +import heurams.kernel.particles as pt +import heurams.services.hasher as hasher +from heurams.context import * class PrecachingScreen(Screen): """预缓存音频文件屏幕""" - BINDINGS = [("q", "go_back", "返回"), ("escape", "quit_app", "退出")] + BINDINGS = [("q", "go_back", "返回")] def __init__(self, nucleon_file = None): super().__init__(name=None, id=None, classes=None) @@ -59,7 +80,7 @@ class PrecachingScreen(Screen): """预缓存单个文本的音频""" cache_dir = pathlib.Path("./cache/voice/") cache_dir.mkdir(parents=True, exist_ok=True) - cache = cache_dir / f"{aux.get_md5(text)}.wav" + cache = cache_dir / f"{hasher.get_md5(text)}.wav" if not cache.exists(): try: import edge_tts as tts @@ -71,10 +92,10 @@ class PrecachingScreen(Screen): return False return True - def precache_file(self, nucleon_union: pt.NucleonUnion): + def precache_file(self, path: pathlib.Path): """预缓存单个文件的所有内容""" - self.current_file = nucleon_union.name - total_items = len(nucleon_union.nucleons) + self.current_file = path.name + total_items = len(pt.load_nucleon(path)) for idx, nucleon in enumerate(nucleon_union.nucleons): # 检查是否被取消 @@ -102,6 +123,7 @@ class PrecachingScreen(Screen): return True + def precache_all_files(self): """预缓存所有文件""" nucleon_path = pathlib.Path("./nucleon") @@ -109,10 +131,11 @@ class PrecachingScreen(Screen): # 计算总项目数 self.total = 0 + nu = list() for file in nucleon_files: try: - nu = pt.NucleonUnion(file) - self.total += len(nu.nucleons) + nu += pt.load_nucleon(file) + self.total = len(nu) except: continue @@ -121,7 +144,7 @@ class PrecachingScreen(Screen): for file in nucleon_files: try: - nu = pt.NucleonUnion(file) + nu += pt.load_nucleon(file) if not self.precache_file(nu): break # 用户取消 except Exception as e: diff --git a/src/heurams/interface/screens/preparation.py b/src/heurams/interface/screens/preparation.py index e69de29..81d1e1b 100644 --- a/src/heurams/interface/screens/preparation.py +++ b/src/heurams/interface/screens/preparation.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +from textual.app import ComposeResult +from textual.widgets import ( + Header, + Footer, + Label, + Static, + Button, + Markdown, +) +from textual.containers import Container +from textual.screen import Screen + +import heurams.kernel.particles as pt +import heurams.services.hasher as hasher +from heurams.context import * + +class PreparationScreen(Screen): + BINDINGS = [ + ("q", "go_back", "返回"), + ("escape", "quit_app", "退出"), + ("p", "precache", "预缓存音频") + ] + + def __init__( + self, nucleon_file: pathlib.Path, electron_file: pathlib.Path + ) -> None: + super().__init__(name=None, id=None, classes=None) + self.nucleon_file = nucleon_file + self.electron_file = electron_file + self.nucleon_union = pt.load_nucleon(self.nucleon_file) + + def compose(self) -> ComposeResult: + yield Header(show_clock=True) + with Container(id="vice_container"): + yield Label(f"准备就绪: [b]{self.nucleon_file.stem}[/b]\n") + yield Label(f"内容源文件对象: ./nucleon/[b]{self.nucleon_file.name}[/b].toml") + yield Label(f"元数据文件对象: ./electron/[b]{self.electron_file.name}[/b].toml") + yield Label(f"\n单元数量:{len(self.nucleon_union)}\n") + + yield Button( + "开始记忆", + id="start_memorizing_button", + variant="primary", + classes="start-button", + ) + yield Button( + "预缓存音频", + id="precache_button", + variant="success", + classes="precache-button", + ) + + yield Static(f"\n单元预览:\n") + yield Markdown(self._get_full_content().replace("/", ""), classes="full") + yield Footer() + + def _get_full_content(self): + content = "" + for i in self.nucleon_union: + content += " - " + i["content"] + " \n" + return content + + def action_go_back(self): + self.app.pop_screen() + + def action_precache(self): + from ..screens.precache import PrecachingScreen + precache_screen = PrecachingScreen(self.nucleon_file) + self.app.push_screen(precache_screen) + + def action_quit_app(self): + self.app.exit() + + def on_button_pressed(self, event) -> None: + if event.button.id == "start_memorizing_button": + from .memorizor import MemScreen + newscr = MemScreen( + self.nucleon_file, self.electron_file, 6 + ) + self.app.push_screen(newscr) + elif event.button.id == "precache_button": + self.action_precache() \ No newline at end of file diff --git a/src/heurams/interface/widgets/base_puzzle.py b/src/heurams/interface/widgets/base_puzzle.py new file mode 100644 index 0000000..b658b7c --- /dev/null +++ b/src/heurams/interface/widgets/base_puzzle.py @@ -0,0 +1,60 @@ +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 heurams.kernel.particles as pt +import heurams.kernel.puzzles as pz +import re +import random +import copy + + +class BasicEvaluation(Composition): + def __init__(self, screen: Screen, reactor, atom: Tuple[pt.Electron, pt.Nucleon, Dict], extra = {}): + 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 \ No newline at end of file diff --git a/src/heurams/interface/widgets/cloze_puzzle.py b/src/heurams/interface/widgets/cloze_puzzle.py new file mode 100644 index 0000000..1a2773b --- /dev/null +++ b/src/heurams/interface/widgets/cloze_puzzle.py @@ -0,0 +1,69 @@ +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 heurams.kernel.particles as pt +import heurams.kernel.puzzles as pz +import re +import random +import copy + + +class FillBlank(Composition): + def __init__(self, screen: Screen, reactor, atom: Tuple[pt.Electron, pt.Nucleon, Dict], extra:Dict = {}): + super().__init__(screen, reactor, atom) + self.extra = extra + 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): + if self.extra.get("feedback_msg"): + yield Label("反馈提示:" + self.extra["feedback_msg"]) + 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 2 \ No newline at end of file diff --git a/src/heurams/interface/widgets/fileexpr.py b/src/heurams/interface/widgets/fileexpr.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/heurams/interface/widgets/finished.py b/src/heurams/interface/widgets/finished.py new file mode 100644 index 0000000..fe9aae9 --- /dev/null +++ b/src/heurams/interface/widgets/finished.py @@ -0,0 +1,30 @@ +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 heurams.kernel.particles as pt +import heurams.kernel.puzzles as pz +import re +import random +import copy + +class Finished(Composition): + def __init__(self, screen: Screen, reactor, atom: Tuple[pt.Electron, pt.Nucleon, Dict], extra = {}): + super().__init__(screen, reactor, atom) + + def compose(self): + yield Label("本次记忆进程结束", id=self.regid("msg")) diff --git a/src/heurams/interface/widgets/mcq_puzzle.py b/src/heurams/interface/widgets/mcq_puzzle.py new file mode 100644 index 0000000..d074af5 --- /dev/null +++ b/src/heurams/interface/widgets/mcq_puzzle.py @@ -0,0 +1,64 @@ +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 heurams.kernel.particles as pt +import heurams.kernel.puzzles as pz +import re +import random +import copy + + +class DrawCard(Composition): + def __init__(self, screen: Screen, reactor, atom: Tuple[pt.Electron, pt.Nucleon, Dict], extra = {}): + 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 2 \ No newline at end of file diff --git a/src/heurams/interface/widgets/placeholder.py b/src/heurams/interface/widgets/placeholder.py new file mode 100644 index 0000000..77f166c --- /dev/null +++ b/src/heurams/interface/widgets/placeholder.py @@ -0,0 +1,34 @@ +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 heurams.kernel.particles as pt +import heurams.kernel.puzzles as pz +import re +import random +import copy + +class Placeholder(Composition): + def __init__(self, screen: Screen, extra = {}): + 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") diff --git a/src/heurams/interface/widgets/recognition.py b/src/heurams/interface/widgets/recognition.py new file mode 100644 index 0000000..44c666b --- /dev/null +++ b/src/heurams/interface/widgets/recognition.py @@ -0,0 +1,74 @@ +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 heurams.kernel.particles as pt +import heurams.kernel.puzzles as pz +import re +import random +import copy + +class Recognition(Composition): + def __init__(self, screen: Screen, reactor, atom: Tuple[pt.Electron, pt.Nucleon, Dict], extra = {}): + 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 diff --git a/src/heurams/kernel/particles/__init__.py b/src/heurams/kernel/particles/__init__.py index 09801e1..d8fd87c 100644 --- a/src/heurams/kernel/particles/__init__.py +++ b/src/heurams/kernel/particles/__init__.py @@ -3,6 +3,7 @@ from .nucleon import Nucleon from .orbital import Orbital from .atom import Atom from .probe import probe_all, probe_by_filename +from .loader import load_nucleon, load_electron __all__ = [ "Electron", @@ -11,4 +12,6 @@ __all__ = [ "Atom", "probe_all", "probe_by_filename", + "load_nucleon", + "load_electron", ] \ No newline at end of file diff --git a/src/heurams/kernel/reactor/procession.py b/src/heurams/kernel/reactor/procession.py index 7418f8e..e8bbaea 100644 --- a/src/heurams/kernel/reactor/procession.py +++ b/src/heurams/kernel/reactor/procession.py @@ -26,12 +26,18 @@ class Procession(): def append(self, atom = None): if atom == None: - self.queue.append(self.current_atom) - else: - self.queue.append(atom) + atom = self.current_atom + if self.queue[len(self.queue) - 1] != atom or len(self) <= 1: + self.queue.append(atom) def __len__(self): return (len(self.queue) - self.cursor) + + def process(self): + return (self.cursor) + + def total_length(self): + return len(self.queue) def is_empty(self): return len(self.queue) \ No newline at end of file diff --git a/src/heurams/services/config.py b/src/heurams/services/config.py index e9ef979..8f15a9a 100644 --- a/src/heurams/services/config.py +++ b/src/heurams/services/config.py @@ -33,3 +33,14 @@ class ConfigFile: def get(self, key: str, default: typing.Any = None) -> typing.Any: """获取配置值,如果不存在返回默认值""" return self.data.get(key, default) + + def __getitem__(self, key: str) -> typing.Any: + return self.data[key] + + def __setitem__(self, key: str, value: typing.Any): + self.data[key] = value + self.save() + + def __contains__(self, key: str) -> bool: + """支持 in 语法""" + return key in self.data \ No newline at end of file