338 lines
12 KiB
Python
338 lines
12 KiB
Python
import os
|
||
import comtypes.client
|
||
import random
|
||
import datetime
|
||
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||
QPushButton, QFrame, QListWidget, QListWidgetItem,
|
||
QFileDialog, QProgressBar, QMessageBox, QSpacerItem)
|
||
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSize
|
||
from PyQt5.QtGui import QPixmap, QIcon, QDragEnterEvent, QDropEvent
|
||
|
||
|
||
def generate_unique_filename(original_path):
|
||
"""生成唯一的文件名格式:原名称_日期_随机数.pdf"""
|
||
base_name = os.path.basename(original_path)
|
||
name_without_ext = os.path.splitext(base_name)[0]
|
||
|
||
# 获取当前日期
|
||
current_date = datetime.datetime.now().strftime("%Y%m%d")
|
||
|
||
# 生成4位随机数
|
||
random_num = random.randint(1000, 9999)
|
||
|
||
# 组合成新文件名
|
||
new_filename = f"{name_without_ext}_{current_date}_{random_num}.pdf"
|
||
return os.path.join(os.path.dirname(original_path), new_filename)
|
||
|
||
|
||
class ConversionThread(QThread):
|
||
progress_updated = pyqtSignal(int, int, str) # current, total, filename
|
||
conversion_done = pyqtSignal(str, bool, str) # filename, success, message
|
||
finished_all = pyqtSignal()
|
||
|
||
def __init__(self, files):
|
||
super().__init__()
|
||
self.files = files
|
||
|
||
def run(self):
|
||
word = None
|
||
try:
|
||
word = comtypes.client.CreateObject('Word.Application')
|
||
word.Visible = False
|
||
|
||
total = len(self.files)
|
||
for i, file_path in enumerate(self.files):
|
||
self.progress_updated.emit(i + 1, total, os.path.basename(file_path))
|
||
|
||
try:
|
||
# 使用新的文件名生成逻辑
|
||
pdf_path = generate_unique_filename(file_path)
|
||
doc = word.Documents.Open(os.path.abspath(file_path))
|
||
doc.SaveAs(pdf_path, FileFormat=17) # 17 = PDF
|
||
doc.Close()
|
||
self.conversion_done.emit(file_path, True, pdf_path) # 传递新文件路径
|
||
except Exception as e:
|
||
self.conversion_done.emit(file_path, False, str(e))
|
||
|
||
self.finished_all.emit()
|
||
finally:
|
||
if word:
|
||
word.Quit()
|
||
|
||
|
||
class WordToPDFPage(QWidget):
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.files = []
|
||
self.converted_files = {}
|
||
self.conversion_thread = None
|
||
self.init_ui()
|
||
|
||
def init_ui(self):
|
||
self.setObjectName("wordToPdfPage")
|
||
self.setAcceptDrops(True)
|
||
|
||
layout = QHBoxLayout()
|
||
layout.setContentsMargins(20, 20, 20, 20)
|
||
layout.setSpacing(20)
|
||
|
||
# 左侧面板 - 文件上传
|
||
left_panel = QFrame()
|
||
left_panel.setObjectName("leftPanel")
|
||
left_panel.setMinimumWidth(300)
|
||
|
||
left_layout = QVBoxLayout(left_panel)
|
||
left_layout.setContentsMargins(0, 0, 0, 0)
|
||
left_layout.setSpacing(15)
|
||
|
||
# 上传区域
|
||
upload_area = QFrame()
|
||
upload_area.setObjectName("uploadArea")
|
||
upload_area.setFixedHeight(200)
|
||
|
||
upload_layout = QVBoxLayout(upload_area)
|
||
upload_layout.setAlignment(Qt.AlignCenter)
|
||
|
||
upload_icon = QLabel()
|
||
# 确保你有正确的图标路径
|
||
# upload_icon.setPixmap(QPixmap(":/icons/upload.png").scaled(64, 64, Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
||
upload_icon.setAlignment(Qt.AlignCenter)
|
||
|
||
upload_text = QLabel("拖拽文件或文件夹到此处\n或点击上传")
|
||
upload_text.setAlignment(Qt.AlignCenter)
|
||
upload_text.setWordWrap(True)
|
||
|
||
upload_btn = QPushButton("选择文件或文件夹")
|
||
upload_btn.setObjectName("uploadBtn")
|
||
upload_btn.clicked.connect(self.handle_upload)
|
||
|
||
upload_layout.addWidget(upload_icon)
|
||
upload_layout.addWidget(upload_text)
|
||
upload_layout.addWidget(upload_btn)
|
||
|
||
# 文件列表
|
||
self.file_list = QListWidget()
|
||
self.file_list.setObjectName("fileList")
|
||
self.file_list.setSelectionMode(QListWidget.ExtendedSelection)
|
||
|
||
# 操作按钮
|
||
btn_layout = QHBoxLayout()
|
||
|
||
convert_btn = QPushButton("开始转换")
|
||
convert_btn.setObjectName("convertBtn")
|
||
convert_btn.clicked.connect(self.start_conversion)
|
||
|
||
delete_btn = QPushButton("删除选中")
|
||
delete_btn.setObjectName("deleteBtn")
|
||
delete_btn.clicked.connect(self.delete_selected)
|
||
|
||
btn_layout.addWidget(convert_btn)
|
||
btn_layout.addWidget(delete_btn)
|
||
|
||
# 组装左侧面板
|
||
left_layout.addWidget(upload_area)
|
||
left_layout.addWidget(self.file_list)
|
||
left_layout.addLayout(btn_layout)
|
||
|
||
# 右侧面板 - 转换结果
|
||
right_panel = QFrame()
|
||
right_panel.setObjectName("rightPanel")
|
||
|
||
right_layout = QVBoxLayout(right_panel)
|
||
right_layout.setContentsMargins(0, 0, 0, 0)
|
||
right_layout.setSpacing(15)
|
||
|
||
# 进度条
|
||
self.total_progress = QProgressBar()
|
||
self.total_progress.setTextVisible(False)
|
||
|
||
self.file_progress = QProgressBar()
|
||
self.file_progress.setTextVisible(False)
|
||
|
||
# 结果列表
|
||
self.result_list = QListWidget()
|
||
self.result_list.setObjectName("resultList")
|
||
|
||
# 下载按钮
|
||
self.download_btn = QPushButton("全部下载")
|
||
self.download_btn.setObjectName("downloadBtn")
|
||
self.download_btn.setEnabled(False)
|
||
self.download_btn.clicked.connect(self.download_all)
|
||
|
||
# 日志
|
||
log_area = QFrame()
|
||
log_area.setObjectName("logArea")
|
||
|
||
log_layout = QVBoxLayout(log_area)
|
||
|
||
log_title = QLabel("转换日志")
|
||
log_title.setObjectName("logTitle")
|
||
|
||
self.log_content = QListWidget()
|
||
self.log_content.setObjectName("logContent")
|
||
|
||
log_layout.addWidget(log_title)
|
||
log_layout.addWidget(self.log_content)
|
||
|
||
# 组装右侧面板
|
||
right_layout.addWidget(self.total_progress)
|
||
right_layout.addWidget(self.file_progress)
|
||
right_layout.addWidget(self.result_list)
|
||
right_layout.addWidget(self.download_btn)
|
||
right_layout.addWidget(log_area)
|
||
|
||
# 主布局
|
||
layout.addWidget(left_panel)
|
||
layout.addWidget(right_panel)
|
||
|
||
self.setLayout(layout)
|
||
|
||
def dragEnterEvent(self, event: QDragEnterEvent):
|
||
if event.mimeData().hasUrls():
|
||
event.acceptProposedAction()
|
||
|
||
def dropEvent(self, event: QDropEvent):
|
||
urls = event.mimeData().urls()
|
||
files = []
|
||
|
||
for url in urls:
|
||
path = url.toLocalFile()
|
||
if os.path.isdir(path):
|
||
# 处理文件夹
|
||
for root, _, filenames in os.walk(path):
|
||
for f in filenames:
|
||
if f.lower().endswith(('.doc', '.docx')):
|
||
files.append(os.path.join(root, f))
|
||
elif os.path.isfile(path) and path.lower().endswith(('.doc', '.docx')):
|
||
files.append(path)
|
||
|
||
if files:
|
||
self.add_files(files)
|
||
|
||
def handle_upload(self):
|
||
"""处理文件或文件夹上传"""
|
||
# 使用getExistingDirectory获取文件夹
|
||
folder = QFileDialog.getExistingDirectory(self, "选择文件夹")
|
||
if folder:
|
||
files = []
|
||
for root, _, filenames in os.walk(folder):
|
||
for f in filenames:
|
||
if f.lower().endswith(('.doc', '.docx')):
|
||
files.append(os.path.join(root, f))
|
||
self.add_files(files)
|
||
else:
|
||
# 如果用户没有选择文件夹,则选择文件
|
||
files, _ = QFileDialog.getOpenFileNames(
|
||
self, "选择Word文件", "", "Word Files (*.doc *.docx);;All Files (*)"
|
||
)
|
||
if files:
|
||
self.add_files(files)
|
||
|
||
def add_files(self, files):
|
||
"""添加文件到列表,避免重复"""
|
||
for file in files:
|
||
if file not in self.files and os.path.exists(file):
|
||
self.files.append(file)
|
||
item = QListWidgetItem(os.path.basename(file))
|
||
item.setData(Qt.UserRole, file)
|
||
self.file_list.addItem(item)
|
||
|
||
def delete_selected(self):
|
||
"""删除选中的文件"""
|
||
for item in self.file_list.selectedItems():
|
||
self.files.remove(item.data(Qt.UserRole))
|
||
self.file_list.takeItem(self.file_list.row(item))
|
||
|
||
def start_conversion(self):
|
||
"""开始转换文件"""
|
||
if not self.files:
|
||
QMessageBox.warning(self, "警告", "请先添加要转换的文件!")
|
||
return
|
||
|
||
# 重置状态
|
||
self.converted_files = {}
|
||
self.result_list.clear()
|
||
self.log_content.clear()
|
||
self.download_btn.setEnabled(False)
|
||
|
||
self.conversion_thread = ConversionThread(self.files.copy())
|
||
self.conversion_thread.progress_updated.connect(self.update_progress)
|
||
self.conversion_thread.conversion_done.connect(self.handle_conversion_done)
|
||
self.conversion_thread.finished_all.connect(self.handle_finished_all)
|
||
self.conversion_thread.start()
|
||
|
||
def update_progress(self, current, total, filename):
|
||
"""更新进度条"""
|
||
self.total_progress.setMaximum(total)
|
||
self.total_progress.setValue(current)
|
||
|
||
self.file_progress.setMaximum(100)
|
||
self.file_progress.setValue(0)
|
||
|
||
self.log_content.addItem(f"正在转换: {filename} ({current}/{total})")
|
||
|
||
def handle_conversion_done(self, file_path, success, message):
|
||
"""处理单个文件转换完成"""
|
||
basename = os.path.basename(file_path)
|
||
if success:
|
||
# 成功时,message包含新的PDF路径
|
||
pdf_path = message
|
||
self.converted_files[file_path] = pdf_path
|
||
|
||
# 从待转换列表移除
|
||
for i in range(self.file_list.count()):
|
||
item = self.file_list.item(i)
|
||
if item.data(Qt.UserRole) == file_path:
|
||
self.file_list.takeItem(i)
|
||
break
|
||
|
||
# 添加到结果列表
|
||
item = QListWidgetItem(f"{basename} → {os.path.basename(pdf_path)}")
|
||
item.setData(Qt.UserRole, pdf_path)
|
||
self.result_list.addItem(item)
|
||
|
||
self.log_content.addItem(f"✅ 转换成功: {basename} -> {os.path.basename(pdf_path)}")
|
||
else:
|
||
# 失败时显示错误信息
|
||
self.log_content.addItem(f"❌ 转换失败: {basename} - {message}")
|
||
|
||
def handle_finished_all(self):
|
||
"""所有文件转换完成"""
|
||
self.log_content.addItem(f"🎉 全部转换完成! 共转换 {len(self.converted_files)} 个文件")
|
||
self.download_btn.setEnabled(True)
|
||
|
||
def download_all(self):
|
||
"""下载所有转换后的文件"""
|
||
if not self.converted_files:
|
||
return
|
||
|
||
folder = QFileDialog.getExistingDirectory(self, "选择保存位置")
|
||
if not folder:
|
||
return
|
||
|
||
success = 0
|
||
errors = []
|
||
|
||
for pdf_path in self.converted_files.values():
|
||
try:
|
||
file_name = os.path.basename(pdf_path)
|
||
destination = os.path.join(folder, file_name)
|
||
|
||
# 复制文件
|
||
with open(pdf_path, 'rb') as src_file:
|
||
with open(destination, 'wb') as dest_file:
|
||
dest_file.write(src_file.read())
|
||
success += 1
|
||
except Exception as e:
|
||
errors.append(f"{os.path.basename(pdf_path)}: {str(e)}")
|
||
|
||
# 显示结果
|
||
if errors:
|
||
error_msg = "\n".join(errors[:10]) # 最多显示10个错误
|
||
if len(errors) > 10:
|
||
error_msg += f"\n...等共 {len(errors)} 个错误"
|
||
QMessageBox.warning(self, "部分文件下载失败", f"成功下载 {success} 个文件\n失败文件:\n{error_msg}")
|
||
else:
|
||
QMessageBox.information(self, "下载完成", f"已成功下载 {success} 个文件到 {folder}")
|
||
|
||
self.log_content.addItem(f"已下载 {success} 个文件到 {folder}") |