import os import comtypes.client import pythoncom from loguru import logger from nicegui import ui from ui.views.templates.back_home import backHome 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('') # 添加样式 ui.add_head_html( """ """ ) with ui.header().classes("app-header items-center justify-between shadow-md"): backHome() 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")