feat: 代码格式化, 改进仪表盘, 新增多CSS支持
This commit is contained in:
@@ -35,6 +35,8 @@ class DashboardScreen(Screen):
|
||||
("q", "go_back", "返回"),
|
||||
]
|
||||
|
||||
CSS_PATH = Path(__file__).parent.parent / 'css' / "screens" / "dashboard.tcss"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str | None = None,
|
||||
@@ -42,97 +44,91 @@ class DashboardScreen(Screen):
|
||||
classes: str | None = None,
|
||||
) -> None:
|
||||
super().__init__(name, id, classes)
|
||||
self.repostat = {}
|
||||
self.title2dirname = {}
|
||||
self.title2repo = {}
|
||||
self.dirname2repo = {}
|
||||
self._load_data()
|
||||
self.repolink = {}
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
"""组合界面组件"""
|
||||
self._load_data()
|
||||
yield Header(show_clock=True)
|
||||
with ScrollableContainer():
|
||||
yield Horizontal(
|
||||
yield Horizontal( # 顶部的状态
|
||||
Vertical(
|
||||
Label(f'欢迎使用 "潜进" 版本 {version.ver}'),
|
||||
Label(f"当前 UNIX 日时间戳: {timer.get_daystamp()}"),
|
||||
Label(
|
||||
f"当前 UNIX 日时间戳: {timer.get_daystamp()}"
|
||||
f"应用时区修正: UTC+{config_var.get()['services']['timer']['timezone_offset'] / 3600}"
|
||||
),
|
||||
Label(f"应用时区修正: UTC+{config_var.get()['services']['timer']['timezone_offset'] / 3600}"),
|
||||
Label(f"全局算法设置: {config_var.get()['interface']['global']['algorithm']}: {algorithms[config_var.get()['interface']['global']['algorithm']].desc}"),
|
||||
classes="column infview",
|
||||
Label(
|
||||
f"默认算法设置: {config_var.get()['interface']['global']['algorithm']}"
|
||||
),
|
||||
classes="left",
|
||||
),
|
||||
Vertical(
|
||||
Label(f"已加载 {len(self.repostat)} 个单元集", classes='dataview'),
|
||||
Label(f"共计 {reduce(lambda x, y: x + y, map(lambda x: x.get('unit_sum'), self.repostat.values()))} 个单元", classes='dataview'),
|
||||
Label(f"已激活 {reduce(lambda x, y: x + y, map(lambda x: x.get('activated_sum'), self.repostat.values()))} 个单元", classes='dataview'),
|
||||
Label(f"已加载 {len(self.repos)} 个单元集"),
|
||||
Label(
|
||||
f"共计 {reduce(lambda x, y: x + y, map(lambda x: x.progress['total'], self.repos))} 个单元"
|
||||
),
|
||||
Label(
|
||||
f"已激活 {reduce(lambda x, y: x + y, map(lambda x: x.progress['touched'], self.repos))} 个单元"
|
||||
),
|
||||
Label(f""),
|
||||
classes="column dataview",
|
||||
classes="right",
|
||||
),
|
||||
id="dashboardtop"
|
||||
id="header",
|
||||
)
|
||||
|
||||
yield ListView(id="repo-list", classes="repo-list-view")
|
||||
yield Label(f'"潜进" 启发式辅助记忆调度器 版本 {version.ver} {version.stage.capitalize()}')
|
||||
yield ListView(id="repo_list", classes="repo-list") # 单元集选择
|
||||
|
||||
yield Label(
|
||||
f'"潜进" 启发式辅助记忆调度器 版本 {version.ver} {version.stage.capitalize()}'
|
||||
) # 版本信息
|
||||
yield Footer()
|
||||
|
||||
def _load_data(self):
|
||||
self.repo_dirs = Repo.probe_valid_repos_in_dir(
|
||||
Path(config_var.get()['global']["paths"]["data"]) / "repo"
|
||||
repo_dirs = Repo.probe_valid_repos_in_dir(
|
||||
Path(config_var.get()["global"]["paths"]["repo"])
|
||||
)
|
||||
for repo_dir in self.repo_dirs:
|
||||
repo = Repo.create_from_repodir(repo_dir)
|
||||
self.repos = list(map(Repo.from_repodir, repo_dirs))
|
||||
for repo in self.repos:
|
||||
self._analyse_repo(repo)
|
||||
|
||||
def _analyse_repo(self, repo: Repo):
|
||||
dirname = repo.source.name # type: ignore
|
||||
title = repo.manifest["title"]
|
||||
is_due = 0
|
||||
unit_sum = len(repo)
|
||||
activated_sum = 0
|
||||
nextdate = float('inf')
|
||||
for i in repo.ident_index:
|
||||
nucleon = pt.Nucleon.create_on_nucleonic_data(
|
||||
nucleonic_data=repo.nucleonic_data_lict.get_itemic_unit(i)
|
||||
)
|
||||
electron = pt.Electron.create_on_electonic_data(
|
||||
electronic_data=repo.electronic_data_lict.get_itemic_unit(i),
|
||||
algo_name=config_var.get()['repo'][repo.manifest['title']]['algorithm']
|
||||
)
|
||||
if electron.is_activated():
|
||||
activated_sum += 1
|
||||
if electron.is_due():
|
||||
is_due = 1
|
||||
nextdate = min(nextdate, electron.nextdate())
|
||||
is_unfinished = unit_sum > activated_sum
|
||||
if is_unfinished:
|
||||
nextdate = min(nextdate, timer.get_daystamp())
|
||||
need_to_study = is_due or is_unfinished
|
||||
prompt = f"{title}\0\n 进度: {activated_sum}/{unit_sum} ({round(activated_sum/unit_sum*100)}%)\n {'需要学习' if need_to_study else '无需操作'}"
|
||||
stat = {
|
||||
"is_due": is_due,
|
||||
"unit_sum": unit_sum,
|
||||
"title": title,
|
||||
"activated_sum": activated_sum,
|
||||
"nextdate": nextdate,
|
||||
"is_unfinished": is_unfinished,
|
||||
"need_to_study": need_to_study,
|
||||
"prompt": prompt,
|
||||
"dirname": dirname,
|
||||
# need_review: 需要/不需要学习
|
||||
# nearest_review_time: 最近下次学习时间
|
||||
# progress: 进度
|
||||
# algotype: 算法类型
|
||||
## initial_time: 起始时间
|
||||
# package: 包名
|
||||
# prompt: 最终呈现信息
|
||||
repo.package = repo.manifest["package"]
|
||||
repo.nearest_review_time = float("inf")
|
||||
repo.progress = {
|
||||
"total": repo.data_length,
|
||||
"touched": 0,
|
||||
}
|
||||
self.repostat[dirname] = stat
|
||||
self.title2dirname[title] = dirname
|
||||
self.title2repo[title] = repo
|
||||
self.dirname2repo[dirname] = repo
|
||||
initial_time = float("inf")
|
||||
for i in range(repo.data_length):
|
||||
e = pt.Electron.from_data(repo.electronic_data_lict[i])
|
||||
n = pt.Nucleon.from_data(repo.nucleonic_data_lict[i])
|
||||
if e.is_activated():
|
||||
repo.algotype = e.algoname
|
||||
repo.progress["touched"] += 1
|
||||
repo.nearest_review_time = min(repo.nearest_review_time, e.nextdate())
|
||||
# initial_time = min(initial_time, e.)
|
||||
repo.need_review = timer.get_daystamp() >= repo.nearest_review_time
|
||||
repo.prompt = f"""{repo.manifest['title']} ({repo.algotype})
|
||||
进度: {repo.progress['touched']}/{repo.progress['total']} ({round(repo.progress['touched']/repo.progress['total']*100, 1)}%)
|
||||
{'需要学习' if repo.need_review else "无需操作"}
|
||||
"""
|
||||
|
||||
def on_mount(self) -> None:
|
||||
"""挂载组件时初始化"""
|
||||
repo_list_widget = self.query_one("#repo-list", ListView)
|
||||
repo_list_widget = self.query_one("#repo_list", ListView)
|
||||
|
||||
# 按下次复习时间排序
|
||||
repodirs = sorted(
|
||||
self.repo_dirs,
|
||||
key=lambda f: self.repostat[f.name]["nextdate"],
|
||||
self.repos,
|
||||
key=lambda r: r.nearest_review_time,
|
||||
reverse=True,
|
||||
)
|
||||
repotitles = map(lambda f: self.repostat[f.name]["title"], repodirs)
|
||||
@@ -141,43 +137,44 @@ class DashboardScreen(Screen):
|
||||
repo_list_widget.append(
|
||||
ListItem(
|
||||
Static(
|
||||
"在 ./data/repo/ 中未找到任何仓库.\n"
|
||||
f"在 {config_var.get()['global']['paths']['repo']} 中未找到任何仓库.\n"
|
||||
"请导入仓库后重启应用, 或者新建空的仓库."
|
||||
)
|
||||
),
|
||||
id="not-found",
|
||||
)
|
||||
)
|
||||
repo_list_widget.disabled = True
|
||||
return
|
||||
|
||||
for repotitle in repotitles:
|
||||
prompt = self.repostat[self.title2dirname[repotitle]]["prompt"]
|
||||
list_item = ListItem(Label(prompt), Button(f"开始学习", flat=True, variant="primary", classes="repo_listitem_btn", id=f"launch_{self.repostat[self.title2dirname[repotitle]]['dirname']}"), classes="repo_listitem")
|
||||
for r in self.repos:
|
||||
self.repolink[str(id(r))] = r # 用于规避 ctype id 对象还原
|
||||
list_item = ListItem(
|
||||
Label(r.prompt),
|
||||
Button(
|
||||
f"开始学习",
|
||||
flat=True,
|
||||
variant="primary",
|
||||
id=f"slaunch_repo_{id(r)}",
|
||||
classes="repo-list-item-shortcut",
|
||||
),
|
||||
classes="repo-list-item",
|
||||
id=f"launch_repo_{id(r)}",
|
||||
)
|
||||
repo_list_widget.append(list_item)
|
||||
|
||||
# if not self.stay_enabled[repodir]:
|
||||
# list_item.disabled = True
|
||||
|
||||
def on_list_view_selected(self, event) -> None:
|
||||
"""处理列表项选择事件"""
|
||||
if not isinstance(event.item, ListItem):
|
||||
return
|
||||
|
||||
selected_label = event.item.query_one(Label)
|
||||
label_text = str(selected_label.render())
|
||||
|
||||
if "未找到任何仓库" in label_text:
|
||||
if "not-found" == event.item.id:
|
||||
return
|
||||
|
||||
# 提取文件名
|
||||
selected_repotitle = label_text.partition("\0")[0].replace("*", "")
|
||||
selected_repo = self.title2repo[label_text.partition("\0")[0].replace("*", "")]
|
||||
# 还原对象
|
||||
selected_repo = self.repolink[event.item.id.lstrip("launch_repo_")]
|
||||
|
||||
# 跳转到准备屏幕
|
||||
self.app.push_screen(
|
||||
PreparationScreen(
|
||||
selected_repo, self.repostat[self.title2dirname[selected_repotitle]]
|
||||
)
|
||||
)
|
||||
self.app.push_screen(PreparationScreen(selected_repo))
|
||||
|
||||
def action_quit_app(self) -> None:
|
||||
"""退出应用程序"""
|
||||
@@ -188,9 +185,10 @@ class DashboardScreen(Screen):
|
||||
self.app.push_screen(NavigatorScreen())
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
logger.debug(f"event.button.id: {event.button.id}")
|
||||
"""处理按钮点击事件"""
|
||||
if str(event.button.id).startswith("launch_"): # type: ignore
|
||||
logger.debug(f"event.button.id: {event.button.id}")
|
||||
if event.button.id.startswith("slaunch_repo_"): # type: ignore
|
||||
from .preparation import launch
|
||||
launch(repo=self.dirname2repo[event.button.id[7:]], app=self.app, scheduled_num=-1) # type: ignore
|
||||
|
||||
launch(repo=self.repolink[event.button.id.lstrip("slaunch_repo_")], app=self.app, scheduled_num=-1) # type: ignore
|
||||
# TODO: 这样启动的记忆实例的状态机无法绑定到 PreparationScreen 中
|
||||
|
||||
Reference in New Issue
Block a user