From 35ed4e63bee86251da0bc4e63d99ecbfa8ddcd1e Mon Sep 17 00:00:00 2001 From: david-ajax Date: Sun, 9 Nov 2025 12:04:01 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=84=E4=BB=B6=E6=B6=88=E6=81=AF=E7=8E=B0?= =?UTF-8?q?=E4=BB=A3=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/heurams/interface/shim.py | 1 + src/heurams/interface/widgets/cloze_puzzle.py | 75 ++++++++++++------- src/heurams/interface/widgets/recognition.py | 45 ++++------- src/heurams/kernel/puzzles/cloze.py | 5 +- 4 files changed, 70 insertions(+), 56 deletions(-) diff --git a/src/heurams/interface/shim.py b/src/heurams/interface/shim.py index 6ee4f0e..ca1d541 100644 --- a/src/heurams/interface/shim.py +++ b/src/heurams/interface/shim.py @@ -3,6 +3,7 @@ import random import heurams.kernel.particles as pt import heurams.kernel.puzzles as pz import heurams.interface.widgets as pzw +from typing import TypedDict staging = {} # 细粒度缓存区, 是 ident -> quality 的封装 def report_to_staging(atom: pt.Atom, quality): staging[atom.ident] = min(quality, staging[atom.ident]) diff --git a/src/heurams/interface/widgets/cloze_puzzle.py b/src/heurams/interface/widgets/cloze_puzzle.py index f8f9942..bac502a 100644 --- a/src/heurams/interface/widgets/cloze_puzzle.py +++ b/src/heurams/interface/widgets/cloze_puzzle.py @@ -1,18 +1,14 @@ from textual.widgets import ( - Markdown, Label, - Static, Button, ) -from textual.containers import Container, Horizontal, Center -from textual.screen import Screen from textual.widget import Widget import heurams.kernel.particles as pt import heurams.kernel.puzzles as pz from .base_puzzle_widget import BasePuzzleWidget import copy import random -from .. import shim +from textual.message import Message class ClozePuzzle(BasePuzzleWidget): @@ -28,31 +24,60 @@ class ClozePuzzle(BasePuzzleWidget): self.ans = copy.copy(self.puzzle.answer) random.shuffle(self.ans) + class RatingChanged(Message): + def __init__(self, atom: pt.Atom, rating: int, is_correct: bool) -> None: + self.atom = atom + self.rating = rating # 评分 + self.is_correct = is_correct # 是否正确 + super().__init__() + + class InputChanged(Message): + """输入变化消息""" + def __init__(self, current_input: list, max_length: int) -> None: + self.current_input = current_input # 当前输入 + self.max_length = max_length # 最大长度 + self.progress = len(current_input) / max_length # 进度 + super().__init__() + def compose(self): yield Label(self.puzzle.wording, id="sentence") yield Label(f"当前输入: {self.inputlist}", id="inputpreview") for i in self.ans: self.hashtable[str(hash(i))] = i yield Button(i, id=f"{hash(i)}") - yield Button("退格", id=f"delete") + yield Button("退格", id="delete") - def handler(self, event, type_): - # TODO: 改动:在线错误纠正 - if type_ == "button": - if event.button.id == "delete": - if len(self.inputlist) > 0: - self.inputlist.pop() - else: - return 1 - else: - self.inputlist.append(self.hashtable[event.button.id[6:]]) - if len(self.inputlist) < len(self.puzzle.answer): - return 1 - else: - if self.inputlist == self.puzzle.answer: - shim.report_to_staging(self.atom, 4) - return 0 - else: + def update_preview(self): + preview = self.query_one("#inputpreview") + preview.update(f"当前输入: {self.inputlist}") # type: ignore + + self.post_message(self.InputChanged( + current_input=self.inputlist.copy(), + max_length=len(self.puzzle.answer) + )) + + 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_preview() + else: + answer_text = self.hashtable[button_id] + self.inputlist.append(answer_text) + self.update_preview() + + if len(self.inputlist) >= len(self.puzzle.answer): + is_correct = self.inputlist == self.puzzle.answer + rating = 4 if is_correct else 2 + + self.post_message(self.RatingChanged( + atom=self.atom, + rating=rating, + is_correct=is_correct + )) + + if not is_correct: self.inputlist = [] - shim.report_to_staging(self.atom, 2) - return 2 \ No newline at end of file + self.update_preview() \ No newline at end of file diff --git a/src/heurams/interface/widgets/recognition.py b/src/heurams/interface/widgets/recognition.py index 18fc42a..66a235a 100644 --- a/src/heurams/interface/widgets/recognition.py +++ b/src/heurams/interface/widgets/recognition.py @@ -1,30 +1,17 @@ -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.containers import Center from textual.widget import Widget -import uuid -from typing import Tuple, Dict +from typing import Dict import heurams.kernel.particles as pt -import heurams.kernel.puzzles as pz import re -import random -import copy from .base_puzzle_widget import BasePuzzleWidget -from heurams.context import config_var -from .. import shim from typing import TypedDict, List +from textual.message import Message class RecognitionConfig(TypedDict): __origin__: str @@ -35,15 +22,18 @@ class RecognitionConfig(TypedDict): class Recognition(BasePuzzleWidget): - rate_mapping = { - "ok": 5 - } - 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) if alia == "": alia = "Recognition" self.alia = alia + + class RatingChanged(Message): + """评分改变消息""" + def __init__(self, rating: int) -> None: + self.rating = rating + super().__init__() + def compose(self): cfg: RecognitionConfig = self.atom.registry["orbital"]["puzzles"][self.alia] delim = self.atom.registry['nucleon'].metadata["formation"]["delimiter"] @@ -64,8 +54,8 @@ class Recognition(BasePuzzleWidget): primary = nucleon[cfg["primary"]] with Center(): - yield Static(f"[dim]{nucleon[cfg["top_dim"]]}[/]") - yield Label(f"") + yield Static(f"[dim]{nucleon[cfg['top_dim']]}[/]") + yield Label("") for old, new in replace_dict.items(): primary = primary.replace(old, new) @@ -80,7 +70,7 @@ class Recognition(BasePuzzleWidget): for item in nucleon[cfg["secondary"]]: if isinstance(item, list): for j in item: - yield Markdown(f"### {metadata["annotation"][item]}: {j}") + yield Markdown(f"### {metadata['annotation'][item]}: {j}") continue if isinstance(item, Dict): total = "" @@ -93,9 +83,6 @@ class Recognition(BasePuzzleWidget): with Center(): yield Button("我已知晓", id="ok") - def handler(self, event, type_): - if type_ == "button": - if event.button.id == "ok": - shim.report_to_staging(self.atom, 5) - return 0 - return -1 + def on_button_pressed(self, event: Button.Pressed) -> None: + if event.button.id == "ok": + self.post_message(self.RatingChanged(5)) \ No newline at end of file diff --git a/src/heurams/kernel/puzzles/cloze.py b/src/heurams/kernel/puzzles/cloze.py index 804b110..4574d88 100644 --- a/src/heurams/kernel/puzzles/cloze.py +++ b/src/heurams/kernel/puzzles/cloze.py @@ -9,15 +9,16 @@ class ClozePuzzle(BasePuzzle): min_denominator: 最小概率倒数(如占所有可生成填空数的 1/7 中的 7, 若期望值小于 1, 则取 1) """ - def __init__(self, text: str, delimiter: str = "/", min_denominator: int): + def __init__(self, text: str, min_denominator: int, delimiter: str = "/"): self.text = text self.min_denominator = min_denominator self.wording = "填空题 - 尚未刷新谜题" self.answer = ["填空题 - 尚未刷新谜题"] + self.delimiter = delimiter def refresh(self): # 刷新谜题 placeholder = "___SLASH___" - tmp_text = self.text.replace(delimiter, placeholder) + tmp_text = self.text.replace(self.delimiter, placeholder) words = tmp_text.split(placeholder) if not words: return