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,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAAAXNSR0IArs4c6QAAGhpJREFUeF7tXQuYXEWVPuf29ASi0aARITOJk751e4bwUAFBAdkQFgUURfGBIrKLorgioshDUWHBF0QEUUQWdVkFwVWUBXdVBIkKygLRFUjCTJ/qGWEIICJB2CFmuu/Z7wzd2UkymenHfdTtOfV984VMqs75z1/351bVrTqFoCVyBgYGBl44Pj6+MyIulB9m3pmZd0DE5wKA/Mxj5uci4rza3yd+BwAvqIFZDwBP1X+Y+SlEfHrS359GxEcRcaharQ6Vy+WhyINQgxMMoPLQHgNBEBSYeW9m3gsR9waAPQFgfntWm249zsxlRBwU0YRhOOR53uD4+PjQyMjII01b0wabGFCBNPkwFAqFIJfLLWfmgwBgGQC8uEkTSVd/HADuZuY7EfEuALiDiB5LGkRW/alAZui5QqGwOJfLHVwThIiiN6udPQn3amb+EQBcb61d1QHxxBaCCmQKaovF4oJqtfoPiPguAHhpbOy7YfghZr6uJpZb3YDkDgoVSK0v+vr6tsvn829i5mMB4DUAkHOnmxJDIkOv65n5h4sWLbp55cqVlcQ8O+potgsEfd9fhogiirfUVpIc7arEYf0JAC4DgEtn85xlVgrE932DiMcDwDEAsDjxRy9bDv/GzNcw85fK5fK92YLePtpZJZBCoXC453mnAsDy9qmbfRYQ8ZYwDL9orf3pbIm+4wWydOnS7o0bN8oQ6qMAsHS2dGzMca5GxAvz+fzVa9as2Rizr1TNd6xAjDHPA4APAsDJALBTqix3qHNmftjzvIuZ+etE9NdODLPjBNLX17dTPp8/lZnfr5PuxB7Zv4pIqtXqRZ325b5jBGKMmQMAnwCA0wFgu8QeDXU0mYENAPB5ADifiP7WCdR0hEB83z8SAC5CxL5O6JSsx8DMI4h4IhH9LOuxZFogAwMDfZVK5WsAcFjWO6JD8d/AzCdZax/ManyZFIisTI2Pj5/OzGfpcMr5R+8ZADivt7d3RRa/zGdOIEEQHMjM3wIA3/lHQwFuYoCZBwHgeGvtb7JES2YEstdee+XXr1+/AhE/nCWCFetWDFzY29t7ZlbeJpkQSG2ucf0s2Fk7W/R0ZxiGby2Xyw+4HrDzApEVKkT8Tu1oqut8Kr7GGXgSAN5NRDc03iT5ms4KRIZUTz755EW1r+HJM6Mek2Lg4u7u7jNc3bLipEB0SJXUs+mMn9/ncrmjBgcHh51BVAPinEB0SOXaI5IYHsni8i7XhlxOCcT3/XMR8VOJdYk6cpGBTxCRbFdxorgiEDTGXA4AJzjBioJIm4GvEpHswua0gaQukNr3jWsR8c1pk6H+3WEAEa/t6ek5Nu3vJakKZI899njO2NjYjQAg6XS0KANbMnDThg0bjhwdHZXtKqmU1AQi6TkrlcrPAeDlqUSuTrPCwJ2VSuW1IyMjko418ZKKQHzfX4SIvwAAk3jE6jCLDNyfz+eXr1279uGkwScuEN/3d0TEuwFgUdLBqr9MM2Cr1er+w8PDjyYZRaIC6evrm9/V1XUbAOyaZJDqq2MYuC+Xy+03ODgo30wSKYkJpJa58FfM/IpEIlMnHckAIt6Wz+cPTmprSlICyRlj/quW0rMjO06DSpSBG4joTQAQxu01EYH4vv9dRHxH3MGo/VnFwDeJ6L1xRxy7QHzfPx8RJdOIFmUgUgaY+Txr7acjNbqFsVgFEgTBicwsCZC1KANxMXASEV0al/HYBGKMkSsEJIdrbD7iIkXtZooBZuaDrbWx3G0Sy8NbLBZ7wjC8L4W7+jLVswo2MgYer1aru8bxjSRygSxbtqxrdHT0DgDYK7Lw1ZAyMDMDtxPRgVGvbEUuEJ2Uz9yTWiMeBuKYtEcqkNq8I/PpJuPpPrWaAAORz0ciE4jOOxLofnXRCAORzkciEYjOOxrpN62TIAORzUciEUgQBCuY+WMJEqCulIFpGUDEz5ZKpU+2S1PbAikUCgd4nvfrdoFoe2UgYgYYEV9VKpX+ux27bQmkdv/faj341E4XaNsYGSh1d3fv1s7O37YEoku6MXatmo6KAbnt6sxWjbUskCAIXsbMcjIw16pzbacMJMBAFQBeQUS/b8VXywIxxojDl7XiVNsoAwkz8Hsi2rMVny0JxBhzDABc1YpDbaMMpMSApDW9ulnfTQtEbpNl5mFE3LlZZ1pfGUiRgXUAUGj29t2mBeL7/lmI+JkUA43T9SoA2FEzrsRJcaq2zyKizzWDoCmBGGNeBAAjADC3GSeO170fAH7LzHdYa/+ljjUIgqOY+VQAeJXj+BVe4ww87XnekqGhoT832qQpgfi+f3EH3RH4OwA4lojWTEeW7/sfQ8QVjRKq9dxmABG/WCqVTmsUZcMC2WWXXXYeHx+XcVzWyzOI+JFSqSTZ5BsqxhhZR3cmJX9DoLXSthh4Jp/P+41maWxYIMaYKwAg9iwSMffrBmZ+nbVW0p42VYwx9wDA7k010squMiDXK3yoEXANCaS/v39JtVotN2LQ5TqIeEipVLq5FYy+7y9HxFtaaatt3GMgl8v1DA4OzjgiakggQRBcwswNKc49Kp5F1I446jEZY24HgP1cjVFxNcVAQ1tQZhRIf3//vGq1KgmDt2/KvTuVWx5WbRmCMeYUAJCbd7Vkn4GnxsbGdlq3bt3YdKHMKJAgCE5n5vMzykdk4pD4gyDYV5aDM8qFwt6CAVmRLZVKl7QlEN/312X0q/mGMAxfXy6XI5s31I4Vj+qT1jEMlInIb1kgxpi3AMD3M0jHRmY+rJXVquli1cNhGXwSZob8xumunp52iGWMuQkADpnZh1M1RBxvtNZKVsdIi340jJROV4z9mIiO2BaYbQqkUCgs9jxPtpXMOE9xJVIAiE0cEqMx5gcAcJRD8SqU9hkI8/l877Y+HG7z4TfGyKauj7fvPzELsYqjUCgUPc+7FwC6E4tIHSXFwNlEdO5UzqYTyCMA8OKkELbrh5kPt9b+pF0722pvjJFcw3p1XFwEp2v3QSJa3LBAfN8/qHYLbbqwG/QehuHryuWy3GAVSzHGfAUATorFuBp1hYFXEdFWS/hTvkF8378MEU90Bfk0OGIdVtXmHecAwNkZ4EIhtsfARUT00S1NTCUQNMY8DgA7tOcv9taxi0MT4sXehy45eISItjolu5VAMjK8UnG49Gh1DpathllbCcQY8yUA+IjLMScw55CzHy3nUnKZO8W2bQaYeYW1drP7NKd6g9yPiP2OEllh5iPi+AhYj1cn5I72fDKwVhPRbpNdbSaQJUuWvCSXy8nHQVfLtNsC2gW9cOHCuXPnzt2nXTvafisGdpT/6TKz/Mi5Gpcz4iwiok377TYTiDHmgwDwVQc7uCJfsKfbM+MgZoU0BQOFQuH5nudJcoy3uUgQM7/PWiunZyfKlgK5EQBe7yDwI4noPxzEpZBaZMD3/eMQ8coWm8fZ7Doikk26UwrkKQB4bpzem7Vd25Ub+cbDZnFo/egZMMbId4cLo7fclsXHiWjBVgLxfX83RJS9Ri6VU4lIVtW0dCgDxphfAoDcTutMCcOwv1wuD202xPJ9/wRE3JQ4zQG0JSIqOoBDIcTIQG3XuOxzmxejm6ZMM/Nx1tpvbyYQY8y3AOAfm7IUY2Vm/qS19rMxulDTjjBgjLkWAN7uCByBcRkR/dOWApEMg7s4BPJQItIrpR3qkLigGGO+AABnxGW/Bbt/IKKJqz0mVrFq6///24KhOJtMubsyTodqOx0GgiA4kZkvS8f71F43bNgwd3R09JkJgRSLxX3CMGzrssMYglOBxECqiyaDIDiZmb/sEjZm3ttau2pCIMaY9wDAN1wCKFnVp9qf7xhGhRMBA8YYmRAfG4GpyEwg4vGlUulfJwTi+/6XEfHkyKxHY+gNRCQfLrV0OAOOntacOB9Sf4NIMueDHOuHK4jofY5hUjgRM1AsFl8dhuGvIjbbtjlmvtlae0hdIA/JXL1tq9Ea2JDL5fxGEgxH61atJcmAMaYko/wkfTboa5SIFmFvb+/222233bT5SRs0GEe1W4loeRyG1Wb6DBhjnD7OTESIxWJxIAzDtenTNTUCZr7GWvtOV/EprtYYMMZ8AAC+1lrrZFoxc4BBELyemV2fDN+NiO8ulUrOCjmZLsu+l9qeP1kx3df1aOTKDEnQ4OoZkKn4k1SovwUAuV/wr64TrPieZQARl4RhuC8i7g8Am53Yc5yjE9D3/XMR8VOOA1V4ykAaDJwjbxAZB8p4UIsyoAxMYoCZLxGBfM/V44/aW8pAygxcLQKRC2Z0KTXlnlD3TjLwUxHI/wDAS52Ep6CUgRQZQMS7RCAWAAop4lDXyoCrDKwVgUgOoB5XESouZSBFBsoikD8BwItSBKGulQFXGRgVgTwJAM9zFaHiUgZSZOAxEcgzALBdiiDUtTLgKgNPikDkLPpcVxEqLmUgRQaeEoE8AQDzUwShrpUBVxn4swjkUQDY0VWEiksZSJGBh0QgDwJAb4og1LUy4CoDE8u8JHkbXEWouJSBFBmY+FCoW01S7AF17TQDd+pmRaf7R8GlzMBPRCD/DgBvTRmIulcGXGTgKjlReBkinugiOsWkDKTJgKRDFYGch4ifTBOI+lYGXGQAET8tQyzn069MIu87zCwHvEpdXV33Dg4OypVxDZe+vr7tKpXKRLK8MAwn/lywYAHWf1etVid+J3/W/33yn/Pmzdv073Ub9X9n5k126/+9/fbbb/W7yfXr9eTPOXPmTNSdys7kevWM/Pl8flP9Lf5dzOCWv5O/T24zyddmfmvtJn7X1dW1lY+6/8n16r6k/lRYar7mI6JcKSAZPJ26UWpbD5CMrOQNcigi/qThpyydik8w83ustT9Kx716jZIBY8yeAPBNAJi4g8PVIvdjOp84DgDWep53yNDQkKRH1dJBDBhjrgeAN7oakud5u7ieehQ8z3vZ0NDQH1wlUXG1zoAMebu6um4FgFe2biW+lnKJTj159SMA8OL4XLVmGRHPKJVKF7TWWltlgYH+/v4l1Wq17CDWR4lop7pAbgMAyXrnVJHkwU4BUjCxMBAEwTXMfHQsxls3+ksiWla/QOfriPj+1m1F35KZf22tzcRqR/TRzy6Lvu8fjYjXOBb1pUR0Ul0gH0LESxwD+DkiOssxTAonBgYKhcIBnuf9OgbTLZtExA+USqWv1wVyECLKLVPOFEQ8olQq/dgZQAokNgZcFEgYhq8ul8u31QWyIyLKwSlnCjMfZ62Vyx21dDgDLgqkUqnsMDIysn7TJNgY8wAALHKlLxDxK6VSybWLRV2hp6NwGGOOAIAbXAmKmUestUsEzyaBBEFwpfxf2yGQOkl3pTNixuH7/vmIeHrMbpoxfxURTVxLvUkgvu8fh4hXNmMl7rphGL6iXC7fHbcftZ8uA77vr0PEndNF8f/eZQ9WqVS6fDOBFIvFnjAMJQ2pS2UNEe3qEiDFEi0DQRCsYOaPRWu1bWu7EtGazQQif3H0fLou97bd324aCILgKGb+gWPoHieiBXVMm32pNsZcAQDvdQwwI+IV1Wr19HK5LGlStXQAA8aYYwBAVik9x8K5jojeMqVAgiB4BzN/1zHAdTiyDH1atVr91fDw8B8dxaiwGmDA0S/ndeTvJSLZij9RNnuD9PX17dTV1fVwAzG6UEVuum3qwJQLoF3GwMz3W2tjPX7tuDggn88vXLt27SYNbLUZ0Bgjd5EPuNyRii0WBobz+fz+kx+OqL0YY2Toci0A5KK2HZG9+4ho98m2phLIVwFA7k7XMnsYGGXm/ay1kmUzluL7/tsRUcThbGHmC6y1Z0wrkCAIDmHmm5yNQoFFygAzP9zV1bX/4ODgcKSGJxnLwJtjAi0z72+t/c20ApF5iTFGDlBpQuu4nhh37P4lDMNXlsvlUlyQsiIOAJg4ILUlD1MeSHLw039c/Teb7T5ZrVYPHB4evicuEjIkDnl7fNlae0pDAjHGLAWA1XERp3adYOBQIvpZXEiyJI7a8OoAa+3tDQlEKhljZA/UXnERqHZTZeCbRBTbB+GaOL6faoTNOR8loil3sm/zzHcQBCfLa6c5P1o7Aww8MHfu3KX33HOPXL0Xecnam6NGwDlE9M9TkbFNgSxevHiH7u7uxxxes468c2eJwXOJ6Ow4YjXGvAEAfpixZyasVCo9IyMjsjC1VZk2a4gxRg6xyGEWLZ3DwNuIKPLhT00c10nG0oxR9WMi2uYzPq1AHN1tmTH+3YKLiEtLpZLsloisZFgcwsEbiOjGbZExrUCWLVvWNTo6Kh+Q9A7DyB6ndA0x8+7W2vuiQpFxcfyRiAqSy7wlgUijIAhOk0/wURGqdlJn4FgiuioKFBkXh1DwHiL61nRczJi5sL+/f161WpUJzNwoSFUbqTNwIRG1fYIv6+KQLTaLFi1avHLlykpbApHGxpiLAGCrr4ypd7UCaIWBR3K5XLHZu1UmO8q6OCQWZv6QtVY25k5bZnyDSOtCobDY8zw9pDQTm9n594uI6KOtwDXGyHUFcm1BlsuU+66mCqghgUhD3/edy9+b5R5KGXs1DMOXl8vle5vB0QlvDolXUgyVSqUVjcTesED6+/sXVqtVvcSmEVazUed+RHxzo0u+vu+7mL+5Fab/0t3d/ZI1a9Y83UjjhgVSm4t8CQA+0ohhrZMJBjYw82nTjcUHBgZeWKlUZNXr0ExENDPIs4no3JmrPVujKYEUi8UFYRjKXERXtBplOBv1VjHz3Yg4xMxrEfEFAFAEgH4AOFjuOs1GGDOifLpSqSySnLsz1qxVaEog0iYIgk8xc8MKbBSI1lMGEmDgTCI6vxk/TQukdq8cAUBPM460rjKQJgPMPDh//vzdV61aNd4MjqYFIsZdzOPbTNBad/YxMNV580ZYaEkgkg3PGCM3z+7WiBOtowykzMCmbO3N4mhVIDIX2ZeZf9vsRL9ZgFpfGWiTgac8zysMDQ39uRU7LQtEnBljdNm3Fda1TZIMnEREl7bqsC2B9Pb2bj9nzpw1iNjXKgBtpwzEyMC9RPRS2XrVqo+2BCJOgyA4kJl/2SoAbacMxMQAh2G4T7sXMLUtkNqq1gWIeFpMgapZZaAVBr5ARB9vpeHkNpEIpHbyUHIK7dMuIG2vDETAwO1EdOB0JwUb9RGJQGoTdjmWK7tD5zfqXOspAzEw8Hi1Wt11eHg4kmvNIxOIBFooFA73PO8/YwhaTSoDDTHAzMuttbc2VLmBSpEKpPYmuRAAWjqM0wBeraIMTMfA+UR0ZpQURS4QnY9E2T1qq1EGEPGunp6e/WY6Y96ovXq9yAWi85Fmu0DrR8DAes/zdhsaGor8QF8sAtH5SARdriYaZoCZD7PW/rThBk1UjE0ggsH3/bMQ8TNN4NGqykBTDCDiKaVSKbYk67EKpDbc0jsPm+pyrdwoA8y8wlp7eqP1W6kXu0BqV7p9DwDe2gpAbaMMTMUAM19jrX1n3OwkIRCorWxJguBOOfgfd7+o/ekZuImIDgeAatxEJSIQCaJ2VFc2Nep2lLh7tYPty3Lu+Pj4gSMjIxuSCDMxgdREMj+Xy92BiJItQ4sy0CwDqyuVygHNZCVp1sGW9RMViDivJaC7CwAWtgte288qBh5k5r2ttX9KMurEBSLBDQwM9FUqlZ/LocQkg1VfmWXAAsAyIhpNOoJUBFITiWTsE5G8POmg1V+mGLh7zpw5r129evVf0kCdmkAk2D322OM5Y2Njcunja9IIXn26zQAz31ytVo9IakI+FRupCkQAyRLwQw899B1mPtrt7lJ0CTNwNREdl8RS7nRxpS6QGjj0ff8LkpY+4U5Qd24yEPm29VbDdEUgE/h93z8JES/RXFutdmfm20n2kQ8S0WWuROKUQGoiOQwRJd2+ZBjXMnsYeIKZj4ryNGAU1DknEAmqWCz2hGEok3f96h5FL7tv4458Pv/mtWvXPuwaVCcFMmny/nlmbvtGVtdIVzybGGBm/qK1VtLzxL6vqhXenRVIPRjf92XIdQ0APL+VALWNsww8AQBHE9FNziLMymS4dsvu93XI5fKj1BS2Vfl8/ggXh1RbRuH8G6QOuLZl/gK9I7GpB9G1yoyIF/b09Hw86uQKcQWaGYFMGnIdhIiXS1rguEhRu9EzIDc8AcDx1trfRG89PouZE4hQsXTp0u6NGzd+AgBkctcdHz1qOQIGngGAz/T29l6QlbfG5JgzKZB6AIVCIfA87woA+LsIOlJNRMwAM0uWzQ9Yax+M2HRi5jItkEnDruNkbAsAL0yMOXU0HQNlmSsS0Q1Zp6kjBCKd0NfXN7+rq0v2cp0MAM/JesdkFL8Mp+SaZbl64G8ZjWEz2B0jkHpUxWJxATN/mpnfr/OTxB7RCgBc6XneOXFkN0wsiikcdZxAJglFtqucBwDvBoBcmiR3sG/5En5tV1fXWYODg8OdGGfHCqTeWbXjvR8GgBN06BXpI/xtALiAiFZHatUxYx0vkDrfxpjnyTp8bY6yxLF+yAqc9QBweaVSuXhkZOSRrIBuB+esEchkkoIgOAoATmHmA9ohbxa1/R0AfGNsbOzf1q1bNzaL4oZZKZB6BxcKhaLnecfIpjnZZT+bOr6BWB9n5quZ+Rvlclmu1puVZVYLZHKPG2P2RMR3MPPbAWDRrHwaAB4DgOuZ+Yfz58+/ZdWqVeOzlIdNYatApngCane/HwkAfw8Au3f4QyKT7J8BwI1EtLLDY206PBXIDJQNDAxI/q7lzHywJGHJetpUZh5BxFuZ+SZEvIWI5K2hZRsMqECafDRqqVMPlttUEXE5ACxu0kTS1WW16Rfyk8vlftGp3yviIlUF0iazxpgXhWHY73negLxdmFkSc8tP0pN+y8z3I+KQ/Mh/53K5ocHBwXVthjirm6tAYuz+YrE4IOJBxCIzy5tmZ0nazcw7I2JfM65rQyNJaiAP/MOI+AAzT4ihVCqtbcaW1m2cgf8DkV/jh6DyaVEAAAAASUVORK5CYII="
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_())
相关推荐
alwaysrun8 分钟前
Python自动提取邮件订阅链接并解析
python·url·邮件·ai提取
何中应9 分钟前
Conda安装&使用
python·conda·python3.11
无敌昊哥战神19 分钟前
【LeetCode 37】解数独 (Sudoku Solver) —— 回溯法详解 (Python/C/C++)
c语言·c++·python·算法·leetcode
风流 少年25 分钟前
Python Web框架:FastAPI
前端·python·fastapi
Qres82136 分钟前
Rabrg/artificial-life test
python·模拟
财经资讯数据_灵砚智能43 分钟前
基于全球经济类多源新闻的NLP情感分析与数据可视化(夜间-次晨)2026年5月1日
大数据·人工智能·python·信息可视化·自然语言处理
好奇龙猫1 小时前
[大学院ーpython-base learning3: python and recommendation system ]
开发语言·python
篮子里的玫瑰1 小时前
Python与网络爬虫——字典与集合
开发语言·python
skilllite作者1 小时前
Zed 1.0 编辑器深度评测与实战指南
开发语言·人工智能·windows·python·编辑器·agi
2401_882273721 小时前
pattern属性在旧版Android浏览器无效怎么办_手动验证补充【操作】
jvm·数据库·python