Archived
0
0
This commit is contained in:
2025-12-16 21:28:53 +08:00
parent 11d130c3fd
commit 1e534e5fe5
37 changed files with 428 additions and 207 deletions

View File

@@ -8,6 +8,7 @@ from heurams.services.logger import get_logger
logger = get_logger(__name__)
class HeurAMSApp(App):
TITLE = "潜进"
CSS_PATH = "css/main.tcss"
@@ -37,8 +38,10 @@ class HeurAMSApp(App):
print("DO NOTHING")
self.refresh()
def environment_check():
from pathlib import Path
logger.debug("检查环境路径")
for i in config_var.get()["paths"].values():

View File

@@ -34,12 +34,12 @@ class AboutScreen(Screen):
开发人员:
- [@pluvium27](https://github.com/pluvium27) (Wang Zhiyu)
- Wang Zhiyu([@pluvium27](https://github.com/pluvium27)): 项目作者
特别感谢:
- [Piotr A. Woźniak](https://supermemo.guru/wiki/Piotr_Wozniak): SuperMemo-2 算法
- [Thoughts Memo](https://www.zhihu.com/people/L.M.Sherlock): 文献参考
- [Piotr A. Woźniak](https://supermemo.guru/wiki/Piotr_Wozniak): SuperMemo-2 算法
- [Thoughts Memo](https://www.zhihu.com/people/L.M.Sherlock): 文献参考
# 参与贡献
@@ -47,7 +47,7 @@ class AboutScreen(Screen):
通过我们协力开发的软件为所有人谋取福祉.
此项目不是 KDE 软件, 但上述工作不可避免地让我们确立了和 KDE 宣言相同的下列价值观:
上述工作不可避免地让我们确立了下列价值观 (取自 KDE 宣言):
- 开放治理 确保更多人能参与我们的领导和决策进程;
@@ -68,8 +68,13 @@ class AboutScreen(Screen):
我们的共同目标是为人人带来高品质的辅助记忆 & 学习软件.
不管您来自何方, 我们都欢迎您加入社区并做出贡献.
"""
# """
# 学术数据
# "潜进" 的用户数据可用于科学方面的研究, 我们将在未来版本添加学术数据的收集和展示平台
# """
yield Markdown(about_text, classes="about-markdown")
yield Button(

View File

@@ -24,8 +24,10 @@ import pathlib
logger = get_logger(__name__)
class DashboardScreen(Screen):
SUB_TITLE = "仪表盘"
def compose(self) -> ComposeResult:
yield Header(show_clock=True)
yield ScrollableContainer(
@@ -71,6 +73,7 @@ class DashboardScreen(Screen):
nextdate = 0x3F3F3F3F
for i in electron_dict.values():
i: pt.Electron
logger.debug(i, i.is_due())
if i.is_due():
is_due = 1
if i.is_activated():

View File

@@ -18,8 +18,10 @@ class AtomState(Enum):
FAILED = auto()
NORMAL = auto()
logger = get_logger(__name__)
class MemScreen(Screen):
BINDINGS = [
("q", "pop_screen", "返回"),
@@ -45,6 +47,7 @@ class MemScreen(Screen):
self.phaser = Phaser(atoms)
# logger.debug(self.phaser.state)
self.procession: Procession = self.phaser.current_procession() # type: ignore
self.atom: pt.Atom = self.procession.current_atom
# logger.debug(self.phaser.state)
# self.procession.forward(1)
for i in atoms:
@@ -58,12 +61,12 @@ class MemScreen(Screen):
try:
logger.debug(self.phaser.state)
logger.debug(self.procession.cursor)
logger.debug(self.procession.current_atom)
self.fission = Fission(self.procession.current_atom, self.phaser.state)
logger.debug(self.atom)
self.fission = Fission(self.atom, self.phaser.state)
puzzle_debug = next(self.fission.generate())
#logger.debug(puzzle_debug)
# logger.debug(puzzle_debug)
return shim.puzzle2widget[puzzle_debug["puzzle"]](
atom=self.procession.current_atom, alia=puzzle_debug["alia"]
atom=self.atom, alia=puzzle_debug["alia"]
)
except (KeyError, StopIteration, AttributeError) as e:
logger.debug(f"调度展开出错: {e}")
@@ -74,7 +77,7 @@ class MemScreen(Screen):
yield Header(show_clock=True)
with ScrollableContainer():
yield Label(self._get_progress_text(), id="progress")
# self.mount(self.current_widget()) # type: ignore
yield ScrollableContainer(id="puzzle-container")
# yield Button("重新学习此单元", id="re-recognize", variant="warning")
@@ -85,7 +88,7 @@ class MemScreen(Screen):
def update_display(self):
progress_widget = self.query_one("#progress")
progress_widget.update(self._get_progress_text()) # type: ignore
progress_widget.update(self._get_progress_text()) # type: ignore
def load_puzzle(self):
container = self.query_one("#puzzle-container")
@@ -98,6 +101,7 @@ class MemScreen(Screen):
for i in container.children:
i.remove()
from heurams.interface.widgets.finished import Finished
container.mount(Finished())
def on_button_pressed(self, event):
@@ -108,11 +112,12 @@ class MemScreen(Screen):
return
forwards = 1 if new_rating >= 4 else 0
self.rating = -1
logger.debug(f"试图前进: {"允许" if forwards else "禁止"}")
if forwards:
ret = self.procession.forward(1)
if ret == 0:
if ret == 0: # 若结束了此次队列
self.procession = self.phaser.current_procession() # type: ignore
if self.procession == 0:
if self.procession == 0: # 若所有队列都结束了
logger.debug(f"记忆进程结束")
for i in self.atoms:
i: pt.Atom
@@ -121,10 +126,10 @@ class MemScreen(Screen):
return
else:
logger.debug(f"建立新队列 {self.procession.phase}")
else:
self.load_puzzle()
else: # 若不通过
self.procession.append()
self.update_display()
self.load_puzzle()
def action_play_voice(self):
"""朗读当前内容"""

View File

@@ -94,28 +94,28 @@ class NucleonCreatorScreen(Screen):
template_select = self.query_one("#template_select")
author_input = self.query_one("#author_input")
desc_input = self.query_one("#desc_input")
name = name_input.value.strip() # type: ignore
author = author_input.value.strip() # type: ignore
desc = desc_input.value.strip() # type: ignore
selected = template_select.value # type: ignore
name = name_input.value.strip() # type: ignore
author = author_input.value.strip() # type: ignore
desc = desc_input.value.strip() # type: ignore
selected = template_select.value # type: ignore
# 验证
if not name:
self.notify("单元集名称不能为空", severity="error")
return
# 获取配置路径
config = config_var.get()
nucleon_dir = Path(config["paths"]["nucleon_dir"])
template_dir = Path(config["paths"]["template_dir"])
# 检查文件是否已存在
nucleon_path = nucleon_dir / f"{name}.toml"
if nucleon_path.exists():
self.notify(f"单元集 '{name}' 已存在", severity="error")
return
# 确定模板文件
if selected is None:
self.notify("请选择一个模板", severity="error")
@@ -123,7 +123,8 @@ class NucleonCreatorScreen(Screen):
# selected 是描述字符串,格式如 "描述 (filename.toml)"
# 提取文件名
import re
match = re.search(r'\(([^)]+)\)$', selected)
match = re.search(r"\(([^)]+)\)$", selected)
if not match:
self.notify("模板选择格式无效", severity="error")
return
@@ -132,15 +133,15 @@ class NucleonCreatorScreen(Screen):
if not template_path.exists():
self.notify(f"模板文件不存在: {template_filename}", severity="error")
return
# 加载模板
try:
with open(template_path, 'r', encoding='utf-8') as f:
with open(template_path, "r", encoding="utf-8") as f:
template_data = toml.load(f)
except Exception as e:
self.notify(f"加载模板失败: {e}", severity="error")
return
# 更新元数据
metadata = template_data.get("__metadata__", {})
attribution = metadata.get("attribution", {})
@@ -153,17 +154,17 @@ class NucleonCreatorScreen(Screen):
attribution["version"] = ver
metadata["attribution"] = attribution
template_data["__metadata__"] = metadata
# 确保 nucleon_dir 存在
nucleon_dir.mkdir(parents=True, exist_ok=True)
# 写入新文件
try:
with open(nucleon_path, 'w', encoding='utf-8') as f:
with open(nucleon_path, "w", encoding="utf-8") as f:
toml.dump(template_data, f)
except Exception as e:
self.notify(f"保存单元集失败: {e}", severity="error")
return
self.notify(f"单元集 '{name}' 创建成功")
self.app.pop_screen()

View File

@@ -19,7 +19,7 @@ from heurams.context import *
class PreparationScreen(Screen):
SUB_TITLE = "准备记忆集"
BINDINGS = [
("q", "go_back", "返回"),
("p", "precache", "预缓存音频"),
@@ -70,7 +70,6 @@ class PreparationScreen(Screen):
def action_go_back(self):
self.app.pop_screen()
def action_precache(self):
from ..screens.precache import PrecachingScreen

View File

@@ -1,3 +1,5 @@
from typing import Iterable
from textual.app import ComposeResult
from textual.widget import Widget
import heurams.kernel.particles as pt
@@ -22,3 +24,9 @@ class BasePuzzleWidget(Widget):
markup=markup
)
self.atom = atom
def compose(self) -> Iterable[Widget]:
return super().compose()
def handler(self, rating) -> None:
pass

View File

@@ -15,12 +15,14 @@ from typing import TypedDict
logger = get_logger(__name__)
class Setting(TypedDict):
__origin__: str
__hint__: str
text: str
delimiter: str
min_denominator: str
min_denominator: str
class ClozePuzzle(BasePuzzleWidget):
@@ -58,7 +60,7 @@ class ClozePuzzle(BasePuzzleWidget):
min_denominator=int(setting["min_denominator"]),
)
self.puzzle.refresh()
self.ans = copy.copy(self.puzzle.answer) # 乱序
self.ans = copy.copy(self.puzzle.answer) # 乱序
random.shuffle(self.ans)
def compose(self):
@@ -78,7 +80,6 @@ class ClozePuzzle(BasePuzzleWidget):
preview = self.query_one("#inputpreview")
preview.update(f"当前输入: {self.inputlist}") # type: ignore
def on_button_pressed(self, event: Button.Pressed) -> None:
button_id = event.button.id
@@ -87,16 +88,22 @@ class ClozePuzzle(BasePuzzleWidget):
self.inputlist.pop()
self.update_display()
else:
answer_text = self.hashmap[button_id[7:]] # type: ignore
answer_text = self.hashmap[button_id[7:]] # type: ignore
self.inputlist.append(answer_text)
self.update_display()
if len(self.inputlist) >= len(self.puzzle.answer):
is_correct = self.inputlist == self.puzzle.answer
rating = 4 if is_correct else 2
self.screen.rating = rating # type: ignore
self.handler(rating)
self.screen.rating = rating # type: ignore
if not is_correct:
self.inputlist = []
self.update_display()
def handler(self, rating):
if self.atom.lock():
pass
else:
self.atom.minimize(rating)

View File

@@ -14,6 +14,7 @@ from heurams.services.logger import get_logger
logger = get_logger(__name__)
class Setting(TypedDict):
__origin__: str
__hint__: str
@@ -49,6 +50,7 @@ class MCQPuzzle(BasePuzzleWidget):
self.alia = alia
self.hashmap = dict()
self.cursor = 0
self.atom = atom
self._load()
def _load(self):
@@ -79,7 +81,7 @@ class MCQPuzzle(BasePuzzleWidget):
yield Button("退格", id="delete")
def update_display(self, error = 0):
def update_display(self, error=0):
# 更新预览标签
preview = self.query_one("#inputpreview")
preview.update(f"当前输入: {self.inputlist}") # type: ignore
@@ -113,7 +115,7 @@ class MCQPuzzle(BasePuzzleWidget):
rating = 4 if is_correct else 2
self.screen.rating = rating # type: ignore
self.handler(rating)
# 重置输入(如果回答错误)
if not is_correct:
self.inputlist = []
@@ -138,7 +140,7 @@ class MCQPuzzle(BasePuzzleWidget):
for button in buttons_to_remove:
logger.info(button)
container.remove_children("#"+button.id) # type: ignore
container.remove_children("#" + button.id) # type: ignore
# 添加当前题目的选项按钮
current_question_index = len(self.inputlist)
@@ -150,3 +152,10 @@ class MCQPuzzle(BasePuzzleWidget):
self.hashmap[button_id] = option
new_button = Button(option, id=button_id)
container.mount(new_button)
def handler(self, rating):
if self.atom.lock():
pass
else:
self.atom.minimize(rating)

View File

@@ -13,7 +13,9 @@ import re
from .base_puzzle_widget import BasePuzzleWidget
from typing import TypedDict, List
from textual.message import Message
from heurams.services.logger import get_logger
logger = get_logger(__name__)
class RecognitionConfig(TypedDict):
__origin__: str
@@ -100,3 +102,11 @@ class Recognition(BasePuzzleWidget):
def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "ok":
self.screen.rating = 5 # type: ignore
self.handler(5)
def handler(self, rating):
if not self.atom.registry["electron"].is_activated() and not self.atom.registry["runtime"]["locked"]:
self.atom.registry["electron"].activate()
logger.debug(f"激活原子 {self.atom}")
self.atom.lock(1)
self.atom.minimize(5)