CustomTkinter实现Excel批量替换时遇RuntimeError: can't re-enter readline错误求助
解决CustomTkinter批量替换Excel内容的RuntimeError问题
问题背景
用CustomTkinter开发简易Excel内容替换工具,Tab1单条文本替换功能正常;Tab2计划实现批量替换,允许用户在文本框输入多行键值对(格式如"AL": "Alabama"),但点击替换按钮时触发RuntimeError: can't re-enter readline错误。
报错原因
GUI程序基于事件循环运行,而input()是命令行交互函数,会尝试启动独立的readline输入循环,与Tkinter的事件循环冲突,导致无法重入readline的错误。此外原代码还存在两个逻辑问题:
- Tab2错误复用了Tab1的路径输入框
self.path,应该用Tab2自己的self.path2 - 将替换字典转成JSON字符串后调用
keys(),JSON字符串没有该方法,无法实现替换匹配
修复方案
- 移除所有
input()调用,直接从CTkTextbox读取用户输入的多行内容 - 解析文本框每行内容,提取键值对(适配
"key": "value"格式) - 使用正确的路径输入框
self.path2 - 直接使用字典作为替换映射,无需转成JSON字符串
修改后的核心代码
修复后的btn2函数
def btn2(): # 从文本框获取所有内容,按行分割 text_content = self.textbox.get('0.0', 'end').strip() lines = text_content.split('\n') pairs = {} for line in lines: line = line.strip() if not line: continue # 跳过空行 try: # 处理 "AL": "Alabama" 格式,去掉引号和冒号分割 key_part, value_part = map(str.strip, line.split(':', 1)) # 去除前后的引号(支持单/双引号) key = key_part.strip('"\'') value = value_part.strip('"\'') pairs[key] = value except ValueError: print(f"无效格式行:{line}") continue if not pairs: print("未解析到有效替换键值对") return BASE_DIR = Path(__file__).parent # 使用Tab2的路径输入框 INPUT_DIR = BASE_DIR / self.path2.get().strip() OUTPUT_DIR = BASE_DIR / self.path2.get().strip() OUTPUT_DIR.mkdir(parents=True, exist_ok=True) replacement_pair = pairs files = list(INPUT_DIR.rglob("*.xls*")) for file in files: wb = openpyxl.load_workbook(file) for ws in wb.worksheets: for row in ws.iter_rows(): for cell in row: if cell.value in replacement_pair: cell.value = replacement_pair[cell.value] wb.save(OUTPUT_DIR / f"{file.stem}.xlsx") print("批量替换完成")
完整修复后的代码
import tkinter import tkinter.messagebox import customtkinter from pathlib import Path import openpyxl customtkinter.set_appearance_mode("System") customtkinter.set_default_color_theme("blue") class App(customtkinter.CTk): def __init__(self): super().__init__() def btn1(): BASE_DIR = Path(__file__).parent INPUT_DIR = BASE_DIR / self.path.get().strip() OUTPUT_DIR = BASE_DIR / self.path.get().strip() OUTPUT_DIR.mkdir(parents=True, exist_ok=True) replacement_pair = { self.find.get().strip(): self.repl.get().strip() } files = list(INPUT_DIR.rglob("*.xls*")) for file in files: wb = openpyxl.load_workbook(file) for ws in wb.worksheets: for row in ws.iter_rows(): for cell in row: if cell.value in replacement_pair: cell.value = replacement_pair[cell.value] wb.save(OUTPUT_DIR / f"{file.stem}.xlsx") print("单条替换完成") def btn2(): text_content = self.textbox.get('0.0', 'end').strip() lines = text_content.split('\n') pairs = {} for line in lines: line = line.strip() if not line: continue try: key_part, value_part = map(str.strip, line.split(':', 1)) key = key_part.strip('"\'') value = value_part.strip('"\'') pairs[key] = value except ValueError: print(f"无效格式行:{line}") continue if not pairs: print("未解析到有效替换键值对") return BASE_DIR = Path(__file__).parent INPUT_DIR = BASE_DIR / self.path2.get().strip() OUTPUT_DIR = BASE_DIR / self.path2.get().strip() OUTPUT_DIR.mkdir(parents=True, exist_ok=True) replacement_pair = pairs files = list(INPUT_DIR.rglob("*.xls*")) for file in files: wb = openpyxl.load_workbook(file) for ws in wb.worksheets: for row in ws.iter_rows(): for cell in row: if cell.value in replacement_pair: cell.value = replacement_pair[cell.value] wb.save(OUTPUT_DIR / f"{file.stem}.xlsx") print("批量替换完成") # 窗口配置 self.title("Find and Replace") self.geometry(f"{500}x{525}") self.grid_columnconfigure(1, weight=1) self.grid_columnconfigure((2, 3), weight=0) self.grid_rowconfigure((0, 1, 2), weight=1) # 标签页设置 self.tabview = customtkinter.CTkTabview(self, width=250) self.tabview.grid(row=0, column=1, padx=(20, 10), pady=(20, 10), sticky="nsew") self.tabview.add("Tab 1") self.tabview.add("Tab 2") self.tabview.add("Tab 3") self.tabview.tab("Tab 1").grid_columnconfigure(0, weight=1) self.tabview.tab("Tab 2").grid_columnconfigure(0, weight=1) self.tabview.tab("Tab 3").grid_columnconfigure(0, weight=1) # Tab1 单条替换控件 self.find = customtkinter.CTkEntry(self.tabview.tab("Tab 1"), placeholder_text="Find") self.find.grid(row=0, column=0, padx=20, pady=(20, 10)) self.repl = customtkinter.CTkEntry(self.tabview.tab("Tab 1"), placeholder_text="Replace") self.repl.grid(row=1, column=0, padx=20, pady=(20, 10)) self.path = customtkinter.CTkEntry(self.tabview.tab("Tab 1"), placeholder_text="Path") self.path.grid(row=2, column=0, padx=20, pady=(20, 10)) self.button = customtkinter.CTkButton(self.tabview.tab("Tab 1"), text="Replace", command=btn1) self.button.grid(row=3, column=0, padx=20, pady=(20, 10)) # Tab2 批量替换控件 self.textbox = customtkinter.CTkTextbox(self.tabview.tab("Tab 2"), width=250) self.textbox.grid(row=0, column=0, padx=(5, 5), pady=(0, 0), sticky="nsew") self.path2 = customtkinter.CTkEntry(self.tabview.tab("Tab 2"), placeholder_text="Path") self.path2.grid(row=2, column=0, padx=20, pady=(20, 10)) self.button2 = customtkinter.CTkButton(self.tabview.tab("Tab 2"), text="Replace", command=btn2) self.button2.grid(row=3, column=0, padx=20, pady=(20, 10)) def open_input_dialog_event(self): dialog = customtkinter.CTkInputDialog(text="Type in a number:", title="CTkInputDialog") print("CTkInputDialog:", dialog.get_input()) def change_appearance_mode_event(self, new_appearance_mode: str): customtkinter.set_appearance_mode(new_appearance_mode) def change_scaling_event(self, new_scaling: str): new_scaling_float = int(new_scaling.replace("%", "")) / 100 customtkinter.set_widget_scaling(new_scaling_float) def sidebar_button_event(self): print("sidebar_button click") if __name__ == "__main__": app = App() app.mainloop()
使用说明
- 在Tab2的文本框中输入多行键值对,每行格式为
"原内容": "替换内容",例如:"AL": "Alabama" "NY": "New York" "CA": "California" - 输入目标文件夹路径
- 点击替换按钮,程序会批量处理文件夹下的所有Excel文件,完成内容替换
内容的提问来源于stack exchange,提问作者Leo Eckert
相关产品推荐
相关产品推荐

