fix:实现配置功能,实现园长一键签名功能
This commit is contained in:
157
config/config.py
157
config/config.py
@@ -1,130 +1,121 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
# 尝试导入 toml 解析库
|
||||
# 1. 处理读取库
|
||||
try:
|
||||
import tomllib as toml # Python 3.11+
|
||||
import tomllib as toml_read # Python 3.11+
|
||||
except ImportError:
|
||||
try:
|
||||
import tomli as toml # pip install tomli
|
||||
import tomli as toml_read
|
||||
except ImportError:
|
||||
print("错误: 缺少 TOML 解析库。请运行: pip install tomli")
|
||||
print("错误: 缺少 TOML 读取库。请运行: pip install tomli")
|
||||
sys.exit(1)
|
||||
|
||||
# 2. 处理写入库 (必须安装 pip install tomli-w)
|
||||
try:
|
||||
import tomli_w as toml_write
|
||||
except ImportError:
|
||||
# 如果没安装,提供一个 fallback 提示
|
||||
toml_write = None
|
||||
|
||||
def get_base_dir():
|
||||
"""
|
||||
获取程序运行的基准目录 (即 EXE 所在的目录 或 开发环境的项目根目录)
|
||||
用于确定 output_folder 等需要写入的路径。
|
||||
"""
|
||||
if getattr(sys, 'frozen', False):
|
||||
# 打包环境: EXE 所在目录
|
||||
return os.path.dirname(sys.executable)
|
||||
else:
|
||||
# 开发环境: 项目根目录 (假设此脚本在 utils/ 文件夹中,需要向上两级)
|
||||
# 假设当前文件在项目根目录或根目录下的某个文件夹中
|
||||
return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
def get_resource_path(relative_path):
|
||||
"""
|
||||
智能路径获取:
|
||||
1. 优先检查 EXE 旁边是否有该文件 (外部资源)。
|
||||
2. 如果没有,则使用 EXE 内部打包的资源 (内部资源)。
|
||||
"""
|
||||
# 1. 获取外部基准路径
|
||||
base_path = get_base_dir()
|
||||
|
||||
# 拼接外部路径
|
||||
external_path = os.path.join(base_path, relative_path)
|
||||
|
||||
# 如果外部文件存在,直接返回 (优先使用用户修改过的文件)
|
||||
if os.path.exists(external_path):
|
||||
return external_path
|
||||
|
||||
# 2. 如果外部不存在,且处于打包环境,则回退到内部临时目录 (sys._MEIPASS)
|
||||
if getattr(sys, 'frozen', False):
|
||||
# sys._MEIPASS 是 PyInstaller 解压临时文件的目录
|
||||
internal_path = os.path.join(sys._MEIPASS, relative_path)
|
||||
|
||||
if os.path.exists(internal_path):
|
||||
return internal_path
|
||||
|
||||
# 3. 默认返回外部路径
|
||||
# (如果都没找到,让报错信息指向外部路径,提示用户文件缺失)
|
||||
return external_path
|
||||
|
||||
|
||||
# ==========================================
|
||||
# 1. 配置加载 (Config Loader)
|
||||
# ==========================================
|
||||
def load_config(config_filename="config.toml"):
|
||||
"""读取 TOML 配置文件"""
|
||||
|
||||
# 1. 智能获取配置文件路径
|
||||
# (优先找 EXE 旁边的 config.toml,找不到则用打包在里面的)
|
||||
config_path = get_resource_path(config_filename)
|
||||
|
||||
if not os.path.exists(config_path):
|
||||
print(f"错误: 找不到配置文件 {config_filename}")
|
||||
print(f"尝试寻找的路径是: {config_path}")
|
||||
# 如果是打包环境,提示用户可能需要把 config.toml 复制出来
|
||||
if getattr(sys, 'frozen', False):
|
||||
print("提示: 请确保 config.toml 位于程序同级目录下,或已正确打包。")
|
||||
sys.exit(1)
|
||||
# 如果彻底找不到,返回一个最小化的默认值,防止程序奔溃
|
||||
return { "source_file": "", "ai": {"api_key": ""}, "teachers": [] }
|
||||
|
||||
try:
|
||||
with open(config_path, "rb") as f:
|
||||
data = toml.load(f)
|
||||
data = toml_read.load(f)
|
||||
|
||||
# 获取基准目录(用于 output_folder)
|
||||
base_dir = get_base_dir()
|
||||
|
||||
# 将 TOML 的层级结构映射回扁平结构
|
||||
# ⚠️ 注意:
|
||||
# - 读取类文件 (模板, Excel, 图片, 字体) 使用 get_resource_path (支持内外回退)
|
||||
# - 写入类文件夹 (output_folder) 使用 os.path.join(base_dir, ...) (必须在外部)
|
||||
# 使用 .get() 安全获取,防止 KeyError: 'paths'
|
||||
paths = data.get("paths", {})
|
||||
class_info = data.get("class_info", {})
|
||||
defaults = data.get("defaults", {})
|
||||
|
||||
config = {
|
||||
"root_path": base_dir,
|
||||
|
||||
# --- 资源文件 (使用智能路径) ---
|
||||
|
||||
# 假设 config.toml 里写的是 "report_template.pptx",文件在 templates 文件夹下
|
||||
"source_file": get_resource_path(
|
||||
os.path.join("templates", data["paths"]["source_file"])
|
||||
),
|
||||
|
||||
# 假设 config.toml 里写的是 "names.xlsx",文件在 data 文件夹下
|
||||
# 如果 config.toml 里写的是 "data/names.xlsx",则不需要 os.path.join("data", ...)
|
||||
"excel_file": get_resource_path(
|
||||
os.path.join("data", data["paths"]["excel_file"])
|
||||
),
|
||||
|
||||
"image_folder": get_resource_path(
|
||||
os.path.join("data", data["paths"]["image_folder"])
|
||||
),
|
||||
|
||||
"fonts_dir": get_resource_path(
|
||||
os.path.join(data["paths"]["fonts_dir"])
|
||||
),
|
||||
|
||||
# --- 输出文件夹 (必须强制在外部,不能指向临时目录) ---
|
||||
"output_folder": os.path.join(base_dir, data["paths"]["output_folder"]),
|
||||
|
||||
# --- 其他配置 ---
|
||||
"class_name": data["class_info"]["class_name"],
|
||||
"teachers": data["class_info"]["teachers"],
|
||||
"default_comment": data["defaults"].get("default_comment", "暂无评语"),
|
||||
"age_group": data["defaults"].get("age_group", "大班上学期"),
|
||||
"ai": data["ai"],
|
||||
# 扁平化映射
|
||||
"source_file": get_resource_path(os.path.join("templates", paths.get("source_file", ""))),
|
||||
"excel_file": get_resource_path(os.path.join("data", paths.get("excel_file", ""))),
|
||||
"image_folder": get_resource_path(os.path.join("data", paths.get("image_folder", ""))),
|
||||
"fonts_dir": get_resource_path(paths.get("fonts_dir", "fonts")),
|
||||
"output_folder": os.path.join(base_dir, paths.get("output_folder", "output")),
|
||||
"signature_image": get_resource_path(os.path.join("data", paths.get("signature_image", ""))),
|
||||
|
||||
"class_name": class_info.get("class_name", "未命名班级"),
|
||||
"teachers": class_info.get("teachers", []),
|
||||
"default_comment": defaults.get("default_comment", "暂无评语"),
|
||||
"age_group": defaults.get("age_group", "大班上学期"),
|
||||
"ai": data.get("ai", {"api_key": "", "api_url": "", "model": ""}),
|
||||
}
|
||||
return config
|
||||
|
||||
except KeyError as e:
|
||||
print(f"配置文件格式错误,缺少键值: {e}")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"读取配置文件出错: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
print(f"解析配置文件失败: {e}")
|
||||
return {}
|
||||
|
||||
# ==========================================
|
||||
# 2. 配置保存 (Config Saver)
|
||||
# ==========================================
|
||||
def save_config(config_data, config_filename="config.toml"):
|
||||
if not toml_write:
|
||||
return False, "未安装 tomli-w 库,无法保存。请运行 pip install tomli-w"
|
||||
|
||||
base_path = get_base_dir()
|
||||
save_path = os.path.join(base_path, config_filename)
|
||||
|
||||
try:
|
||||
# 将扁平化的数据重新打包成嵌套结构,以适配 load_config 的读取逻辑
|
||||
new_data = {
|
||||
"paths": {
|
||||
"source_file": os.path.basename(config_data.get("source_file", "")),
|
||||
"output_folder": os.path.basename(config_data.get("output_folder", "output")),
|
||||
"excel_file": os.path.basename(config_data.get("excel_file", "")),
|
||||
"image_folder": os.path.basename(config_data.get("image_folder", "")),
|
||||
"fonts_dir": os.path.basename(config_data.get("fonts_dir", "fonts")),
|
||||
"signature_image": get_resource_path(os.path.join("data", paths.get("signature_image", ""))),
|
||||
},
|
||||
"class_info": {
|
||||
"class_name": config_data.get("class_name", ""),
|
||||
"teachers": config_data.get("teachers", []),
|
||||
},
|
||||
"defaults": {
|
||||
"default_comment": config_data.get("default_comment", ""),
|
||||
"age_group": config_data.get("age_group", ""),
|
||||
},
|
||||
"ai": config_data.get("ai", {})
|
||||
}
|
||||
|
||||
# 写入文件
|
||||
with open(save_path, "wb") as f:
|
||||
f.write(toml_write.dumps(new_data).encode("utf-8"))
|
||||
|
||||
return True, f"成功保存到: {save_path}"
|
||||
|
||||
except Exception as e:
|
||||
return False, f"写入失败: {str(e)}"
|
||||
Reference in New Issue
Block a user