对于python和tkinter来说,我还很陌生,并编写了一个简单的程序,用户可以用输入填充4个变量,然后单击一次即可将其打印出来。现在我很纳闷:是否有一种方法可以使程序不断扫描用户输入的窗口并在用户更改输入后立即刷新输出?
这是我的程序:
from tkinter import *
def calc():
val1 = e1.get()
val2 = e2.get()
val3 = e3.get()
val4 = e4.get()
res = val1 + " " + val2 + " " + val3 + " " + val4
label2 = Label(master)
label2["text"] = res
label2.grid(row=4, column = 1)
master = Tk()
Label(master, text="Main Value").grid(row=0, sticky = E)
Label(master, text="Second Value").grid(row=1, sticky = E)
Label(master, text="Third Value").grid(row=2, sticky = E)
Label(master, text="Fourth Value").grid(row=3, sticky = E)
e1 = Entry(master)
e2 = Entry(master)
e3 = Entry(master)
e4 = Entry(master)
e1.grid(row=0, column=1)
e2.grid(row=1, column=1)
e3.grid(row=2, column=1)
e4.grid(row=3, column=1)
button1 = Button(master, text="Calculate", command=calc)
button1.grid(row=4, column=0, sticky=W, pady=4)
master.mainloop()
我希望用户一旦更改输入之一就更改输出。
答案 0 :(得分:3)
我将使用tkinter variable classes的trace_add
函数。这样可以确保每次更改条目的内容时都会调用该函数。这样做的缺点是,您需要为每个Entry创建一个StringVar
对象,但是您可以确保捕获所有更改,不需要时也不需要调用该函数,并且不需要延迟。
此外,每次您现在调用calc
时,都将创建一个新的Label小部件。而不是每次都创建一个新标签,您应该制作一个标签并在calc
中更新其文本。
将这些更改放在一起:
from tkinter import *
# Let calc accept the arguments passes by trace_add
def calc(*args):
# Get the values from the StringVar objects
val1 = v1.get()
val2 = v2.get()
val3 = v3.get()
val4 = v4.get()
res = val1 + " " + val2 + " " + val3 + " " + val4
# Only change the text of the existing Label
label2["text"] = res
master = Tk()
# make this Label once
label2 = Label(master)
label2.grid(row=4, column=1)
Label(master, text="Main Value").grid(row=0, sticky=E)
Label(master, text="Second Value").grid(row=1, sticky=E)
Label(master, text="Third Value").grid(row=2, sticky=E)
Label(master, text="Fourth Value").grid(row=3, sticky=E)
# Create StringVars
v1 = StringVar()
v2 = StringVar()
v3 = StringVar()
v4 = StringVar()
e1 = Entry(master, textvariable=v1)
e2 = Entry(master, textvariable=v2)
e3 = Entry(master, textvariable=v3)
e4 = Entry(master, textvariable=v4)
# Trace when the StringVars are written
v1.trace_add("write", calc)
v2.trace_add("write", calc)
v3.trace_add("write", calc)
v4.trace_add("write", calc)
e1.grid(row=0, column=1)
e2.grid(row=1, column=1)
e3.grid(row=2, column=1)
e4.grid(row=3, column=1)
master.mainloop()
答案 1 :(得分:1)
是的,有可能这样做。
这是我的方式
您可以使用window.after(ms, func=None, args)
保持后台运行,这将更新用户输入,而无需按下按钮。
更新代码
from tkinter import *
def calc():
val1 = e1.get()
val2 = e2.get()
val3 = e3.get()
val4 = e4.get()
res = val1 + " " + val2 + " " + val3 + " " + val4
label2["text"] = res
# This will run the function in every 100ms (0.1 secs).
master.after(100, calc)
master = Tk()
Label(master, text="Main Value").grid(row=0, sticky = E)
Label(master, text="Second Value").grid(row=1, sticky = E)
Label(master, text="Third Value").grid(row=2, sticky = E)
Label(master, text="Fourth Value").grid(row=3, sticky = E)
e1 = Entry(master)
e2 = Entry(master)
e3 = Entry(master)
e4 = Entry(master)
e1.grid(row=0, column=1)
e2.grid(row=1, column=1)
e3.grid(row=2, column=1)
e4.grid(row=3, column=1)
button1 = Button(master, text="Calculate", command=calc)
button1.grid(row=4, column=0, sticky=W, pady=4)
label2 = Label(master)
label2.grid(row=4, column = 1)
# Run the function and it will keep running in the background.
calc()
master.mainloop()
答案 2 :(得分:1)
跟踪是正确的方法,tkinter负责更改。我将它与数据结构一起使用,然后立即使用trace在循环中创建小部件,因为它的代码少得多。以下是GUI的完整示例,该GUI使用了许多实时监视的输入,并在创建时添加了跟踪。跟踪条目中的每个更改或单击“检查”按钮上的每个更改,并触发对处理该更改的方法的调用。
这是一个全Python> = 3 tkinter跟踪学习示例:
# inputpanel.py derived and improved from my Coolprop GUI on github
#
from tkinter import *
import tkinter.ttk as ttk
class InputFrame(LabelFrame):
#
# This input frame creates Entries and selects for Variables
# contained in a Dictionary structure. It traces the inputs
# and keeps the values updated according to the type of the value.
#
# datadict needs at least the three dicts and the list below
# for one key must be an entry in every dict
# the list order is used for processing
# You can pass a list order with only one field e.g. to init
# and only this field will be processed
#
# datadict={
# 'verbose_names':{},
# 'values':{},
# 'callback_vars':{},
# 'order':[],
# }
#
# if a dict units is added to the datadict, the units will be displayed behind the entry widgets
#
def __init__(self, parent,cnf={}, title=None,datadict=None,order=None,frameborder=5, InputWidth=60,**kwargs):
#
LabelFrame.__init__(self, parent)
#
self.InputWidth=InputWidth
if datadict :
self.datadict=datadict
else:
self.datadict={
'verbose_names':{},
'values':{},
'callback_vars':{},
'order':[],
}
#
if order :
self.order=order
else:
self.order=self.datadict['order']
#
if title :
self.IFrame = LabelFrame(parent, relief=GROOVE, text=title,bd=frameborder,font=("Arial", 10, "bold"))
else:
self.IFrame = LabelFrame(parent, relief=GROOVE,bd=frameborder,font=("Arial", 10, "bold"))
#
self.IFrame.grid(row=1,column=1,padx=8,pady=5,sticky=W)
#
self.InputPanel(self.IFrame)
def InputPanel(self, PanelFrame, font=("Arial", 10, "bold")):
'''
'''
#
order_number=1
for Dkey in self.order :
if self.datadict['verbose_names'][Dkey] :
#
self.datadict['callback_vars'][Dkey].trace("w", lambda name, index, mode,
var=self.datadict['callback_vars'][Dkey],
value=self.datadict['values'][Dkey],
key=Dkey: self.InputPanelUpdate(var, key, value)
)
Label(PanelFrame, text=self.datadict['verbose_names'][Dkey], font=font).grid(column=1, row=order_number, padx=8, pady=5, sticky=W)
if type(self.datadict['values'][Dkey])==type(True):
Checkbutton(PanelFrame, width=self.InputWidth, variable=self.datadict['callback_vars'][Dkey], font=font).grid(column=2, row=order_number, padx=8, pady=5, sticky=W)
else:
Entry(PanelFrame, width=self.InputWidth, textvariable=self.datadict['callback_vars'][Dkey], font=font).grid(column=2, row=order_number, padx=8, pady=5, sticky=W)
try:
Label(PanelFrame, text=self.datadict['units'][Dkey],font=font).grid(column=3, row=order_number,padx=8,pady=5,sticky=W)
except KeyError :
Label(PanelFrame, text=' ',font=font).grid(column=3, row=order_number,padx=8,pady=5,sticky=W)
else :
Label(PanelFrame, text=' ', font=font).grid(column=1, row=order_number, padx=8, pady=5, sticky=W)
#
order_number+=1
def InputPanelUpdate(self, tkVar, key, value):
#
# Called on ever button press in an entry or click in a Checkbutton
#
if type(self.datadict['values'][key])==type(True):
# For booleans we misuse a string because it is so easy
self.datadict['values'][key] = True if tkVar.get()=='1' else False
elif type(self.datadict['values'][key])==type(1):
# int
self.datadict['values'][key] = int(tkVar.getint())
elif type(self.datadict['values'][key])==type(1.1):
# float
self.datadict['values'][key] = float(tkVar.getdouble())
else:
# all the rest
self.datadict['values'][key] = tkVar.get()
这是一个为sphinx-quickstart创建参数的对话框。 它不完整,但是如果不需要自定义模板,则可以使用。 添加一个包含InputPaneUpdate方法的参数的打印命令 SPInputFrame类,您将快速了解跟踪 监视控制台输出...
# sphinx_quickstartpanel.py
#
from tkinter import filedialog
from tkinter import messagebox
from tkinter import *
import tkinter.ttk as ttk
from tkinter.simpledialog import Dialog
from .inputpanel import InputFrame
import os
#
import subprocess
#
from django.template.defaultfilters import slugify
class SpInputFrame(InputFrame):
#
# Add local functions to InputPanelUpdate
#
def InputPanelUpdate(self, tkVar, key, value):
#
# overwrite InputPanelUpdate
#
if type(self.datadict['values'][key])==type(True):
self.datadict['values'][key] = True if tkVar.get()=='1' else False
else:
self.datadict['values'][key] = tkVar.get()
if key=='project':
#
# On project update, update slugged name too
#
self.datadict['values']['project_fn']=slugify(self.datadict['values'][key])
self.datadict['callback_vars']['project_fn'].set(self.datadict['values']['project_fn'])
class sphinx_startpanel(Dialog):
#
# use gui to run sphinx-quickstart
#
def __init__(self, parent, title=None, data=None):
#
# Constructor
#
self.parent=parent
self.data=data
#
self.Row1Frame = LabelFrame(parent, relief=GROOVE, text=' 1.) Enter project name',bd=5,font=("Arial", 10, "bold"))
self.Row1Frame.grid(row=1,column=1,padx=8,pady=5,sticky=W+E, columnspan=3)
#
self.Row2Frame = LabelFrame(parent, relief=GROOVE, text=' 2.) Choose base directory' ,bd=5,font=("Arial", 10, "bold"))
self.Row2Frame.grid(row=2,column=1,padx=8,pady=5,sticky=W+E, columnspan=3 )
#
self.Row3Frame = LabelFrame(parent, relief=GROOVE, text=' 3.) Enter main parameters',bd=5,font=("Arial", 10, "bold"))
self.Row3Frame.grid(row=3,column=1,padx=8,pady=5,sticky=W)
#
self.Row4Frame = LabelFrame(parent, relief=GROOVE, text=' 4.) Run quickstart',bd=5,font=("Arial", 10, "bold"))
self.Row4Frame.grid(row=4,column=1,padx=8,pady=5,sticky=W)
#
self.Row1IFrame=SpInputFrame(self.Row1Frame, title='Project Name',datadict=self.data,order=['project'])
#
self.b2=Button(self.Row2Frame,text="Choose parent directory of your new project")
self.b2.grid(row=1,column=1,padx=8,pady=5,stick=W+E, columnspan=3)
self.b2.bind("<ButtonRelease-1>", self.Button_2_Click)
#
self.Row3IFrame=SpInputFrame(self.Row3Frame, title='Main configuration',datadict=self.data)
#
self.b4=Button(self.Row4Frame,text="Run this configuration and build the empty project")
self.b4.grid(row=1,column=1,padx=8,pady=5,stick=W+E, columnspan=3)
self.b4.bind("<ButtonRelease-1>", self.runQuickstart)
#
def Button_2_Click(self,event):
#
START_DIR = os.path.dirname(os.path.abspath(__file__) )
#
BASE_DIR = filedialog.askdirectory(parent=self.parent, initialdir=START_DIR ,title="Basisverzeichnis auswählen")
self.data['values']['BASE_DIR']=BASE_DIR
self.data['callback_vars']['BASE_DIR'].set(self.data['values']['BASE_DIR'])
#
self.data['values']['path']=os.path.join(BASE_DIR,self.data['values']['project_fn'])
self.data['callback_vars']['path'].set(self.data['values']['path'])
#
print(self.data['values'])
def getCommandline(self):
'''
creates the command for subprocess.Popen
'''
print('Running getCommandline ')
#
cmdline=['sphinx-quickstart']
cmdline.append(self.data['values']['path'])
cmdline.append('-q')
#
print('getCommandline cmdline :',str(cmdline))
#
for key in self.data['argument_keys']:
#
if key in ['path','project_fn' ,'BASE_DIR'] :
pass
else:
if self.data['values'][key] not in ['',False,' ']:
cmdline.append(self.data['argument_keys'][key])
if type(self.data['values'][key])==type(True):
pass
else :
cmdline.append(self.data['values'][key])
#
print(cmdline)
return cmdline
def runQuickstart(self,event):
'''
run sphinx quickstart -q with gathered information
'''
cmd=self.getCommandline()
#
retval = subprocess.call(["/bin/mkdir", "-p",self.data['values']['path']])
#
fproc=subprocess.Popen(cmd, stdout=subprocess.PIPE)
#
formbuffer,errortext=fproc.communicate()
#
print(errortext)
class Sphinxdialog:
def __init__(self, master):
dummyvar = sphinx_startpanel(master,data=self.getData())
def getData(self):
#
# Define, create and deliver the initial data structure
#
# datadict needs at least the three dicts and the list below
#
# datadict={
# 'verbose_names':{},
# 'values':{},
# 'callback_vars':{},
# 'order':[],
# }
#
# for each key must be an entry in every dict
# the list order is used for processing
# You can pass a list order with only one field e.g. to init
# and only this field will be processed
#
# see self.Row1IFrame above, passig the full dict but order contains only ['project']
#
# if a dict units is added to the datadict, the units will be displayed behind the entry widgets
# the units dict can be incomplete
#
# the argument_keys dict was added to call quickstart by commandline
#
datadict = {
'verbose_names':{
'path' : 'The directory name for the new project',
'sep' : 'if True, separate source and build dirs',
'dot' : 'replacement for dot in _templates etc.',
'project' : 'project name',
'project_fn' : 'Slugged project name for filenames',
'author' : 'author names',
'version' : 'version of project',
'release' : 'release of project',
'language' : 'document language',
'suffix' : 'source file suffix',
'master' : 'master document name',
'epub' : 'use epub',
'autodoc' : 'enable autodoc extension',
'doctest' : 'enable doctest extension',
'intersphinx' : 'enable intersphinx extension',
'todo' : 'enable todo extension',
'coverage' : 'enable coverage extension',
'imgmath' : 'enable imgmath for pdf, disable mathjax)',
'mathjax' : 'enable mathjax extension',
'ifconfig' : 'enable ifconfig extension',
'viewcode' : 'enable viewcode extension',
'githubpages' : 'enable githubpages extension',
'BASE_DIR' : 'Directory to create your project folder',
'makefile' : 'Create Makefile',
'batchfile' : 'Create batch command file',
'TEMPLATE_DIR' : 'Where to find the script templates (OPTIONAL)',
},
'values':{
'path' : '.',
'sep' : True,
'dot' : '_',
'project' : 'project name',
'project_fn' : 'Slugged project name for filenames',
'author' : 'author names',
'version' : 'version of project',
'release' : 'release of project',
'language' : 'de',
'suffix' : '.rst',
'master' : 'index',
'epub' : False,
'autodoc' : True,
'doctest' : False,
'intersphinx' : True,
'todo' : False,
'coverage' : False,
'imgmath' : False,
'mathjax' : True,
'ifconfig' : True,
'viewcode' : False,
'githubpages' : False,
'BASE_DIR' : '.',
'makefile' : True,
'batchfile' : False,
'TEMPLATE_DIR' : '',
},
'argument_keys':{
'path' : ' ',
'sep' : '--sep',
'dot' : '--dot',
'project' : '--project',
'project_fn' : None,
'author' : '--author',
'version' : '-v',
'release' : '--release',
'language' : '--language',
'suffix' : '--suffix',
'master' : '--master',
'epub' : '--epub',
'autodoc' : '--ext-autodoc',
'doctest' : '--ext-doctest',
'intersphinx' : '--ext-intersphinx',
'todo' : '--ext-todo',
'coverage' : '--ext-coverage',
'imgmath' : '--ext-imgmath',
'mathjax' : '--ext-mathjax',
'ifconfig' : '--ext-ifconfig',
'viewcode' : '--ext-viewcode',
'githubpages' : '--ext-githubpages',
'BASE_DIR' : None,
'makefile' : '--makefile',
'batchfile' : '--batchfile',
'TEMPLATE_DIR' : '',
},
'order':[],
'callback_vars':{},
}
#
for key in datadict['verbose_names'] :
datadict['callback_vars'][key]=StringVar()
datadict['order'].append(key)
datadict['callback_vars'][key].set(datadict['values'][key])
return datadict
def main():
root = Tk()
app = Sphinxdialog(root)
root.mainloop()
if __name__ == '__main__':
main()
您需要安装django,因为我使用slugify才能使示例正常工作。