Python Pandas+PySimpleGUI文件转换工具故障求助
调试指导与代码修复
针对你开发的文件格式转换工具,以下是代码中存在的核心问题及修复方案:
核心问题梳理
- 文件验证逻辑缺陷
- 未处理用户未选择文件时的空值情况,调用
pd.read_excel会直接报错 - 特价列表的日期格式验证完全缺失,不符合需求
- 使用函数属性
validation.df存储DataFrame,易引发全局变量冲突
- 未处理用户未选择文件时的空值情况,调用
- 事件处理逻辑混乱
- 提交按钮的条件判断逻辑与需求不符,且重复触发不必要的文件验证
- 替换按钮采用toggle切换状态,用户体验模糊,不符合"点击替换按钮替换表头"的需求
- 状态管理缺失
- 未保存已验证的文件DataFrame和状态,每次事件触发都重复读取文件
修复后的完整代码
import pandas as pd import PySimpleGUI as sg from datetime import datetime def main(): sg.theme('SystemDefaultForReal') layout = [ [sg.FileBrowse('CUSID', size=(15,2), file_types=(('Excel File', '*.xlsx'),), key='cusid', enable_events=True), sg.FileBrowse('SPECIAL PRICE LIST', size=(18,2), file_types=(('Excel File', '*.xlsx'),), key='SPlist', enable_events=True)], [sg.Button('Convert', key='submit'), sg.Button("Cancel", key='cancel')], [sg.StatusBar('', size=(50,1), key='status', justification='center'), sg.Button('Replace Header', key='-replace-', visible=False), sg.Button('Replace Headers', key='-replace2-', visible=False)] ] window = sg.Window("Special Price Converter", layout, element_justification='center', finalize=True) # 存储文件状态:(是否已验证, DataFrame, 是否需要替换表头) cusid_status = (False, None, False) splist_status = (False, None, False) cusidHeader = ['CUS_ID'] SPlistHeader = ['PROD_CD','UT_PRICE','EXP_DATE'] def validate_cusid(file_path): """验证客户ID文件并返回(验证结果, DataFrame, 需要替换表头)""" try: df = pd.read_excel(file_path, header=None) if len(df.columns) != 1: return (False, None, False), "错误:所选文件必须仅含1列!" first_row = df.iloc[0,0] if first_row == cusidHeader[0]: return (True, df, False), "CUS_ID表头已存在" else: return (True, df, True), f"当前表头:{first_row}" except Exception as e: return (False, None, False), f"文件读取错误:{str(e)}" def validate_splist(file_path): """验证特价列表文件并返回(验证结果, DataFrame, 需要替换表头)""" try: df = pd.read_excel(file_path, header=None) if len(df.columns) != 3: return (False, None, False), "错误:所选文件必须含3列!" first_row = df.iloc[0].tolist() # 验证表头是否匹配 header_match = first_row == SPlistHeader # 验证日期格式(仅当表头匹配或需要替换表头时验证数据行) if not header_match: # 假设第一行是无效表头,数据从第二行开始 date_col = df.iloc[1:, 2] else: date_col = df.iloc[1:, 2] valid_dates = True for date_str in date_col: try: datetime.strptime(str(date_str), "%m/%d/%Y") except ValueError: valid_dates = False break if not valid_dates: return (False, None, False), "错误:最后一列必须是mm/dd/yyyy格式的日期!" if header_match: return (True, df, False), "特价列表表头已存在" else: return (True, df, True), f"当前表头:{str(first_row)}" except Exception as e: return (False, None, False), f"文件读取错误:{str(e)}" def process_file(df, need_replace, target_header): """处理文件:替换表头或自动生成表头""" if need_replace: # 替换第一行为目标表头 df.iloc[0] = target_header new_df = df else: # 自动生成表头,第一行作为数据 new_df = df.rename(columns=df.iloc[0]).drop(df.index[0]) new_df.columns = target_header return new_df while True: event, values = window.read() if event in (sg.WIN_CLOSED, 'cancel'): break elif event == 'cusid': file_path = values['cusid'] if not file_path: window['status'].update("请选择有效文件") window['-replace-'].update(visible=False) cusid_status = (False, None, False) continue (valid, df, need_replace), msg = validate_cusid(file_path) window['status'].update(msg) window['-replace-'].update(visible=valid and need_replace) window['-replace2-'].update(visible=False) cusid_status = (valid, df, need_replace) elif event == 'SPlist': file_path = values['SPlist'] if not file_path: window['status'].update("请选择有效文件") window['-replace2-'].update(visible=False) splist_status = (False, None, False) continue (valid, df, need_replace), msg = validate_splist(file_path) window['status'].update(msg) window['-replace2-'].update(visible=valid and need_replace) window['-replace-'].update(visible=False) splist_status = (valid, df, need_replace) elif event == '-replace-': # 点击替换按钮,标记需要替换表头 valid, df, _ = cusid_status if valid: cusid_status = (valid, df, True) window['status'].update("将替换客户ID文件表头为CUS_ID") elif event == '-replace2-': # 点击替换按钮,标记需要替换表头 valid, df, _ = splist_status if valid: splist_status = (valid, df, True) window['status'].update("将替换特价列表文件表头为指定格式") elif event == 'submit': # 处理客户ID文件 cusid_valid, cusid_df, cusid_replace = cusid_status # 处理特价列表文件 splist_valid, splist_df, splist_replace = splist_status processed_files = [] # 处理客户ID文件(如果已选择且验证通过) if cusid_valid and cusid_df is not None: processed_cusid = process_file(cusid_df, cusid_replace, cusidHeader) processed_files.append(("客户ID列表", processed_cusid)) # 这里可以添加保存逻辑,比如:processed_cusid.to_excel("processed_cusid.xlsx", index=False) # 处理特价列表文件(如果已选择且验证通过) if splist_valid and splist_df is not None: processed_splist = process_file(splist_df, splist_replace, SPlistHeader) processed_files.append(("特价列表", processed_splist)) # 这里可以添加保存逻辑,比如:processed_splist.to_excel("processed_splist.xlsx", index=False) if processed_files: window['status'].update(f"已处理{len(processed_files)}个文件,请添加保存逻辑") else: window['status'].update("没有可处理的有效文件") window.close() if __name__ == '__main__': main()
关键修复说明
- 拆分验证函数:将客户ID和特价列表的验证逻辑拆分为独立函数,返回验证结果、DataFrame和状态,避免全局变量冲突
- 增加空值处理:用户未选择文件时给出提示,避免读取空路径报错
- 完善日期验证:添加特价列表最后一列的日期格式校验,确保符合mm/dd/yyyy要求
- 优化替换按钮逻辑:点击替换按钮直接标记需要替换表头,而非toggle切换,逻辑更清晰
- 状态集中管理:用元组存储每个文件的验证状态、DataFrame和替换需求,避免重复读取文件
- 添加文件处理函数:封装表头替换/生成逻辑,后续只需添加保存代码即可完成格式转换
内容的提问来源于stack exchange,提问作者David L
相关产品推荐
相关产品推荐

