Python tkinter多窗口的交互问题:子窗口无法获取数据
tkinter第二个、第三个子窗口数据不能交互响应的解决方法
先说结论:第二个、第三个子窗口生成时,不能用tk.Tk(),而应改为tk.Toplevel(),问题解决。
问题描述
最近在用tk、ttk开发一个仿真软件时,当在主窗口(tk.Tk()生成)点击相关设置按键时,调用另一模块中的函数(通过import导入),函数执行过程中弹出第二个或更多的子窗口,在子窗口输入数据或点击相关按键时,发现子窗口的交互数据无法获得!而同样的代码段如果放入主窗口则没有问题!
下图为问题出现的场景
点击person行的参数设置,选择自定义模式时,调用另一模块中的函数,弹出新的窗口。
主脚本代码为:
person_par_set = ttk.Combobox(type_table, width=10) # 初始化
person_par_set["values"] = ("Default", "User-defined")
person_par_set.current(0) # 选择第一个
def go_person_par_set(*args): # 事件
var_number = eval(number_set.get()) # 将对应的字符型数值转换为数字
var_person_prop = eval(person_prop.get())
var_person_par = person_par_set.get()
var_person_label = person_label.cget("text")
var_person = {'label': var_person_label, 'prop': var_person_prop, 'par': var_person_par}
person_row.set(var_person) # 将字典型以字符串形式赋值给控件型变量
set_person(var_number, var_person) # 调用另一模块中的函数,实现person的设置
person_par_set.bind("<<ComboboxSelected>>", go_person_par_set) # 绑定事件,(下拉列表框被选中时,绑定go()函数)
调用的函数代码为:
def set_person(var_number, var_person): # 进行person型成员的设置
person_number = round(var_number * var_person['prop'] * 0.01, 0) # 四舍五入取0位小数
p = int(person_number)
if var_person['par'] == "Default":
tf_data = np.random.uniform(0, 1, p) # 生成0~1之间,p个均匀分布的随机数
# 为将list中每个数字保留2位,将list转为numpy数组,然后对numpy进行操作,最后将numpy再转为数组。
tf_data = np.array(tf_data)
tf_data = np.round(tf_data, 2) # round不能用于list,可用于array
tf_data = list(tf_data)
np.save('person_par.npy', tf_data)
elif var_person['par'] == "User-defined":
win_person = tk.Tk() # 待详细设置person属性的子窗口
win_person.title('Person Properties Settings')
wid = sub_window(win_person) # 调用sub_window函数,设置窗口,并返回窗口的宽度值
tf_table = ttk.LabelFrame(win_person, text='information set', labelanchor='n')
tf_table.grid(column=0, row=3, padx=8, pady=15) # row,column指示相对行号和列号,非绝对位置;padx,y为挂靠坐标位置
tf_radva = tk.IntVar()
tf_radva.set(99)
def radCall(*args):
print(tf_radva.get())
curRad1 = ttk.Radiobutton(tf_table, text="平均分布", value=1, variable=tf_radva, command=radCall)
curRad1.grid(column=0, row=0, sticky=tk.W)
curRad2 = ttk.Radiobutton(tf_table, text="正态分布", value=2, variable=tf_radva, command=radCall)
curRad2.grid(column=0, row=1, sticky=tk.W)
tf_data = np.random.normal(0.5, 0.1, p) # 生成0~1之间,p个均匀分布的随机数
# 为将list中每个数字保留2位,将list转为numpy数组,然后对numpy进行操作,最后将numpy再转为数组。
tf_data = np.array(tf_data)
tf_data = np.round(tf_data, 2) # round不能用于list,可用于array
tf_data = list(tf_data)
np.save('person_par.npy', tf_data)
弹出设置窗口
上述代码设计为:tf_radva初值设置为99;当radio按钮被选中时,其值(分别为1,2)会赋给tf_radva,通过tf_radva这个IntVar型控件变量指向radio的variable的方式。拟实现的效果为,当选中<平均分布radio>时,打印1;选中<正态分布>时,打印2。而真实情况是,无论选中哪个radio,都打印99,功能不能实现。而将此段代码放于主窗口的某个位置而不弹出新窗口的话,则功能能够实现。
问题分析
- 经反复测试,代码功能没有问题。我一度认为是,调用后台函数时,后台函数使用变量的生存周期问题,“在次脚本中tf_radva不能指向对应radio的variable(或者说,这种指向是临时性的,因为次脚本的函数生存周期是临时性,临时性的内存分配,在窗口响应时,该临时内存存储地址未持续到响应事件进行时却已结束)”,——如果说是变量生存周期的问题,那么每次点击radio时,却有事件的响应,每次都打印99,而不是不打印。即事件有响应,那么在响应期,变量是生存的——不是该原因。既然不是变量的生存周期问题,而确实tf_radva未指向对应radio的variable,即程序在执行variable=tf_radva时,未能将tf_radva指向当前窗口radio的变量,而此问题出现在两个窗口共存的情况下。或者可以这样认为,系统此时在确认哪一个是“活跃窗口”发生了问题,需要确定哪一个窗口是active的。
- 耗费了一上午的时间,经反复查阅与搜索论坛,发现问题所在。将子窗口生成时代码改为tk.Toplevel()即可
win_person = tk.Toplevel() # 待详细设置person属性的子窗口
——Toplevel(顶级窗口)组件是一个独立的顶级窗口,这种窗口通常拥有标题栏、边框等部件。
——何时使用 Toplevel 组件?
——Toplevel 组件通常用在显示额外的窗口、对话框和其他弹出窗口上。
——Tkinter Toplevel:顶层部件的工作,直接由窗口管理器管理的窗口。他们不必在它们上面的父widget
参考博文
参考博文
https://blog.youkuaiyun.com/liyuan3970/article/details/82874653
Toplevel(顶层)
https://blog.youkuaiyun.com/m0_37264397/article/details/79101630