From 7e28958462e4e536f0e2a81938b5739fbb4bdf9b Mon Sep 17 00:00:00 2001 From: david-ajax Date: Sat, 19 Jul 2025 23:20:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8D=E6=88=90=E7=86=9F=E7=9A=84=E6=94=B9?= =?UTF-8?q?=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __pycache__/particles.cpython-313.pyc | Bin 0 -> 8898 bytes config.toml | 1 + electron/陈情表.toml | 0 main.py | 236 ++++++++++++++++++++++++++ nucleon/陈情表.toml | 56 +++--- particles.py | 72 +++++++- reactor.py | 1 + styles.tcss | 55 ++++++ 8 files changed, 391 insertions(+), 30 deletions(-) create mode 100644 __pycache__/particles.cpython-313.pyc create mode 100644 config.toml create mode 100644 electron/陈情表.toml create mode 100644 styles.tcss diff --git a/__pycache__/particles.cpython-313.pyc b/__pycache__/particles.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c0b3a3e178c1dfe1915f93a34dcd1646ed97a1ad GIT binary patch literal 8898 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 G|9=2h60p?( literal 0 HcmV?d00001 diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..f4795b1 --- /dev/null +++ b/config.toml @@ -0,0 +1 @@ +tasked_number = 8 # 新记忆核子数量 \ No newline at end of file diff --git a/electron/陈情表.toml b/electron/陈情表.toml new file mode 100644 index 0000000..e69de29 diff --git a/main.py b/main.py index e69de29..53f8d9c 100644 --- a/main.py +++ b/main.py @@ -0,0 +1,236 @@ +from textual.app import App, ComposeResult +from textual.widgets import Header, Footer, ListView, ListItem, Label, Static, Button +from textual.containers import Container +from textual.screen import Screen +import particles as parti +import pathlib + +ver = '0.2.1' + +class MemScreen(Screen): + BINDINGS = [ + ("d", "toggle_dark", "改变色调"), + ("q", "pop_screen", "返回主菜单"), + ("v", "play_voice", "朗读"), + ("0", "press('q0')", None), + ("1", "press('q1')", None), + ("2", "press('q2')", None), + ("3", "press('q3')", None), + ("4", "press('q4')", None), + ("5", "press('q5')", None), + ("[", "press('q5')", None), + ("]", "press('q4')", None), + (";", "press('q3')", None), + ("'", "press('q2')", None), + (".", "press('q1')", None), + ("/", "press('q0')", None), + ] + btn = dict() + def __init__( + self, + atoms_file: str = 'test_atoms.json', + tasked_num: int = 8, # 记忆最小单元数目 + ): + super().__init__(name=None, id=None, classes=None) + self.memobj = MemObject(atoms_file=atoms_file, tasked_num=tasked_num) + self.memobj.next_round() + self.memobj.update_runtime(1) + def compose(self) -> ComposeResult: + yield Header(show_clock=True) + with Container(id="main_container"): + yield Label("", id="round_label") + yield Label("记住了吗?", id="question") + yield Static(self.memobj.runtime['current_atom'].content, id="sentence") + yield Static("", id="feedback") # 用于显示反馈 + yield Label(self._get_progress_text(), id="progress") + with Container(id="button_container"): + self.btn['5'] = Button("完美回想", variant="success", id="q5", classes="choice") + self.btn['4'] = Button("犹豫后正确", variant="success", id="q4", classes="choice") + self.btn['3'] = Button("困难地正确", variant="warning", id="q3", classes="choice") + self.btn['2'] = Button("错误但熟悉", variant="warning", id="q2", classes="choice") + self.btn['1'] = Button("错误且不熟", variant="error", id="q1", classes="choice") + self.btn['0'] = Button("完全空白", variant="error", id="q0", classes="choice") + yield Horizontal(self.btn['5'], self.btn['4']) + yield Horizontal(self.btn['3'], self.btn['2']) + yield Horizontal(self.btn['1'], self.btn['0']) + yield Footer() + + + def _get_progress_text(self): + return f"{self.memobj.runtime['left'] + 1}/{self.memobj.runtime['total']}" + + def _get_round_text(self): + t = { + 'A': "复习模式", + 'B': "建立新记忆", + 'C': "总复习" + } + return "当前模式: " + t[self.memobj.runtime['stage']] + + def on_mount(self): + # 首次挂载时调用 + self._update_ui() + + def _update_ui(self): + self.query_one("#round_label", Label).update(self._get_round_text()) + self.query_one("#sentence", Static).update(self.memobj.runtime['current_atom'].content) + self.query_one("#progress", Label).update(self._get_progress_text()) + self.query_one("#feedback", Static).update("") # 清除任何之前的反馈消息 + + def _show_finished_screen(self, message): + self.query_one("#question", Label).update(message) + self.query_one("#sentence", Static).update("已经完成记忆任务") + self.query_one("#round_label").display = False + self.query_one("#progress").display = False + for i in range(6): + self.query_one(f"#q{i}", Button).display = False + + def on_button_pressed(self, event): + feedback_label = self.query_one("#feedback", Static) + if type(event) == str: + btnid = event + else: + btnid = event.button.id + btnid = str(btnid) + quality = int(btnid.replace('q', '')) + assessment = self.memobj.report(self.memobj.runtime['current_atom'], quality) + if assessment == 1: + # 需要复习 + feedback_label.update(f"评分为 {quality}, 已经加入至复习, 请重复记忆") + else: + ret = self.memobj.update_runtime(1) + if ret == 1: + self.memobj.switch_to_extra_review() + self.memobj.update_runtime(1) + elif ret == 2: + self.memobj.next_round() + self.memobj.update_runtime(1) + elif ret == 3: + self.memobj.save() + self._show_finished_screen("今日目标已完成") + return + #feedback_label.update("") # 清除反馈消息 + self._update_ui() + def action_press(self, btnid): + self.on_button_pressed(btnid) + + def action_play_voice(self): + def play(): + cache = Path(f"./cache/voice/{self.memobj.runtime['current_atom'].content}.wav") + if not cache.exists(): + communicate = tts.Communicate(self.memobj.runtime['current_atom'].content, "zh-CN-YunjianNeural") + communicate.save_sync(f"./cache/voice/{self.memobj.runtime['current_atom'].content}.wav") + playsound(str(cache)) + threading.Thread(target=play).start() + + def action_toggle_dark(self): + self.app.action_toggle_dark() + + def action_pop_screen(self): + """返回到上一个屏幕""" + self.app.pop_screen() + +class PreparationScreen(Screen): + BINDINGS = [ + ("q", "go_back", "返回"), + ("escape", "quit_app", "退出") + ] + + def __init__(self, nucleon_file: parti.AtomicFile, electron_file: parti.AtomicFile) -> None: + super().__init__(name=None, id=None, classes=None) + self.nucleon_file = nucleon_file + self.electron_file = electron_file + + def compose(self) -> ComposeResult: + yield Header(show_clock=True) + with Container(id="learning_screen_container"): + yield Label(f"记忆项目: [b]{self.nucleon_file.name}[/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"核子数量:{self.nucleon_file.get_len()}") + yield Button("开始记忆", id="start_memorizing_button", variant="primary", classes="start-button") + yield Static(f"\n全文如下:\n") + yield Static(self.nucleon_file.get_full_content(), classes="full") + 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: Button.Pressed) -> None: + pass + #if event.button.id == "start_memorizing_button": + # #init_file(Path(self.atom_file).name) + # newscr = proc.MemScreen(Path(self.atom_file).name) + # self.app.push_screen( + # newscr + # ) + #if event.button.id == "edit_metadata_button": + # init_file(Path(self.atom_file).name) + # os.system("reset;nano ./data/" + str(Path(self.atom_file).name.replace(".txt", "_atoms.json"))) + +class FileSelectorScreen(Screen): + global ver + def compose(self) -> ComposeResult: + yield Header(show_clock=True) + yield Container( + Label(f'欢迎使用 "潜进" 辅助记忆软件, 版本 {ver}', classes="title-label"), + Label("选择要学习的文件:", classes="title-label"), + ListView(id="file-list", classes="file-list-view") + ) + yield Footer() + + def on_mount(self) -> None: + file_list_widget = self.query_one("#file-list", ListView) + nucleon_path = pathlib.Path("./nucleon") + nucleon_files = sorted([f.name for f in nucleon_path.iterdir() if f.suffix == ".toml"]) + + if nucleon_files: + for filename in nucleon_files: + file_list_widget.append(ListItem(Label(filename))) + else: + file_list_widget.append(ListItem(Static("在 ./nucleon/ 中未找到任何核子文件. 请放置文件后重启应用."))) + file_list_widget.disabled = True + + def on_list_view_selected(self, event: ListView.Selected) -> None: + if not isinstance(event.item, ListItem): + self.notify("无法选择此项。", severity="error") + return + + selected_label = event.item.query_one(Label) + if "未找到任何 .toml 文件" in str(selected_label.renderable): + self.notify("请先在 `./atoms/` 目录中放置 .toml 文件。", severity="warning") + return + + selected_filename = str(selected_label.renderable) + nucleon_file = parti.AtomicFile(pathlib.Path("./nucleon") / selected_filename, "nucleon") + electron_file_path = pathlib.Path("./electron") / selected_filename + if electron_file_path.exists(): + pass + else: + electron_file_path.touch() + electron_file = parti.AtomicFile(pathlib.Path("./electron") / selected_filename, "electron") + # self.notify(f"已选择: {selected_filename}", timeout=2) + self.app.push_screen(PreparationScreen(nucleon_file, electron_file)) + + def action_quit_app(self) -> None: + self.app.exit() + +class AppLauncher(App): + CSS_PATH = "styles.tcss" + TITLE = '潜进 - 辅助记忆程序' + BINDINGS = [("escape", "quit", "退出"), ("d", "toggle_dark", "改变色调")] + SCREENS = { + "file_selection_screen": FileSelectorScreen, + } + + def on_mount(self) -> None: + self.action_toggle_dark() + self.push_screen("file_selection_screen") + +if __name__ == "__main__": + css_path = pathlib.Path("styles_dashboard.tcss") + app = AppLauncher() + app.run() diff --git a/nucleon/陈情表.toml b/nucleon/陈情表.toml index 1f55a29..6618f2c 100644 --- a/nucleon/陈情表.toml +++ b/nucleon/陈情表.toml @@ -1,139 +1,139 @@ ["臣密言:臣以险衅, 夙遭闵凶."] note = [] translation = "臣子李密陈言:我因命运不好,小时候遭遇到了不幸" -keyword_note = {"险衅":"凶险祸患(这里指命运不好)", "夙":"早时,这里指年幼的时候", "闵":"通'悯',指可忧患的事", "凶":"不幸,指丧父"} +keyword_note = {"险衅"="凶险祸患(这里指命运不好)", "夙"="早时,这里指年幼的时候", "闵"="通'悯',指可忧患的事", "凶"="不幸,指丧父"} ["生孩六月, 慈父见背;行年四岁, 舅夺母志."] note = [] translation = "刚出生六个月,我慈爱的父亲就不幸去世了。经过了四年,舅父逼母亲改嫁" -keyword_note = {"见背":"死的委婉说法", "行年":"经历的年岁", "母志":"母亲守节之志(改嫁的委婉说法)"} +keyword_note = {"见背"="死的委婉说法", "行年"="经历的年岁", "母志"="母亲守节之志(改嫁的委婉说法)"} ["祖母刘愍臣孤弱, 躬亲抚养."] note = [] translation = "我的祖母刘氏,怜悯我从小丧父,便亲自对我加以抚养" -keyword_note = {"愍":"怜悯", "躬亲":"亲身"} +keyword_note = {"愍"="怜悯", "躬亲"="亲身"} ["臣少多疾病, 九岁不行, 零丁孤苦, 至于成立."] note = [] translation = "臣小的时候经常生病,九岁时还不会行走。孤独无靠,一直到成人自立" -keyword_note = {"成立":"成人自立"} +keyword_note = {"成立"="成人自立"} ["既无伯叔, 终鲜兄弟, 门衰祚薄, 晚有儿息."] note = [] translation = "既没有叔叔伯伯,又没什么兄弟,门庭衰微而福分浅薄,很晚才有儿子" -keyword_note = {"鲜":"少,这里指'无'", "祚薄":"福分浅薄", "儿息":"亲生子女"} +keyword_note = {"鲜"="少,这里指'无'", "祚薄"="福分浅薄", "儿息"="亲生子女"} ["外无期功强近之亲, 内无应门五尺之僮, 茕茕孑立, 形影相吊."] note = [] translation = "在外面没有比较亲近的亲戚,在家里又没有照应门户的童仆。生活孤单没有依靠,每天只有自己的身体和影子相互安慰" -keyword_note = {"期功":"指关系较近的亲属", "茕茕孑立":"孤单无依靠的样子", "吊":"安慰"} +keyword_note = {"期功"="指关系较近的亲属", "茕茕孑立"="孤单无依靠的样子", "吊"="安慰"} ["而刘夙婴疾病, 常在床蓐, 臣侍汤药, 未曾废离."] note = [] translation = "但祖母又早被疾病缠绕,常年卧床不起,我侍奉她吃饭喝药,从来就没有停止侍奉而离开她" -keyword_note = {"婴":"被...缠绕", "蓐":"通'褥',床垫", "废":"停止服侍", "离":"离开"} +keyword_note = {"婴"="被...缠绕", "蓐"="通'褥',床垫", "废"="停止服侍", "离"="离开"} ["逮奉圣朝, 沐浴清化."] note = [] translation = "到了晋朝建立,我蒙受着清明的政治教化" -keyword_note = {"逮":"及,到", "奉":"承奉", "圣朝":"指当时的晋朝", "沐浴清化":"蒙受清平教化"} +keyword_note = {"逮"="及,到", "奉"="承奉", "圣朝"="指当时的晋朝", "沐浴清化"="蒙受清平教化"} ["前太守臣逵察臣孝廉;后刺史臣荣举臣秀才."] note = [] translation = "前任太守逵,考察后推举臣下为孝廉,后任刺史荣又推举臣下为优秀人才" -keyword_note = {"察":"考察和推举", "孝廉":"孝顺,品性纯洁", "举":"推举", "秀才":"优秀人才"} +keyword_note = {"察"="考察和推举", "孝廉"="孝顺,品性纯洁", "举"="推举", "秀才"="优秀人才"} ["臣以供养无主, 辞不赴命."] note = [] translation = "臣下因为供奉赡养祖母的事无人承担,辞谢不接受任命" -keyword_note = {"无主":"无人承担"} +keyword_note = {"无主"="无人承担"} ["诏书特下, 拜臣郎中, 寻蒙国恩, 除臣洗马."] note = [] translation = "朝廷又特地下了诏书,任命我为郎中,不久又蒙受国家恩命,任命我为太子洗马" -keyword_note = {"拜":"授予官职", "郎中":"尚书省的属官", "寻":"不久", "除":"拜官受职", "洗马":"太子的属官"} +keyword_note = {"拜"="授予官职", "郎中"="尚书省的属官", "寻"="不久", "除"="拜官受职", "洗马"="太子的属官"} ["猥以微贱, 当侍东宫, 非臣陨首所能上报."] note = [] translation = "像我这样出身微贱地位卑下的人,担当侍奉太子的职务,这实在不是我杀身捐躯所能报答朝廷的" -keyword_note = {"猥":"谦词", "微贱":"卑微低贱", "东宫":"太子居处", "陨首":"杀身"} +keyword_note = {"猥"="谦词", "微贱"="卑微低贱", "东宫"="太子居处", "陨首"="杀身"} ["臣具以表闻, 辞不就职."] note = [] translation = "我将以上苦衷上表报告,加以推辞不去就职" -keyword_note = {"具":"详细", "闻":"使...知道"} +keyword_note = {"具"="详细", "闻"="使...知道"} ["诏书切峻, 责臣逋慢;郡县逼迫, 催臣上道;州司临门, 急于星火."] note = [] translation = "但是诏书急切严峻,责备我逃避命令,有意拖延,态度傲慢。郡县长官催促我立刻上路;州官登门督促,比流星坠落还要急迫" -keyword_note = {"切峻":"急切而严厉", "逋慢":"逃避怠慢", "星火":"流星的光,喻急迫"} +keyword_note = {"切峻"="急切而严厉", "逋慢"="逃避怠慢", "星火"="流星的光,喻急迫"} ["臣欲奉诏奔驰, 则刘病日笃, 欲苟顺私情, 则告诉不许."] note = [] translation = "我很想遵从皇上的旨意赴京就职,但祖母刘氏的病却一天比一天重;想要姑且顺从自己的私情,但报告申诉不被允许" -keyword_note = {"日笃":"病情日益加重", "苟":"姑且", "告诉":"报告申诉"} +keyword_note = {"日笃"="病情日益加重", "苟"="姑且", "告诉"="报告申诉"} ["臣之进退, 实为狼狈."] note = [] translation = "我是进退两难,十分狼狈" -keyword_note = {"狼狈":"进退两难的样子"} +keyword_note = {"狼狈"="进退两难的样子"} ["伏惟圣朝以孝治天下, 凡在故老, 犹蒙矜育, 况臣孤苦, 特为尤甚."] note = [] translation = "我俯伏思量晋朝是用孝道来治理天下的,凡是年老而德高的旧臣,尚且还受到怜悯养育,何况我的孤苦程度更为严重呢" -keyword_note = {"伏惟":"下对上的敬辞", "故老":"年老德高的旧臣", "矜育":"怜悯养育"} +keyword_note = {"伏惟"="下对上的敬辞", "故老"="年老德高的旧臣", "矜育"="怜悯养育"} ["且臣少仕伪朝, 历职郎署, 本图宦达, 不矜名节."] note = [] translation = "况且我年轻的时候曾经做过蜀汉的官,担任过郎官职务,本来就希望做官显达,并不顾惜名声节操" -keyword_note = {"伪朝":"对前朝的蔑称", "历职":"连续任职", "郎署":"尚书郎的官衙", "宦达":"官场上显达"} +keyword_note = {"伪朝"="对前朝的蔑称", "历职"="连续任职", "郎署"="尚书郎的官衙", "宦达"="官场上显达"} ["今臣亡国贱俘, 至微至陋, 过蒙拔擢, 宠命优渥, 岂敢盘桓, 有所希冀!"] note = [] translation = "现在我是一个低贱的亡国俘虏,十分卑微浅陋,受到过分提拔,恩宠优厚,怎敢犹豫不决而有非分的企求呢" -keyword_note = {"拔擢":"提拔", "优渥":"优厚", "盘桓":"徘徊不前", "希冀":"非分的企求"} +keyword_note = {"拔擢"="提拔", "优渥"="优厚", "盘桓"="徘徊不前", "希冀"="非分的企求"} ["但以刘日薄西山, 气息奄奄, 人命危浅, 朝不虑夕."] note = [] translation = "只是因为祖母刘氏寿命即将终了,气息微弱,生命垂危,早上不能想到晚上怎样" -keyword_note = {"日薄西山":"太阳接近西山,喻人寿命将终", "危浅":"生命垂危"} +keyword_note = {"日薄西山"="太阳接近西山,喻人寿命将终", "危浅"="生命垂危"} ["臣无祖母, 无以至今日, 祖母无臣, 无以终余年."] note = [] translation = "臣下我如果没有祖母,就没有今天的样子;祖母如果没有我的照料,也无法度过她的余生" -keyword_note = {"终余年":"度过余生"} +keyword_note = {"终余年"="度过余生"} ["母孙二人, 更相为命, 是以区区不能废远."] note = [] translation = "我们祖孙二人,互相依靠而维持生命,因此我的内心不愿废止奉养,远离祖母" -keyword_note = {"更相":"相互", "区区":"自己的私情", "废远":"废止奉养而远离"} +keyword_note = {"更相"="相互", "区区"="自己的私情", "废远"="废止奉养而远离"} ["臣密今年四十有四, 祖母今年九十有六, 是臣尽节于陛下之日长, 报养刘之日短."] note = [] translation = "臣下我现在的年龄四十四岁了,祖母现在的年龄九十六岁了,臣下我在陛下面前尽忠尽节的日子还长着呢,而在祖母刘氏面前尽孝尽心的日子已经不多了" -keyword_note = {"有":"又", "尽节":"尽忠节"} +keyword_note = {"有"="又", "尽节"="尽忠节"} ["乌鸟私情, 愿乞终养."] note = [] translation = "我怀着乌鸦反哺的私情,乞求能够准许我完成对祖母养老送终的心愿" -keyword_note = {"乌鸟私情":"乌鸦反哺之情,喻孝心", "终养":"养老至终"} +keyword_note = {"乌鸟私情"="乌鸦反哺之情,喻孝心", "终养"="养老至终"} ["臣之辛苦, 非独蜀之人士及二州牧伯所见明知, 皇天后土, 实所共鉴."] note = [] translation = "我的辛酸苦楚,并不仅仅被蜀地的百姓及益州、梁州的长官所亲眼目睹、内心明白,连天地神明也都看得清清楚楚" -keyword_note = {"辛苦":"辛酸苦楚", "牧伯":"州郡长官", "皇天后土":"天地神明", "鉴":"明察"} +keyword_note = {"辛苦"="辛酸苦楚", "牧伯"="州郡长官", "皇天后土"="天地神明", "鉴"="明察"} ["愿陛下矜悯愚诚, 听臣微志, 庶刘侥幸, 保卒余年."] note = [] translation = "希望陛下能怜悯我愚昧诚心,请允许我完成臣下一点小小的心愿,使祖母刘氏能够侥幸地保全她的余生" -keyword_note = {"矜悯":"怜悯", "听":"准许", "庶":"或许(表希望)", "卒余年":"终老"} +keyword_note = {"矜悯"="怜悯", "听"="准许", "庶"="或许(表希望)", "卒余年"="终老"} ["臣生当陨首, 死当结草."] note = [] translation = "我活着应当杀身报效朝廷,死了也要结草衔环来报答陛下的恩情" -keyword_note = {"陨首":"掉脑袋,指献出生命", "结草":"死后报恩的典故"} +keyword_note = {"陨首"="掉脑袋,指献出生命", "结草"="死后报恩的典故"} ["臣不胜犬马怖惧之情, 谨拜表以闻."] note = [] translation = "臣下我怀着牛马一样不胜恐惧的心情,恭敬地呈上此表来使陛下知道这件事" -keyword_note = {"不胜":"禁不住", "犬马怖惧":"臣子谦卑的自比", "闻":"使...知道"} \ No newline at end of file +keyword_note = {"不胜"="禁不住", "犬马怖惧"="臣子谦卑的自比", "闻"="使...知道"} \ No newline at end of file diff --git a/particles.py b/particles.py index c478d44..13022e2 100644 --- a/particles.py +++ b/particles.py @@ -5,6 +5,11 @@ import copy import datetime import json +class Aux(): + @staticmethod + def get_daystamp(): + return (time.time() // (24*3600)) + class Atom(): """原子: 由电子(分析数据)和核子(材料元数据)组成的反应(运行时)中间对象""" @@ -18,7 +23,7 @@ class Electron(): real_rept = 0 # (实际)重复次数 rept = 0 # (有效)重复次数 interval = 0 # 最佳间隔 -ept', last_date = 0 # 上一次复习的时间戳 + last_date = 0 # 上一次复习的时间戳 next_date = 0 # 将要复习的时间戳 is_activated = 0 # 激活状态 # *NOTE: 这里的"时间戳" 是以天为单位的整数, 即 UNIX 时间戳除以一天的秒数取整 @@ -42,6 +47,47 @@ ept', last_date = 0 # 上一次复习的时间戳 setattr(self, var, value) self.last_modify = time.time() + def update(self, quality): + """ + 根据 quality(0 ~ 5) 进行参数迭代 + quality 由主程序评估 + """ + if quality == -1: + return -1 + self.efactor = self.efactor + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02)) + self.efactor = max(1.3, self.efactor) + + if quality < 3: + # 如果回忆质量低于 3,重置重复次数 + self.rept = 0 + self.interval = 0 # 设为0,以便下面重新计算I(1) + else: + self.rept += 1 + self.real_rept += 1 + + if self.rept == 0: # 刚被重置或首次遇到 + self.interval = 1 # I(1) + elif self.rept == 1: + self.interval = 6 # I(2) 经验公式 + else: + self.interval = round(self.interval * self.efactor) + + self.last_date = Aux.get_daystamp() + self.next_date = self.last_date + self.interval + + def __str__(self): + return (f"记忆单元预览 \n" + f"内容: '{self.content}' \n" + f"易度系数: {self.efactor:.2f} \n" + f"已经重复的次数: {self.rept} \n" + f"下次间隔: {self.interval} 天 \n" + f"下次复习日期时间戳: {self.next_date}") + + def __eq__(self, other): + if self.content == other.content: + return 1 + return 0 + def __hash__(self): return hash(self.content) @@ -80,4 +126,26 @@ class Nucleon(): def save_to_file(nucleon_list, path: pathlib.Path): with open(path, 'w') as f: toml.dump(nucleon_list, f) - \ No newline at end of file + +class AtomicFile(): + def __init__(self, path, type_="unknown"): + self.path = path + self.type_ = type_ + if type_ == "nucleon": + self.name, self.datalist = Nucleon.import_from_file(pathlib.Path(path)) + if type_ == "electron": + self.name, self.datalist = Electron.import_from_file(pathlib.Path(path)) + def save(self): + if self.type_ == "nucleon": + Nucleon.save_to_file(self.datalist, self.path) + if self.type_ == "electron": + Electron.save_to_file(self.datalist, self.path) + def get_full_content(self): + if self.type_ == "nucleon": + text = "" + for i in self.datalist: + text += i.content + return text + return "" + def get_len(self): + return len(self.datalist) \ No newline at end of file diff --git a/reactor.py b/reactor.py index e69de29..284b2ca 100644 --- a/reactor.py +++ b/reactor.py @@ -0,0 +1 @@ +class Reactor(tasked_num): \ No newline at end of file diff --git a/styles.tcss b/styles.tcss new file mode 100644 index 0000000..8e7c265 --- /dev/null +++ b/styles.tcss @@ -0,0 +1,55 @@ + +Screen { + align: center middle; +} + +#main_container { + align: center middle; + width: 80%; + height: auto; + border: thick $primary-lighten-2; + padding: 2; +} + +#sentence { + content-align: center middle; + width: 100%; + height: 5; + margin-bottom: 2; + text-style: bold; +} + +#progress { + width: 100%; + content-align: center middle; + margin-bottom: 1; + color: $text-muted; +} + + + +Button { + align-horizontal: center; + width: 50%; + margin: 0 0; +} +.choice { + align-horizontal: center; + width: 50%; + margin: 0 0; + height: auto; +} +/* no_margin.tcss */ + +#button_container { + align-horizontal: center; + height: 9; +} + +/* 选中 #button_container 下所有的 Horizontal 子元素 */ +#button_container > Horizontal { + margin-top: 0; + margin-bottom: 0; + align-horizontal: center; + width: 40%; +} \ No newline at end of file