from tkinter import *
import pandas as pd
df = pd.DataFrame({'item': list('abcde'), 'default_vals': [2,6,4,5,1]})
def input_data(df):
box = Tk()
height = str(int(25*(df.shape[0]+2)))
box.geometry("320x" + height)
box.title("my box")
#initialise
params, checkButtons, intVars = [], [], []
default_vals = list(df.default_vals)
itemList = list(df.item)
for i,label in enumerate(itemList):
Label(box, text = label).grid(row = i, sticky = W)
params.append(Entry(box))
params[-1].grid(row = i, column = 1)
params[-1].insert(i, default_vals[i])
intVars.append(IntVar())
checkButtons.append(Checkbutton(variable = intVars[-1]))
checkButtons[-1].grid(row = i, column = 3)
def sumbit(event=None):
global fields, checked
fields = [params[i].get() for i in range(len(params))]
checked = [intVars[i].get() for i in range(len(intVars))]
box.destroy()
#add submit button
box.bind('<Return>', sumbit)
Button(box, text = "submit",
command = sumbit).grid(row = df.shape[0]+3, sticky = W)
box.focus_force()
mainloop()
return fields, checked
我是tkinter的新手,不确定我想做什么。
目前,我的脚本(这里简化为函数而不是类)构建了一个框,其中在字段中输入了所有默认值:
相反,我想从空字段开始,一旦单击相应的checkButton,它将获得默认值(现在仍然应该能够通过该字段手动更改它),并且一旦输入任何值在给定的字段中,选择了相应的checkButton。
有可能吗?
答案 0 :(得分:1)
有可能,但是让我在当前代码的开头加上一些注意事项:
建议不要进行星号导入(from tkinter import *
),因为您无法控制导入到名称空间中的内容。最好显式导入您需要的内容作为参考:
import tkinter as tk
tk.Label() # same as if you wrote Label()
tk.IntVar() # same as if you called IntVar()
您想要的行为虽然可能,但不一定是用户友好的。当用户已经输入了某些内容并取消选中该复选框时,会发生什么?或者,如果选中此复选框,然后用户删除了信息,会发生什么情况?这些可能是您想考虑的事情。
话虽如此,解决方案是在变量上使用trace
回调函数。您还需要为StringVar()
框添加一个Entry
,因为您需要双向连接:
# add strVars as a list of StringVar() for your Entry box
params, checkButtons, intVars, strVars = [], [], [], []
在enumerate(itemList)
的迭代过程中,添加以下内容:
# Create new StringVar()
strVars.append(StringVar())
# add a trace callback for tracking changes over the StringVar()
strVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_strVar(idx))
# update your Entry to set textvariable to the new strVar
params.append(Entry(box, textvariable=strVars[-1]))
# similarly, add a trace for your IntVar
intVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_intVar(idx))
在迭代小部件创建之前,您需要定义两个trace
回调函数 :
def trace_intVar(idx):
# if Checkbox is checked and Entry is empty...
if intVars[idx].get() and not params[idx].get():
# prefill Entry with default value
params[idx].insert(0, df.default_vals[idx])
def trace_strVar(idx):
# if Entry has something...
if strVars[idx].get():
# and Checkbox is not checked...
if not intVars[idx].get():
# Set the checkbox to checked.
intVars[idx].set(True)
# but if Entry is empty...
else:
# Set the Checkbox to uncheck.
intVars[idx].set(False)
请记住我提到的行为-如果Checkbox
为空,我有点自由地清除了Entry
。但是,如果您不希望这样做,则需要对处理进行一些修改。
注意trace_add
的写入方式。始终使用三个默认参数传递回调函数,即变量名称,变量索引(如果有)和操作(see this great answer from Bryan Oakley)。由于在这种情况下我们不需要任何内容(我们无法将变量名反向引用到变量lists
之间的链接索引中),所以我们必须手动将回调与另一个lambda
包装起来并忽略三个参数:
lambda var, # reserve first pos for variable name
var_idx, # reserve second pos for variable index
oper, # reserve third pos for operation
idx=i: # pass in i by reference for indexing point
trace_intVar(idx) # only pass in the idx
您不能只传递lambda...: trace_intVar(i)
,因为在这种情况下,i
将通过值而不是引用来传递。相信我,我之前犯过这个错误。因此,我们传递了另一个参数idx
,其默认设置为i
,现在将通过引用传递该参数。
如果trace_add
不起作用,请改用trace('w', ...)
。
为了繁荣,这是您问题的完整实施方案:
from tkinter import *
import pandas as pd
df = pd.DataFrame({'item': list('abcde'), 'default_vals': [2,6,4,5,1]})
def input_data(df):
box = Tk()
height = str(int(25*(df.shape[0]+2)))
box.geometry("320x" + height)
box.title("my box")
#initialise
params, checkButtons, intVars, strVars = [], [], [], []
default_vals = list(df.default_vals)
itemList = list(df.item)
def trace_intVar(idx):
if intVars[idx].get() and not params[idx].get():
params[idx].insert(0, df.default_vals[idx])
def trace_strVar(idx):
if strVars[idx].get():
if not intVars[idx].get():
intVars[idx].set(True)
else:
intVars[idx].set(False)
for i,label in enumerate(itemList):
Label(box, text = label).grid(row = i, sticky = W)
strVars.append(StringVar())
strVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_strVar(idx))
params.append(Entry(box, textvariable=strVars[-1]))
params[-1].grid(row = i, column = 1)
#params[-1].insert(i, default_vals[i]) # <-- You don't need this any more
intVars.append(IntVar())
intVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_intVar(idx))
checkButtons.append(Checkbutton(variable = intVars[-1]))
checkButtons[-1].grid(row = i, column = 3)
def sumbit(event=None):
global fields, checked
fields = [params[i].get() for i in range(len(params))]
checked = [intVars[i].get() for i in range(len(intVars))]
box.destroy()
#add submit button
box.bind('<Return>', sumbit)
Button(box, text = "submit",
command = sumbit).grid(row = df.shape[0]+3, sticky = W)
box.focus_force()
mainloop()
return fields, checked