fix:实现基础功能

This commit is contained in:
2025-12-10 22:36:12 +08:00
commit 39aa02c74e
35 changed files with 2990 additions and 0 deletions

415
old/main.py Normal file
View File

@@ -0,0 +1,415 @@
import os
import platform
import shutil
import sys
import time
from pathlib import Path
import pandas as pd
from pptx import Presentation
from pptx.util import Pt
def get_system_fonts():
"""获取系统中可用的字体列表"""
fonts = set()
if platform.system() == "Windows":
try:
# 读取Windows字体目录
system_fonts_dir = Path(os.environ['WINDIR']) / 'Fonts'
user_fonts_dir = Path.home() / 'AppData' / 'Local' / 'Microsoft' / 'Windows' / 'Fonts'
# 检查系统字体目录
if system_fonts_dir.exists():
for font_file in system_fonts_dir.glob('*.ttf'):
fonts.add(font_file.stem)
for font_file in system_fonts_dir.glob('*.ttc'):
fonts.add(font_file.stem)
for font_file in system_fonts_dir.glob('*.otf'):
fonts.add(font_file.stem)
# 检查用户字体目录
if user_fonts_dir.exists():
for font_file in user_fonts_dir.glob('*.ttf'):
fonts.add(font_file.stem)
for font_file in user_fonts_dir.glob('*.ttc'):
fonts.add(font_file.stem)
for font_file in user_fonts_dir.glob('*.otf'):
fonts.add(font_file.stem)
except Exception as e:
print(f"读取系统字体时出错: {e}")
# 备选方案:返回常见字体
fonts = {"微软雅黑", "宋体", "黑体", "楷体", "仿宋", "Arial", "Times New Roman", "Courier New",
"Microsoft YaHei"}
return fonts
def is_font_available(font_name):
"""检查字体是否在系统中可用"""
system_fonts = get_system_fonts()
# 检查字体名称的多种可能形式
check_names = [font_name, font_name.replace(" ", ""), font_name.replace("-", ""), font_name.lower(),
font_name.upper()]
for name in check_names:
if name in system_fonts:
return True
return False
def install_fonts_from_directory(fonts_dir="fonts"):
"""从指定目录安装字体到系统"""
if platform.system() != "Windows":
print("字体安装功能目前仅支持Windows系统")
return False
# 获取系统字体目录
user_fonts_dir = Path.home() / 'AppData' / 'Local' / 'Microsoft' / 'Windows' / 'Fonts'
# 优先使用用户字体目录(不需要管理员权限)
target_font_dir = user_fonts_dir
# 创建目标目录(如果不存在)
target_font_dir.mkdir(parents=True, exist_ok=True)
# 检查字体目录是否存在
if not os.path.exists(fonts_dir):
print(f"字体目录 {fonts_dir} 不存在,请创建该目录并将字体文件放入")
return False
# 遍历字体目录中的字体文件
font_extensions = ['.ttf', '.ttc', '.otf', '.fon']
font_files = []
for ext in font_extensions:
font_files.extend(Path(fonts_dir).glob(f'*{ext}'))
if not font_files:
print(f"{fonts_dir} 目录中未找到字体文件 (.ttf, .ttc, .otf, .fon)")
return False
# 安装字体文件
installed_fonts = []
for font_file in font_files:
try:
# 复制字体文件到系统字体目录
target_path = target_font_dir / font_file.name
if not target_path.exists(): # 避免重复安装
shutil.copy2(font_file, target_path)
print(f"已安装字体: {font_file.name}")
installed_fonts.append(font_file.name)
else:
pass
# print(f"字体已存在: {font_file.name}") # 减少日志输出
except Exception as e:
print(f"安装字体 {font_file.name} 时出错: {str(e)}")
continue
if installed_fonts:
print(f"共安装了 {len(installed_fonts)} 个新字体文件")
print("注意新安装的字体可能需要重启Python环境后才能在PowerPoint中使用")
return True
else:
print("没有安装新字体文件,可能已存在于系统中")
return False
def replace_text_in_slide(prs, slide_index, placeholder, text):
"""
在指定幻灯片中替换指定占位符的文本,并保持原有格式。
:param prs: Presentation 对象
:param slide_index: 要操作的幻灯片索引从0开始
:param placeholder: 占位符名称 (shape.name)
:param text: 要替换的文本
"""
slide = prs.slides[slide_index]
for shape in slide.shapes:
if shape.name == placeholder:
if not shape.has_text_frame:
continue
# --- 1. 保存原有格式信息 ---
original_paragraph_formats = []
for paragraph in shape.text_frame.paragraphs:
paragraph_format = {
'alignment': paragraph.alignment,
'space_before': getattr(paragraph, 'space_before', None),
'space_after': getattr(paragraph, 'space_after', None),
'line_spacing': getattr(paragraph, 'line_spacing', None),
# [修复] 补充读取缩进属性,防止后面恢复时 KeyError
'left_indent': getattr(paragraph, 'left_indent', None),
'right_indent': getattr(paragraph, 'right_indent', None),
'first_line_indent': getattr(paragraph, 'first_line_indent', None),
'font_info': []
}
for run in paragraph.runs:
run_format = {
'font_name': run.font.name,
'font_size': run.font.size,
'bold': run.font.bold,
'italic': run.font.italic,
'underline': run.font.underline,
'color': run.font.color,
'character_space': getattr(run.font, 'space', None),
'all_caps': getattr(run.font, 'all_caps', None),
'small_caps': getattr(run.font, 'small_caps', None)
}
paragraph_format['font_info'].append(run_format)
original_paragraph_formats.append(paragraph_format)
# --- 2. 设置新文本内容 ---
shape.text = text
# --- 3. 恢复原有格式 ---
for i, paragraph in enumerate(shape.text_frame.paragraphs):
# 如果新文本段落数超过原格式数量,使用最后一个格式或默认格式
orig_idx = i if i < len(original_paragraph_formats) else -1
if not original_paragraph_formats: break # 防止空列表
original_para = original_paragraph_formats[orig_idx]
# 恢复段落格式
if original_para['alignment'] is not None:
paragraph.alignment = original_para['alignment']
if original_para['space_before'] is not None:
paragraph.space_before = original_para['space_before']
if original_para['space_after'] is not None:
paragraph.space_after = original_para['space_after']
if original_para['line_spacing'] is not None:
paragraph.line_spacing = original_para['line_spacing']
if original_para['left_indent'] is not None:
paragraph.left_indent = original_para['left_indent']
if original_para['right_indent'] is not None:
paragraph.right_indent = original_para['right_indent']
if original_para['first_line_indent'] is not None:
paragraph.first_line_indent = original_para['first_line_indent']
# 恢复字体格式 (尽量应用到所有 runs)
# 注意shape.text = text 会把所有内容变成一个 run但也可能有多个
for j, run in enumerate(paragraph.runs):
# 通常取第一个run的格式或者按顺序取
font_idx = j if j < len(original_para['font_info']) else 0
if not original_para['font_info']: break
original_font = original_para['font_info'][font_idx]
# 字体名称
if original_font['font_name'] is not None:
font_name = original_font['font_name']
if is_font_available(font_name):
run.font.name = font_name
else:
run.font.name = "微软雅黑"
# print(f"警告: 字体 '{font_name}' 不可用,已替换")
# 其他属性
if original_font['font_size'] is not None:
run.font.size = original_font['font_size']
if original_font['bold'] is not None:
run.font.bold = original_font['bold']
if original_font['italic'] is not None:
run.font.italic = original_font['italic']
if original_font['underline'] is not None:
run.font.underline = original_font['underline']
if original_font['character_space'] is not None:
try:
run.font.space = original_font['character_space']
except:
pass
if original_font['all_caps'] is not None:
run.font.all_caps = original_font['all_caps']
# 颜色处理
if original_font['color'] is not None:
try:
# 仅当它是RGB类型时才复制主题色可能导致报错
if hasattr(original_font['color'], 'rgb'):
run.font.color.rgb = original_font['color'].rgb
except:
pass
def replace_picture(prs, slide_index, placeholder, img_path):
"""
在指定幻灯片中替换指定占位符的图片。
:param prs: Presentation 对象
:param slide_index: 要操作的幻灯片索引从0开始
:param placeholder: 占位符名称
:param img_path: 要替换的图片路径
"""
if not os.path.exists(img_path):
print(f"警告: 图片路径不存在 {img_path}")
return
slide = prs.slides[slide_index]
sp_tree = slide.shapes._spTree
# 先找到要替换的形状及其索引位置
for i, shape in enumerate(slide.shapes):
if shape.name == placeholder:
# 保存旧形状的位置信息
left = shape.left
top = shape.top
width = shape.width
height = 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)
break
if __name__ == "__main__":
# --- 1. 资源准备 ---
# 安装字体如果存在fonts目录
if install_fonts_from_directory("../fonts"):
print("等待系统识别新安装的字体...")
time.sleep(2)
source_file = r"../templates/大班幼儿学期发展报告.pptx"
output_folder = "output"
excel_file = os.path.join("../data/names.xlsx")
image_folder = os.path.join("../data/images")
# 创建输出文件夹
os.makedirs(output_folder, exist_ok=True)
# 检查源文件是否存在
if not os.path.exists(source_file):
print(f"错误: 找不到模版文件 {source_file}")
sys.exit(1)
if not os.path.exists(excel_file):
print(f"错误: 找不到数据文件 {excel_file}")
sys.exit(1)
# --- 2. 定义辅助函数 (显式传入 prs) ---
def replace_one_page(current_prs, name, class_name):
"""替换第一页信息"""
replace_text_in_slide(current_prs, 0, "name", name)
replace_text_in_slide(current_prs, 0, "class", class_name)
def replace_two_page(current_prs, comments, teacher_name):
"""替换第二页信息"""
replace_text_in_slide(current_prs, 1, "comments", comments)
replace_text_in_slide(current_prs, 1, "teacher_name", teacher_name)
def replace_three_page(current_prs, name, english_name, sex, birthday, zodiac, friend, hobby, game, food, me_image):
"""替换第三页信息
:param current_prs: Presentation对象当前演示文稿
:param name: 学生姓名
:param english_name: 学生英文名
:param sex: 性别
:param birthday: 生日
:param zodiac: 生肖
:param friend: 好朋友
:param hobby: 爱好
:param game: 爱玩的游戏
:param food: 爱吃的食物
:param me_image: 自己的照片
"""
replace_text_in_slide(current_prs, 2, "name", name)
replace_text_in_slide(current_prs, 2, "english_name", english_name)
replace_text_in_slide(current_prs, 2, "sex", sex)
replace_text_in_slide(current_prs, 2, "birthday", birthday)
replace_text_in_slide(current_prs, 2, "zodiac", zodiac)
replace_text_in_slide(current_prs, 2, "friend", friend)
replace_text_in_slide(current_prs, 2, "hobby", hobby)
replace_text_in_slide(current_prs, 2, "game", game)
replace_text_in_slide(current_prs, 2, "food", food)
replace_picture(current_prs, 2, "me_image", me_image)
def replace_four_page(current_prs, class_image):
"""替换第四页信息"""
replace_picture(current_prs, 3, "class_image", class_image)
def replace_five_page(current_prs, image1, image2):
"""替换第五页信息"""
replace_picture(current_prs, 4, "image1", image1)
replace_picture(current_prs, 4, "image2", image2)
# --- 3. 读取数据并处理 ---
try:
df = pd.read_excel(excel_file, sheet_name="Sheet1")
datas = df[["姓名", "英文名", "性别", "生日", "属相", "我的好朋友", "我的爱好", "喜欢的游戏", "喜欢吃的食物",
"评价"]].values.tolist()
class_name = "K4D"
teacher_name = ["简蜜", "王敏千", "李玉香"]
print(f"开始处理,共 {len(datas)} 位学生...")
# --- 4. 循环处理 ---
for i, (name, english_name, sex, birthday, zodiac, friend, hobby, game, food, comments) in enumerate(datas):
# 班级图片
print(f"[{i + 1}/{len(datas)}] 正在生成: {name}")
# [修复] 每次循环重新加载模版,保证文件干净
prs = Presentation(source_file)
# 替换第一页内容
replace_one_page(prs, name, class_name)
# 替换第二页内容
teacher_names = " ".join(teacher_name)
replace_two_page(prs, comments, teacher_names)
# 替换第三页内容
student_image_folder = os.path.join(image_folder, name)
if os.path.exists(student_image_folder):
me_image = os.path.join(student_image_folder, "me_image.jpg")
replace_three_page(prs, name, english_name, sex, birthday, zodiac, friend, hobby, game, food, me_image)
else:
print(f"错误: 学生图片文件夹不存在 {student_image_folder}")
# 替换第四页内容
class_image = os.path.join(image_folder, class_name + ".jpg")
if os.path.exists(class_image):
replace_four_page(prs, class_image)
else:
print(f"错误: 班级图片文件不存在 {class_image}")
continue
# 替换第五页内容
if os.path.exists(student_image_folder):
image1 = os.path.join(student_image_folder, "1.jpg")
image2 = os.path.join(student_image_folder, "1.jpg")
replace_five_page(prs, image1, image2)
else:
print(f"错误: 学生图片文件夹不存在 {student_image_folder}")
# 获取文件拓展名
file_ext = os.path.splitext(source_file)[1]
safe_name = str(name).strip() # 去除可能存在的空格
new_filename = f"{class_name} {safe_name} 幼儿成长报告{file_ext}"
output_path = os.path.join(output_folder, new_filename)
# 保存
try:
prs.save(output_path)
except PermissionError:
print(f"保存失败: 文件 {new_filename} 可能已被打开,请关闭后重试。")
print("\n所有报告生成完毕!")
except Exception as e:
print(f"程序运行出错: {str(e)}")
import traceback
traceback.print_exc()