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 from typing import Tuple, Dict import heurams.kernel.particles as pt import heurams.kernel.puzzles as pz from .base_puzzle_widget import BasePuzzleWidget import copy import random from textual.message import Message class MCQPuzzle(BasePuzzleWidget): 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): 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") # 渲染当前问题的选项 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="delete") 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 = [] 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)