Python基于PyQt5和openpyxl实现Excel单元格合并工具

作者:

文章目录
  • 本项目是基于 PyQt5 和 openpyxl 的图形化界面工具。用于合并 Excel 文件中指定列的相同内容单元格,允许用户选择 Excel 文件、指定工作表、设置起始行、选择要合并的列,并控制合并行为
  • import sys from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QFileDialog, QComboBox, QCheckBox, QMessageBox, QSpinBox, QProgressBar) from PyQt5.QtCore import QThread, pyqtSignal from openpyxl import load_workbook from openpyxl.utils import get_column_letter import os class MergeThread(QThread): progress_signal = pyqtSignal(int) finished_signal = pyqtSignal(str) error_signal = pyqtSignal(str) def __init__(self, file_path, sheet_name, start_row, columns, merge_same_content, output_path, parent=None): super().__init__(parent) self.file_path = file_path self.sheet_name = sheet_name self.start_row = start_row self.columns = columns self.merge_same_content = merge_same_content self.output_path = output_path def run(self): try: wb = load_workbook(self.file_path) ws = wb[self.sheet_name] total_steps = len(self.columns) current_step = 0 for col_info in self.columns: col = col_info[‘col’] target_list = [] for row in range(self.start_row, ws.max_row + 1): cell_value = ws[f”{col}{row}”].value target_list.append(cell_value if cell_value is not None else “”) self._merge_cells(ws, target_list, self.start_row, col) current_step += 1 progress = int(current_step / total_steps * 100) self.progress_signal.emit(progress) wb.save(self.output_path) self.finished_signal.emit(self.output_path) except Exception as e: self.error_signal.emit(str(e)) def _merge_cells(self, ws, target_list, start_row, col): start = 0 end = 0 reference = target_list[0] for i in range(len(target_list)): if not self.merge_same_content and i > 0: if i > 0 and start != i – 1: ws.merge_cells(f'{col}{start + start_row}:{col}{i – 1 + start_row}’) start = i else: if i < len(target_list) – 1 and target_list[i] == target_list[i + 1]: end = i continue if start == i: pass else: if target_list[start] == target_list[i]: ws.merge_cells(f'{col}{start + start_row}:{col}{i + start_row}’) start = i + 1 class ExcelMergeTool(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(“Excel 单元格合并工具”) self.setGeometry(100, 100, 600, 450) self.file_path = “” self.sheet_names = [] self.columns = [] self.init_ui() def init_ui(self): main_widget = QWidget() main_layout = QVBoxLayout() file_layout = QHBoxLayout() file_layout.addWidget(QLabel(“Excel文件:”)) self.file_edit = QLineEdit() self.file_edit.setReadOnly(True) file_layout.addWidget(self.file_edit) browse_btn = QPushButton(“浏览…”) browse_btn.clicked.connect(self.browse_file) file_layout.addWidget(browse_btn) main_layout.addLayout(file_layout) sheet_layout = QHBoxLayout() sheet_layout.addWidget(QLabel(“工作表:”)) self.sheet_combo = QComboBox() self.sheet_combo.setEnabled(False) sheet_layout.addWidget(self.sheet_combo) main_layout.addLayout(sheet_layout) row_layout = QHBoxLayout() row_layout.addWidget(QLabel(“起始行:”)) self.start_row_spin = QSpinBox() self.start_row_spin.setMinimum(1) self.start_row_spin.setMaximum(9999) self.start_row_spin.setValue(1) row_layout.addWidget(self.start_row_spin) main_layout.addLayout(row_layout) col_layout = QVBoxLayout() col_layout.addWidget(QLabel(“需要合并的列:”)) self.col_list_widget = QWidget() self.col_list_layout = QVBoxLayout() self.col_list_widget.setLayout(self.col_list_layout) col_layout.addWidget(self.col_list_widget) add_col_btn = QPushButton(“添加列”) add_col_btn.clicked.connect(self.add_column_setting) col_layout.addWidget(add_col_btn) main_layout.addLayout(col_layout) option_layout = QHBoxLayout() self.merge_same_check = QCheckBox(“仅合并相同内容”) self.merge_same_check.setChecked(True) option_layout.addWidget(self.merge_same_check) main_layout.addLayout(option_layout) output_layout = QHBoxLayout() output_layout.addWidget(QLabel(“输出文件名:”)) self.output_edit = QLineEdit() self.output_edit.setPlaceholderText(“自动在原文件名后添加’_wenqingzhou'”) output_layout.addWidget(self.output_edit) main_layout.addLayout(output_layout) self.progress_bar = QProgressBar() self.progress_bar.setValue(0) main_layout.addWidget(self.progress_bar) execute_btn = QPushButton(“执行合并”) execute_btn.clicked.connect(self.execute_merge) main_layout.addWidget(execute_btn) main_widget.setLayout(main_layout) self.setCentralWidget(main_widget) def browse_file(self): file_path, _ = QFileDialog.getOpenFileName( self, “选择Excel文件”, “”, “Excel文件 (*.xlsx *.xls)” ) if file_path: self.file_path = file_path self.file_edit.setText(file_path) try: wb = load_workbook(file_path, read_only=True) self.sheet_names = wb.sheetnames self.sheet_combo.clear() self.sheet_combo.addItems(self.sheet_names) self.sheet_combo.setEnabled(True) base, ext = os.path.splitext(file_path) default_output = f”{base}_wenqingzhou{ext}” self.output_edit.setText(default_output) for i in reversed(range(self.col_list_layout.count())): widget = self.col_list_layout.itemAt(i).widget() if widget is not None: widget.deleteLater() self.columns = [] except Exception as e: QMessageBox.critical(self, “错误”, f”无法读取Excel文件:n{str(e)}”) def add_column_setting(self): if not self.sheet_names: QMessageBox.warning(self, “警告”, “请先选择Excel文件”) return col_widget = QWidget() col_layout = QHBoxLayout() col_layout.addWidget(QLabel(“列:”)) col_combo = QComboBox() col_combo.addItems([get_column_letter(i) for i in range(1, 100)]) # A-CZ col_layout.addWidget(col_combo) del_btn = QPushButton(“删除”) del_btn.clicked.connect(lambda: self.remove_column_setting(col_widget)) col_layout.addWidget(del_btn) col_widget.setLayout(col_layout) self.col_list_layout.addWidget(col_widget) self.columns.append({ ‘widget’: col_widget, ‘col_combo’: col_combo, }) def remove_column_setting(self, widget): self.col_list_layout.removeWidget(widget) widget.deleteLater() self.columns = [col for col in self.columns if col[‘widget’] != widget] def execute_merge(self): if not self.file_path: QMessageBox.warning(self, “警告”, “请先选择Excel文件”) return if not self.columns: QMessageBox.warning(self, “警告”, “请至少添加一列需要合并的列”) return sheet_name = self.sheet_combo.currentText() start_row = self.start_row_spin.value() merge_same_content = self.merge_same_check.isChecked() columns_info = [] for col in self.columns: col_letter = col[‘col_combo’].currentText() columns_info.append({‘col’: col_letter, ‘name’: ”}) output_path = self.output_edit.text().strip() if not output_path: base, ext = os.path.splitext(self.file_path) output_path = f”{base}_wenqingzhou{ext}” self.thread = MergeThread( file_path=self.file_path, sheet_name=sheet_name, start_row=start_row, columns=columns_info, merge_same_content=merge_same_content, output_path=output_path ) self.thread.progress_signal.connect(self.update_progress) self.thread.finished_signal.connect(self.merge_completed) self.thread.error_signal.connect(self.merge_failed) self.thread.start() def update_progress(self, value): self.progress_bar.setValue(value) def merge_completed(self, output_path): QMessageBox.information( self, “完成”, f”单元格合并完成!n输出文件已保存为:n{output_path}” ) self.progress_bar.setValue(0) def merge_failed(self, error_msg): QMessageBox.critical( self, “错误”, f”合并过程中发生错误:n{error_msg}” ) self.progress_bar.setValue(0) if __name__ == “__main__”: app = QApplication(sys.argv) window = ExcelMergeTool() window.show() sys.exit(app.exec_())
  • 目录
    • 一:效果展示:
    • 二:功能描述:
      • 1. 文件选择与工作表选择
      • 2. 合并设置
        • (1)起始行设置
        • (2)列选择
        • (3)合并选项
      • 3. 合并执行
        • (1)后台线程处
        • (2)进度显示
        • (3)结果反馈
      • 4. 输出控制
      • 三:完整代码:
        • 四:代码分析:
          • 1. 线程处理模块
            • 2. 主界面模块
              • 3. 功能方法
                • 4. 回调方法

                本项目是基于 PyQt5openpyxl 的图形化界面工具。用于合并 Excel 文件中指定列的相同内容单元格,允许用户选择 Excel 文件、指定工作表、设置起始行、选择要合并的列,并控制合并行为

                • 浏览按钮:允许用户通过文件对话框选择 Excel 文件(支持 .xlsx.xls 格式)
                • 自动读取工作表:选中文件后自动读取并显示所有工作表名称
                • 默认输出文件名:自动在原文件名后添加 “_wenqingzhou” 作为默认输出文件名

                • 通过 QSpinBox 设置从哪一行开始处理数据

                • 可以动态添加多个列选择控件
                • 每个列选择控件允许选择 A-CU 的列标识
                • 提供删除按钮移除不需要的列设置

                • "仅合并相同内容"复选框:控制是否只合并内容相同的相邻单元格
                • 如果勾选:只合并内容相同的相邻单元格
                • 如果未勾选:每个单元格单独处理(实际不合并,但保留结构以便统一处理)

                • 使用 QThread 在后台执行合并操作,避免界面卡死

                • 通过 QProgressBar 显示合并进度

                • 成功时显示完成消息和输出文件路径
                • 失败时显示错误详情

                • 可自定义输出文件名
                • 默认在原文件名后添加后缀保存

                import sys
                from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                                             QLabel, QLineEdit, QPushButton, QFileDialog, QComboBox,
                                             QCheckBox, QMessageBox, QSpinBox, QProgressBar)
                from PyQt5.QtCore import QThread, pyqtSignal
                from openpyxl import load_workbook
                from openpyxl.utils import get_column_letter
                import os
                
                class MergeThread(QThread):
                    progress_signal = pyqtSignal(int)
                    finished_signal = pyqtSignal(str)
                    error_signal = pyqtSignal(str)
                
                    def __init__(self, file_path, sheet_name, start_row, columns, merge_same_content, output_path, parent=None):
                        super().__init__(parent)
                        self.file_path = file_path
                        self.sheet_name = sheet_name
                        self.start_row = start_row
                        self.columns = columns 
                        self.merge_same_content = merge_same_content
                        self.output_path = output_path
                
                    def run(self):
                        try:
                            wb = load_workbook(self.file_path)
                            ws = wb[self.sheet_name]
                            total_steps = len(self.columns)
                            current_step = 0
                            
                            for col_info in self.columns:
                                col = col_info['col']
                                target_list = []
                                
                                for row in range(self.start_row, ws.max_row + 1):
                                    cell_value = ws[f"{col}{row}"].value
                                    target_list.append(cell_value if cell_value is not None else "")
                                    
                                self._merge_cells(ws, target_list, self.start_row, col)
                                current_step += 1
                                progress = int(current_step / total_steps * 100)
                                self.progress_signal.emit(progress)
                                
                            wb.save(self.output_path)
                            self.finished_signal.emit(self.output_path)
                            
                        except Exception as e:
                            self.error_signal.emit(str(e))
                
                    def _merge_cells(self, ws, target_list, start_row, col):
                        start = 0
                        end = 0
                        reference = target_list[0]
                
                        for i in range(len(target_list)):
                            if not self.merge_same_content and i > 0:
                                if i > 0 and start != i - 1:
                                    ws.merge_cells(f'{col}{start + start_row}:{col}{i - 1 + start_row}')
                                start = i
                            else:
                                if i < len(target_list) - 1 and target_list[i] == target_list[i + 1]:
                                    end = i
                                    continue
                
                                if start == i: 
                                    pass
                                else:
                                    if target_list[start] == target_list[i]:
                                        ws.merge_cells(f'{col}{start + start_row}:{col}{i + start_row}')
                
                                start = i + 1
                
                class ExcelMergeTool(QMainWindow):
                    def __init__(self):
                        super().__init__()
                        self.setWindowTitle("Excel 单元格合并工具")
                        self.setGeometry(100, 100, 600, 450)  
                        self.file_path = ""
                        self.sheet_names = []
                        self.columns = []
                        self.init_ui()
                
                    def init_ui(self):
                        main_widget = QWidget()
                        main_layout = QVBoxLayout()
                        file_layout = QHBoxLayout()
                        file_layout.addWidget(QLabel("Excel文件:"))
                        self.file_edit = QLineEdit()
                        self.file_edit.setReadOnly(True)
                        file_layout.addWidget(self.file_edit)
                        browse_btn = QPushButton("浏览...")
                        browse_btn.clicked.connect(self.browse_file)
                        file_layout.addWidget(browse_btn)
                        main_layout.addLayout(file_layout)
                        sheet_layout = QHBoxLayout()
                        sheet_layout.addWidget(QLabel("工作表:"))
                        self.sheet_combo = QComboBox()
                        self.sheet_combo.setEnabled(False)
                        sheet_layout.addWidget(self.sheet_combo)
                        main_layout.addLayout(sheet_layout)
                        row_layout = QHBoxLayout()
                        row_layout.addWidget(QLabel("起始行:"))
                        self.start_row_spin = QSpinBox()
                        self.start_row_spin.setMinimum(1)
                        self.start_row_spin.setMaximum(9999)
                        self.start_row_spin.setValue(1)
                        row_layout.addWidget(self.start_row_spin)
                        main_layout.addLayout(row_layout)
                        col_layout = QVBoxLayout()
                        col_layout.addWidget(QLabel("需要合并的列:"))
                        self.col_list_widget = QWidget()
                        self.col_list_layout = QVBoxLayout()
                        self.col_list_widget.setLayout(self.col_list_layout)
                        col_layout.addWidget(self.col_list_widget)
                        add_col_btn = QPushButton("添加列")
                        add_col_btn.clicked.connect(self.add_column_setting)
                        col_layout.addWidget(add_col_btn)
                        main_layout.addLayout(col_layout)
                        option_layout = QHBoxLayout()
                        self.merge_same_check = QCheckBox("仅合并相同内容")
                        self.merge_same_check.setChecked(True)
                        option_layout.addWidget(self.merge_same_check)
                        main_layout.addLayout(option_layout)
                        output_layout = QHBoxLayout()
                        output_layout.addWidget(QLabel("输出文件名:"))
                        self.output_edit = QLineEdit()
                        self.output_edit.setPlaceholderText("自动在原文件名后添加'_wenqingzhou'")
                        output_layout.addWidget(self.output_edit)
                        main_layout.addLayout(output_layout)
                        self.progress_bar = QProgressBar()
                        self.progress_bar.setValue(0)
                        main_layout.addWidget(self.progress_bar)
                        execute_btn = QPushButton("执行合并")
                        execute_btn.clicked.connect(self.execute_merge)
                        main_layout.addWidget(execute_btn)
                        main_widget.setLayout(main_layout)
                        self.setCentralWidget(main_widget)
                
                    def browse_file(self):
                        file_path, _ = QFileDialog.getOpenFileName(
                            self, "选择Excel文件", "", "Excel文件 (*.xlsx *.xls)"
                        )
                
                        if file_path:
                            self.file_path = file_path
                            self.file_edit.setText(file_path)
                
                            try:
                                wb = load_workbook(file_path, read_only=True)
                                self.sheet_names = wb.sheetnames
                                self.sheet_combo.clear()
                                self.sheet_combo.addItems(self.sheet_names)
                                self.sheet_combo.setEnabled(True)
                                base, ext = os.path.splitext(file_path)
                                default_output = f"{base}_wenqingzhou{ext}"
                                self.output_edit.setText(default_output)
                
                                for i in reversed(range(self.col_list_layout.count())):
                                    widget = self.col_list_layout.itemAt(i).widget()
                                    if widget is not None:
                                        widget.deleteLater()
                
                                self.columns = []
                
                            except Exception as e:
                                QMessageBox.critical(self, "错误", f"无法读取Excel文件:n{str(e)}")
                
                    def add_column_setting(self):
                        if not self.sheet_names:
                            QMessageBox.warning(self, "警告", "请先选择Excel文件")
                            return
                
                        col_widget = QWidget()
                        col_layout = QHBoxLayout()
                        col_layout.addWidget(QLabel("列:"))
                        col_combo = QComboBox()
                        col_combo.addItems([get_column_letter(i) for i in range(1, 100)])  # A-CZ
                        col_layout.addWidget(col_combo)
                        del_btn = QPushButton("删除")
                        del_btn.clicked.connect(lambda: self.remove_column_setting(col_widget))
                        col_layout.addWidget(del_btn)
                        col_widget.setLayout(col_layout)
                        self.col_list_layout.addWidget(col_widget)
                        self.columns.append({
                            'widget': col_widget,
                            'col_combo': col_combo,
                        })
                
                    def remove_column_setting(self, widget):
                        self.col_list_layout.removeWidget(widget)
                        widget.deleteLater()
                        self.columns = [col for col in self.columns if col['widget'] != widget]
                
                    def execute_merge(self):
                        if not self.file_path:
                            QMessageBox.warning(self, "警告", "请先选择Excel文件")
                            return
                
                        if not self.columns:
                            QMessageBox.warning(self, "警告", "请至少添加一列需要合并的列")
                            return
                
                        sheet_name = self.sheet_combo.currentText()
                        start_row = self.start_row_spin.value()
                        merge_same_content = self.merge_same_check.isChecked()
                
                        columns_info = []
                        for col in self.columns:
                            col_letter = col['col_combo'].currentText()
                            columns_info.append({'col': col_letter, 'name': ''})  
                
                        output_path = self.output_edit.text().strip()
                        if not output_path:
                            base, ext = os.path.splitext(self.file_path)
                            output_path = f"{base}_wenqingzhou{ext}"
                
                        self.thread = MergeThread(
                            file_path=self.file_path,
                            sheet_name=sheet_name,
                            start_row=start_row,
                            columns=columns_info,
                            merge_same_content=merge_same_content,
                            output_path=output_path
                        )
                
                        self.thread.progress_signal.connect(self.update_progress)
                        self.thread.finished_signal.connect(self.merge_completed)
                        self.thread.error_signal.connect(self.merge_failed)
                        self.thread.start()
                
                    def update_progress(self, value):
                        self.progress_bar.setValue(value)
                
                    def merge_completed(self, output_path):
                        QMessageBox.information(
                            self,
                            "完成",
                            f"单元格合并完成!n输出文件已保存为:n{output_path}"
                        )
                        self.progress_bar.setValue(0)
                
                    def merge_failed(self, error_msg):
                        QMessageBox.critical(
                            self,
                            "错误",
                            f"合并过程中发生错误:n{error_msg}"
                        )
                        self.progress_bar.setValue(0)
                
                if __name__ == "__main__":
                    app = QApplication(sys.argv)
                    window = ExcelMergeTool()
                    window.show()
                    sys.exit(app.exec_())
                

                class MergeThread(QThread):
                    
                    """后台处理Excel合并的线程类,避免界面卡顿"""
                    
                    # 定义线程信号
                    progress_signal = pyqtSignal(int)  # 进度信号
                    finished_signal = pyqtSignal(str)  # 完成信号(带输出路径)
                    error_signal = pyqtSignal(str)     # 错误信号
                
                    def __init__(self, file_path, sheet_name, start_row, columns, merge_same_content, output_path, parent=None):
                        super().__init__(parent)
                        # 初始化参数
                        self.file_path = file_path
                        self.sheet_name = sheet_name
                        self.start_row = start_row
                        self.columns = columns  # 格式:[{'col': 'A'}, {'col': 'B'}]
                        self.merge_same_content = merge_same_content
                        self.output_path = output_path
                
                    def run(self):
                        """线程执行的主逻辑"""
                        try:
                            # 1. 加载Excel文件
                            wb = load_workbook(self.file_path)
                            ws = wb[self.sheet_name]
                            
                            # 2. 计算总进度步数(基于列数)
                            total_steps = len(self.columns)
                            current_step = 0
                            
                            # 3. 遍历处理每一列
                            for col_info in self.columns:
                                col = col_info['col']
                                target_list = []
                                
                                # 3.1 收集该列所有单元格数据
                                for row in range(self.start_row, ws.max_row + 1):
                                    cell_value = ws[f"{col}{row}"].value
                                    target_list.append(cell_value if cell_value is not None else "")
                                
                                # 3.2 执行单元格合并
                                self._merge_cells(ws, target_list, self.start_row, col)
                                
                                # 3.3 更新进度
                                current_step += 1
                                progress = int(current_step / total_steps * 100)
                                self.progress_signal.emit(progress)
                            
                            # 4. 保存结果
                            wb.save(self.output_path)
                            self.finished_signal.emit(self.output_path)
                            
                        except Exception as e:
                            self.error_signal.emit(str(e))
                
                    def _merge_cells(self, ws, target_list, start_row, col):
                        """
                        实际执行单元格合并的算法:
                        :param ws: 工作表对象
                        :param target_list: 该列所有单元格值列表
                        :param start_row: 起始行号
                        :param col: 列字母
                        """
                        start = 0
                        reference = target_list[0]
                
                        for i in range(len(target_list)):
                            if not self.merge_same_content and i > 0:
                                # 模式1:强制合并(无论内容是否相同)
                                if i > 0 and start != i - 1:
                                    ws.merge_cells(f'{col}{start + start_row}:{col}{i - 1 + start_row}')
                                start = i
                            else:
                                # 模式2:仅合并相同内容
                                if i < len(target_list) - 1 and target_list[i] == target_list[i + 1]:
                                    continue  # 相同内容则继续扩展合并范围
                                
                                if start == i: 
                                    pass  # 单个单元格无需合并
                                else:
                                    if target_list[start] == target_list[i]:
                                        ws.merge_cells(f'{col}{start + start_row}:{col}{i + start_row}')
                                start = i + 1
                

                class ExcelMergeTool(QMainWindow):
                    
                    """主窗口类,负责UI展示和用户交互"""
                    def __init__(self):
                        super().__init__()
                        self.setWindowTitle("Excel 单元格合并工具")
                        self.setGeometry(100, 100, 600, 450)  
                        self.file_path = ""
                        self.sheet_names = []
                        self.columns = []  # 存储用户添加的列设置
                        self.init_ui()
                
                    def init_ui(self):
                        
                        """初始化用户界面"""
                        main_widget = QWidget()
                        main_layout = QVBoxLayout()
                        
                        # ------- 文件选择区域 -------
                        file_layout = QHBoxLayout()
                        file_layout.addWidget(QLabel("Excel文件:"))
                        self.file_edit = QLineEdit()
                        self.file_edit.setReadOnly(True)
                        file_layout.addWidget(self.file_edit)
                        
                        browse_btn = QPushButton("浏览...")
                        browse_btn.clicked.connect(self.browse_file)
                        file_layout.addWidget(browse_btn)
                        main_layout.addLayout(file_layout)
                        
                        # ------- 工作表选择区域 -------
                        sheet_layout = QHBoxLayout()
                        sheet_layout.addWidget(QLabel("工作表:"))
                        self.sheet_combo = QComboBox()
                        self.sheet_combo.setEnabled(False)  # 初始禁用,直到选择文件
                        sheet_layout.addWidget(self.sheet_combo)
                        main_layout.addLayout(sheet_layout)
                        
                        # ------- 起始行设置 -------
                        row_layout = QHBoxLayout()
                        row_layout.addWidget(QLabel("起始行:"))
                        self.start_row_spin = QSpinBox()
                        self.start_row_spin.setMinimum(1)
                        self.start_row_spin.setMaximum(9999)
                        self.start_row_spin.setValue(1)
                        row_layout.addWidget(self.start_row_spin)
                        main_layout.addLayout(row_layout)
                        
                        # ------- 列设置区域 -------
                        col_layout = QVBoxLayout()
                        col_layout.addWidget(QLabel("需要合并的列:"))
                        
                        # 动态列设置容器
                        self.col_list_widget = QWidget()
                        self.col_list_layout = QVBoxLayout()
                        self.col_list_widget.setLayout(self.col_list_layout)
                        col_layout.addWidget(self.col_list_widget)
                        
                        add_col_btn = QPushButton("添加列")
                        add_col_btn.clicked.connect(self.add_column_setting)
                        col_layout.addWidget(add_col_btn)
                        main_layout.addLayout(col_layout)
                        
                        # ------- 选项设置 -------
                        option_layout = QHBoxLayout()
                        self.merge_same_check = QCheckBox("仅合并相同内容")
                        self.merge_same_check.setChecked(True)
                        option_layout.addWidget(self.merge_same_check)
                        main_layout.addLayout(option_layout)
                        
                        # ------- 输出设置 -------
                        output_layout = QHBoxLayout()
                        output_layout.addWidget(QLabel("输出文件名:"))
                        self.output_edit = QLineEdit()
                        self.output_edit.setPlaceholderText("自动在原文件名后添加'_wenqingzhou'")
                        output_layout.addWidget(self.output_edit)
                        main_layout.addLayout(output_layout)
                        
                        # ------- 进度条 -------
                        self.progress_bar = QProgressBar()
                        self.progress_bar.setValue(0)
                        main_layout.addWidget(self.progress_bar)
                        
                        # ------- 执行按钮 -------
                        execute_btn = QPushButton("执行合并")
                        execute_btn.clicked.connect(self.execute_merge)
                        main_layout.addWidget(execute_btn)
                        
                        main_widget.setLayout(main_layout)
                        self.setCentralWidget(main_widget)
                

                    def browse_file(self):
                        
                        """打开文件对话框选择Excel文件"""
                        file_path, _ = QFileDialog.getOpenFileName(
                            self, "选择Excel文件", "", "Excel文件 (*.xlsx *.xls)"
                        )
                
                        if file_path:
                            self.file_path = file_path
                            self.file_edit.setText(file_path)
                
                            try:
                                # 读取工作表列表
                                wb = load_workbook(file_path, read_only=True)
                                self.sheet_names = wb.sheetnames
                                self.sheet_combo.clear()
                                self.sheet_combo.addItems(self.sheet_names)
                                self.sheet_combo.setEnabled(True)
                                
                                # 设置默认输出文件名
                                base, ext = os.path.splitext(file_path)
                                default_output = f"{base}_wenqingzhou{ext}"
                                self.output_edit.setText(default_output)
                
                                # 清空已有列设置
                                for i in reversed(range(self.col_list_layout.count())):
                                    widget = self.col_list_layout.itemAt(i).widget()
                                    if widget is not None:
                                        widget.deleteLater()
                                self.columns = []
                
                            except Exception as e:
                                QMessageBox.critical(self, "错误", f"无法读取Excel文件:n{str(e)}")
                
                    def add_column_setting(self):
                        
                        """添加一列合并设置"""
                        if not self.sheet_names:
                            QMessageBox.warning(self, "警告", "请先选择Excel文件")
                            return
                
                        # 创建列设置控件组
                        col_widget = QWidget()
                        col_layout = QHBoxLayout()
                        col_layout.addWidget(QLabel("列:"))
                        
                        # 列选择下拉框(A-CZ)
                        col_combo = QComboBox()
                        col_combo.addItems([get_column_letter(i) for i in range(1, 100)])
                        col_layout.addWidget(col_combo)
                        
                        # 删除按钮
                        del_btn = QPushButton("删除")
                        del_btn.clicked.connect(lambda: self.remove_column_setting(col_widget))
                        col_layout.addWidget(del_btn)
                        
                        col_widget.setLayout(col_layout)
                        self.col_list_layout.addWidget(col_widget)
                        
                        # 记录到列设置列表
                        self.columns.append({
                            'widget': col_widget,
                            'col_combo': col_combo,
                        })
                
                    def remove_column_setting(self, widget):
                        
                        """删除指定的列设置"""
                        self.col_list_layout.removeWidget(widget)
                        widget.deleteLater()
                        self.columns = [col for col in self.columns if col['widget'] != widget]
                
                    def execute_merge(self):
                        
                        """执行合并操作"""
                        # 参数验证
                        if not self.file_path:
                            QMessageBox.warning(self, "警告", "请先选择Excel文件")
                            return
                
                        if not self.columns:
                            QMessageBox.warning(self, "警告", "请至少添加一列需要合并的列")
                            return
                
                        # 收集参数
                        sheet_name = self.sheet_combo.currentText()
                        start_row = self.start_row_spin.value()
                        merge_same_content = self.merge_same_check.isChecked()
                
                        # 准备列信息
                        columns_info = []
                        for col in self.columns:
                            col_letter = col['col_combo'].currentText()
                            columns_info.append({'col': col_letter, 'name': ''})  # name保留字段
                
                        # 处理输出路径
                        output_path = self.output_edit.text().strip()
                        if not output_path:
                            base, ext = os.path.splitext(self.file_path)
                            output_path = f"{base}_wenqingzhou{ext}"
                
                        # 创建并启动后台线程
                        self.thread = MergeThread(
                            file_path=self.file_path,
                            sheet_name=sheet_name,
                            start_row=start_row,
                            columns=columns_info,
                            merge_same_content=merge_same_content,
                            output_path=output_path
                        )
                
                        # 连接信号槽
                        self.thread.progress_signal.connect(self.update_progress)
                        self.thread.finished_signal.connect(self.merge_completed)
                        self.thread.error_signal.connect(self.merge_failed)
                        self.thread.start()
                

                    def update_progress(self, value):
                        
                        """更新进度条"""
                        self.progress_bar.setValue(value)
                
                    def merge_completed(self, output_path):
                        
                        """合并完成处理"""
                        QMessageBox.information(
                            self,
                            "完成",
                            f"单元格合并完成!n输出文件已保存为:n{output_path}"
                        )
                        self.progress_bar.setValue(0)
                
                    def merge_failed(self, error_msg):
                        
                        """合并失败处理"""
                        QMessageBox.critical(
                            self,
                            "错误",
                            f"合并过程中发生错误:n{error_msg}"
                        )
                        self.progress_bar.setValue(0)
                

                以上就是Python基于PyQt5和openpyxl实现Excel单元格合并工具的详细内容,更多关于Python Excel单元格合并的资料请关注风君子博客其它相关文章!

                您可能感兴趣的文章:

                • Python使用openpyxl设置Excel单元格公式和工作簿合并
                • 使用Python合并 Excel单元格指定行列或单元格范围
                • Python excel合并居中值相同的单元格实例代码
                • Python实现Excel自动分组合并单元格
                • python操作openpyxl导出Excel 设置单元格格式及合并处理代码实例

                站内搜索