diff --git a/src/heurams/interface/widgets/cloze_puzzle.py b/src/heurams/interface/widgets/cloze_puzzle.py index bac502a..1cca2ec 100644 --- a/src/heurams/interface/widgets/cloze_puzzle.py +++ b/src/heurams/interface/widgets/cloze_puzzle.py @@ -12,14 +12,16 @@ from textual.message import Message class ClozePuzzle(BasePuzzleWidget): - def __init__(self, *children: Widget, atom: pt.Atom, name: str | None = None, id: str | None = None, classes: str | None = None, disabled: bool = False, markup: bool = True) -> None: + 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) self.inputlist = list() self.hashtable = {} + self.alia = alia self._work() def _work(self): - self.puzzle = pz.ClozePuzzle(text=self.atom.registry["nucleon"]["content"], min_denominator=2) + cfg = self.atom.registry["orbital"]["puzzles"][self.alia] + self.puzzle = pz.ClozePuzzle(text=cfg["content"], delimiter=cfg["delimiter"], min_denominator=cfg["min_denominator"]) self.puzzle.refresh() self.ans = copy.copy(self.puzzle.answer) random.shuffle(self.ans) diff --git a/src/heurams/interface/widgets/mcq_puzzle.py b/src/heurams/interface/widgets/mcq_puzzle.py index a7cf937..5382597 100644 --- a/src/heurams/interface/widgets/mcq_puzzle.py +++ b/src/heurams/interface/widgets/mcq_puzzle.py @@ -20,44 +20,142 @@ 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 MCQPuzzle(BasePuzzleWidget): - def __init__(self, *children: Widget, atom: pt.Atom, name: str | None = None, id: str | None = None, classes: str | None = None, disabled: bool = False, markup: bool = True) -> None: + 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) self.inputlist = [] + self.alia = alia self.hashtable = {} self._work() def _work(self): - self.puzzle = pz.SelectionPuzzle(self.atom[1]["keyword_note"], [], 2, "选择正确词义: ") # type: ignore + cfg = self.atom.registry["orbital"]["puzzles"][self.alia] + self.puzzle = pz.MCQPuzzle(cfg["mapping"], cfg["jammer"], cfg["max_riddles_num"], cfg['prefix']) self.puzzle.refresh() + class PuzzleCompleted(Message): + """选择题完成消息""" + def __init__(self, atom: pt.Atom, rating: int, is_correct: bool, user_answers: list, correct_answers: list) -> None: + self.atom = atom + self.rating = rating # 评分 + self.is_correct = is_correct # 是否正确 + self.user_answers = user_answers # 用户答案 + self.correct_answers = correct_answers # 正确答案 + super().__init__() + + class InputChanged(Message): + """输入变化消息""" + def __init__(self, current_input: list, current_question: int, total_questions: int, current_question_text: str) -> None: + self.current_input = current_input # 当前输入 + self.current_question = current_question # 当前题号 + self.total_questions = total_questions # 总题数 + self.current_question_text = current_question_text # 当前问题文本 + self.progress = current_question / total_questions # 进度 + super().__init__() + + class QuestionAdvanced(Message): + """题目切换消息""" + def __init__(self, question_index: int, question_text: str, options: list) -> None: + self.question_index = question_index # 题目索引 + self.question_text = question_text + self.options = options # 选项列表 + super().__init__() + def compose(self): yield Label(self.atom[1].content.replace("/",""), id="sentence") yield Label(self.puzzle.wording[len(self.inputlist)], id="puzzle") yield Label(f"当前输入: {self.inputlist}", id="inputpreview") - for i in self.puzzle.options[len(self.inputlist)]: + + # 渲染当前问题的选项 + current_options = self.puzzle.options[len(self.inputlist)] + for i in current_options: self.hashtable[str(hash(i))] = i yield Button(i, id=f"select{hash(i)}") - yield Button("退格", id=f"delete") + + yield Button("退格", id="delete") - def handler(self, event, type_): - 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_display(self): + # 更新预览标签 + preview = self.query_one("#inputpreview") + preview.update(f"当前输入: {self.inputlist}") # type: ignore + + # 更新问题标签 + puzzle_label = self.query_one("#puzzle") + current_question_index = len(self.inputlist) + if current_question_index < len(self.puzzle.wording): + puzzle_label.update(self.puzzle.wording[current_question_index]) # type: ignore + + # 发送输入变化消息 + self.post_message(self.InputChanged( + current_input=self.inputlist.copy(), + current_question=current_question_index, + total_questions=len(self.puzzle.answer), + current_question_text=self.puzzle.wording[current_question_index] if current_question_index < len(self.puzzle.wording) else "" + )) + + # 如果还有下一题,发送题目切换消息 + if current_question_index < len(self.puzzle.options): + self.post_message(self.QuestionAdvanced( + question_index=current_question_index, + question_text=self.puzzle.wording[current_question_index], + options=self.puzzle.options[current_question_index] + )) + + 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.refresh_buttons() + self.update_display() + elif button_id.startswith("select"): # type: ignore + # 选项选择处理 + answer_text = self.hashtable[button_id[6:]] # type: ignore + self.inputlist.append(answer_text) + + # 检查是否完成所有题目 + 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.PuzzleCompleted( + atom=self.atom, + rating=rating, + is_correct=is_correct, + user_answers=self.inputlist.copy(), + correct_answers=self.puzzle.answer.copy() + )) + + # 重置输入(如果回答错误) + if not is_correct: self.inputlist = [] - shim.report_to_staging(self.atom, 2) - return 2 \ No newline at end of file + self.refresh_buttons() + self.update_display() + else: + # 进入下一题 + self.refresh_buttons() + self.update_display() + + def refresh_buttons(self): + """刷新按钮显示(用于题目切换)""" + # 移除所有选项按钮 + buttons_to_remove = [child for child in self.children if hasattr(child, 'id') and child.id and child.id.startswith('select')] + for button in buttons_to_remove: + self.remove_child(button) # type: ignore + + # 添加当前题目的选项按钮 + current_question_index = len(self.inputlist) + if current_question_index < len(self.puzzle.options): + current_options = self.puzzle.options[current_question_index] + for option in current_options: + button_id = f"select{hash(option)}" + if button_id not in self.hashtable: + self.hashtable[button_id] = option + new_button = Button(option, id=button_id) + self.mount(new_button) \ No newline at end of file