Python / Tkinter:有没有办法不断“扫描”输入?

问题描述 投票:0回答:3

我是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()

我想在用户更改其中一个输入后立即更改输出。

python tkinter
3个回答
3
投票

我会利用trace_addtkinter variable classes函数。这确保了每次更改Entry的内容时都会调用该函数。这样做的缺点是您需要为每个条目创建一个StringVar对象,但是您确定捕获所有更改,不需要时不调用该函数且没有延迟。

接下来,您每次调用calc时都会创建一个新的Label小部件。您应该制作一个Label并在calc中更新其文本,而不是每次都创建一个新的Label。

将这些变化放在一起:

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
投票

是的,有可能这样做。

这是我的方式

您可以使用window.after(ms, func=None, args)to继续在后台运行,这将更新用户输入而无需按下按钮。

更新的代码

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()

1
投票

Trace是正确的方法,tkinter负责这些变化。我会将它与数据结构一起使用,然后立即创建小部件,然后立即跟踪,因为它的代码要少得多。以下是来自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创建参数的对话框。它不完整,但如果您不需要自定义模板,它就可以工作。添加包含SPInputFrame类的InputPaneUpdate方法参数的打印命令,您将通过监视控制台输出快速了解跟踪...

# 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使示例工作。

© www.soinside.com 2019 - 2024. All rights reserved.