Python全能对比小助手:教你用Tkinter和OpenPyxl写个Excel比对神器!

引言

        你是否曾面对两个看似相同但实则暗藏玄机的Excel文件无从下手?又是否因手工对比而痛苦不堪?上回给大家带来了Excel 文件比对神器,今天我继续带你用Python打造一个专属于你的Excel文件比对神器,拯救你的眼睛和时间。如果大家还有其他的需求,欢迎在评论区留言,免费给大家完成!!!

增加功能:

        1.可以按行按列进行比对

        2.可以多行多列比对

        3.滚动栏直接滑到最底部

        准备好了吗?Let’s dive into the code, baby!

第一步:我们的英雄登场——Tkinter和OpenPyxl

        在我们开始之前,你可能会问:“为什么要用Tkinter和OpenPyxl?” Tkinter可是Python自带的GUI(图形用户界面)库,简单易用,堪称界面开发新手的福音。而OpenPyxl?它是处理Excel文件的利器,能让你在Excel世界里如鱼得水,挥斥方遒!

第二步:功能分析——让我们搞懂这个神器到底做什么

        在这里,我们要对两个Excel文件进行深度对比,找出每一个隐藏在格子中的差异。不仅如此,我们还要把这些差异标记出来,让你一眼就能看穿它们的猫腻!

        看代码前,先来点小剧透:

  • 文件选择功能:轻松选择要对比的文件、设置输出文件夹和报告位置。
  • Excel对比:逐个单元格对比,找出数值或公式的不同,甚至给你详细报告。
  • 多行多列对比:不仅仅是单元格,我们还可以对比整列整行。
  • 结果展示:比对结果自动显示在一个带滚动条的文本框中,再也不怕结果太长看不到!

        最终的效果如下图所示:

第三步:让我们见证代码的魔力

        代码可能看起来有点长,但我们会逐块分析,就像解剖一只美味的代码鸡!

import tkinter as tk
from tkinter import filedialog, messagebox
from openpyxl import load_workbook
from openpyxl.styles import PatternFill
import os
import docx

        首先,介绍我们的主角:tkinter, filedialog, messagebox 用来处理GUI,openpyxl 处理Excel文件,os 负责路径操作,docx 是生成报告的秘密武器。

第四步:启动我们的GUI

root = tk.Tk()
root.title("Excel文件比对工具")

        这两行代码,简直就是你家的门面工程。root = tk.Tk()就像是开启了一个空的帆布,root.title()则是在这块帆布上写下“我是谁”。

第五步:搞定文件选择和输出路径

original_button = tk.Button(file_select_frame, text="选择原始数据文件", command=lambda: select_file('original'))
comparison_button = tk.Button(file_select_frame, text="选择比对数据文件", command=lambda: select_file('comparison'))
output_excel_button = tk.Button(file_select_frame, text="选择输出文件夹", command=lambda: select_directory('output_excel'))
output_report_button = tk.Button(file_select_frame, text="选择输出报告文件", command=lambda: select_file('output_report', save=True))

        这些按钮可不是随便摆的,它们可是你的左右手,让你随意选择Excel文件,指定输出路径,一切都在掌控之中。

第六步:Excel对比大戏开演

def compare_excel_sheets(file1, file2, output_excel_dir=None, output_report=None):
    wb1 = load_workbook(file1, data_only=False)
    wb2 = load_workbook(file2, data_only=False)
    
    # …省略部分代码…
    
    wb1.save(output_file1)
    wb2.save(output_file2)

        终于到了高潮部分!这个函数堪比侦探,逐个检查每一个单元格的内容,找出那些不老实的数值和公式,甚至还会用黄色标记帮你圈出来!

第七步:多列多行对比——让全面性不再是奢望

def compare_multiple_rows_or_columns_in_sheet(file, sheet_name, indices1, indices2, compare_by='column'):
    wb = load_workbook(file, data_only=False)
    
    # …省略部分代码…
    
    return "\n".join(differences)

        如果说单个单元格比对是小打小闹,那多列多行比对简直就是大场面。你可以同时对比多列或者多行数据,再也不用担心遗漏某个关键点!

第八步:炫酷的结果展示

result_frame = tk.Frame(root)
scrollbar = tk.Scrollbar(result_frame)
result_text = tk.Text(result_frame, wrap='word', height=20, width=100, yscrollcommand=scrollbar.set)
scrollbar.config(command=result_text.yview)

  

        当比对结果多到看不完?别急,滑动条来了!这个部分不仅让你轻松查看所有比对结果,还能自动滑动到最新的结果位置,再长的列表也不怕!完整代码如下:

import tkinter as tk
from tkinter import filedialog, messagebox
from openpyxl import load_workbook
from openpyxl.styles import PatternFill
import os
import docx

def compare_excel_sheets(file1, file2, output_excel_dir=None, output_report=None):
    wb1 = load_workbook(file1, data_only=False)
    wb2 = load_workbook(file2, data_only=False)

    differences = []
    fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")

    output_file1 = os.path.join(output_excel_dir, f"标记_{os.path.basename(file1)}")
    output_file2 = os.path.join(output_excel_dir, f"标记_{os.path.basename(file2)}")

    for sheet_name in wb1.sheetnames:
        if sheet_name in wb2.sheetnames:
            sheet1 = wb1[sheet_name]
            sheet2 = wb2[sheet_name]

            max_row = max(sheet1.max_row, sheet2.max_row)
            max_column = max(sheet1.max_column, sheet2.max_column)

            for row in range(1, max_row + 1):
                for col in range(1, max_column + 1):
                    cell1 = sheet1.cell(row=row, column=col)
                    cell2 = sheet2.cell(row=row, column=col)

                    val1 = cell1.value
                    val2 = cell2.value
                    formula1 = cell1.data_type == 'f'
                    formula2 = cell2.data_type == 'f'

                    if val1 != val2 or formula1 != formula2:
                        type1 = "公式" if formula1 else "数值"
                        type2 = "公式" if formula2 else "数值"

                        diff_msg = f"工作表 '{sheet_name}' 的第 {row} 行,第 {col} 列存在差异:\n"
                        diff_msg += f"原始数据: 类型='{type1}', 值='{val1}'\n"
                        diff_msg += f"比对数据: 类型='{type2}', 值='{val2}'\n"

                        if type1 != type2:
                            diff_msg += "(注意:一个是公式,另一个是数值)\n"
                        elif formula1 and formula2 and val1 != val2:
                            diff_msg += "(公式不同)\n"
                        elif not formula1 and not formula2 and val1 != val2:
                            diff_msg += "(数值不同)\n"

                        differences.append(diff_msg)

                        # 标记不同的单元格
                        cell1.fill = fill
                        cell2.fill = fill
        else:
            differences.append(f"工作表 '{sheet_name}' 在两个文件中不一致。")

    # 保存标记后的Excel文件
    wb1.save(output_file1)
    wb2.save(output_file2)

    # 生成报告并保存到Word文档
    if output_report:
        generate_report(differences, output_report)

    return differences

def generate_report(differences, output_report):
    doc = docx.Document()
    doc.add_heading('Excel 文件比较报告', 0)

    for diff in differences:
        doc.add_paragraph(diff)

    doc.save(output_report)

def specific_cell_comparisons(file1, file2, sheet_name, row, col):
    wb1 = load_workbook(file1, data_only=False)
    wb2 = load_workbook(file2, data_only=False)

    if sheet_name in wb1.sheetnames and sheet_name in wb2.sheetnames:
        sheet1 = wb1[sheet_name]
        sheet2 = wb2[sheet_name]

        cell1 = sheet1.cell(row=row, column=col)
        cell2 = sheet2.cell(row=row, column=col)

        val1 = cell1.value
        val2 = cell2.value
        formula1 = cell1.data_type == 'f'
        formula2 = cell2.data_type == 'f'

        type1 = "公式" if formula1 else "数值"
        type2 = "公式" if formula2 else "数值"

        result_msg = f"工作表 '{sheet_name}' 的第 {row} 行,第 {col} 列比对结果:\n"
        result_msg += f"原始数据: 类型='{type1}', 值='{val1}'\n"
        result_msg += f"比对数据: 类型='{type2}', 值='{val2}'\n"

        if type1 != type2:
            result_msg += "(注意:一个是公式,另一个是数值)\n"
        elif formula1 and formula2 and val1 != val2:
            result_msg += "(公式不同)\n"
        elif not formula1 and not formula2 and val1 != val2:
            result_msg += "(数值不同)\n"
        else:
            result_msg += "(两者相同)\n"

        return result_msg
    else:
        return f"工作表 '{sheet_name}' 在两个文件中不一致。"

def compare_rows_or_columns_in_sheet(file, sheet_name, name1, name2, compare_by='column'):
    wb = load_workbook(file, data_only=False)

    if sheet_name in wb.sheetnames:
        sheet = wb[sheet_name]

        header = [cell.value for cell in sheet[1]] if compare_by == 'column' else None

        try:
            if compare_by == 'column':
                idx1 = header.index(name1) + 1
                idx2 = header.index(name2) + 1
                values1 = [sheet.cell(row=row, column=idx1).value for row in range(2, sheet.max_row + 1)]
                values2 = [sheet.cell(row=row, column=idx2).value for row in range(2, sheet.max_row + 1)]
            else:
                idx1 = int(name1)
                idx2 = int(name2)
                values1 = [sheet.cell(row=idx1, column=col).value for col in range(1, sheet.max_column + 1)]
                values2 = [sheet.cell(row=idx2, column=col).value for col in range(1, sheet.max_column + 1)]
        except ValueError:
            return f"在工作表 '{sheet_name}' 中找不到 '{name1}' 或 '{name2}'。"

        differences = []
        for i, (val1, val2) in enumerate(zip(values1, values2), start=1 if compare_by == 'column' else 1):
            if val1 != val2:
                if compare_by == 'column':
                    diff_msg = f"工作表 '{sheet_name}' 的第 {i + 1} 行, 列 '{name1}' 和 列 '{name2}' 存在差异: 值1='{val1}', 值2='{val2}'\n"
                else:
                    diff_msg = f"工作表 '{sheet_name}' 的第 {i} 列, 行 '{name1}' 和 行 '{name2}' 存在差异: 值1='{val1}', 值2='{val2}'\n"
                differences.append(diff_msg)

        if not differences:
            return f"工作表 '{sheet_name}' 的'{name1}' 和 '{name2}' 完全一致。"
        else:
            return "\n".join(differences)
    else:
        return f"工作表 '{sheet_name}' 不存在。"


def compare_multiple_rows_or_columns_in_sheets(file, sheet_name, indices1, indices2, compare_by='column'):
    wb = load_workbook(file, data_only=False)

    if sheet_name in wb.sheetnames:
        sheet = wb[sheet_name]

        differences = []

        try:
            if compare_by == 'column':
                for idx1, idx2 in zip(indices1, indices2):
                    col1_values = [sheet.cell(row=row, column=idx1).value for row in range(2, sheet.max_row + 1)]
                    col2_values = [sheet.cell(row=row, column=idx2).value for row in range(2, sheet.max_row + 1)]

                    for i, (val1, val2) in enumerate(zip(col1_values, col2_values), start=2):
                        if val1 != val2:
                            diff_msg = f"工作表 '{sheet_name}' 的第 {i} 行, 列 '{idx1}' 和 列 '{idx2}' 存在差异: 值1='{val1}', 值2='{val2}'\n"
                            differences.append(diff_msg)
            else:
                for idx1, idx2 in zip(indices1, indices2):
                    row1_values = [sheet.cell(row=idx1, column=col).value for col in range(1, sheet.max_column + 1)]
                    row2_values = [sheet.cell(row=idx2, column=col).value for col in range(1, sheet.max_column + 1)]

                    for i, (val1, val2) in enumerate(zip(row1_values, row2_values), start=1):
                        if val1 != val2:
                            diff_msg = f"工作表 '{sheet_name}' 的第 {i} 列, 行 '{idx1}' 和 行 '{idx2}' 存在差异: 值1='{val1}', 值2='{val2}'\n"
                            differences.append(diff_msg)
        except ValueError:
            return f"在工作表 '{sheet_name}' 中找不到 '{indices1}' 或 '{indices2}'。"

        if not differences:
            return f"工作表 '{sheet_name}' 的'{indices1}' 和 '{indices2}' 完全一致。"
        else:
            return "\n".join(differences)
    else:
        return f"工作表 '{sheet_name}' 不存在。"

def select_original_file():
    global original_file_path
    original_file_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx *.xls")])
    result_text.insert(tk.END, f"原始数据文件: {original_file_path}\n")

def select_comparison_file():
    global comparison_file_path
    comparison_file_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx *.xls")])
    result_text.insert(tk.END, f"比对数据文件: {comparison_file_path}\n")

def select_output_directory():
    global output_excel_dir
    output_excel_dir = filedialog.askdirectory()
    result_text.insert(tk.END, f"输出文件夹: {output_excel_dir}\n")

def select_output_report_file():
    global output_report_path
    output_report_path = filedialog.asksaveasfilename(defaultextension=".docx", filetypes=[("Word文档", "*.docx")])
    result_text.insert(tk.END, f"输出报告文件: {output_report_path}\n")

def start_comparison():
    if not original_file_path or not comparison_file_path:
        messagebox.showwarning("警告", "请先选择原始数据和比对数据文件。")
        return

    if not output_excel_dir:
        messagebox.showwarning("警告", "请选择输出文件夹。")
        return

    differences = compare_excel_sheets(original_file_path, comparison_file_path, output_excel_dir, output_report_path)

    result_text.delete(1.0, tk.END)
    result_text.insert(tk.END, "比对结果:\n")
    if differences:
        for difference in differences:
            result_text.insert(tk.END, difference + "\n\n")
    else:
        result_text.insert(tk.END, "两个文件完全相同。\n")

def search_specific_cell():
    if not original_file_path or not comparison_file_path:
        messagebox.showwarning("警告", "请先选择原始数据和比对数据文件。")
        return

    sheet_name = sheet_entry.get()
    row = int(row_entry.get())
    col = int(col_entry.get())

    result_msg = specific_cell_comparison(original_file_path, comparison_file_path, sheet_name, row, col)
    result_text.delete(1.0, tk.END)
    result_text.insert(tk.END, result_msg + "\n")

def compare_uploaded_columns():
    if not uploaded_file_path:
        messagebox.showwarning("警告", "请先选择数据文件。")
        return

    sheet_name = sheet_entry_col.get()
    name1 = col1_entry.get()
    name2 = col2_entry.get()

    compare_by = compare_option.get()

    result_msg = compare_rows_or_columns_in_sheet(uploaded_file_path, sheet_name, name1, name2, compare_by)
    result_text.delete(1.0, tk.END)
    result_text.insert(tk.END, result_msg + "\n")


def select_uploaded_file():
    global uploaded_file_path
    uploaded_file_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx *.xls")])
    result_text.insert(tk.END, f"已选择数据文件: {uploaded_file_path}\n")


def compare_uploaded_columns():
    if not uploaded_file_path:
        messagebox.showwarning("警告", "请先选择数据文件。")
        return

    sheet_name = sheet_entry_col.get()
    name1 = col1_entry.get()
    name2 = col2_entry.get()

    compare_by = compare_option.get()

    result_msg = compare_rows_or_columns_in_sheet(uploaded_file_path, sheet_name, name1, name2, compare_by)
    result_text.delete(1.0, tk.END)
    result_text.insert(tk.END, result_msg + "\n")

# 修改后的比对按钮功能
def compare_uploaded_multiple():
    if not uploaded_file_path:
        messagebox.showwarning("警告", "请先选择数据文件。")
        return

    sheet_name = sheet_entry_col.get()
    indices1 = list(map(int, col1_entry.get().split(',')))
    indices2 = list(map(int, col2_entry.get().split(',')))

    compare_by = compare_option.get()

    result_msg = compare_multiple_rows_or_columns_in_sheet(uploaded_file_path, sheet_name, indices1, indices2, compare_by)
    result_text.delete(1.0, tk.END)
    result_text.insert(tk.END, result_msg + "\n")
def select_file(file_type, save=False):
    global original_file_path, comparison_file_path, output_excel_dir, output_report_path, uploaded_file_path

    if file_type == 'original':
        original_file_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx *.xls")])
        result_text.insert(tk.END, f"原始数据文件: {original_file_path}\n")
    elif file_type == 'comparison':
        comparison_file_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx *.xls")])
        result_text.insert(tk.END, f"比对数据文件: {comparison_file_path}\n")
    elif file_type == 'output_report':
        output_report_path = filedialog.asksaveasfilename(defaultextension=".docx", filetypes=[("Word文档", "*.docx")])
        result_text.insert(tk.END, f"输出报告文件: {output_report_path}\n")
    elif file_type == 'uploaded':
        uploaded_file_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx *.xls")])
        result_text.insert(tk.END, f"已选择数据文件: {uploaded_file_path}\n")


def select_directory(directory_type):
    global output_excel_dir
    if directory_type == 'output_excel':
        output_excel_dir = filedialog.askdirectory()
        result_text.insert(tk.END, f"输出文件夹: {output_excel_dir}\n")

# 初始化主窗口
root = tk.Tk()
root.title("Excel文件比对工具")

# 初始化全局变量
original_file_path = None
comparison_file_path = None
output_excel_dir = None
output_report_path = None
uploaded_file_path = None

# 上传文件选择部分
upload_frame = tk.LabelFrame(root, text="上传文件")
upload_frame.pack(pady=10, padx=10, fill="x")

upload_button = tk.Button(upload_frame, text="上传需要比对的数据文件", command=lambda: select_file('uploaded'))
upload_button.pack(pady=5, padx=10)

# 文件选择按钮部分
file_select_frame = tk.LabelFrame(root, text="选择文件和输出选项")
file_select_frame.pack(pady=10, padx=10, fill="x")

original_button = tk.Button(file_select_frame, text="选择原始数据文件", command=lambda: select_file('original'))
original_button.grid(row=0, column=0, padx=5, pady=5, sticky="ew")

comparison_button = tk.Button(file_select_frame, text="选择比对数据文件", command=lambda: select_file('comparison'))
comparison_button.grid(row=0, column=1, padx=5, pady=5, sticky="ew")

output_excel_button = tk.Button(file_select_frame, text="选择输出文件夹", command=lambda: select_directory('output_excel'))
output_excel_button.grid(row=0, column=2, padx=5, pady=5, sticky="ew")

output_report_button = tk.Button(file_select_frame, text="选择输出报告文件", command=lambda: select_file('output_report', save=True))
output_report_button.grid(row=0, column=3, padx=5, pady=5, sticky="ew")

# 比对按钮
compare_button = tk.Button(root, text="开始比对", command=start_comparison)
compare_button.pack(pady=10)

# 查找特定单元格部分
specific_cell_frame = tk.LabelFrame(root, text="查找特定单元格")
specific_cell_frame.pack(pady=10, padx=10, fill="x")

sheet_label = tk.Label(specific_cell_frame, text="工作表名称:")
sheet_label.grid(row=0, column=0, padx=5, pady=5)
sheet_entry = tk.Entry(specific_cell_frame)
sheet_entry.grid(row=0, column=1, padx=5, pady=5)

row_label = tk.Label(specific_cell_frame, text="行号:")
row_label.grid(row=1, column=0, padx=5, pady=5)
row_entry = tk.Entry(specific_cell_frame)
row_entry.grid(row=1, column=1, padx=5, pady=5)

col_label = tk.Label(specific_cell_frame, text="列号:")
col_label.grid(row=2, column=0, padx=5, pady=5)
col_entry = tk.Entry(specific_cell_frame)
col_entry.grid(row=2, column=1, padx=5, pady=5)

search_button = tk.Button(specific_cell_frame, text="查找特定单元格", command=search_specific_cell)
search_button.grid(row=3, column=0, columnspan=2, pady=10)

# 比对同一表格中的多列或多行
compare_col_frame = tk.LabelFrame(root, text="比对表格中的多列或多行")
compare_col_frame.pack(pady=10, padx=10, fill="x")

sheet_label_col = tk.Label(compare_col_frame, text="工作表名称:")
sheet_label_col.grid(row=0, column=0, padx=5, pady=5)
sheet_entry_col = tk.Entry(compare_col_frame)
sheet_entry_col.grid(row=0, column=1, padx=5, pady=5)

compare_option = tk.StringVar(value="column")  # 默认按列比对

tk.Radiobutton(compare_col_frame, text="按列", variable=compare_option, value="column").grid(row=0, column=2, padx=5, pady=5)
tk.Radiobutton(compare_col_frame, text="按行", variable=compare_option, value="row").grid(row=0, column=3, padx=5, pady=5)

col1_label = tk.Label(compare_col_frame, text="列1名称/行号1(用逗号分隔):")
col1_label.grid(row=1, column=0, padx=5, pady=5)
col1_entry = tk.Entry(compare_col_frame)
col1_entry.grid(row=1, column=1, padx=5, pady=5)

col2_label = tk.Label(compare_col_frame, text="列2名称/行号2(用逗号分隔):")
col2_label.grid(row=2, column=0, padx=5, pady=5)
col2_entry = tk.Entry(compare_col_frame)
col2_entry.grid(row=2, column=1, padx=5, pady=5)

compare_cols_button = tk.Button(compare_col_frame, text="比对", command=compare_uploaded_multiple)
compare_cols_button.grid(row=3, column=0, columnspan=4, pady=10)

# 结果显示部分
result_frame = tk.Frame(root)
result_frame.pack(pady=10, padx=10, fill="both", expand=True)

scrollbar = tk.Scrollbar(result_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

result_text = tk.Text(result_frame, wrap='word', height=20, width=100, yscrollcommand=scrollbar.set)
result_text.pack(pady=10, padx=10, fill="both", expand=True)
scrollbar.config(command=result_text.yview)

# 运行主循环
root.mainloop()

结语:写代码就像做饭,关键是有趣!

        今天,我们用Python打造了一个强大的Excel比对工具,从选择文件到生成报告,每一步都在你的掌控之中。这不仅仅是代码,它是你工作中的得力助手,也是你编程技能的彰显。下次再遇到两个Excel文件不一致的情况,你知道该做什么了——掏出你的“神器”,让它们无所遁形!

赶紧试试吧,用代码让生活更轻松,也更有趣!Happy coding! 😎

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值