Возможно, но позвольте мне предисловие к моему решению с несколькими предостережениями относительно вашего текущего кода:
Редко рекомендуется делать импорт звездочек (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
.Функция обратного вызова всегда передается с тремя аргументами по умолчанию, а именно: Имя переменной, Индекс переменной (если есть) и Операция (см. Этот великолепный ответ от Брайана Оукли) .Так как в этом случае они нам не нужны (мы не можем обратить ссылку на имя переменной на связанный индекс между переменной 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