
欢迎大家B站搜索:丁老师的技术随笔

采用 PySide6+Python|自制批量转PDF工具,附代码思路
文末有打包完成的Docx转PDF工具.exe文件,无需安装,双击即可使用.

#!/usr/bin/env python3# -*- coding: utf-8 -*-"""批量把 .docx 转 PDF —— PySide6 GUIauthor : qustdjx"""import sys, os, pathlib, threading, time, tracebackfrom typing import Listfrom PySide6.QtCore import Qt, QThread, Signal, QObjectfrom PySide6.QtWidgets import ( QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit, QFileDialog, QProgressBar, QTextEdit, QLabel)# 用 docx2pdf 做转换(底层调用 Word COM,Windows 需装 Word;macOS 用 Office 或 Pages)from docx2pdf import convert# ========== 后台线程 ==========class Worker(QObject): # 信号 log_signal = Signal(str) # 给日志框 progress_max = Signal(int) # 设置进度条最大值 progress = Signal(int) # 当前进度 finished = Signal(bool) # 全部完成/被中止 def __init__(self, input_dir: str, output_dir: str): super().__init__() self.input_dir = input_dir self.output_dir = output_dir self._running = True def stop(self): self._running = False def run(self): try: # 收集所有 docx docx_files: List[pathlib.Path] = [] for root, _, files in os.walk(self.input_dir): for f in files: if f.lower().endswith(".docx"): docx_files.append(pathlib.Path(root) / f) total = len(docx_files) if total == 0: self.log_signal.emit("未找到任何 .docx 文件。") self.finished.emit(True) return self.progress_max.emit(total) self.log_signal.emit(f"共找到 {total} 个文件,开始转换…") for idx, docx in enumerate(docx_files, 1): if not self._running: self.log_signal.emit("*** 用户中止 ***") self.finished.emit(False) return # 构造输出路径,保持目录层级 rel_path = docx.relative_to(self.input_dir) out_pdf = pathlib.Path(self.output_dir) / rel_path.with_suffix(".pdf") out_pdf.parent.mkdir(parents=True, exist_ok=True) self.log_signal.emit(f"[{idx}/{total}] {docx.name}") try: # docx2pdf 的 convert 接口:输入可以是文件或目录,这里指定单文件 convert(str(docx), str(out_pdf)) except Exception as e: self.log_signal.emit(f" 失败:{e}") self.progress.emit(idx) time.sleep(0.05) # 给 UI 一点刷新时间 self.log_signal.emit("=== 全部完成!===") self.finished.emit(True) except Exception as e: self.log_signal.emit("发生异常:\n" + traceback.format_exc()) self.finished.emit(False)# ========== 主窗口 ==========class MainWindow(QWidget): def __init__(self): super().__init__() self.setWindowTitle("批量 docx → PDF 转换器") self.resize(700, 500) # 输入目录 line_in, btn_in = self._make_browser("输入目录") self.line_in = line_in btn_in.clicked.connect(lambda: self.browse_dir(line_in)) # 输出目录 line_out, btn_out = self._make_browser("输出目录") self.line_out = line_out btn_out.clicked.connect(lambda: self.browse_dir(line_out)) # 控制按钮 self.btn_start = QPushButton("开始转换") self.btn_stop = QPushButton("中止") self.btn_stop.setEnabled(False) self.btn_start.clicked.connect(self.start) self.btn_stop.clicked.connect(self.stop) hbox = QHBoxLayout() hbox.addWidget(self.btn_start) hbox.addWidget(self.btn_stop) # 进度条 self.progress = QProgressBar() self.progress.setTextVisible(True) # 日志 self.log = QTextEdit() self.log.setReadOnly(True) # 整体布局 vbox = QVBoxLayout(self) vbox.addWidget(QLabel("请选择输入/输出目录,然后点击“开始转换”")) vbox.addLayout(self._make_browser_layout("输入目录", line_in, btn_in)) vbox.addLayout(self._make_browser_layout("输出目录", line_out, btn_out)) vbox.addLayout(hbox) vbox.addWidget(self.progress) vbox.addWidget(self.log) vbox.setStretchFactor(self.log, 1) # 线程相关 self.thread: QThread | None = None self.worker: Worker | None = None # --------------- UI 小工具 --------------- def _make_browser(self, title: str): line = QLineEdit() line.setClearButtonEnabled(True) btn = QPushButton("浏览…") return line, btn def _make_browser_layout(self, title: str, line: QLineEdit, btn: QPushButton): h = QHBoxLayout() h.addWidget(QLabel(title + ":")) h.addWidget(line) h.addWidget(btn) return h def browse_dir(self, line: QLineEdit): path = QFileDialog.getExistingDirectory(self, "选择目录") if path: line.setText(path) # --------------- 转换逻辑 --------------- def start(self): in_dir = self.line_in.text().strip() out_dir = self.line_out.text().strip() if not in_dir or not out_dir: self.log.append("请先选择输入/输出目录!") return if not os.path.isdir(in_dir): self.log.append("输入目录无效!") return # 初始化界面 self.btn_start.setEnabled(False) self.btn_stop.setEnabled(True) self.progress.setValue(0) self.log.clear() # 启动线程 self.thread = QThread() self.worker = Worker(in_dir, out_dir) self.worker.moveToThread(self.thread) self.thread.started.connect(self.worker.run) self.worker.finished.connect(self.thread.quit) self.worker.finished.connect(self.done) self.worker.log_signal.connect(self.log_append) self.worker.progress_max.connect(self.progress.setMaximum) self.worker.progress.connect(self.progress.setValue) self.thread.start() def stop(self): if self.worker: self.worker.stop() def done(self, success: bool): self.btn_start.setEnabled(True) self.btn_stop.setEnabled(False) self.thread = None self.worker = None def log_append(self, txt: str): self.log.append(txt)# ========== main ==========if __name__ == "__main__": app = QApplication(sys.argv) w = MainWindow() w.show() sys.exit(app.exec())使用pyinstaller进行 .exe 打包,让系统不安装python 也可以正常使用
pyinstaller --onefile --windowed --icon=app.ico --name="Docx转PDF工具" main.py打包效果:

通过网盘分享的文件:Docx转PDF工具.exe 链接: https://pan.baidu.com/s/1Ako0EVG_qA7ahEq7sjkJhA 提取码: 6det
通过网盘分享的文件:Docx转PDF工具.exe 提取码: 6det
END


关注二维码
获取更多精彩内容
