# -*- coding: utf-8 -*- # 指定文件编码格式,支持中文
# 导入需要的标准库
import os # 操作系统接口模块,用于文件路径处理
import threading # 多线程支持模块
from queue import Queue # 线程安全队列,用于任务管理
from tkinter import Tk # GUI基础库
from tkinter.filedialog import askopenfilenames # 文件选择对话框
from pdf2docx import Converter # PDF转Word核心库
from concurrent.futures import ThreadPoolExecutor # 线程池执行器
class PDFConverter:
def __init__(self):
""" 初始化转换器实例 """
self.task_queue = Queue() # 创建任务队列(先进先出)
self.results = [] # 存储转换结果列表
self.lock = threading.Lock() # 创建线程锁(保证结果记录的线程安全)
self.executor = ThreadPoolExecutor(max_workers=4) # 创建包含4个工作线程的线程池
def add_task(self, pdf_path):
""" 添加转换任务到队列 """
# 生成Word文件路径:将.pdf替换为.docx
word_path = os.path.splitext(pdf_path)[0] + ".docx"
# 将任务元组(PDF路径,Word路径)放入队列
self.task_queue.put((pdf_path, word_path))
def _convert_task(self, pdf_path, word_path):
""" 实际执行转换的内部方法(仅供线程调用) """
try:
# 创建PDF转换器对象(需要try块因为可能打开文件失败)
cv = Converter(pdf_path)
# 执行转换操作(核心功能)
cv.convert(word_path)
# 关闭转换器释放资源
cv.close()
# 使用线程锁保证结果列表的线程安全
with self.lock:
# 记录成功结果(文件路径,成功标志,提示信息)
self.results.append((pdf_path, True, "转换成功"))
except Exception as e:
# 捕获所有异常(包括文件不存在、转换错误等)
with self.lock:
# 记录失败结果(文件路径,失败标志,错误信息)
self.results.append((pdf_path, False, str(e)))
finally:
# 确保无论是否异常都关闭转换器
if 'cv' in locals(): # 检查cv变量是否存在
cv.close()
def start_convert(self):
""" 启动转换任务(从队列中取出任务分配给线程池) """
# 循环直到任务队列为空
while not self.task_queue.empty():
# 从队列获取一个任务(自动遵循先进先出顺序)
pdf_path, word_path = self.task_queue.get()
# 将任务提交给线程池执行(自动分配空闲线程)
self.executor.submit(self._convert_task, pdf_path, word_path)
# 打印当前任务启动信息
print(f"开始转换: {os.path.basename(pdf_path)}")
# 关闭线程池并等待所有任务完成
self.executor.shutdown(wait=True)
def show_results(self):
""" 显示最终转换结果 """
print("\n转换结果汇总:")
# 遍历所有结果记录
for result in self.results:
# 解包结果元组
file_path, success, msg = result
# 根据成功标志生成状态文本
status = "成功" if success else "失败"
# 打印文件名、状态和详细信息
print(f"{os.path.basename(file_path)}: {status} | 详细信息: {msg}")
def main():
""" 主程序入口函数 """
# 创建Tk根窗口后立即隐藏(不显示空白窗口)
Tk().withdraw()
# 打开文件选择对话框(多选模式)
pdf_files = askopenfilenames(
title="选择PDF文件(可多选)", # 对话框标题
filetypes=[("PDF文件", "*.pdf"), ("所有文件", "*.*")] # 文件过滤器
)
# 如果没有选择文件则退出
if not pdf_files:
print("未选择文件,程序退出。")
return
# 创建PDF转换器实例
converter = PDFConverter()
# 将选中的文件逐个添加到任务队列
for file in pdf_files:
converter.add_task(file)
# 显示开始提示信息
print(f"开始批量转换 {len(pdf_files)} 个文件...")
# 启动转换流程
converter.start_convert()
# 显示汇总结果
converter.show_results()
# 等待用户回车退出(防止控制台窗口立即关闭)
input("按回车键退出...")
# Python程序标准入口(当直接运行本文件时执行)
if __name__ == "__main__":
main()
类结构设计:class PDFConverter:
def __init__(self): ... # 初始化组件
def add_task(self): ... # 添加任务
def _convert_task(self): ... # 实际转换方法(带下划线表示内部方法)
def start_convert(self): ... # 启动转换
def show_results(self): ... # 显示结果
2.多线程工作流程: 主线程:main()
↓
创建转换器实例 → 添加任务到队列
↓
启动start_convert()
↓
线程池中的工作线程:执行_convert_task()
↓
所有线程完成后 → 显示结果
重要技术点:
ThreadPoolExecutor
:Python标准库的线程池实现
Queue
:保证多线程环境下的任务安全存取
threading.Lock()
:防止多个线程同时修改结果列表
with
语句:自动管理资源(如文件关闭)