Custom Tkinter切换Frame后输入框内容丢失问题求助
问题:Custom Tkinter页面切换后输入框内容丢失
切换Basic/Icon页面后,之前选择的文件路径会清空,返回原页面时输入框为空。
问题原因
- 每次切换重建页面:
switch函数会销毁main_frame内所有子组件,再重新调用basic_page()/icon_page(),每次调用都会创建新的StringVar和输入控件,旧控件被销毁时输入数据也随之丢失。 - 全局变量覆盖:
input_file_entry作为全局变量,切换到Icon页面时会被重新赋值为Icon页面的输入框,覆盖Basic页面的输入框引用,导致无法访问之前的控件数据。
解决方案
核心思路
- 提前定义全局状态变量,持久保存各页面的输入数据
- 页面仅初始化一次,切换时隐藏/显示对应页面,而非销毁重建
- 移除易引发冲突的全局控件引用,改用绑定
StringVar的方式管理输入
修改后的代码实现
1. 全局变量定义
在所有函数前定义全局的状态变量和页面实例:
from customtkinter import * from tkinter import filedialog from PIL import Image, ImageTk # 全局状态变量:保存各页面输入数据 basic_input_var = StringVar() basic_output_var = StringVar() icon_file_var = StringVar() # 全局页面实例:保存已创建的页面控件 basic_frame = None icon_frame = None
2. 页面创建函数修改
页面仅初始化一次,后续切换直接显示已创建的页面:
def basic_page(): global basic_frame # 页面已创建则直接显示 if basic_frame is not None: basic_frame.pack(fill=BOTH, expand=True) return def choose_input_file(): input_file = filedialog.askopenfilename(title="Select Input File") basic_input_var.set(input_file) def choose_output_file(): output_file = filedialog.asksaveasfilename(title="Name anything...") basic_output_var.set(output_file) basic_frame = CTkFrame(main_frame) input_file_entry = CTkEntry(basic_frame, textvariable=basic_input_var, placeholder_text="手动输入要加密的文件路径", width=450) input_file_entry.pack(pady=50) input_file_button = CTkButton(basic_frame, text="选择输入文件", command=choose_input_file, width=150, corner_radius=40) input_file_button.pack() output_file_entry = CTkEntry(basic_frame, textvariable=basic_output_var, placeholder_text="手动输入输出文件路径(不要加.exe)", width=450) output_file_entry.pack(pady=50) output_file_button = CTkButton(basic_frame, text="另存为", command=choose_output_file, width=150, corner_radius=40) output_file_button.pack() basic_frame.pack(fill=BOTH, expand=True) def icon_page(): global icon_frame # 页面已创建则直接显示 if icon_frame is not None: icon_frame.pack(fill=BOTH, expand=True) return def choose_icon_file(): icon_path = filedialog.askopenfilename(title="选择图标", filetypes=[("图标文件", "*.ico")]) if icon_path: icon_file_var.set(icon_path) display_icon(icon_path) def display_icon(icon_path): icon_image = Image.open(icon_path) icon_image = ImageTk.PhotoImage(icon_image) display_label.configure(image=icon_image) display_label.image = icon_image icon_frame = CTkFrame(main_frame) input_file_entry = CTkEntry(icon_frame, textvariable=icon_file_var, placeholder_text="图标文件路径", width=450) input_file_entry.pack(pady=50) input_file_button = CTkButton(icon_frame, text="选择.ico图标", command=choose_icon_file, width=150, corner_radius=40) input_file_button.pack() display_label = CTkLabel(icon_frame, text='') display_label.pack(pady=50) icon_frame.pack(fill=BOTH, expand=True)
3. 页面切换逻辑修改
切换时隐藏所有页面,再显示目标页面,不再销毁组件:
def hide_bg(): basic_indicate.configure(bg_color='transparent') assembly_indicate.configure(bg_color='transparent') icon_indicate.configure(bg_color='transparent') build_indicate.configure(bg_color='transparent') advanced_indicate.configure(bg_color='transparent') def hide_all_pages(): # 隐藏所有已创建的页面 if basic_frame is not None: basic_frame.pack_forget() if icon_frame is not None: icon_frame.pack_forget() # 其他页面可在此处添加 def switch(widget, page): hide_bg() widget.configure(bg_color='blue') hide_all_pages() page()
4. 主应用代码调整
主应用部分基本不变,注意补充其他页面的函数绑定:
set_appearance_mode("System") # 模式:system(默认)、light、dark app = CTk() app.geometry("625x625") app.iconbitmap('icon.ico') app.title("FG_Crypter") options_frame = CTkFrame(app) basic_btn = CTkButton(options_frame, text="Basic", font=("Arial",13), width=125, command=lambda: switch(basic_indicate, basic_page)) basic_btn.place(x=0,y=0) basic_indicate = CTkLabel(options_frame, text="", bg_color='blue', width=100, height=2) basic_indicate.place(x=12,y=30) icon_btn = CTkButton(options_frame, text="Icon", font=("Arial",13), width=125, command=lambda: switch(icon_indicate, icon_page)) icon_btn.place(x=125,y=0) icon_indicate = CTkLabel(options_frame, text="", width=100, height=2) icon_indicate.place(x=137,y=30) # 以下为其他按钮,需替换为对应页面函数 Assembly_btn = CTkButton(options_frame, text="Assembly", font=("Arial",13), width=125, command=lambda: switch(assembly_indicate, lambda: None)) Assembly_btn.place(x=250,y=0) assembly_indicate = CTkLabel(options_frame, text="", width=100, height=2) assembly_indicate.place(x=262,y=30) Advanced_btn = CTkButton(options_frame, text="Advanced", font=("Arial",13), width=125, command=lambda: switch(advanced_indicate, lambda: None)) Advanced_btn.place(x=375,y=0) advanced_indicate = CTkLabel(options_frame, text="", width=100, height=2) advanced_indicate.place(x=387,y=30) Build_btn = CTkButton(options_frame, text="Build", font=("Arial",13), width=125, command=lambda: switch(build_indicate, lambda: None)) Build_btn.place(x=500,y=0) build_indicate = CTkLabel(options_frame, text="", width=100, height=2) build_indicate.place(x=512,y=30) options_frame.pack(pady=5) options_frame.propagate(False) options_frame.configure(width=650, height=35) main_frame = CTkFrame(app) main_frame.pack(fill=BOTH, expand=True) basic_page() # 初始加载Basic页面 app.mainloop()
说明
- 全局
StringVar会持续保存用户输入,页面隐藏时数据不会丢失 - 页面仅创建一次,切换时用
pack_forget()隐藏、pack()显示,避免重复创建控件 - 移除了易引发冲突的全局控件引用,通过
StringVar直接获取输入值
内容的提问来源于stack exchange,提问作者Furjack gaming
相关产品推荐
相关产品推荐

