Files
growth_report/ui/views/convert_pdf_page.py
2026-01-29 23:10:06 +08:00

267 lines
8.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import comtypes.client
import pythoncom
from loguru import logger
from nicegui import ui
from utils.file_utils import open_folder
progress_bar = None
progress_label = None
powerpoint = None
def onload_page(folder: str = ""):
global powerpoint
pdf_path = os.path.join(folder, "PDF")
if not os.path.exists(pdf_path):
os.makedirs(pdf_path)
if not powerpoint:
try:
pythoncom.CoInitialize()
powerpoint = comtypes.client.CreateObject("PowerPoint.Application")
logger.success("PowerPoint 应用启动成功")
except Exception as e:
logger.error(f"PowerPoint 应用启动失败: {str(e)}")
def update_progress(current: int, total: int, message: str):
global progress_bar, progress_label
if total <= 0:
pct = 0
text = f"{message}: 准备中..."
else:
pct = current / total
text = f"{message}: {current}/{total} ({int(pct * 100)}%)"
# 更新 UI
if progress_bar:
progress_bar.set_value(pct)
if progress_label:
progress_label.set_text(text)
def cleanup_powerpoint():
"""清理 PowerPoint COM 组件资源"""
global powerpoint
if powerpoint:
try:
powerpoint.Quit()
logger.success("PowerPoint 应用已关闭")
except Exception as e:
logger.error(f"关闭 PowerPoint 应用失败: {str(e)}")
finally:
powerpoint = None
def create_convert_pdf_page(folder: str = ""):
onload_page(folder)
ui.add_head_html('<link href="/assets/style.css" rel="stylesheet" />')
# 添加样式
ui.add_head_html(
"""
<style>
.file-list { max-height: 450px; overflow-y: auto; }
.file-list::-webkit-scrollbar {
width: 6px;
}
.file-list::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.file-list::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
.file-list::-webkit-scrollbar-thumb:hover {
background: #a1a1a1;
}
.file-item { transition: background-color 0.2s ease; }
.file-item:hover { background-color: #f8f9fa; }
</style>
"""
)
with ui.header().classes("app-header items-center justify-between shadow-md"):
# 左侧:图标和标题
with ui.row().classes("items-center gap-2"):
ui.image("/assets/icon.ico").classes("w-8 h-8").props("fit=contain")
ui.label("尚城幼儿园成长报告助手").classes("text-xl font-bold")
# 右侧:署名 + 配置按钮
with ui.row().classes("items-center gap-4"):
ui.label("By 寒寒 | 这里的每一份评语都充满爱意").classes(
"text-xs opacity-90"
)
ui.button(icon="home", on_click=lambda: ui.navigate.to("/")).props(
"flat round color=white"
)
with ui.card().classes("w-full"):
with ui.row().classes("w-full justify-between"):
ui.label("📑 格式转换").classes("section-title")
with ui.row().classes("flex-1 justify-end"):
ui.button(
"📑 格式转换",
on_click=lambda: convert_all_pdf_files(folder),
).props().classes()
ui.button(
"📂 打开文件夹",
on_click=lambda: open_folder(folder),
).props("outline").classes()
ui.button(
"🔄 刷新数据",
on_click=lambda: convert_files_list(list_card, folder),
).props("outline").classes()
# 进度条
with ui.row().classes("w-full"):
global progress_label, progress_bar
progress_bar = ui.linear_progress(value=0, show_value=False).classes(
"h-4 rounded"
)
progress_bar.props("color=positive")
progress_label = ui.label("⛳ 任务进度: 待命").classes(
"font-bold text-gray-700 mb-1"
)
list_card = ui.card().classes("w-full p-4 gap-4")
convert_files_list(list_card, folder)
# 遍历目录
def convert_files_list(list_card, folder):
# 清空旧内容
list_card.clear()
files = get_convert_files(folder)
with list_card:
ui.label(f"📄 找到 {len(files)} 个 PPT 文件").classes(
"text-sm text-gray-600 mb-4"
)
# 创建可滚动的文件列表
with ui.grid(columns=2).classes("file-list"):
for ppt_file in files:
with ui.row().classes(
"w-full justify-between items-center file-item p-3 rounded mb-2"
):
ui.label(ppt_file).classes("flex-1 text-sm")
with ui.row().classes("items-center gap-2"):
# 打开文件按钮
ui.button(
"📂 打开",
on_click=lambda f=ppt_file: open_folder(
os.path.join(folder, f)
),
).props("outline").classes("text-xs")
# 签名按钮
ui.button(
"📑 转换",
on_click=lambda f=ppt_file: (convert_pdf_file(folder, f),),
).props("outline").classes("text-xs")
def get_convert_files(folder: str) -> list:
"""获取目录下所有 需要转换的PPT 文件"""
if not os.path.exists(folder):
return []
files = []
for filename in os.listdir(folder):
if not filename.startswith(".") and filename.endswith(".pptx"):
files.append(filename)
return sorted(files)
def convert_file(folder, file_name: str):
"""
转换单个PPT文件为PDF格式
:param folder: PPT 文件所在目录
:param file_name: PPT 文件名称
"""
global progress_bar, progress_label, powerpoint
if not powerpoint:
logger.error("PowerPoint 应用未初始化,请重新加载页面")
ui.notify("PowerPoint 应用未初始化", type="negative")
return
try:
ppt_path = os.path.join(folder, file_name)
pdf_name = os.path.splitext(file_name)[0] + ".pdf"
pdf_path = os.path.join(folder, "PDF", pdf_name)
# 如果 PDF 已存在,可以选择跳过
if os.path.exists(pdf_path):
logger.info(f"[跳过] 已存在: {pdf_name}")
return True
# 打开 -> 另存为 -> 关闭
deck = powerpoint.Presentations.Open(ppt_path)
deck.SaveAs(pdf_path, 32)
deck.Close()
logger.success(f"文件转换完成: {file_name}")
return True
except Exception as e:
error_msg = f"转换失败: {str(e)}"
logger.error(error_msg)
return False
def convert_pdf_file(folder, file_name: str):
"""
转换单个PPT文件为PDF格式带UI更新
:param folder: PPT 文件所在目录
:param file_name: PPT 文件名称
"""
update_progress(0, 1, f"⛳ 任务进度: 开始转换 {file_name}")
result = convert_file(folder, file_name)
if result is True:
update_progress(1, 1, f"✅ 转换完成: {file_name}")
ui.notify(f"转换完成: {file_name}", type="positive")
elif result is None:
update_progress(1, 1, f"⏭️ 已存在: {file_name}")
ui.notify(f"已存在: {file_name}", type="info")
else:
update_progress(1, 1, f"❌ 转换失败: {file_name}")
ui.notify(f"转换失败: {file_name}", type="negative")
def convert_all_pdf_files(folder: str):
"""批量转换目录下所有PPT文件为PDF格式"""
global progress_bar, progress_label
pdf_files = get_convert_files(folder)
pdf_total = len(pdf_files)
if pdf_total == 0:
ui.notify("没有找到需要转换的PPT文件", type="warning")
return
success_count = 0
skip_count = 0
fail_count = 0
try:
for index, file in enumerate(pdf_files):
update_progress(index, pdf_total, f"正在转换: {file}")
result = convert_file(folder, file)
if result is True:
success_count += 1
elif result is None:
skip_count += 1
else:
fail_count += 1
update_progress(pdf_total, pdf_total, f"✅ 批量转换完成")
summary_msg = f"转换完成: 成功 {success_count} 个, 跳过 {skip_count} 个, 失败 {fail_count}"
logger.success(summary_msg)
ui.notify(summary_msg, type="positive")
except Exception as e:
error_msg = f"批量转换失败: {str(e)}"
update_progress(pdf_total, pdf_total, f"❌ 批量转换失败")
logger.error(error_msg)
ui.notify(error_msg, type="negative")