吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 497|回复: 20
上一主题 下一主题
收起左侧

[Python 原创] word合并工具,支持doc和docx(带GUI)

[复制链接]
跳转到指定楼层
楼主
hustjinlong 发表于 2026-7-23 01:08 回帖奖励
本帖最后由 hustjinlong 于 2026-7-23 22:08 编辑

用python写了一个带GUI界面的word合并工具,选择需合并的文件的目录后可以显示所有文件,支持文件的删除和顺序调整,输出支持自定义文件名,支持显示合并进度。
同时,合并后的文档保证每个原文档都是从奇数页开始,方便双面打印。
界面如图:


7.23更新
原代码需要有qfluentwidget依赖,win7不支持,进行了降级,界面稍逊色一点,界面如图:

附件略大,无法直接上传,见谅。win7和win10版本下载地址里均有:
下载地址.txt (133 Bytes, 下载次数: 11)


附上源码
[Python] 纯文本查看 复制代码
import sys, os
import pythoncom
import win32com.client
from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout,
    QFileDialog, QListWidget, QAbstractItemView
)
from qfluentwidgets import (
    PrimaryPushButton, PushButton, LineEdit, RadioButton,
    SimpleCardWidget, TitleLabel,
    InfoBar, InfoBarPosition, setThemeColor
)


class Worker(QThread):
    progress = Signal(int, int)
    log = Signal(str)
    finished = Signal(str)
    error = Signal(str)

    def __init__(self, files, folder_path, output_option, output_name):
        super().__init__()
        self.files = files
        self.folder_path = folder_path
        self.output_option = output_option
        self.output_name = output_name

    def _ensure_docx(self, word, file_path):
        if not file_path.lower().endswith('.doc'):
            return file_path
        tmp = os.path.join(os.environ.get('TEMP', os.path.dirname(file_path)),
                           f'~_convert_{os.path.basename(file_path)}x')
        doc = word.Documents.Open(file_path)
        doc.SaveAs(tmp, FileFormat=16)
        doc.Close()
        return tmp

    def run(self):
        pythoncom.CoInitialize()
        folder_name = self.output_name or os.path.basename(os.path.normpath(self.folder_path))
        if self.output_option == '桌面':
            output_file = os.path.join(os.environ['USERPROFILE'], 'Desktop', folder_name + '.docx')
        else:
            output_file = os.path.join(self.folder_path, folder_name + '.docx')

        word = win32com.client.Dispatch("Word.Application")
        word.visible = False
        tmp_files = []
        try:
            out_doc = word.Documents.Add()

            for i, f in enumerate(self.files, start=1):
                path = os.path.join(self.folder_path, f)
                orig_is_doc = path.lower().endswith('.doc')
                path = self._ensure_docx(word, path)
                if orig_is_doc:
                    tmp_files.append(path)

                if i > 1:
                    word.Selection.EndKey(6)
                    out_doc.Repaginate()
                    if word.Selection.Information(3) % 2 == 1:
                        word.Selection.InsertBreak(5)

                word.Selection.EndKey(6)
                word.Selection.InsertFile(path)

                self.log.emit(f"已添加: {f}")
                self.progress.emit(i, len(self.files))

            out_doc.SaveAs(output_file)
            out_doc.Close()
        except Exception as e:
            self.error.emit(f"合并失败: {e}")
        finally:
            for t in tmp_files:
                try:
                    os.remove(t)
                except OSError:
                    pass
            word.Quit()
            pythoncom.CoUninitialize()

        self.finished.emit(output_file)


class WordCombineInterface(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAttribute(Qt.WA_StyledBackground)
        self.setStyleSheet("background-color: #FFFFFF; SimpleCardWidget { border-radius: 12px; }")
        layout = QVBoxLayout(self)
        layout.setSpacing(20)
        layout.setContentsMargins(40, 30, 40, 30)

        card1 = SimpleCardWidget(self)
        c1 = QVBoxLayout(card1)
        c1.setSpacing(12)
        c1.addWidget(TitleLabel("输入文件夹"))
        row1 = QHBoxLayout()
        self.folder_path = LineEdit()
        self.folder_path.setPlaceholderText("请选择要读取文件的目录...")
        self.folder_path.setReadOnly(True)
        row1.addWidget(self.folder_path)
        btn_browse = PushButton("浏览...")
        btn_browse.clicked.connect(self.browse_folder)
        row1.addWidget(btn_browse)
        c1.addLayout(row1)
        layout.addWidget(card1)

        card_files = SimpleCardWidget(self)
        cf = QVBoxLayout(card_files)
        cf.setSpacing(8)
        cf.addWidget(TitleLabel("文件列表"))
        self.file_list = QListWidget()
        self.file_list.setDragDropMode(QAbstractItemView.InternalMove)
        self.file_list.setDefaultDropAction(Qt.MoveAction)
        self.file_list.keyPressEvent = lambda e: (
            self.file_list.takeItem(self.file_list.currentRow())
            if e.key() == Qt.Key_Delete and self.file_list.currentRow() >= 0
            else QListWidget.keyPressEvent(self.file_list, e)
        )
        cf.addWidget(self.file_list)
        layout.addWidget(card_files, 1)

        card2 = SimpleCardWidget(self)
        c2 = QVBoxLayout(card2)
        c2.setSpacing(12)
        c2.addWidget(TitleLabel("输出设置"))
        self.radio_desktop = RadioButton("桌面")
        self.radio_same = RadioButton("与输入文件夹相同")
        self.radio_desktop.setChecked(True)
        c2.addWidget(self.radio_desktop)
        c2.addWidget(self.radio_same)
        c2.addSpacing(4)
        c2.addWidget(TitleLabel("文件名(留空默认用文件夹名)"))
        self.output_name = LineEdit()
        self.output_name.setPlaceholderText("留空则使用输入文件夹名称...")
        c2.addWidget(self.output_name)
        layout.addWidget(card2)

        self.btn_start = PrimaryPushButton("开始合并")
        self.btn_start.clicked.connect(self.start_merge)
        layout.addWidget(self.btn_start)
        layout.addStretch()

    def browse_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "选择文件夹")
        if folder:
            self.folder_path.setText(folder)
            self.refresh_file_list(folder)

    def refresh_file_list(self, folder):
        self.file_list.clear()
        files = sorted([f for f in os.listdir(folder) if f.endswith(('.docx', '.doc'))])
        if not files:
            self.file_list.addItem("(未发现 .docx / .doc 文件)")
            return
        for f in files:
            self.file_list.addItem(f)

    def start_merge(self):
        folder = self.folder_path.text()
        if not folder or not os.path.isdir(folder):
            InfoBar.error(title="错误", content="请先选择一个有效的文件夹",
                          parent=self, position=InfoBarPosition.TOP, duration=3000)
            return

        items = [self.file_list.item(i).text() for i in range(self.file_list.count())]
        items = [f for f in items if f.endswith(('.docx', '.doc'))]
        if not items:
            InfoBar.error(title="错误", content="没有可合并的 Word 文档",
                          parent=self, position=InfoBarPosition.TOP, duration=3000)
            return

        self.btn_start.setEnabled(False)
        self.btn_start.setText("处理中 0%")

        out_opt = '桌面' if self.radio_desktop.isChecked() else '相同文件夹'
        self.worker = Worker(items, folder, out_opt, self.output_name.text().strip())
        self.worker.progress.connect(self.update_progress)
        self.worker.finished.connect(self.on_finished)
        self.worker.error.connect(self.on_error)
        self.worker.start()

    def update_progress(self, current, total):
        self.btn_start.setText(f"处理中 {int(current / total * 100)}%")

    def on_finished(self, output_file):
        self.btn_start.setText("开始合并")
        self.btn_start.setEnabled(True)
        InfoBar.success(title="合并完成", content=f"文件已保存到: {output_file}",
                        parent=self, position=InfoBarPosition.TOP, duration=5000)

    def on_error(self, msg):
        self.btn_start.setText("开始合并")
        self.btn_start.setEnabled(True)
        InfoBar.error(title="错误", content=msg,
                      parent=self, position=InfoBarPosition.TOP, duration=5000)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    setThemeColor("#0078D4")
    w = WordCombineInterface()
    w.resize(700, 600)
    w.setWindowTitle("Word 文件一键合并工具")
    w.show()
    sys.exit(app.exec())

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
宜城小站 发表于 2026-7-23 14:15
期待楼主成品链接,总结性材料合并收集非常有用
推荐
wufashihu 发表于 2026-7-24 01:34
宜城小站 发表于 2026-7-23 14:15
期待楼主成品链接,总结性材料合并收集非常有用

帮你们弄好了成品。

下载链接.txt (57 Bytes, 下载次数: 0)


下载:https://wwaye.lanzoue.com/ifHLX3y9tete 密码:fm7y
沙发
lvcha128 发表于 2026-7-23 13:17
3#
xz1203 发表于 2026-7-23 13:17
win7系统可以用吗
4#
zxxputian 发表于 2026-7-23 13:22
没有下载地址?
5#
ouzhzh0 发表于 2026-7-23 13:59
没有下载地址,另外,会合并时会遗漏吗?
7#
w_y_j 发表于 2026-7-23 14:18
楼主是好人
8#
zz4zyfox 发表于 2026-7-23 14:58
好人不骗人
9#
Remberance 发表于 2026-7-23 15:11
这个不错啊,单合并吗?有的时候需要分解呢。
10#
Ling1234 发表于 2026-7-23 16:26
感谢楼主!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-7-25 00:41

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表