Python 使用 PyQt5 + Pandas 实现 Excel(xlsx)批量合并工具(带图形界面)

Python 使用 PyQt5 + Pandas 实现 Excel(xlsx)批量合并工具(带图形界面)

在日常办公或数据处理中,经常会遇到多个 Excel 文件需要合并的场景。手工复制不仅效率低,而且容易出错。

本文将手把手教你使用 Python + PyQt5 + Pandas 编写一个带图形界面的 Excel 合并工具,支持:

✅ 选择文件夹

✅ 自动读取多个 .xlsx 文件

✅ 不同列自动对齐合并

✅ 一键生成合并后的 Excel 文件

✅ 自带程序图标(Base64 内嵌)


📌 一、效果预览

功能说明:

  • 选择包含多个 .xlsx 文件的文件夹

  • 自动显示文件列表

  • 点击「合并 Excel 文件」

  • 在当前目录生成 合并.xlsx


📌 二、环境准备

1️⃣ Python 版本

推荐:

bash 复制代码
Python 3.8+

2️⃣ 安装依赖

bash 复制代码
pip install pyqt5 pandas openpyxl

说明:

库名 用途
PyQt5 GUI 图形界面
pandas Excel 数据处理
openpyxl 读取 / 写入 xlsx

📌 三、功能设计说明

1️⃣ 合并逻辑说明

  • 逐个读取 Excel 文件

  • 自动收集 所有出现过的列

  • 不同文件中缺失的列自动补 NaN

  • 最终按列名对齐合并

示意:

文件
A.xlsx 姓名、年龄
B.xlsx 姓名、性别
合并结果 姓名、年龄、性别

2️⃣ GUI 组件说明

组件 功能
QLabel 提示文本
QPushButton 选择文件夹 / 合并
QFileDialog 目录选择
QTableWidget 文件列表
QMessageBox 完成 / 错误提示

📌 四、完整源码(可直接运行)

💡 代码内嵌 Base64 图标,首次运行会自动生成 icon.png

python 复制代码
import sys
import os
import pandas as pd
import base64
from PyQt5.QtWidgets import (
    QApplication, QWidget, QMessageBox, QPushButton,
    QFileDialog, QVBoxLayout, QLabel, QTableWidget, QTableWidgetItem
)
from PyQt5.QtGui import QIcon, QPixmap

🔹 Base64 图标处理

python 复制代码
base64_icon = "data:image/png;base64,......(略)"
icon_data = base64_icon.split(',')[1]
icon_bytes = base64.b64decode(icon_data)

🔹 主窗口类

python 复制代码
class ExcelMergerApp(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

🔹 初始化 UI

python 复制代码
def initUI(self):
    self.setWindowTitle('xlsx多个文件合并')
    self.setGeometry(100, 100, 800, 500)

    if os.path.exists('icon.png'):
        self.setWindowIcon(QIcon('icon.png'))
    else:
        pixmap = QPixmap()
        pixmap.loadFromData(icon_bytes)
        self.setWindowIcon(QIcon(pixmap))
        pixmap.save('icon.png', 'PNG')

    self.info_label = QLabel('选择包含要合并的Excel文件的文件夹:')
    self.select_folder_button = QPushButton('选择文件夹')
    self.merge_button = QPushButton('合并Excel文件')
    self.folder_label = QLabel('')

    self.file_table = QTableWidget()
    self.file_table.setColumnCount(1)
    self.file_table.setHorizontalHeaderLabels(['文件列表'])

    layout = QVBoxLayout()
    layout.addWidget(self.info_label)
    layout.addWidget(self.select_folder_button)
    layout.addWidget(self.folder_label)
    layout.addWidget(self.file_table)
    layout.addWidget(self.merge_button)

    self.setLayout(layout)

    self.select_folder_button.clicked.connect(self.get_folder)
    self.merge_button.clicked.connect(self.merge_excel_files)

🔹 选择文件夹 & 显示文件列表

python 复制代码
def get_folder(self):
    folder = QFileDialog.getExistingDirectory(self, "选择包含Excel文件的文件夹")
    if folder:
        self.folder_path = folder
        self.folder_label.setText(f'选择的文件夹: {folder}')
        self.populate_file_table()
python 复制代码
def populate_file_table(self):
    files = [f for f in os.listdir(self.folder_path) if f.endswith('.xlsx')]
    files.sort()
    self.file_table.setRowCount(len(files))
    for i, f in enumerate(files):
        self.file_table.setItem(i, 0, QTableWidgetItem(f))

🔹 Excel 合并核心逻辑

python 复制代码
def merge_excel_files(self):
    if not hasattr(self, 'folder_path'):
        QMessageBox.critical(self, '错误', '请先选择文件夹')
        return

    merged_df = pd.DataFrame()

    for row in range(self.file_table.rowCount()):
        file_name = self.file_table.item(row, 0).text()
        file_path = os.path.join(self.folder_path, file_name)
        df = pd.read_excel(file_path)

        for col in df.columns:
            if col not in merged_df.columns:
                merged_df[col] = None

        merged_df = pd.concat([merged_df, df], ignore_index=True)

    output_path = os.path.join(self.folder_path, '合并.xlsx')
    merged_df.to_excel(output_path, index=False)

    QMessageBox.information(self, '完成', f'合并完成,文件保存到:\n{output_path}')

🔹 程序入口

python 复制代码
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = ExcelMergerApp()
    window.show()
    sys.exit(app.exec_())

🔹 完整代码

复制代码
import sys
import os
import pandas as pd
from PyQt5.QtWidgets import QApplication, QWidget,QMessageBox, QPushButton, QFileDialog, QVBoxLayout, QLabel, QTableWidget, QTableWidgetItem
from PyQt5.QtCore import Qt,QByteArray
from PyQt5.QtGui import QIcon, QPixmap
import base64
base64_icon = "data:image/png;base64,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"
icon_data = base64_icon.split(',')[1]
icon_bytes = base64.b64decode(icon_data)
class ExcelMergerApp(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('xlsx多个文件合并')
        self.setGeometry(100, 100, 800, 500)
        if os.path.exists('icon.png'):
            icon = QIcon('icon.png')
            self.setWindowIcon(icon)
        else:
             # 将字节数据转换为 QPixmap
            pixmap = QPixmap()
            pixmap.loadFromData(icon_bytes)

            # 创建 QIcon 并将其设置为窗口图标
            icon = QIcon(pixmap)
            self.setWindowIcon(icon)
            pixmap.save('icon.png', 'PNG')
        

        self.info_label = QLabel('选择包含要合并的Excel文件的文件夹:')
        self.select_folder_button = QPushButton('选择文件夹')
        self.merge_button = QPushButton('合并Excel文件')

        self.folder_label = QLabel('')
        
        self.file_table = QTableWidget()
        self.file_table.setColumnCount(1)
        self.file_table.setHorizontalHeaderLabels(['文件列表'])

        layout = QVBoxLayout()
        layout.addWidget(self.info_label)
        layout.addWidget(self.select_folder_button)
        layout.addWidget(self.folder_label)
        layout.addWidget(self.file_table)
        layout.addWidget(self.merge_button)
        layout.addStretch()  
        self.setLayout(layout)

        self.select_folder_button.clicked.connect(self.get_folder)
        self.merge_button.clicked.connect(self.merge_excel_files)

    def get_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "选择包含Excel文件的文件夹")
        if folder:
            self.folder_path = folder
            self.folder_label.setText(f'选择的文件夹: {folder}')
            self.populate_file_table()

    def populate_file_table(self):
        file_names = [file for file in os.listdir(self.folder_path) if file.endswith(".xlsx")]
        file_names.sort()  # Sort the file names
        self.file_table.setRowCount(len(file_names))
        for i, file_name in enumerate(file_names):
            item = QTableWidgetItem(file_name)
            self.file_table.setItem(i, 0, item)

    def merge_excel_files(self):
        if hasattr(self, 'folder_path'):
            # 添加您在上面的代码中执行的Excel文件合并操作
            unique_columns = pd.DataFrame()
            for row in range(self.file_table.rowCount()):
                file_name = self.file_table.item(row, 0).text()
                file_path = os.path.join(self.folder_path, file_name)
                df = pd.read_excel(file_path)
                file_columns = df.columns
                for column in file_columns:
                    if column not in unique_columns.columns:
                        unique_columns[column] = None
                unique_columns = pd.concat([unique_columns, df[file_columns]], ignore_index=True)

            # 保存合并后的数据到output.xlsx文件,包括列名
            output_file = "合并.xlsx"
            output_file_path = os.path.join(self.folder_path, output_file)
            unique_columns.to_excel(output_file_path, index=False)
            msg = QMessageBox()
            icon = QIcon("icon.png")
            msg.setWindowIcon(icon)
            msg.setIcon(QMessageBox.Information)
            msg.setText(f'合并完成,结果保存到 {output_file_path}')
            msg.setWindowTitle('合并完成')
            msg.exec_()
        else:
            msg = QMessageBox()
            icon = QIcon("icon.png")
            msg.setWindowIcon(icon)
            msg.setIcon(QMessageBox.Critical)
            msg.setText('请选择文件夹以合并Excel文件')
            msg.setWindowTitle('错误')
            msg.exec()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = ExcelMergerApp()
    ex.show()
    sys.exit(app.exec_())
相关推荐
码路飞1 小时前
写了个 AI 聊天页面,被 5 种流式格式折腾了一整天 😭
javascript·python
曲幽3 小时前
FastAPI压力测试实战:Locust模拟真实用户并发及优化建议
python·fastapi·web·locust·asyncio·test·uvicorn·workers
敏编程8 小时前
一天一个Python库:jsonschema - JSON 数据验证利器
python
前端付豪8 小时前
LangChain记忆:通过Memory记住上次的对话细节
人工智能·python·langchain
databook8 小时前
ManimCE v0.20.1 发布:LaTeX 渲染修复与动画稳定性提升
python·动效
花酒锄作田21 小时前
使用 pkgutil 实现动态插件系统
python
前端付豪1 天前
LangChain链 写一篇完美推文?用SequencialChain链接不同的组件
人工智能·python·langchain
曲幽1 天前
FastAPI实战:打造本地文生图接口,ollama+diffusers让AI绘画更听话
python·fastapi·web·cors·diffusers·lcm·ollama·dreamshaper8·txt2img
老赵全栈实战1 天前
Pydantic配置管理最佳实践(一)
python
阿尔的代码屋1 天前
[大模型实战 07] 基于 LlamaIndex ReAct 框架手搓全自动博客监控 Agent
人工智能·python