feat: 补全设置提示与完善设置页

This commit is contained in:
2026-04-20 05:05:44 +08:00
parent b3f95861f0
commit 41af2ada45
23 changed files with 114 additions and 36 deletions

6
data/config/_.toml Normal file
View File

@@ -0,0 +1,6 @@
_services_desc = '服务模块(services)设置'
_providers_desc = '驱动模块(providers)设置'
_repo_desc = '单元集独立设置'
_interface_desc = '基本用户界面设置'
_default_desc = '默认设置'
_global_desc = '底层设置'

View File

@@ -1,7 +1,13 @@
enable_built_in_interface = true
_enable_built_in_interface_desc = "启用内置基本用户界面\n(当且仅当 HeurAMS 作为程序库时禁用, 以跳过用户界面逻辑)"
_paths_desc = "用户数据路径定义"
[paths]
data = "./data"
_data_desc = "用户数据根目录"
cache = "./data/cache"
_cache_desc = "缓存根目录\n(如音频缓存在 voice 子目录)"
config = "./data/config"
_config_desc = "配置文件根目录"
repo = "./data/repo"
_repo_desc = "记忆单元集根目录"

View File

@@ -0,0 +1,4 @@
_global_desc = "用户界面通用设置"
_widgets_desc = "各组件设置"
_screens_desc = "各界面设置"
_puzzles_desc = "谜题生成器设置"

View File

@@ -1,6 +1,17 @@
persist_to_file = true
_persist_to_file_desc = "[调试] 将记忆更改保存到文件"
quick_pass = true
_quick_pass_desc = "[调试] 启用快速应答功能(跳过测验)"
auto_pass = false
_auto_pass_desc = "[调试] 自动通过测试模式"
scheduled_num = 420
_scheduled_num_desc = "默认记忆单元数量(可被单元集设置覆盖)"
algorithm = "NSP-0"
_algorithm_candidate = [ "SM-2", "SM-15M", "FSRS", "NSP-0", "None",]
_algorithm_desc = "默认记忆调度算法(可被单元集设置覆盖)"
[_algorithm_candidate]
NSP-0 = "筛选用非间隔重复调度器"
none = "不设置默认调度器"
SM-2 = "第二代 SuperMemo 简单间隔重复调度器"
SM-15M = "第15代 SuperMemo 复杂间隔重复调度器 (不稳定且逆向工程)"
FSRS = "先进开放间隔重复调度器"

View File

@@ -0,0 +1,2 @@
_cloze_desc = "填空题"
_mcq_desc = "选择题"

View File

@@ -1 +1,2 @@
min_denominator = 3
_min_denominator_desc = "设空比例系数的倒数"

View File

@@ -1 +1,2 @@
max_riddles_num = 2
_max_riddles_num_desc = "单次生成的最大谜题数量"

View File

@@ -1 +0,0 @@
autovoice = true

View File

@@ -0,0 +1 @@
_recognition_desc = "用于 '辨识' 组件的设置"

View File

@@ -1 +1,2 @@
autovoice = true
_autovoice_desc = "自动语音播放"

View File

@@ -0,0 +1 @@
_tts_desc = '文本转语音驱动'

View File

@@ -0,0 +1 @@
_edgetts_desc = "微软文本转语音驱动"

View File

@@ -1,4 +1,5 @@
voice = "zh-CN-XiaoxiaoNeural"
_voice_desc = "音色"
[_voice_candidate]
zh-CN-XiaoxiaoNeural = "晓晓: 中文温柔女声"

View File

@@ -0,0 +1,5 @@
_audio_desc = '音频服务'
_llm_desc = '语言模型服务'
_sync_desc = '数据同步服务'
_timer_desc = '时间服务'
_tts_desc = '文本转语音服务'

View File

@@ -1,4 +1,5 @@
provider = "playsound"
_provider_desc = "音频驱动类型"
[_provider_candidate]
playsound = "python 跨平台音频系统"

View File

@@ -1,4 +1,5 @@
provider = "openai"
_provider_desc = "模型接口类型"
[_provider_candidate]
openai = "OpenAI 风格 API, 同时支持与其相容的模型服务 (如 deepseek)"

View File

@@ -1,4 +1,5 @@
provider = "webdav"
_provider_desc = "同步服务驱动类型"
[_provider_candidate]
webdav = "WebDAV 兼容网络文件系统 (包括 webdavs)"

View File

@@ -1,3 +1,6 @@
daystamp_override = -1
_daystamp_override_desc = "[调试] 覆写 UNIX 日时间戳, 单位为日\n(设为 -1 禁用)"
timestamp_override = -1
_timestamp_override_desc = "[调试] 覆写 UNIX 时间戳, 单位为秒\n(设为 -1 禁用)"
timezone_offset = 28800
_timezone_offset_desc = "时区偏移设置, 用于取消跨天时区误差, 单位为秒\n(如 28800 为 UTC+8.0, 中国标准时间)"

View File

@@ -1,4 +1,5 @@
provider = "edgetts"
_provider_desc = "文本转语音驱动类型"
[_provider_candidate]
edgetts = "微软神经网络语音合成, 依赖微软网络服务"

View File

@@ -1,4 +0,0 @@
[aa.bb]
str = "sq"
boolean = true
int = 123

View File

@@ -39,7 +39,10 @@ NavigatorScreen {
margin-top: 1;
align: center middle;
}
.container {
height: auto;
padding: 0 0 1 0;
}
#message-input {
width: 1fr;
margin-right: 1;

View File

@@ -8,7 +8,7 @@ import os
from textual.app import ComposeResult
from textual.containers import ScrollableContainer, Container, Horizontal, Vertical
from textual.screen import Screen
from textual.widgets import Button, Footer, Header, Label, ListItem, ListView, Static, Collapsible, Input, Switch
from textual.widgets import Button, Footer, Header, Label, ListItem, ListView, Static, Collapsible, Input, Switch, Select
from textual.layouts import horizontal
import heurams.kernel.particles as pt
@@ -45,9 +45,13 @@ class SettingScreen(Screen):
"""组合界面组件"""
yield Header(show_clock=True)
with ScrollableContainer():
yield Label('设置页面')
yield Label('[b]设置页面[/b]')
for i in config_var.get():
yield Collapsible(*self._get_subcfg(f'{i}'), title=i)
if i.startswith('_'):
continue
a = self._get_subcfg(f'{i}')
if a:
yield Collapsible(*a, title=i + f'\n{config_var.get().get(f"_{i}_desc", "")}')
yield Footer()
def _get_subcfg(self, parent_epath: str):
@@ -56,7 +60,11 @@ class SettingScreen(Screen):
if parent.is_dir:
lst = list()
for i in parent:
lst.append(Collapsible(*self._get_subcfg(f"{parent_epath}.{i}"), title=i))
if i.startswith('_'):
continue
a = self._get_subcfg(f"{parent_epath}.{i}")
if a:
lst.append(Collapsible(*a, title=i + f'\n{parent.get(f"_{i}_desc", "")}'))
return lst
if isinstance(parent, dict) or (isinstance(parent, ConfigDict) and not parent.is_dir):
lst = list()
@@ -64,32 +72,47 @@ class SettingScreen(Screen):
if i.startswith('_'):
continue
if isinstance(parent[i], dict):
lst.append(Collapsible(*self._get_subcfg(f"{parent_epath}.{i}"), title=i))
elif isinstance(parent[i], float):
lst.extend([
Label(i),
Input(value=str(parent[i]), placeholder='要求一个浮点数', type='number', id=domize(f"{parent_epath}.{i}"))
])
a = self._get_subcfg(f"{parent_epath}.{i}")
if a:
lst.append(Collapsible(*a, title=i + f'\n{parent.get(f"_{i}_desc", "")}'))
elif f'_{i}_candidate' in parent:
if isinstance(parent[f'_{i}_candidate'], dict):
lst.append(Horizontal(
Label(i + f'\n{parent.get(f"_{i}_desc", "")}'),
Select((f"{j} ({k})", j) for j, k in parent[f'_{i}_candidate'].items()),
classes='container'
))
elif isinstance(parent[f'_{i}_candidate'], list):
lst.append(Horizontal(
Label(i + f'\n{parent.get(f"_{i}_desc", "")}'),
Select((j, j) for j in parent[f'_{i}_candidate']),
classes='container'
))
else:
if isinstance(parent[i], float):
lst.append(Horizontal(
Label(i + f'\n{parent.get(f"_{i}_desc", "")}'),
Input(value=str(parent[i]), placeholder='要求一个浮点数', type='number', id=domize(f"{parent_epath}.{i}")),
classes='container'))
elif isinstance(parent[i], str):
lst.extend([
Label(i),
Input(value=parent[i], placeholder='要求一个字符串', type='text', id=domize(f"{parent_epath}.{i}"))
])
lst.append(Horizontal(
Label(i + f'\n{parent.get(f"_{i}_desc", "")}'),
Input(value=parent[i], placeholder='要求一个字符串', type='text', id=domize(f"{parent_epath}.{i}")),
classes='container'))
elif isinstance(parent[i], bool):
lst.extend([
Label(i),
Switch(value=str(parent[i]), id=domize(f"{parent_epath}.{i}"))
])
lst.append(Horizontal(
Label(i + f'\n{parent.get(f"_{i}_desc", "")}'),
Switch(value=str(parent[i]), id=domize(f"{parent_epath}.{i}")),
classes='container'))
elif isinstance(parent[i], int):
lst.extend([
Label(i),
Input(value=str(parent[i]), placeholder='要求一个整数', type='integer', id=domize(f"{parent_epath}.{i}"))
])
lst.append(Horizontal(
Label(i + f'\n{parent.get(f"_{i}_desc", "")}'),
Input(value=str(parent[i]), placeholder='要求一个整数', type='integer', id=domize(f"{parent_epath}.{i}")),
classes='container'))
elif isinstance(parent[i], list):
pass
else:
lst.append(Label('未知类型'))
return lst
return [Label('无子项')]
@@ -97,6 +120,10 @@ class SettingScreen(Screen):
"""挂载组件时初始化"""
pass
def action_go_back(self) -> None:
"""返回上一屏幕"""
self.app.pop_screen()
def action_quit_app(self) -> None:
"""退出应用程序"""
self.app.exit()

View File

@@ -56,6 +56,11 @@ class ConfigDict(UserDict): # 舒服了
def update_index(self): # 如果有人没事干在config里面创建指向config的符号链接 这玩意会崩溃 但是不要修复: 需要这个符号链接特性
for i in self.path.iterdir():
if i.name.startswith('_'):
if i.name == '_.toml' and not i.is_dir():
with open(self.path/'_.toml', 'r+') as f:
self.data.update(dict(toml.load(f)))
continue
if i.is_dir():
self.data[i.name] = i
else: