Compare commits
2 Commits
1c2d5db393
...
de71594812
| Author | SHA1 | Date | |
|---|---|---|---|
| de71594812 | |||
| f3d16ec1f9 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,3 +14,4 @@ data/images/*
|
|||||||
data/*.xlsx
|
data/*.xlsx
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
|
.trae/
|
||||||
7
.idea/workspace.xml
generated
7
.idea/workspace.xml
generated
@@ -4,7 +4,9 @@
|
|||||||
<option name="autoReloadType" value="SELECTIVE" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="41690157-d51b-4dae-98de-6b96990d681a" name="更改" comment="fix:优化一些命名规范" />
|
<list default="true" id="41690157-d51b-4dae-98de-6b96990d681a" name="更改" comment="fix:优化一些命名规范">
|
||||||
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
@@ -95,6 +97,9 @@
|
|||||||
<workItem from="1766256207534" duration="960000" />
|
<workItem from="1766256207534" duration="960000" />
|
||||||
<workItem from="1766287241685" duration="2135000" />
|
<workItem from="1766287241685" duration="2135000" />
|
||||||
<workItem from="1766329711762" duration="741000" />
|
<workItem from="1766329711762" duration="741000" />
|
||||||
|
<workItem from="1768312728552" duration="228000" />
|
||||||
|
<workItem from="1768312972093" duration="486000" />
|
||||||
|
<workItem from="1768314152581" duration="7000" />
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00001" summary="fix:修复一些BUG">
|
<task id="LOCAL-00001" summary="fix:修复一些BUG">
|
||||||
<option name="closed" value="true" />
|
<option name="closed" value="true" />
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ output_folder = "output"
|
|||||||
excel_file = "names.xlsx"
|
excel_file = "names.xlsx"
|
||||||
image_folder = "images"
|
image_folder = "images"
|
||||||
fonts_dir = "fonts"
|
fonts_dir = "fonts"
|
||||||
signature_image = "C:\\Users\\Administrator\\Desktop\\文档资料\\code\\growth_report\\data\\"
|
signature_image = "d:\\working\\tools\\growth_report\\data\\signature.png"
|
||||||
|
|
||||||
[class_info]
|
[class_info]
|
||||||
class_name = "K4D"
|
class_name = "K4D"
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ def load_config(config_filename="config.toml"):
|
|||||||
base_dir, paths.get("output_folder", "output")
|
base_dir, paths.get("output_folder", "output")
|
||||||
),
|
),
|
||||||
"signature_image": get_resource_path(
|
"signature_image": get_resource_path(
|
||||||
os.path.join("data", paths.get("signature_image", ""))
|
os.path.join("data", paths.get("signature_image", "signature.png"))
|
||||||
),
|
),
|
||||||
"class_name": class_info.get("class_name", "未命名班级"),
|
"class_name": class_info.get("class_name", "未命名班级"),
|
||||||
"teachers": class_info.get("teachers", []),
|
"teachers": class_info.get("teachers", []),
|
||||||
@@ -118,7 +118,9 @@ def save_config(config_data, config_filename="config.toml"):
|
|||||||
"image_folder": os.path.basename(config_data.get("image_folder", "")),
|
"image_folder": os.path.basename(config_data.get("image_folder", "")),
|
||||||
"fonts_dir": os.path.basename(config_data.get("fonts_dir", "fonts")),
|
"fonts_dir": os.path.basename(config_data.get("fonts_dir", "fonts")),
|
||||||
"signature_image": get_resource_path(
|
"signature_image": get_resource_path(
|
||||||
os.path.join("data", config_data.get("signature_image", ""))
|
os.path.join(
|
||||||
|
"data", config_data.get("signature_image", "signature.png")
|
||||||
|
)
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
"class_info": {
|
"class_info": {
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ def create_config_page():
|
|||||||
"output_folder": output_folder.value,
|
"output_folder": output_folder.value,
|
||||||
"class_name": class_name.value,
|
"class_name": class_name.value,
|
||||||
"age_group": age_group.value,
|
"age_group": age_group.value,
|
||||||
|
"signature_image": conf_data.get("signature_image", ""),
|
||||||
"teachers": [
|
"teachers": [
|
||||||
t.strip() for t in teachers_text.value.split("\n") if t.strip()
|
t.strip() for t in teachers_text.value.split("\n") if t.strip()
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from ui.core.task_runner import run_task
|
|||||||
from utils.generate_utils import (
|
from utils.generate_utils import (
|
||||||
generate_template,
|
generate_template,
|
||||||
generate_comment_all,
|
generate_comment_all,
|
||||||
batch_convert_folder,
|
generate_convert_pdf,
|
||||||
generate_report,
|
generate_report,
|
||||||
generate_zodiac,
|
generate_zodiac,
|
||||||
generate_signature,
|
generate_signature,
|
||||||
@@ -66,14 +66,10 @@ def create_home_page():
|
|||||||
f"outline"
|
f"outline"
|
||||||
).classes("w-full")
|
).classes("w-full")
|
||||||
|
|
||||||
# 特殊处理带参数的
|
|
||||||
async def run_convert():
|
|
||||||
await run_task(batch_convert_folder, config.get("output_folder"))
|
|
||||||
|
|
||||||
func_btn("📁 生成图片路径", generate_template)
|
func_btn("📁 生成图片路径", generate_template)
|
||||||
func_btn("🤖 生成评语 (AI)", generate_comment_all)
|
func_btn("🤖 生成评语 (AI)", generate_comment_all)
|
||||||
func_btn("📊 生成报告 (PPT)", generate_report)
|
func_btn("📊 生成报告 (PPT)", generate_report)
|
||||||
func_btn("📑 格式转换 (PDF)", run_convert)
|
func_btn("📑 格式转换 (PDF)", generate_convert_pdf)
|
||||||
func_btn("🐂 生肖转化 (生日)", generate_zodiac)
|
func_btn("🐂 生肖转化 (生日)", generate_zodiac)
|
||||||
func_btn("💴 园长一键签名", generate_signature)
|
func_btn("💴 园长一键签名", generate_signature)
|
||||||
|
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ from utils.growt_utils import (
|
|||||||
replace_four_page,
|
replace_four_page,
|
||||||
replace_five_page,
|
replace_five_page,
|
||||||
)
|
)
|
||||||
from utils.pptx_utils import replace_picture
|
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 1. 生成模板(根据names.xlsx文件生成名字图片文件夹)
|
# 1. 生成模板(根据names.xlsx文件生成名字图片文件夹)
|
||||||
# ==========================================
|
# ==========================================
|
||||||
def generate_template(stop_event: threading.Event = None, progress_callback=None):
|
def generate_template(stop_event: threading.Event = None, progress_callback=None):
|
||||||
""""
|
""" "
|
||||||
根据学生姓名生成相对应的以学生姓名的存放照片的文件夹
|
根据学生姓名生成相对应的以学生姓名的存放照片的文件夹
|
||||||
:params stop_event 任务是否停止事件(监听UI的事件监听)
|
:params stop_event 任务是否停止事件(监听UI的事件监听)
|
||||||
:params progress_callback 进度回调函数
|
:params progress_callback 进度回调函数
|
||||||
@@ -87,7 +87,7 @@ def generate_comment_all(stop_event: threading.Event = None, progress_callback=N
|
|||||||
:params stop_event 任务是否停止事件(监听UI的事件监听)
|
:params stop_event 任务是否停止事件(监听UI的事件监听)
|
||||||
:params progress_callback 进度回调函数
|
:params progress_callback 进度回调函数
|
||||||
"""
|
"""
|
||||||
# 1. 加载配置文件
|
# 1. 加载配置文件
|
||||||
try:
|
try:
|
||||||
config = load_config("config.toml")
|
config = load_config("config.toml")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -139,7 +139,9 @@ def generate_comment_all(stop_event: threading.Event = None, progress_callback=N
|
|||||||
continue
|
continue
|
||||||
# 添加进度条
|
# 添加进度条
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(i + 1, total_count, f"[{i + 1}/{total_count}] 正在生成评价: {name}")
|
progress_callback(
|
||||||
|
i + 1, total_count, f"[{i + 1}/{total_count}] 正在生成评价: {name}"
|
||||||
|
)
|
||||||
logger.info(f"[{i + 1}/{total_count}] 正在生成评价: {name}")
|
logger.info(f"[{i + 1}/{total_count}] 正在生成评价: {name}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -180,7 +182,7 @@ def generate_report(stop_event: threading.Event = None, progress_callback=None):
|
|||||||
根据学生姓名生成成长报告
|
根据学生姓名生成成长报告
|
||||||
:params stop_event 任务是否停止事件(监听UI的事件监听)
|
:params stop_event 任务是否停止事件(监听UI的事件监听)
|
||||||
:params progress_callback 进度回调函数
|
:params progress_callback 进度回调函数
|
||||||
""" # 1. 加载配置文件
|
""" # 1. 加载配置文件
|
||||||
try:
|
try:
|
||||||
config = load_config("config.toml")
|
config = load_config("config.toml")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -237,7 +239,11 @@ def generate_report(stop_event: threading.Event = None, progress_callback=None):
|
|||||||
) = row_data
|
) = row_data
|
||||||
# 更新进度条
|
# 更新进度条
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(i + 1, total_count, f"[{i + 1}/{len(datas)}] 正在生成: 【{name}】 成长报告")
|
progress_callback(
|
||||||
|
i + 1,
|
||||||
|
total_count,
|
||||||
|
f"[{i + 1}/{len(datas)}] 正在生成: 【{name}】 成长报告",
|
||||||
|
)
|
||||||
logger.info(f"[{i + 1}/{len(datas)}] 正在生成: {name}")
|
logger.info(f"[{i + 1}/{len(datas)}] 正在生成: {name}")
|
||||||
|
|
||||||
# 每次循环重新加载模版
|
# 每次循环重新加载模版
|
||||||
@@ -271,11 +277,31 @@ def generate_report(stop_event: threading.Event = None, progress_callback=None):
|
|||||||
"birthday": (
|
"birthday": (
|
||||||
birthday.strftime("%Y-%m-%d") if pd.notna(birthday) else " "
|
birthday.strftime("%Y-%m-%d") if pd.notna(birthday) else " "
|
||||||
),
|
),
|
||||||
"zodiac": str(zodiac).strip() if str(zodiac).strip() or not str(zodiac).strip().lower() else " ",
|
"zodiac": (
|
||||||
"friend": str(friend).strip() if str(friend).strip() or not str(friend).strip().lower() else " ",
|
str(zodiac).strip()
|
||||||
"hobby": str(hobby).strip() if str(hobby).strip() or not str(hobby).strip().lower() else " ",
|
if str(zodiac).strip() or not str(zodiac).strip().lower()
|
||||||
"game": str(game).strip() if str(game).strip() or not str(game).strip().lower() else " ",
|
else " "
|
||||||
"food": str(food).strip() if str(food).strip() or not str(food).strip().lower() else " ",
|
),
|
||||||
|
"friend": (
|
||||||
|
str(friend).strip()
|
||||||
|
if str(friend).strip() or not str(friend).strip().lower()
|
||||||
|
else " "
|
||||||
|
),
|
||||||
|
"hobby": (
|
||||||
|
str(hobby).strip()
|
||||||
|
if str(hobby).strip() or not str(hobby).strip().lower()
|
||||||
|
else " "
|
||||||
|
),
|
||||||
|
"game": (
|
||||||
|
str(game).strip()
|
||||||
|
if str(game).strip() or not str(game).strip().lower()
|
||||||
|
else " "
|
||||||
|
),
|
||||||
|
"food": (
|
||||||
|
str(food).strip()
|
||||||
|
if str(food).strip() or not str(food).strip().lower()
|
||||||
|
else " "
|
||||||
|
),
|
||||||
}
|
}
|
||||||
# 获取学生个人照片路径
|
# 获取学生个人照片路径
|
||||||
me_image_path = find_image_path(student_image_folder, "me")
|
me_image_path = find_image_path(student_image_folder, "me")
|
||||||
@@ -343,14 +369,14 @@ def generate_report(stop_event: threading.Event = None, progress_callback=None):
|
|||||||
# ==========================================
|
# ==========================================
|
||||||
# 4. 转换格式(根据names.xlsx文件生成PPT转PDF)
|
# 4. 转换格式(根据names.xlsx文件生成PPT转PDF)
|
||||||
# ==========================================
|
# ==========================================
|
||||||
def batch_convert_folder(folder_path, stop_event: threading.Event = None, progress_callback=None):
|
def generate_convert_pdf(stop_event: threading.Event = None, progress_callback=None):
|
||||||
"""
|
"""
|
||||||
批量转换文件夹下的所有 PPT
|
批量转换文件夹下的所有 PPT
|
||||||
:params folder_path 需要转换的PPT文件夹
|
:params folder_path 需要转换的PPT文件夹
|
||||||
:params stop_event 任务是否停止事件(监听UI的事件监听)
|
:params stop_event 任务是否停止事件(监听UI的事件监听)
|
||||||
:params progress_callback 进度回调函数
|
:params progress_callback 进度回调函数
|
||||||
"""
|
"""
|
||||||
# 1. 加载配置文件
|
# 1. 加载配置文件
|
||||||
try:
|
try:
|
||||||
config = load_config("config.toml")
|
config = load_config("config.toml")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -360,7 +386,7 @@ def batch_convert_folder(folder_path, stop_event: threading.Event = None, progre
|
|||||||
# 子线程初始化 COM 组件
|
# 子线程初始化 COM 组件
|
||||||
pythoncom.CoInitialize()
|
pythoncom.CoInitialize()
|
||||||
try:
|
try:
|
||||||
folder_path = os.path.abspath(folder_path)
|
folder_path = os.path.abspath(config.get("output_folder"))
|
||||||
if not os.path.exists(folder_path):
|
if not os.path.exists(folder_path):
|
||||||
logger.error(f"文件夹不存在: {folder_path}")
|
logger.error(f"文件夹不存在: {folder_path}")
|
||||||
return
|
return
|
||||||
@@ -400,7 +426,9 @@ def batch_convert_folder(folder_path, stop_event: threading.Event = None, progre
|
|||||||
logger.info(f"[跳过] 已存在: {filename}")
|
logger.info(f"[跳过] 已存在: {filename}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.info(f"[{files.index(filename)}/{total_count}]正在转换: {filename} ...")
|
logger.info(
|
||||||
|
f"[{files.index(filename)}/{total_count}]正在转换: {filename} ..."
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 打开 -> 另存为 -> 关闭
|
# 打开 -> 另存为 -> 关闭
|
||||||
@@ -467,7 +495,7 @@ def generate_zodiac(stop_event: threading.Event = None, progress_callback=None):
|
|||||||
logger.info(f"开始生成学生属相,共 {total_count} 位学生...")
|
logger.info(f"开始生成学生属相,共 {total_count} 位学生...")
|
||||||
|
|
||||||
# 3. 预处理:将“生日”列转换为 datetime 格式
|
# 3. 预处理:将“生日”列转换为 datetime 格式
|
||||||
df['temp_date'] = pd.to_datetime(df[date_column], errors="coerce")
|
df["temp_date"] = pd.to_datetime(df[date_column], errors="coerce")
|
||||||
|
|
||||||
# 4. 遍历 DataFrame 并计算/更新数据
|
# 4. 遍历 DataFrame 并计算/更新数据
|
||||||
for i, row in df.iterrows():
|
for i, row in df.iterrows():
|
||||||
@@ -481,7 +509,7 @@ def generate_zodiac(stop_event: threading.Event = None, progress_callback=None):
|
|||||||
progress_callback(i + 1, total_count, "生成属相")
|
progress_callback(i + 1, total_count, "生成属相")
|
||||||
|
|
||||||
name = row.get("姓名", f"学生_{i + 1}")
|
name = row.get("姓名", f"学生_{i + 1}")
|
||||||
date = row['temp_date']
|
date = row["temp_date"]
|
||||||
|
|
||||||
logger.info(f"[{i + 1}/{total_count}] 正在处理学生:{name}...")
|
logger.info(f"[{i + 1}/{total_count}] 正在处理学生:{name}...")
|
||||||
|
|
||||||
@@ -502,7 +530,7 @@ def generate_zodiac(stop_event: threading.Event = None, progress_callback=None):
|
|||||||
logger.info(f" -> 属相计算成功:{name} ,属相: {zodiac}")
|
logger.info(f" -> 属相计算成功:{name} ,属相: {zodiac}")
|
||||||
|
|
||||||
# 6. 清理和保存结果
|
# 6. 清理和保存结果
|
||||||
df = df.drop(columns=['temp_date'])
|
df = df.drop(columns=["temp_date"])
|
||||||
|
|
||||||
save_path = excel_path
|
save_path = excel_path
|
||||||
try:
|
try:
|
||||||
@@ -521,14 +549,15 @@ def generate_zodiac(stop_event: threading.Event = None, progress_callback=None):
|
|||||||
logger.error(f"程序运行出错: {str(e)}")
|
logger.error(f"程序运行出错: {str(e)}")
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 6. 一键生成园长签名(根据输出文件夹生成签名)
|
# 6. 一键生成园长签名(根据输出文件夹生成签名)
|
||||||
# ==========================================
|
# ==========================================
|
||||||
def generate_signature(progress_callback=None) -> str:
|
def generate_signature(progress_callback=None) -> str:
|
||||||
"""
|
"""
|
||||||
生成园长签名
|
生成园长签名(不依赖占位符,直接在指定位置添加)
|
||||||
"""
|
"""
|
||||||
# 1. 加载配置文件
|
# 1. 加载配置文件
|
||||||
try:
|
try:
|
||||||
config = load_config("config.toml")
|
config = load_config("config.toml")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -545,28 +574,54 @@ def generate_signature(progress_callback=None) -> str:
|
|||||||
|
|
||||||
logger.info(f"开始生成签名,共 {len(pptx_files)} 个 PPT 文件...")
|
logger.info(f"开始生成签名,共 {len(pptx_files)} 个 PPT 文件...")
|
||||||
|
|
||||||
img_path = config.get("signature_image") # 签名图片路径
|
img_path = config.get("signature_image") # 签名图片路径
|
||||||
if not img_path or not os.path.exists(img_path):
|
if not img_path or not os.path.exists(img_path):
|
||||||
logger.error(f"签名图片不存在: {img_path}")
|
logger.error(f"签名图片不存在: {img_path}")
|
||||||
logger.warning(f"⚠️ 警告: 缺少签名照片('signature')")
|
logger.warning(f"⚠️ 警告: 缺少签名照片('signature')")
|
||||||
return
|
return
|
||||||
logger.info(f"签名图片存在: {img_path}")
|
logger.info(f"签名图片存在: {img_path}")
|
||||||
|
|
||||||
|
# 从配置文件获取签名位置信息,如果没有则使用默认值
|
||||||
|
signature_left = config.get("signature_left", 2987040) # 左位置
|
||||||
|
signature_top = config.get("signature_top", 8273415) # 上位置
|
||||||
|
signature_width = config.get("signature_width", 1800000) # 宽度
|
||||||
|
signature_height = config.get("signature_height", 720000) # 高度
|
||||||
|
# 导入必要的模块
|
||||||
|
from utils.image_utils import get_corrected_image_stream
|
||||||
|
|
||||||
for i, filename in enumerate(pptx_files):
|
for i, filename in enumerate(pptx_files):
|
||||||
# 获取完整绝对路径
|
# 获取完整绝对路径
|
||||||
pptx_path = os.path.join(config["output_folder"], filename)
|
pptx_path = os.path.join(config["output_folder"], filename)
|
||||||
|
|
||||||
# --- 关键修改点 1: 打开 PPT 对象 ---
|
# 打开 PPT 对象
|
||||||
prs = Presentation(pptx_path)
|
prs = Presentation(pptx_path)
|
||||||
|
|
||||||
# --- 关键修改点 2: 传递 prs 对象而不是路径字符串 ---
|
# 获取第二张幻灯片 (索引为1)
|
||||||
replace_picture(prs, 1, "signature", img_path)
|
slide = prs.slides[1]
|
||||||
|
|
||||||
# --- 关键修改点 3: 保存修改后的 PPT ---
|
# 获取修正后的图片流
|
||||||
|
img_stream = get_corrected_image_stream(img_path)
|
||||||
|
|
||||||
|
# 直接在指定位置添加签名图片
|
||||||
|
slide.shapes.add_picture(
|
||||||
|
img_stream,
|
||||||
|
signature_left,
|
||||||
|
signature_top,
|
||||||
|
signature_width,
|
||||||
|
signature_height,
|
||||||
|
)
|
||||||
|
logger.info(f"在幻灯片 1 上添加签名图片")
|
||||||
|
|
||||||
|
# 保存修改后的 PPT
|
||||||
prs.save(pptx_path)
|
prs.save(pptx_path)
|
||||||
|
|
||||||
# 更新进度条 (如果有 callback)
|
# 更新进度条 (如果有 callback)
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(i + 1, len(pptx_files),f"[{i + 1}/{len(pptx_files)}] 生成签名完成: {filename}")
|
progress_callback(
|
||||||
|
i + 1,
|
||||||
|
len(pptx_files),
|
||||||
|
f"[{i + 1}/{len(pptx_files)}] 生成签名完成: {filename}",
|
||||||
|
)
|
||||||
logger.success(f"[{i + 1}/{len(pptx_files)}] 生成签名完成: {filename}")
|
logger.success(f"[{i + 1}/{len(pptx_files)}] 生成签名完成: {filename}")
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(len(pptx_files), len(pptx_files), "签名生成完成")
|
progress_callback(len(pptx_files), len(pptx_files), "签名生成完成")
|
||||||
|
|||||||
@@ -100,14 +100,18 @@ def replace_text_in_slide(prs, slide_index, placeholder, text):
|
|||||||
try:
|
try:
|
||||||
run.font.space = original_font["character_space"]
|
run.font.space = original_font["character_space"]
|
||||||
except:
|
except:
|
||||||
logger.error(f"错误: 无法设置字体间距 {original_font['character_space']}")
|
logger.error(
|
||||||
|
f"错误: 无法设置字体间距 {original_font['character_space']}"
|
||||||
|
)
|
||||||
|
|
||||||
if original_font["color"]:
|
if original_font["color"]:
|
||||||
try:
|
try:
|
||||||
if hasattr(original_font["color"], "rgb"):
|
if hasattr(original_font["color"], "rgb"):
|
||||||
run.font.color.rgb = original_font["color"].rgb
|
run.font.color.rgb = original_font["color"].rgb
|
||||||
except:
|
except:
|
||||||
logger.error(f"错误: 无法设置字体颜色 {original_font['color']}")
|
logger.error(
|
||||||
|
f"错误: 无法设置字体颜色 {original_font['color']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def replace_picture(prs, slide_index, placeholder, img_path):
|
def replace_picture(prs, slide_index, placeholder, img_path):
|
||||||
@@ -138,6 +142,10 @@ def replace_picture(prs, slide_index, placeholder, img_path):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if target_shape:
|
if target_shape:
|
||||||
|
logger.debug(f"找到占位符 {placeholder},索引 {target_index}")
|
||||||
|
logger.debug(
|
||||||
|
f"占位符位置: 左 {target_shape.left}, 上 {target_shape.top}, 宽 {target_shape.width}, 高 {target_shape.height}"
|
||||||
|
)
|
||||||
# 获取原位置信息
|
# 获取原位置信息
|
||||||
left, top, width, height = (
|
left, top, width, height = (
|
||||||
target_shape.left,
|
target_shape.left,
|
||||||
|
|||||||
Reference in New Issue
Block a user