最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python實(shí)現(xiàn)Excel拆分和合并的優(yōu)化版本

 更新時(shí)間:2026年02月25日 09:21:03   作者:忘憂記  
這篇文章主要為大家詳細(xì)介紹了一個(gè)基于Python的Excel文件拆分與合并工具,采用模塊化設(shè)計(jì)實(shí)現(xiàn)高效處理,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下

基礎(chǔ)版本可以參考:基于Python實(shí)現(xiàn)excel拆分和合并工具并打包

代碼總覽

import pandas as pd
import os
import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter import ttk
import math
from collections import defaultdict
import logging
from openpyxl import load_workbook, Workbook
from openpyxl.utils import get_column_letter
import copy
import time

# --- 1. 配置日志 ---
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# --- 2. UI 文本常量 (提高可讀性) ---
UI_TEXTS = {
    'APP_TITLE': "Excel工具:拆分與合并 (V4 可讀性重構(gòu))",
    'TAB_SPLIT': "文件拆分",
    'TAB_COMBINE': "智能合并",
    'SELECT_FILE_LABEL': "選擇Excel文件:",
    'BROWSE_BUTTON': "瀏覽",
    'ROWS_PER_FILE_LABEL': "每份文件行數(shù):",
    'ROWS_UNIT': "行",
    'START_SPLIT_BUTTON': "開始拆分",
    'START_COMBINE_BUTTON': "開始智能合并",
    
    'PRESERVE_FORMAT_LABEL': "保留視覺格式 (字體/顏色/列寬,速度很慢)",
    'SPEED_FIRST_INFO': "取消勾選 = 速度最快,僅保留文本數(shù)據(jù) (推薦, 可保留 '00123' 和身份證號(hào))",
    
    'COMBINE_BASE_LABEL': "選擇基準(zhǔn)Excel文件:",
    'COMBINE_INFO': "系統(tǒng)會(huì)自動(dòng)查找與選定文件相似的其他文件進(jìn)行合并\n(基于文件名模式識(shí)別,如:文件1.xlsx, 文件2.xlsx)",
    
    'FILE_INFO_DEFAULT': "請(qǐng)選擇Excel文件",
    'FILE_INFO_SELECTED': "已選擇: {filename}",
    'FILE_INFO_ROWS': "文件總行數(shù): {rows} 行 (不含表頭)",
    
    'WARN_TITLE': "警告",
    'WARN_NO_FILE': "請(qǐng)先選擇有效的Excel文件!",
    'WARN_INVALID_ROWS': "請(qǐng)輸入有效的行數(shù)(大于0)!",
    'WARN_NAN': "請(qǐng)輸入有效的數(shù)字!",
    'WARN_EMPTY_FILE': "文件為空,沒有數(shù)據(jù)可拆分。",

    'ERROR_TITLE': "錯(cuò)誤",
    'ERROR_CANT_READ': "無法讀取文件信息",
    'ERROR_NO_FILES_FOUND': "目錄中沒有找到 Excel 文件!",
    'ERROR_NO_SIMILAR_FILES': "沒有找到與 '{filename}' 相似的其他文件",
    'ERROR_NO_DATA_READ': "無法讀取任何文件的數(shù)據(jù)。",
    'ERROR_DURING_SPLIT': "拆分過程中出現(xiàn)錯(cuò)誤:{error}",
    'ERROR_DURING_COMBINE': "合并過程中出現(xiàn)錯(cuò)誤:{error}",
    
    'SUCCESS_TITLE': "成功",
    'SUCCESS_SPLIT_FORMAT': (
        "文件拆分完成!(保留格式)\n"
        "共拆分成 {num_files} 個(gè)文件\n"
        "總耗時(shí): {time:.2f}秒"
    ),
    'SUCCESS_SPLIT_FAST': (
        "文件拆分完成!(速度優(yōu)先)\n"
        "共拆分成 {num_files} 個(gè)文件\n"
        "總耗時(shí): {time:.2f}秒\n"
        "(注意:僅保留文本數(shù)據(jù),視覺格式已丟失)"
    ),
    'SUCCESS_COMBINE_FORMAT': (
        "數(shù)據(jù)合并完成!(保留格式)\n"
        "合并了 {num_files} 個(gè)文件\n"
        "保存位置:{path}\n"
        "總行數(shù):{rows} (不含表頭)\n"
        "總耗時(shí): {time:.2f}秒"
    ),
    'SUCCESS_COMBINE_FAST': (
        "數(shù)據(jù)合并完成!(速度優(yōu)先)\n"
        "合并了 {num_files} 個(gè)文件\n"
        "保存位置:{path}\n"
        "總行數(shù):{rows}\n"
        "總耗時(shí): {time:.2f}秒\n"
        "(注意:僅保留文本數(shù)據(jù),視覺格式已丟失)"
    ),
}

# --- 3. 邏輯類:文件名處理 ---
class ExcelFileUtils:
    """只負(fù)責(zé)文件名匹配和模式識(shí)別"""
    
    @staticmethod
    def extract_common_pattern(filename):
        name_without_ext = os.path.splitext(filename)[0]
        patterns = []
        if any(char.isdigit() for char in name_without_ext):
            non_digit_parts = []
            current_part = ""
            for char in name_without_ext:
                if char.isdigit():
                    if current_part:
                        non_digit_parts.append(current_part)
                        current_part = ""
                else:
                    current_part += char
            if current_part:
                non_digit_parts.append(current_part)
            if non_digit_parts:
                patterns.append(''.join(non_digit_parts).strip(' _-'))
        
        for sep in ['_', '-', ' ']:
            if sep in name_without_ext:
                parts = name_without_ext.split(sep)
                if len(parts) > 1:
                    patterns.append(sep.join(parts[:-1]))
        
        if not patterns:
            patterns.append(name_without_ext)
        return patterns

    @staticmethod
    def find_similar_files(all_files, patterns, base_file):
        similar_files = [base_file]
        for pattern in patterns:
            if pattern:
                for filename in all_files:
                    if filename == base_file:
                        continue
                    name_without_ext = os.path.splitext(filename)[0]
                    if (pattern in filename or 
                        pattern in name_without_ext or
                        filename.startswith(pattern) or
                        name_without_ext.startswith(pattern)):
                        similar_files.append(filename)
        return list(dict.fromkeys(similar_files))

    @staticmethod
    def generate_output_name(patterns, base_file):
        if patterns:
            longest_pattern = max(patterns, key=len)
            if len(longest_pattern) > 3:
                return longest_pattern
        return os.path.splitext(base_file)[0]

# --- 4. 邏輯類:Pandas 快速處理 ---
class PandasProcessor:
    """只負(fù)責(zé)速度優(yōu)先的 Pandas 邏輯 (dtype=str)"""

    @staticmethod
    def split_excel_pandas(file_path, rows_per_file):
        try:
            start_time = time.time()
            logging.info(f"[Pandas-STR] 開始拆分文件: {file_path}")
            
            df = pd.read_excel(file_path, dtype=str, engine='openpyxl') 
            total_data_rows = len(df)
            
            if total_data_rows == 0:
                messagebox.showerror(UI_TEXTS['ERROR_TITLE'], UI_TEXTS['WARN_EMPTY_FILE'])
                return
            
            num_files = math.ceil(total_data_rows / rows_per_file)
            file_dir = os.path.dirname(file_path)
            file_name = os.path.splitext(os.path.basename(file_path))[0]
            
            for i in range(num_files):
                start_row = i * rows_per_file
                end_row = min((i + 1) * rows_per_file, total_data_rows)
                chunk_df = df.iloc[start_row:end_row]
                
                output_filename = f"{file_name}_拆分_{i+1}.xlsx"
                output_path = os.path.join(file_dir, output_filename)
                
                chunk_df.to_excel(output_path, index=False, engine='openpyxl')
                logging.info(f"[Pandas-STR] 保存拆分文件: {output_path}")

            total_time = time.time() - start_time
            messagebox.showinfo(UI_TEXTS['SUCCESS_TITLE'], 
                                UI_TEXTS['SUCCESS_SPLIT_FAST'].format(
                                    num_files=num_files, 
                                    time=total_time
                                ))

        except Exception as e:
            messagebox.showerror(UI_TEXTS['ERROR_TITLE'], 
                                 UI_TEXTS['ERROR_DURING_SPLIT'].format(error=e))
            logging.error(f"[Pandas-STR] 拆分過程中出現(xiàn)錯(cuò)誤:{e}")

    @staticmethod
    def combine_excel_pandas(selected_file):
        try:
            start_time = time.time()
            logging.info(f"[Pandas-STR] 開始合并文件,以 {selected_file} 為基準(zhǔn)")
            file_dir = os.path.dirname(selected_file)
            file_name = os.path.basename(selected_file)
            
            listdir = os.listdir(file_dir)
            excel_files = [f for f in listdir if f.endswith(('.xlsx', '.xls'))]
            
            if not excel_files:
                messagebox.showerror(UI_TEXTS['ERROR_TITLE'], UI_TEXTS['ERROR_NO_FILES_FOUND'])
                return

            base_name = file_name
            common_patterns = ExcelFileUtils.extract_common_pattern(base_name)
            similar_files = ExcelFileUtils.find_similar_files(excel_files, common_patterns, base_name)
            
            if len(similar_files) <= 1:
                messagebox.showinfo(UI_TEXTS['SUCCESS_TITLE'], 
                                    UI_TEXTS['ERROR_NO_SIMILAR_FILES'].format(filename=file_name))
                return

            file_list = "\n".join(similar_files)
            confirm = messagebox.askyesno(
                f"{UI_TEXTS['SUCCESS_TITLE']} ({UI_TEXTS['TAB_SPLIT']})", 
                f"將合并以下文件 (僅保留文本數(shù)據(jù)):\n\n{file_list}\n\n共 {len(similar_files)} 個(gè)文件"
            )
            
            if not confirm:
                logging.info("[Pandas-STR] 用戶取消合并")
                return
            
            all_dfs = []
            for filename in similar_files:
                file_path = os.path.join(file_dir, filename)
                try:
                    df = pd.read_excel(file_path, dtype=str, engine='openpyxl')
                    all_dfs.append(df)
                except Exception as e:
                    logging.warning(f"[Pandas-STR] 無法讀取文件 {filename}: {e}")
            
            if not all_dfs:
                messagebox.showerror(UI_TEXTS['ERROR_TITLE'], UI_TEXTS['ERROR_NO_DATA_READ'])
                return

            merged_df = pd.concat(all_dfs, ignore_index=True)
            total_rows = len(merged_df)

            output_name = ExcelFileUtils.generate_output_name(common_patterns, base_name)
            output_path = os.path.join(file_dir, f"{output_name}_合并結(jié)果_Pandas.xlsx")
            
            counter = 1
            while os.path.exists(output_path):
                output_path = os.path.join(file_dir, f"{output_name}_合并結(jié)果_Pandas({counter}).xlsx")
                counter += 1
            
            merged_df.to_excel(output_path, index=False, engine='openpyxl')
            total_time = time.time() - start_time
            logging.info(f"[Pandas-STR] 保存合并結(jié)果: {output_path} (總耗時(shí): {total_time:.2f}s)")
            
            messagebox.showinfo(
                UI_TEXTS['SUCCESS_TITLE'], 
                UI_TEXTS['SUCCESS_COMBINE_FAST'].format(
                    num_files=len(similar_files),
                    path=output_path,
                    rows=total_rows,
                    time=total_time
                ))
            
        except Exception as e:
            messagebox.showerror(UI_TEXTS['ERROR_TITLE'], 
                                 UI_TEXTS['ERROR_DURING_COMBINE'].format(error=e))
            logging.error(f"[Pandas-STR] 合并過程中出現(xiàn)錯(cuò)誤:{e}")

# --- 5. 邏輯類:OpenPyXL 格式處理 ---
class OpenPyXLProcessor:
    """只負(fù)責(zé)保留格式的 OpenPyXL 邏輯 (慢)"""
    
    @staticmethod
    def _cache_styles(ws):
        """輔助函數(shù):緩存標(biāo)題行和列寬"""
        header_format = []
        for row in ws.iter_rows(min_row=1, max_row=1):
            header_format.append([(cell.value, cell.number_format, cell.font, cell.fill, 
                                 cell.border, cell.alignment) for cell in row])
        
        column_widths = {}
        for col_idx in range(1, ws.max_column + 1):
            col_letter = get_column_letter(col_idx)
            column_dim = ws.column_dimensions[col_letter]
            if column_dim and hasattr(column_dim, 'width') and column_dim.width:
                column_widths[col_letter] = column_dim.width
        
        return header_format, column_widths

    @staticmethod
    def _apply_header_styles(ws, header_format):
        """輔助函數(shù):應(yīng)用緩存的標(biāo)題行格式"""
        for col_idx, (value, num_format, font, fill, border, alignment) in enumerate(header_format[0], 1):
            cell = ws.cell(row=1, column=col_idx, value=value)
            cell.number_format = num_format
            if font: cell.font = copy.copy(font)
            if fill: cell.fill = copy.copy(fill)
            if border: cell.border = copy.copy(border)
            if alignment: cell.alignment = copy.copy(alignment)
        return ws.max_column

    @staticmethod
    def _apply_column_widths(ws, column_widths):
        """輔助函數(shù):應(yīng)用緩存的列寬"""
        for col_letter, width in column_widths.items():
            ws.column_dimensions[col_letter].width = width

    @staticmethod
    def split_excel_preserve_format(file_path, rows_per_file, total_data_rows):
        try:
            start_time = time.time()
            logging.info(f"[PreserveFormat] 開始拆分文件: {file_path}")
            
            wb = load_workbook(file_path)
            ws = wb.active
            
            if total_data_rows == 0:
                messagebox.showerror(UI_TEXTS['ERROR_TITLE'], UI_TEXTS['WARN_EMPTY_FILE'])
                return
            
            num_files = math.ceil(total_data_rows / rows_per_file)
            file_dir = os.path.dirname(file_path)
            file_name = os.path.splitext(os.path.basename(file_path))[0]
            
            header_format, column_widths = OpenPyXLProcessor._cache_styles(ws)
            max_col = ws.max_column
            
            for i in range(num_files):
                file_start_time = time.time()
                start_row = i * rows_per_file + 2  
                end_row = min((i + 1) * rows_per_file + 1, total_data_rows + 1)
                
                new_wb = Workbook()
                new_ws = new_wb.active
                
                OpenPyXLProcessor._apply_header_styles(new_ws, header_format)
                
                # 核心:逐個(gè)復(fù)制數(shù)據(jù)和格式
                data_row_idx = 2
                for row in range(start_row, end_row + 1):
                    for col in range(1, max_col + 1):
                        source_cell = ws.cell(row=row, column=col)
                        target_cell = new_ws.cell(row=data_row_idx, column=col, value=source_cell.value)
                        
                        target_cell.number_format = source_cell.number_format
                        if source_cell.font:
                            target_cell.font = copy.copy(source_cell.font)
                        if source_cell.fill:
                            target_cell.fill = copy.copy(source_cell.fill)
                        if source_cell.border:
                            target_cell.border = copy.copy(source_cell.border)
                        if source_cell.alignment:
                            target_cell.alignment = copy.copy(source_cell.alignment)
                    data_row_idx += 1
                
                OpenPyXLProcessor._apply_column_widths(new_ws, column_widths)

                output_filename = f"{file_name}_拆分_{i+1}.xlsx"
                output_path = os.path.join(file_dir, output_filename)
                
                new_wb.save(output_path)
                file_time = time.time() - file_start_time
                logging.info(f"[PreserveFormat] 保存拆分文件: {output_path} (耗時(shí): {file_time:.2f}s)")
            
            total_time = time.time() - start_time
            messagebox.showinfo(UI_TEXTS['SUCCESS_TITLE'], 
                                UI_TEXTS['SUCCESS_SPLIT_FORMAT'].format(
                                    num_files=num_files,
                                    time=total_time
                                ))
            
        except Exception as e:
            messagebox.showerror(UI_TEXTS['ERROR_TITLE'], 
                                 UI_TEXTS['ERROR_DURING_SPLIT'].format(error=e))
            logging.error(f"[PreserveFormat] 拆分過程中出現(xiàn)錯(cuò)誤:{e}")

    @staticmethod
    def combine_excel_preserve_format(selected_file):
        try:
            start_time = time.time()
            logging.info(f"[PreserveFormat] 開始合并文件,以 {selected_file} 為基準(zhǔn)")
            file_dir = os.path.dirname(selected_file)
            file_name = os.path.basename(selected_file)
            
            listdir = os.listdir(file_dir)
            excel_files = [f for f in listdir if f.endswith(('.xlsx', '.xls'))]
            
            if not excel_files:
                messagebox.showerror(UI_TEXTS['ERROR_TITLE'], UI_TEXTS['ERROR_NO_FILES_FOUND'])
                return
            
            base_name = file_name
            common_patterns = ExcelFileUtils.extract_common_pattern(base_name)
            similar_files = ExcelFileUtils.find_similar_files(excel_files, common_patterns, base_name)
            
            if len(similar_files) <= 1:
                messagebox.showinfo(UI_TEXTS['SUCCESS_TITLE'], 
                                    UI_TEXTS['ERROR_NO_SIMILAR_FILES'].format(filename=file_name))
                return
            
            file_list = "\n".join(similar_files)
            confirm = messagebox.askyesno(
                f"{UI_TEXTS['SUCCESS_TITLE']} ({UI_TEXTS['TAB_COMBINE']})", 
                f"將合并以下文件 (此操作可能較慢):\n\n{file_list}\n\n共 {len(similar_files)} 個(gè)文件"
            )
            
            if not confirm:
                logging.info("[PreserveFormat] 用戶取消合并")
                return
            
            template_file = os.path.join(file_dir, similar_files[0])
            template_wb = load_workbook(template_file)
            template_ws = template_wb.active
            
            merged_wb = Workbook()
            merged_ws = merged_wb.active
            
            header_format, column_widths = OpenPyXLProcessor._cache_styles(template_ws)
            max_col_count = OpenPyXLProcessor._apply_header_styles(merged_ws, header_format)
            
            current_row = 2
            total_rows = 0
            
            for filename in similar_files:
                file_path = os.path.join(file_dir, filename)
                wb = load_workbook(file_path)
                ws = wb.active
                
                for row_idx, row in enumerate(ws.iter_rows(min_row=2), 2):
                    for col_idx, cell in enumerate(row, 1):
                        if col_idx <= max_col_count:
                            target_cell = merged_ws.cell(row=current_row, column=col_idx, value=cell.value)
                            
                            target_cell.number_format = cell.number_format
                            if cell.font:
                                target_cell.font = copy.copy(cell.font)
                            if cell.fill:
                                target_cell.fill = copy.copy(cell.fill)
                            if cell.border:
                                target_cell.border = copy.copy(cell.border)
                            if cell.alignment:
                                target_cell.alignment = copy.copy(cell.alignment)
                    
                    current_row += 1
                    total_rows += 1
                
                logging.info(f"[PreserveFormat] 合并文件: {filename}, 添加了 {ws.max_row - 1} 行數(shù)據(jù)")
                wb.close()
            
            OpenPyXLProcessor._apply_column_widths(merged_ws, column_widths)
            
            output_name = ExcelFileUtils.generate_output_name(common_patterns, base_name)
            output_path = os.path.join(file_dir, f"{output_name}_合并結(jié)果.xlsx")
            
            counter = 1
            while os.path.exists(output_path):
                output_path = os.path.join(file_dir, f"{output_name}_合并結(jié)果({counter}).xlsx")
                counter += 1
            
            merged_wb.save(output_path)
            total_time = time.time() - start_time
            logging.info(f"[PreserveFormat] 保存合并結(jié)果: {output_path} (總耗時(shí): {total_time:.2f}s)")
            
            messagebox.showinfo(
                UI_TEXTS['SUCCESS_TITLE'], 
                UI_TEXTS['SUCCESS_COMBINE_FORMAT'].format(
                    num_files=len(similar_files),
                    path=output_path,
                    rows=total_rows,
                    time=total_time
                ))
            
        except Exception as e:
            messagebox.showerror(UI_TEXTS['ERROR_TITLE'], 
                                 UI_TEXTS['ERROR_DURING_COMBINE'].format(error=e))
            logging.error(f"[PreserveFormat] 合并過程中出現(xiàn)錯(cuò)誤:{e}")

# --- 6. UI 類:拆分標(biāo)簽頁 ---
class SplitterTab(ttk.Frame):
    """文件拆分標(biāo)簽頁的UI和邏輯 (已重構(gòu))"""
    
    def __init__(self, parent):
        super().__init__(parent, padding="15")
        self.total_data_rows = 0
        
        self.split_file_var = tk.StringVar()
        self.rows_var = tk.StringVar(value="10000")
        self.split_info_var = tk.StringVar(value=UI_TEXTS['FILE_INFO_DEFAULT'])
        self.preserve_format_var = tk.BooleanVar(value=True) # 默認(rèn)保留格式
        
        self.create_widgets()

    def create_widgets(self):
        # --- 使用 UI_TEXTS 常量 ---
        ttk.Label(self, text=UI_TEXTS['SELECT_FILE_LABEL']).grid(row=0, column=0, sticky=tk.W, pady=5)
        ttk.Entry(self, textvariable=self.split_file_var, width=40).grid(row=0, column=1, padx=5, pady=5)
        ttk.Button(self, text=UI_TEXTS['BROWSE_BUTTON'], command=self.select_file).grid(row=0, column=2, padx=5, pady=5)

        ttk.Label(self, textvariable=self.split_info_var, foreground="blue").grid(row=1, column=1, sticky=tk.W, pady=2)

        ttk.Label(self, text=UI_TEXTS['ROWS_PER_FILE_LABEL']).grid(row=2, column=0, sticky=tk.W, pady=10)
        ttk.Entry(self, textvariable=self.rows_var, width=15).grid(row=2, column=1, sticky=tk.W, padx=5, pady=10)
        ttk.Label(self, text=UI_TEXTS['ROWS_UNIT']).grid(row=2, column=1, sticky=tk.W, padx=100, pady=10)

        ttk.Checkbutton(
            self, 
            text=UI_TEXTS['PRESERVE_FORMAT_LABEL'], 
            variable=self.preserve_format_var
        ).grid(row=3, column=1, sticky=tk.W, pady=(10, 0))

        ttk.Label(
            self, 
            text=UI_TEXTS['SPEED_FIRST_INFO'],
            foreground="gray", font=("Arial", 9)
        ).grid(row=4, column=1, sticky=tk.W, padx=5, pady=(0, 10))

        ttk.Button(self, text=UI_TEXTS['START_SPLIT_BUTTON'], command=self.start_split).grid(row=5, column=1, pady=10)

        self.columnconfigure(1, weight=1)

    def select_file(self):
        file_path = filedialog.askopenfilename(
            title=UI_TEXTS['SELECT_FILE_LABEL'],
            filetypes=[("Excel files", "*.xlsx *.xls"), ("All files", "*.*")]
        )
        if file_path:
            self.split_file_var.set(file_path)
            if self._read_row_count(file_path): # 自動(dòng)讀取行數(shù)
                self.split_info_var.set(UI_TEXTS['FILE_INFO_ROWS'].format(rows=self.total_data_rows))
            else:
                self.split_info_var.set(UI_TEXTS['ERROR_CANT_READ'])

    def _read_row_count(self, file_path):
        """輔助函數(shù):讀取行數(shù)"""
        try:
            df = pd.read_excel(file_path, engine='openpyxl')
            self.total_data_rows = len(df)
            logging.info(f"選中拆分文件: {file_path}, 總行數(shù): {self.total_data_rows}")
            return True
        except Exception as e:
            self.total_data_rows = 0
            logging.error(f"無法讀取文件信息: {e}")
            return False

    def _validate_input(self):
        """輔助函數(shù):集中處理所有輸入驗(yàn)證"""
        file_path = self.split_file_var.get()
        
        if not file_path or not os.path.exists(file_path):
            messagebox.showwarning(UI_TEXTS['WARN_TITLE'], UI_TEXTS['WARN_NO_FILE'])
            return None, None
        
        # 如果選擇文件時(shí)讀取失敗,在此重試
        if self.total_data_rows == 0 and UI_TEXTS['ERROR_CANT_READ'] in self.split_info_var.get():
            if not self._read_row_count(file_path):
                messagebox.showerror(UI_TEXTS['ERROR_TITLE'], UI_TEXTS['ERROR_CANT_READ'])
                return None, None
        
        if self.total_data_rows == 0:
             messagebox.showwarning(UI_TEXTS['WARN_TITLE'], UI_TEXTS['WARN_EMPTY_FILE'])
             return None, None

        try:
            rows_per_file = int(self.rows_var.get())
            if rows_per_file <= 0:
                messagebox.showwarning(UI_TEXTS['WARN_TITLE'], UI_TEXTS['WARN_INVALID_ROWS'])
                return None, None
        except ValueError:
            messagebox.showwarning(UI_TEXTS['WARN_TITLE'], UI_TEXTS['WARN_NAN'])
            return None, None
            
        return file_path, rows_per_file

    def start_split(self):
        """開始拆分(邏輯更清晰)"""
        file_path, rows_per_file = self._validate_input()
        
        if not file_path:
            return # 驗(yàn)證失敗
        
        if self.preserve_format_var.get():
            # 方案1:保留格式 (慢)
            OpenPyXLProcessor.split_excel_preserve_format(file_path, rows_per_file, self.total_data_rows)
        else:
            # 方案2:速度優(yōu)先 (快)
            PandasProcessor.split_excel_pandas(file_path, rows_per_file)

# --- 7. UI 類:合并標(biāo)簽頁 ---
class CombinerTab(ttk.Frame):
    """智能合并標(biāo)簽頁的UI和邏輯 (已重構(gòu))"""
    
    def __init__(self, parent):
        super().__init__(parent, padding="15")
        
        self.combine_file_var = tk.StringVar()
        self.combine_info_var = tk.StringVar(value=UI_TEXTS['FILE_INFO_DEFAULT'])
        self.preserve_format_var = tk.BooleanVar(value=True)
        
        self.create_widgets()

    def create_widgets(self):
        # --- 使用 UI_TEXTS 常量 ---
        ttk.Label(self, text=UI_TEXTS['COMBINE_BASE_LABEL']).grid(row=0, column=0, sticky=tk.W, pady=5)
        ttk.Entry(self, textvariable=self.combine_file_var, width=50).grid(row=0, column=1, padx=5, pady=5)
        ttk.Button(self, text=UI_TEXTS['BROWSE_BUTTON'], command=self.select_file).grid(row=0, column=2, padx=5, pady=5)

        ttk.Label(self, textvariable=self.combine_info_var, foreground="blue").grid(row=1, column=1, sticky=tk.W, pady=5)

        ttk.Label(self, text=UI_TEXTS['COMBINE_INFO'], foreground="gray", font=("Arial", 9)).grid(row=2, column=1, sticky=tk.W, pady=5)

        ttk.Checkbutton(
            self, 
            text=UI_TEXTS['PRESERVE_FORMAT_LABEL'], 
            variable=self.preserve_format_var
        ).grid(row=3, column=1, sticky=tk.W, pady=(10, 0))

        ttk.Label(
            self, 
            text=UI_TEXTS['SPEED_FIRST_INFO'],
            foreground="gray", font=("Arial", 9)
        ).grid(row=4, column=1, sticky=tk.W, padx=5, pady=(0, 10))
        
        ttk.Button(self, text=UI_TEXTS['START_COMBINE_BUTTON'], command=self.start_combine).grid(row=5, column=1, pady=15)

        self.columnconfigure(1, weight=1)

    def select_file(self):
        file_path = filedialog.askopenfilename(
            title=UI_TEXTS['COMBINE_BASE_LABEL'],
            filetypes=[("Excel files", "*.xlsx *.xls"), ("All files", "*.*")]
        )
        if file_path:
            self.combine_file_var.set(file_path)
            file_name = os.path.basename(file_path)
            self.combine_info_var.set(UI_TEXTS['FILE_INFO_SELECTED'].format(filename=file_name))
            logging.info(f"選中合并基準(zhǔn)文件: {file_path}")

    def start_combine(self):
        """開始合并"""
        file_path = self.combine_file_var.get()
        
        if not file_path or not os.path.exists(file_path):
            messagebox.showwarning(UI_TEXTS['WARN_TITLE'], UI_TEXTS['WARN_NO_FILE'])
            return
        
        if self.preserve_format_var.get():
            OpenPyXLProcessor.combine_excel_preserve_format(file_path)
        else:
            PandasProcessor.combine_excel_pandas(file_path)

# --- 8. 主應(yīng)用 ---
class ExcelToolApp:
    """主應(yīng)用程序類"""
    def __init__(self, root):
        self.root = root
        self.root.title(UI_TEXTS['APP_TITLE'])
        self.root.geometry("600x380")
        
        self.create_widgets()

    def create_widgets(self):
        notebook = ttk.Notebook(self.root)
        notebook.pack(fill='both', expand=True, padx=10, pady=10)

        split_frame = SplitterTab(notebook)
        notebook.add(split_frame, text=UI_TEXTS['TAB_SPLIT'])

        combine_frame = CombinerTab(notebook)
        notebook.add(combine_frame, text=UI_TEXTS['TAB_COMBINE'])

    def run(self):
        self.root.mainloop()

# --- 9. 啟動(dòng)入口 ---
if __name__ == "__main__":
    root = tk.Tk()
    app = ExcelToolApp(root)
    app.run()

打包步驟

1.創(chuàng)建一個(gè)虛擬環(huán)境

python -m venv venv_pack

2.激活虛擬環(huán)境

.\venv_pack\Scripts\activate

3.安裝最小依賴: 在這個(gè)干凈的環(huán)境中,只安裝您的程序真正需要的庫。

pip install pyinstaller pandas openpyxl

4.運(yùn)行 PyInstaller 打包命令

選項(xiàng) A:打包成一個(gè)文件夾(推薦,啟動(dòng)快),這會(huì)創(chuàng)建一個(gè) dist\Excel工具 文件夾,里面包含您的 Excel工具.exe 和所有依賴。啟動(dòng)速度比“單文件”快得多。

pyinstaller --noconsole --name="Excel工具" --upx-dir="." "excel_tool.py"

選項(xiàng) B:打包成一個(gè)單獨(dú)的 EXE 文件(整潔,但啟動(dòng)慢),這會(huì)創(chuàng)建一個(gè) dist\Excel工具.exe 單文件。

pyinstaller --noconsole --onefile --name="Excel工具" --upx-dir="." "excel_tool.py"

5.找到您的 EXE

打包完成后,PyInstaller 會(huì)創(chuàng)建幾個(gè)文件夾:

  • build:(臨時(shí)文件,可以刪除)
  • dist:您的程序在這里!
  • Excel工具.spec:(打包配置文件,可以保留)

您只需要進(jìn)入 dist 文件夾。

如果使用選項(xiàng) A,您需要將整個(gè) Excel工具 文件夾 復(fù)制給其他人。

如果使用選項(xiàng) B,您只需要將 Excel工具.exe 文件復(fù)制給其他人。

到此這篇關(guān)于Python實(shí)現(xiàn)Excel拆分和合并的優(yōu)化版本的文章就介紹到這了,更多相關(guān)Python Excel拆分和合并內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

三原县| 龙井市| 东方市| 平顶山市| 璧山县| 蓬莱市| 呼图壁县| 双牌县| 航空| 若尔盖县| 奉新县| 余姚市| 颍上县| 海丰县| 崇州市| 调兵山市| 陈巴尔虎旗| 白城市| 陇西县| 四会市| 安吉县| 沙湾县| 商洛市| 阳东县| 萨迦县| 江口县| 都昌县| 南部县| 阜宁县| 开封县| 青海省| 鄂托克旗| 新干县| 古田县| 新田县| 北辰区| 周至县| 略阳县| 昌都县| 台前县| 新干县|