fix:优化启动方式

This commit is contained in:
2025-12-12 12:37:41 +08:00
parent 4d50c73ecb
commit 7275699c25
22 changed files with 449 additions and 398 deletions

View File

@@ -6,6 +6,8 @@ import platform
import shutil
from pathlib import Path
from loguru import logger
def get_system_fonts():
"""获取系统中可用的字体列表"""
@@ -21,7 +23,7 @@ def get_system_fonts():
for font_file in folder.glob(ext):
fonts.add(font_file.stem)
except Exception as e:
print(f"读取系统字体时出错: {e}")
logger.error(f"读取系统字体时出错: {e}")
fonts = {"微软雅黑", "宋体", "黑体", "Arial", "Microsoft YaHei"}
return fonts
@@ -40,14 +42,14 @@ def is_font_available(font_name):
def install_fonts_from_directory(fonts_dir="fonts"):
"""从指定目录安装字体到系统"""
if platform.system() != "Windows":
print("字体安装功能目前仅支持Windows系统")
logger.success("字体安装功能目前仅支持Windows系统")
return False
target_font_dir = Path.home() / 'AppData' / 'Local' / 'Microsoft' / 'Windows' / 'Fonts'
target_font_dir.mkdir(parents=True, exist_ok=True)
if not os.path.exists(fonts_dir):
print(f"字体目录 {fonts_dir} 不存在,请创建该目录并将字体文件放入")
logger.error(f"字体目录 {fonts_dir} 不存在,请创建该目录并将字体文件放入")
return False
font_files = []
@@ -55,7 +57,7 @@ def install_fonts_from_directory(fonts_dir="fonts"):
font_files.extend(Path(fonts_dir).glob(f'*{ext}'))
if not font_files:
print(f"{fonts_dir} 目录中未找到字体文件")
logger.error(f"{fonts_dir} 目录中未找到字体文件")
return False
installed_count = 0
@@ -64,12 +66,12 @@ def install_fonts_from_directory(fonts_dir="fonts"):
target_path = target_font_dir / font_file.name
if not target_path.exists():
shutil.copy2(font_file, target_path)
print(f"已安装字体: {font_file.name}")
logger.success(f"已安装字体: {font_file.name}")
installed_count += 1
except Exception as e:
print(f"安装字体 {font_file.name} 时出错: {str(e)}")
logger.error(f"安装字体 {font_file.name} 时出错: {str(e)}")
if installed_count > 0:
print(f"共安装了 {installed_count} 个新字体文件建议重启Python环境")
logger.success(f"共安装了 {installed_count} 个新字体文件建议重启Python环境")
return True
return False

View File

@@ -6,6 +6,7 @@ import pandas as pd
from loguru import logger
from pptx import Presentation
from rich.console import Console
import traceback
import comtypes.client
from config.config import load_config
@@ -65,8 +66,6 @@ def generate_template():
except Exception as e:
logger.error(f"程序运行出错: {str(e)}")
# 打印详细报错位置,方便调试
import traceback
logger.error(traceback.format_exc())
@@ -83,11 +82,10 @@ def generate_comment_all():
if "评价" not in df.columns:
df["评价"] = ""
# --- 获取总行数,用于日志 ---
# 强制将“评价”列转换为 object 类型
# 获取学生数据行数
total_count = len(df)
logger.info(f"开始生成学生评语,共 {total_count} 位学生...")
# 强制将“评价”列转换为 object 类型
df["评价"] = df["评价"].astype("object")
# --- 遍历 DataFrame 的索引 (index) ---
# 这样我们可以通过索引 i 精准地把数据写回某一行
@@ -121,28 +119,27 @@ def generate_comment_all():
logger.info(f"[{i + 1}/{total_count}] 正在生成评价: {name}")
try:
# 调用你的生成函数,并【接收返回值】
# 注意:这里假设 generate_comment 返回的是清洗后的字符串
# 调用AI大模型生成内容
generated_text = generate_comment(
name, config["age_group"], traits, sex
)
# --- 将结果写入 DataFrame ---
df.at[i, "评价"] = generated_text
if generated_text:
# 赋值
df.at[i, "评价"] = str(generated_text).strip()
else:
df.at[i, "评价"] = "" # 防空处理
logger.success(f"学生:{name},评语生成完毕")
# 可选:每生成 5 个就保存一次,防止程序崩溃数据丢失
# 可选:每生成 5 个就保存一次
if (i + 1) % 5 == 0:
df.to_excel(excel_path, index=False)
logger.info("--- 阶段性保存成功 ---")
time.sleep(1) # 避免触发API速率限制
logger.success(" 阶段性保存成功")
# 避免触发API速率限制
time.sleep(1)
except Exception as e:
logger.error(f"学生:{name},生成评语出错: {str(e)}")
# --- 修改点 4: 循环结束后最终保存文件 ---
# --- 循环结束后最终保存文件 ---
# index=False 表示不把 pandas 的索引 (0,1,2...) 写到 Excel 第一列
df.to_excel(excel_path, index=False)
logger.success(f"所有评语已生成并写入文件:{excel_path}")
@@ -151,8 +148,6 @@ def generate_comment_all():
logger.error(f"保存失败!请先关闭 Excel 文件:{config['excel_file']}")
except Exception as e:
logger.error(f"程序运行出错: {str(e)}")
import traceback
logger.error(traceback.format_exc())
@@ -306,9 +301,6 @@ def generate_report():
except Exception as e:
logger.error(f"程序运行出错: {str(e)}")
# 打印详细报错位置,方便调试
import traceback
logger.error(traceback.format_exc())
@@ -320,9 +312,8 @@ def batch_convert_folder(folder_path):
【推荐】批量转换文件夹下的所有 PPT (只启动一次 PowerPoint速度快)
已修复多线程 CoInitialize 报错,并适配 GUI 日志
"""
# 【核心修复 1】子线程初始化 COM 组件
# 子线程初始化 COM 组件
pythoncom.CoInitialize()
try:
folder_path = os.path.abspath(folder_path)
if not os.path.exists(folder_path):
@@ -429,6 +420,4 @@ def generate_zodiac():
logger.error(f"找不到文件 {config.get('excel_file')}")
except Exception as e:
logger.error(f"程序运行出错: {str(e)}")
import traceback
logger.error(traceback.format_exc())

View File

@@ -1,4 +1,6 @@
import os
from PIL import Image, ExifTags
import io
def find_image_path(folder, base_filename):
@@ -26,3 +28,39 @@ def find_image_path(folder, base_filename):
return full_path
return None
def get_corrected_image_stream(img_path):
"""
读取图片,根据 EXIF 信息修正旋转方向,并返回 BytesIO 对象。
这样不需要修改原文件,直接在内存中处理。
"""
image = Image.open(img_path)
# 获取 EXIF 数据
try:
for orientation in ExifTags.TAGS.keys():
if ExifTags.TAGS[orientation] == 'Orientation':
break
exif = dict(image._getexif().items())
if exif[orientation] == 3:
image = image.rotate(180, expand=True)
elif exif[orientation] == 6:
image = image.rotate(270, expand=True)
elif exif[orientation] == 8:
image = image.rotate(90, expand=True)
except (AttributeError, KeyError, IndexError):
# 如果图片没有 EXIF 数据或不需要旋转,则忽略
pass
# 将处理后的图片保存到内存流中
image_stream = io.BytesIO()
# 注意:保存时保持原格式,如果是 PNG 等无 EXIF 的格式会自动处理
img_format = image.format if image.format else 'JPEG'
image.save(image_stream, format=img_format)
image_stream.seek(0) # 指针回到开头
return image_stream

27
utils/log_handler.py Normal file
View File

@@ -0,0 +1,27 @@
import queue
import re
from loguru import logger
# 全局日志队列
log_queue = queue.Queue()
def ansi_cleaner(text):
"""去除 loguru 输出中的颜色代码,防止在 UI 显示乱码"""
ansi_escape = re.compile(r"\x1b\[[0-9;]*m")
return ansi_escape.sub("", text)
def queue_sink(message):
"""Loguru 的回调函数"""
clean_msg = ansi_cleaner(message)
log_queue.put(clean_msg)
def setup_logging():
"""配置日志系统"""
# 清除默认的控制台输出,防止干扰
logger.remove()
# 添加队列输出 (给 UI 用)
logger.add(queue_sink, format="{time:HH:mm:ss} | {level: <8} | {message}", level="INFO")
# 添加文件输出 (给开发者排查用)
# logger.add("logs/app_runtime.log", rotation="1 MB", encoding="utf-8", level="DEBUG")

View File

@@ -3,7 +3,10 @@
# ==========================================
import os
from loguru import logger
from utils.font_utils import is_font_available
from utils.image_utils import get_corrected_image_stream
def replace_text_in_slide(prs, slide_index, placeholder, text):
@@ -94,18 +97,36 @@ def replace_text_in_slide(prs, slide_index, placeholder, text):
def replace_picture(prs, slide_index, placeholder, img_path):
"""在指定幻灯片中替换指定占位符的图片"""
"""在指定幻灯片中替换指定占位符的图片(包含自动旋转修复)"""
if not os.path.exists(img_path):
print(f"警告: 图片路径不存在 {img_path}")
logger.warning(f"警告: 图片路径不存在 {img_path}")
return
slide = prs.slides[slide_index]
sp_tree = slide.shapes._spTree
target_shape = None
target_index = -1
# 1. 先找到目标形状和它的索引
for i, shape in enumerate(slide.shapes):
if shape.name == placeholder:
left, top, width, height = shape.left, shape.top, shape.width, shape.height
sp_tree.remove(shape._element)
new_shape = slide.shapes.add_picture(img_path, left, top, width, height)
sp_tree.insert(i, new_shape._element)
target_shape = shape
target_index = i
break
if target_shape:
# 获取原位置信息
left, top, width, height = target_shape.left, target_shape.top, target_shape.width, target_shape.height
# 2. 获取修正后的图片流
img_stream = get_corrected_image_stream(img_path)
# 3. 移除旧形状
sp_tree.remove(target_shape._element)
# 4. 插入新图片 (使用流而不是路径)
new_shape = slide.shapes.add_picture(img_stream, left, top, width, height)
# 5. 恢复层级位置 (z-order)
sp_tree.insert(target_index, new_shape._element)