基于PyQt5+Python实现Excel内容对比

Excel内容对比

记录一下最近在研究的一个程序,如何实现两个Excel表格之间的内容对比。之前做出来的版本1,版本2都存在较大的逻辑问题,使用的openpyxl第三方库,没有透彻理解到openpyxl的特性,导致程序无法对比多数据量多的文件;在一些不对齐表格的对比上也出现了一些严重问题,出现IndexError、ValueError等一些bug。现在改进了效率,也修复了处理主副文件行数不同列数不同的问题

使用环境:Python3.8 + openpyxl 3.0.9 + PyQt5 5.15.4 + Pandas 1.3.5

界面GUI
在这里插入图片描述

使用到的一些较核心代码

数据处理

pandas处理DataFrame

def fill_rows(df1, df2, df1_rows, df2_rows):
    """
    解决 dataframe行数不一的问题
    :param df1: 主文件读取的dataframe
    :param df2: 副文件读取的dataframe
    :param df1_rows: 主文件的最大行数
    :param df2_rows: 副文件的最大行数
    :return: 处理完的df1,df2
    """
    df1_cols, df2_cols = df1.shape[1], df2.shape[1]
    if df1_rows > df2_rows:
        n = df1_rows - df2_rows  # 获取缺失行数
        for i in range(n):
            df2.loc[i + 2] = ["~\\" for i in range(df2_cols)] # 列表推导式补全
        return df1, df2
    elif df1_rows < df2_rows:
        n = df2_rows - df1_rows
        for i in range(n):
            df1.loc[i + 2] = ["~\\" for i in range(df1_cols)]
        return df1, df2
    else:
        return df1, df2

效果:
请添加图片描述
处理DataFrame具体行中的列数问题

def fill_cols(lst1, lst2):
    """
    处理读取的列数据,长度不一问题
    :param lst1: 主文件读取的一行数据
    :param lst2: 副文件读取的一行数据
    :return: 
    	lst1:处理后的lst1
    	lst2:处理后的lst2
    	len1/len2:返回最大长度
    """
    len1 = len(lst1)
    len2 = len(lst2)
    if len1 > len2:
        for i in range(len1 - len2):
            lst2.append("~\\")  # 填充特殊字符
        return lst1, lst2, len1
    elif len1 < len2:
        for i in range(len2 - len1):
            lst1.append("~\\")
        return lst1, lst2, len2
    else:
        return lst1, lst2, len1

在最后设置单元格背景时,如果文件打开会产生 PermissionError异常,因此读取数据的时候需要判断文件是否被打开

def check_PermissionError(path):
    """
    通过文件是否可以打开 判断文件是否打开
    :param path: 文件路径
    :return: None
    """
    try:
        f = open(path, "a")
    except OSError as e:
        return False
    else:
        f.close()
        return True

获取单元格范围内的所有单元格坐标

为什么要获取合并单元格范围?在最后获取到 fill_cell中,如果包含合并单元格,在设置背景颜色时不出现效果,
因此需要获取此异同单元格所在的合并单元格范围,然后将整个范围都设置背景颜色
def dudge_merged(cell):
    """
    返回某个合并范围内的所有单元格
    :param cell:合并范围
    :return: cells_list:拆解的合并单元格坐标
    """
	# 百度中提及的判断单元格是否为合并单元格,未实现效果
    # if type(cell).__name__ == 'MergedCell':
    #     print("True")

    cells_list = []  # 存储单元格坐标

    lst = cell.split(':')
    start, end = lst[0], lst[-1]  # 获取头字母,尾字母
    start_letter, start_number = ord(start[0]), int(start[1:])  # 转换为ASCII码
    end_letter, end_number = ord(end[0]), int(end[1:])  # 获取头数字,尾数字
    if start_letter == end_letter:  # 判断是否为横向合并
        for i in range(end_number - start_number + 1):  # 根据头尾数字差 获取列数
            cells_list.append(chr(start_letter) + str(start_number + i))
    else:
        col = column_index_from_string(end[0]) - column_index_from_string(start[0]) + 1  # 计算列数
        row = end_number - start_number + 1  # 计算行数
        col_list = [chr(start_letter + i) for i in range(col)]  # 生成范围内的所有列字母
        row_list = [start_number + i for i in range(row)]  # 生成范围内的所有行号
        for c in col_list:
            for r in row_list:
                cells_list.append(c + str(r))
    return cells_list

def get_merage_list(merage):  # 与dudge_merged()配合使用
    """
    返回所有合并单元格范围内的所有单元格
    :param merage: 合并单元格合集
    :return: 扩充的单元格

    get_merage_list('A1:A3') --> return: [['A1','A2','A3']]
    get_merage_list('A1:B2') --> return: [['A1','A2','B1','B2']]
    """
    merged_cells = []
    if len(merage) > 0:
        for range_cell in merage:
            values = dudge_merged(str(range_cell))
            merged_cells.append(values)
    else:
        values = dudge_merged(str(merage[0]))
        merged_cells.append(values)
    return merged_cells

设置单元格背景颜色

def set_cellcolor(wb, sheet, fill_list, path1, path2, fgColor="FFFF0000"):
    """
    设置一组单元格背景颜色
    :param wb: openpyxl.workbook对象
    :param sheet:Worksheet 对象
    :param fill_list:填充单元格集
    :param path1:主文件路径
    :param path2:副文件路径
    :param fgColor:背景颜色
    :return:None
    """
    fill = PatternFill("solid", fgColor=fgColor)  # 设置样式
    for cell in fill_list:
        sheet[cell].fill = fill  # 遍历集合填充
    if check_PermissionError(path1) and check_PermissionError(path2):  # 判断是否打开
        wb.save(path1)  # 保存文件


def set_acellcolor(wb, sheet, cell, path1, path2, fgColor="FFFF0000"):
    """
    设置单个单元格背景颜色
    :param wb: openpyxl.workbook对象
    :param sheet:Worksheet 对象
    :param cell:准备填充的单个单元格
    :param path1:主文件路径
    :param path2:副文件路径
    :param fgColor:背景颜色
    :return:None
    """
    fill = PatternFill("solid", fgColor=fgColor)  # 设置样式
    sheet[cell].fill = fill
    if check_PermissionError(path1) and check_PermissionError(path2):  # 判断是否打开
        wb.save(path1)  # 保存文件

判断文件内容读取判断

def judege_cols(self, master_cols, other_cols, max_rows, wf1, wf2):
    """
    判断文件内容读取判断
    :param master_cols: 主文件列数
    :param other_cols: 副文件列数
    :param max_rows: 最大列数
    :param wf1: 主文件对象
    :param wf2: 副文件对象
    :return: 异同单元格合集
    """
    fell_list = []  # 异同单元格合集
    for i in range(max_rows):
		
		# 逐行读取数据;如果行数不一会,短的一方会报IndexError
        master_lst, other_lst = [], []
        try: 
            master_lst = list(wf1.values[i])
        except IndexError:
            master_lst = (["~\\" for i in range(other_cols)])
        try:
            other_lst = list(wf2.values[i])
        except IndexError:
            other_lst = (["~\\" for i in range(master_cols)])
        
        # 如果主文件读取的一行与副文件读取的一行,长度不一,则需要补齐,不然会报IndexError
        if len(master_lst) != len(other_lst):
            master_lst, other_lst, lenl = WtoolsFuc.fill_cols(master_lst, other_lst) # 补齐列
            if not eq(master_lst, other_lst): # 具体判断内容
                for n in range(lenl):
                    if not eq(master_lst[n], other_lst[n]):
                        fell_list.append(get_column_letter(n + 1) + str(i + 1)) # 返回坐标
    return fell_list

判断异同单元格是否为合并

"""
判断异同单元格的是否为合并单元格
falsecell_count: 异同单元格数量
merage:全文件中所有单元格合集
merage_count:合并单元格数量
"""
if falsecell_count > 0:
    if len(merage) > 0:
        merged_cells = WtoolsFuc.get_merage_list(merage)  # 返回文件中合并单元格的具体范围
        for merged_cell in merged_cells:
            for fi_cell in fill_cell:
                if fi_cell in merged_cell:
                    merage_count += 1
                    WtoolsFuc.set_cellcolor(master_wb, master_sheet, merged_cell, path1, path2,
                                            fgColor="1874CD") # 异同单元格设置为蓝色
                else:
                    WtoolsFuc.set_acellcolor(master_wb, master_sheet, fi_cell, path1, path2)
    else:
        WtoolsFuc.set_cellcolor(master_wb, master_sheet, fill_cell, path1, path2)

窗体部分

实现button点击打开文件

def open_master_file(self):
    """
    打开主文件
    :return: None
    QFileDialog.getOpenFileName(self, 文件选择框标题, 打开的默认路径, 过滤文件后缀)
    返回:('C:/Users/Meaauf/Desktop/p7.xlsx', 'Excel Files(*.xlsx)')元组
    """
    self.lineEdit.setText(QFileDialog.getOpenFileName(self, "选择主文件", "", "Excel Files(*.xlsx)")[0])

设置下拉列表框

def master_combobox(self):
    """
    设置下拉列表框值
    :return: None
    """
    if self.comboBox.count() > 0:
        self.comboBox.clear()
    wb = openpyxl.load_workbook(self.lineEdit.text(), read_only=True)
    self.comboBox.addItems(wb.sheetnames)
Pyinstaller打包命令

-w:不使用窗口打开,如果不添加-w,会在运行程序时打开控制台,显示print信息
-p:python会指定寻找路径下的第三方库打包
-i:设置exe可执行文件的图标
【在打包后,会在目录下显示一个dist文件夹,将图标文件以及使用的一些图片资源都放在里面】

pyinstaller -w -F -p C:\Users\Meaauf\AppData\Local\Programs\Python\Python38\Lib\site-packages  -i favicon.ico Wtools.py WtoolsFuc.py

Pyinstaller
在这里插入图片描述
在这里插入图片描述

相关源码及程序

GitHub:  https://github.com/Meaauff/Wtools.git
CSDN :  https://download.csdn.net/download/weixin_45564816/77696529
  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
当出现"'vue' 不是内部或外部命令,也不是可运行的程序 或批处理文件"的报错时,通常是因为系统找不到全局安装的Vue CLI命令。以下是解决该问题的步骤: 1. 首先,确保已经全局安装了Vue CLI。可以在终端运行`npm list -g @vue/cli`命令来检查是否已安装Vue CLI。如果没有安装,可以使用以下命令进行全局安装:`npm install -g @vue/cli`。 2. 如果已经安装了Vue CLI,但仍然出现报错,请检查环境变量是否正确设置。在终端运行`vue --version`命令,如果报错提示"'vue' 不是内部或外部命令",则说明环境变量没有正确设置。你可以尝试以下步骤来解决该问题: - 在Windows操作系统,打开系统的环境变量设置(在控制面板搜索“环境变量”),然后将包含Vue CLI安装路径的目录(通常是`C:\Users\你的用户名\AppData\Roaming\npm`)添加到系统的PATH变量。 - 在Mac或Linux操作系统,打开终端并编辑`~/.bashrc`或`~/.bash_profile`文件(根据你的系统设置而定),添加以下内容:`export PATH="$PATH:/usr/local/bin"`,然后保存并关闭文件。重新启动终端或运行`source ~/.bashrc`或`source ~/.bash_profile`命令来更新环境变量。 3. 如果以上步骤都没有解决问题,可以尝试删除项目的`node_modules`文件夹,并重新运行`npm install`命令来重新安装项目的依赖。 通过执行上述步骤,应该能够解决"'vue' 不是内部或外部命令,也不是可运行的程序 或批处理文件"的报错。如果问题仍然存在,请在评论区提供更多细节,以便我们可以进一步帮助你解决问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [“ ‘vue-cli-service‘ 不是内部或外部命令,也不是可运行的程序或批处理文件”的报错解决方案 ”](https://blog.csdn.net/qq_57587705/article/details/124352490)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [VSCode npm : ‘vue-cli-service‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件](https://blog.csdn.net/u010541670/article/details/125640117)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [vue-cli3.0配置及使用注意事项详解](https://download.csdn.net/download/weixin_38732463/14904787)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Meaauf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值