如何实现可复用的Tkinter变量与Pickle文件互转脚本?
问题描述
我想做一个能在所有Python项目里复用的脚本,实现Tkinter变量与Pickle文件之间的读写功能。我可以直接把读写函数移到project.py里,但不想这么做。现在separate.py里的两个函数没法正常工作,因为它们用globals()只能访问自身文件的变量,拿不到project.py里的内容。有没有其他实现方式,还是只能把两个文件合并?
project.py 代码
from os import getcwd from separate import load_tkinter_variables_from_pickle, save_tkinter_variables_to_pickle from tkinter import BooleanVar, Button, Checkbutton, DoubleVar, Entry, IntVar, Label, StringVar, Tk cwd = getcwd() settings_file = cwd + '\\' + 'settings.pkl' main_window = Tk() main_window.title('House Listing') main_window.attributes('-toolwindow', True) city_str = StringVar() Label(master=main_window, text='City').pack() Entry(master=main_window, textvariable=city_str).pack() bedrooms_int = IntVar() Label(master=main_window, text='Bedrooms').pack() Entry(master=main_window, textvariable=bedrooms_int, width=5).pack() furnished_bool = BooleanVar() Label(master=main_window, text='Furnished?').pack() Checkbutton(master=main_window, onvalue=True, offvalue=False, variable=furnished_bool).pack() acres_dbl = DoubleVar() Label(master=main_window, text='Acres of Land').pack() Entry(master=main_window, textvariable=acres_dbl, width=5).pack() Button(master=main_window, text='Load', command=lambda: load_tkinter_variables_from_pickle(pickle_file_path=settings_file)).pack(side='left') Button(master=main_window, text='Save', command=lambda: save_tkinter_variables_to_pickle(pickle_file_path=settings_file)).pack(side='right') main_window.mainloop()
separate.py 代码
from os import path from pickle import dump, load def save_tkinter_variables_to_pickle(pickle_file_path): """ save all existing tkinter variables to a pickle file """ data = {} for i in globals(): if i.endswith('_bool') or i.endswith('_dbl') or i.endswith('_int') or i.endswith('_str'): print(f'{i} \t= \t{globals()[i].get()}') data[i] = globals()[i].get() with open(pickle_file_path, 'wb') as file: dump(data, file) def load_tkinter_variables_from_pickle(pickle_file_path): """ load all variables from a pickle, create the appropriate Tk variables and set their values """ if path.exists(pickle_file_path): with open(pickle_file_path, 'rb') as file: data = load(file) for i in data: if i.endswith('_bool') or i.endswith('_dbl') or i.endswith('_int') or i.endswith('_str'): globals()[i].set(data[i]) print(f'{i} = {data[i]}')
解决方案
不需要合并文件,以下几种方式都能实现复用需求:
方式一:显式传递变量字典(最直观可控)
把需要读写的Tkinter变量打包成字典,传给separate.py的函数,完全不依赖全局命名空间:
修改后的 separate.py
from os import path from pickle import dump, load def save_tkinter_variables_to_pickle(pickle_file_path, variables): """ 保存指定的Tkinter变量到Pickle文件 """ data = {name: var.get() for name, var in variables.items()} with open(pickle_file_path, 'wb') as file: dump(data, file) # 可选:打印保存内容 for name, value in data.items(): print(f'{name} \t= \t{value}') def load_tkinter_variables_from_pickle(pickle_file_path, variables): """ 从Pickle文件加载值到指定的Tkinter变量 """ if path.exists(pickle_file_path): with open(pickle_file_path, 'rb') as file: data = load(file) for name, var in variables.items(): if name in data: var.set(data[name]) print(f'{name} = {data[name]}')
修改 project.py 的按钮命令
在project.py中定义变量字典,传给读写函数:
# 打包需要读写的Tk变量 tk_vars = { 'city_str': city_str, 'bedrooms_int': bedrooms_int, 'furnished_bool': furnished_bool, 'acres_dbl': acres_dbl } # 更新按钮命令 Button(master=main_window, text='Load', command=lambda: load_tkinter_variables_from_pickle(settings_file, tk_vars)).pack(side='left') Button(master=main_window, text='Save', command=lambda: save_tkinter_variables_to_pickle(settings_file, tk_vars)).pack(side='right')
方式二:传递全局命名空间(保留原命名规则)
如果不想手动维护变量字典,可以让project.py把自身的全局命名空间传给separate.py的函数:
修改后的 separate.py
from os import path from pickle import dump, load def save_tkinter_variables_to_pickle(pickle_file_path, namespace): """ 从指定命名空间中读取符合规则的Tk变量并保存 """ data = {} for name, var in namespace.items(): # 过滤变量名后缀,同时验证是否是Tk变量(有get()方法) if name.endswith(('_bool', '_dbl', '_int', '_str')): try: value = var.get() data[name] = value print(f'{name} \t= \t{value}') except AttributeError: continue with open(pickle_file_path, 'wb') as file: dump(data, file) def load_tkinter_variables_from_pickle(pickle_file_path, namespace): """ 加载Pickle值到指定命名空间的Tk变量中 """ if path.exists(pickle_file_path): with open(pickle_file_path, 'rb') as file: data = load(file) for name, value in data.items(): if name in namespace and name.endswith(('_bool', '_dbl', '_int', '_str')): try: namespace[name].set(value) print(f'{name} = {value}') except AttributeError: continue
修改 project.py 的按钮命令
直接把globals()作为命名空间传入:
Button(master=main_window, text='Load', command=lambda: load_tkinter_variables_from_pickle(settings_file, globals())).pack(side='left') Button(master=main_window, text='Save', command=lambda: save_tkinter_variables_to_pickle(settings_file, globals())).pack(side='right')
方式三:封装为类(适合长期复用扩展)
如果要做一个高复用性的模块,把功能封装成类会更优雅,扩展性更强:
修改后的 separate.py
from os import path from pickle import dump, load from tkinter import BooleanVar, DoubleVar, IntVar, StringVar class TkSettingsHandler: def __init__(self, pickle_path): self.pickle_path = pickle_path # 映射变量后缀到对应的Tk变量类型 self.var_type_map = { '_bool': BooleanVar, '_dbl': DoubleVar, '_int': IntVar, '_str': StringVar } def save(self, variables): """ 保存变量字典(name: TkVar)到Pickle文件 """ data = {name: var.get() for name, var in variables.items()} with open(self.pickle_path, 'wb') as f: dump(data, f) for name, val in data.items(): print(f'已保存: {name} = {val}') def load(self, variables=None): """ 加载值到已有变量,或创建新的Tk变量并返回 """ if not path.exists(self.pickle_path): return {} with open(self.pickle_path, 'rb') as f: data = load(f) if variables: # 给已有变量赋值 for name, var in variables.items(): if name in data: var.set(data[name]) print(f'已加载: {name} = {data[name]}') return else: # 创建新的Tk变量并赋值 new_vars = {} for name, val in data.items(): for suffix, var_cls in self.var_type_map.items(): if name.endswith(suffix): new_vars[name] = var_cls(value=val) print(f'已创建变量: {name} = {val}') break return new_vars
在 project.py 中使用
# 初始化配置处理器 settings_handler = TkSettingsHandler(settings_file) # 打包需要读写的Tk变量 tk_vars = { 'city_str': city_str, 'bedrooms_int': bedrooms_int, 'furnished_bool': furnished_bool, 'acres_dbl': acres_dbl } # 更新按钮命令 Button(master=main_window, text='Load', command=lambda: settings_handler.load(tk_vars)).pack(side='left') Button(master=main_window, text='Save', command=lambda: settings_handler.save(tk_vars)).pack(side='right')
内容的提问来源于stack exchange,提问作者David Saylor
相关产品推荐
相关产品推荐

