【问题标题】:How do I get an event callback when a Tkinter Entry widget is modified?修改 Tkinter 条目小部件时如何获取事件回调?
【发布时间】:2011-07-01 13:49:24
【问题描述】:

正如问题所说。 Text 小部件具有 <<Modified>> 事件,但 Entry 小部件似乎没有。

【问题讨论】:

    标签: python tkinter


    【解决方案1】:

    将 Tkinter StringVar 添加到您的 Entry 小部件。使用 trace 方法将您的回调绑定到 StringVar。

    from Tkinter import *
    
    def callback(sv):
        print sv.get()
    
    root = Tk()
    sv = StringVar()
    sv.trace("w", lambda name, index, mode, sv=sv: callback(sv))
    e = Entry(root, textvariable=sv)
    e.pack()
    root.mainloop()  
    

    【讨论】:

    • @Nicholas:代码示例按原样工作。您发现缺少什么?
    • 我在具有自我结构的代码中尝试了您的解决方案,因为我需要更改某些小部件的属性,但我无法将自我结构传递给跟踪。因此我需要使用 'bind' 方法和 来调用具有 'self' 结构的方法
    • @ArtOfWarfare:你错了。将代码剪切并粘贴到 Python 2.7 解释器中(printTkinter 需要为 Python 3 更改导入;lambda 将保持不变)。您将看到代码按预期运行。 lambda 表达式类似于def anonymous(name, index, mode, sv=sv):return callback(sv),只是它是一个表达式,而不是一个语句。 StringVar.trace 使用我们忽略的不需要的参数(名称、索引、模式)调用它的回调,而是对 StringVar 本身(sv)进行闭包,然后我们在回调中使用它。
    • 是的,我的评论是错误的。我认为让我失望的是您为 lambda 的参数分配了默认值。直到今天早些时候,我还没有意识到 lambda 语法允许您分配默认值。因此,我没有将= 视为将参数名称与默认值分开,而是将其视为参数名称和传递给该参数的值。
    【解决方案2】:

    在撰写本文时(2017,Python 3.6,tkinter 版本 8.6.6),文档建议 .trace 已弃用。现在建议的形式似乎是:

    sv.trace_add("write", callback)
    

    如果您希望在变量更改时得到通知,这非常有效。但是,我的应用程序只希望在用户完成文本编辑时得到通知。我发现捎带“验证”机制在这里可以很好地工作:

    from tkinter import *
    
    root = Tk()
    sv = StringVar()
    
    def callback():
        print(sv.get())
        return True
    
    e = Entry(root, textvariable=sv, validate="focusout", validatecommand=callback)
    e.grid()
    e = Entry(root)
    e.grid()
    root.mainloop()
    

    每当入口小部件失去焦点时,这将调用callback(我添加了第二个入口小部件,因此第一个实际上可以失去焦点!)

    【讨论】:

      【解决方案3】:

      谢谢史蒂文! Russell Owen 的 Tkinter Folklore 解释了如何使用 globalgetvar() 直接从名称参数 (PY_VAR#) 中获取 StringVar 值,而不是如何将名称映射到小部件。您更改回调参数的 lambda 方法就像魔术一样(至少对我们 Python 新手来说)。

      当有多个条目时,通常不仅需要知道值,还需要知道更改了哪个条目。稍微扩展一下 Steven 的示例,以下 (Python3) 传递了一个索引,该索引可用于跟踪多个条目。

      from tkinter import Tk, Frame, Label, Entry, StringVar
      
      class Fruitlist:
          def entryupdate(self, sv, i):
              print(sv, i, self.fruit[i], sv.get())
      
          def __init__(self, root):
              cf = Frame(root)
              cf.pack()
              self.string_vars = []
              self.fruit = ("Apple", "Banana", "Cherry", "Date")
              for f in self.fruit:
                  i = len(self.string_vars)
                  self.string_vars.append(StringVar())
                  self.string_vars[i].trace("w", lambda name, index, mode, var=self.string_vars[i], i=i:
                                    self.entryupdate(var, i))
                  Label(cf, text=f).grid(column=2, row=i)
                  Entry(cf, width=6, textvariable=self.string_vars[i]).grid(column=4, row=i)
      
      root = Tk()
      root.title("EntryUpdate")
      app = Fruitlist(root)
      root.mainloop() 
      

      【讨论】:

        【解决方案4】:

        您还可以使用每次用户单击小部件时都会触发的 KeyRelease 事件。
        您可以过滤更改。

        from tkinter import * 
        from tkinter import ttk
        
        class GUI():                              
            def __init__(self):  
                self.root = Tk()
                self.sv = StringVar() 
                self.prevlaue=''
                #entry
                self.entry = ttk.Entry(self.root, width=30, textvariable =self.sv)
                self.entry.grid(pady=20,padx=20) 
                self.entry.bind("<KeyRelease>", self.OnEntryClick) #keyup                  
                self.root.mainloop()       
        
            def OnEntryClick(self, event):
                value=self.sv.get().strip()
                changed = True if self.prevlaue != value else False
                print(value, 'Text has changed ? {}'.format(changed))
                self.prevlaue = value
        
        #create the gui
        GUI()
        

        希望对你有帮助。

        【讨论】:

          【解决方案5】:

          我使用 Python 3.6,但无法让 .trace 工作。以下代码允许接受或编辑 StringVar 的默认值。 on_changed 在按下返回键时被调用。

          from tkinter import Tk, LEFT, BOTH, StringVar
          from tkinter.ttk import Entry, Frame
          
          
          class Example(Frame):
              def __init__(self, parent):
                  Frame.__init__(self, parent)
                  self.parent = parent
                  self.initUI()
          
              def initUI(self):
                  self.parent.title("Entry")
                  self.pack(fill=BOTH, expand=1)
                  self.contents = StringVar()
                  # give the StringVar a default value
                  self.contents.set('test')
                  self.entry = Entry(self)
                  self.entry.pack(side=LEFT, padx=15)
                  self.entry["textvariable"] = self.contents
                  self.entry.bind('<Key-Return>', self.on_changed)
          
              def on_changed(self, event):
                  print('contents: {}'.format(self.contents.get()))
                  return True
          
          
          def main():
              root = Tk()
              ex = Example(root)
              root.geometry("250x100+300+300")
              root.mainloop()
          
          
          if __name__ == '__main__':
              main()
          

          【讨论】:

            【解决方案6】:

            我知道其他变种。

            在我输入代码之前,它可以更好地解释编码路径:阅读here

            还有我的代码:

            from Tkinter import *
            
            class ttt:
               def __init__(self):
                  self.str1 = StringVar()
                  self.e1 = Entry(root, textvariable=self.str1)
                  self.str1.trace('w', self.callback_1)
                  self.e1.pack()
            
                  self.str2 = StringVar()
                  self.e2 = Entry(root, textvariable=self.str2, state='readonly')
                  self.e2.pack()
            
                  self.str3 = StringVar()
                  self.e3 = Entry(root, textvariable=self.str3, state='readonly')
                  self.e3.pack()
            
                  bt = Button(root, text = 'ещё', command = self.callback_2)
                  bt.pack()
            
               def callback_1(self, name='', index='', mode=''):
                  tmp = self.str1.get()
                  if tmp:
                     self.str2.set(int(tmp) * 6)
                     print self.str2.get()
            
               def callback_2(self, name='', index='', mode=''):
                  tmp = self.str1.get()
                  if tmp:
                     self.str3.set(int(tmp) * 6)
                     print self.str3.get()   
            
            root = Tk()
            t = ttt()
            root.mainloop()
            

            有 2 种变体:按下按钮并进入。 现在您可以选择任何变体

            【讨论】:

              【解决方案7】:

              我已经能够使用&lt;FocusOut&gt; 事件来做我需要的事情。这是一个示例:

              import tkinter
              from tkinter import ttk
              
              def doSomething(event):
                  text = event.widget.get()
                  print(text)
              
              window = tkinter.Tk()
              testEntry = ttk.Entry(window)
              testEntry.bind('<FocusOut>', doSomething)
              testEntry.grid(column=0,row=0)
              thingEntry = ttk.Entry(window)
              thingEntry.bind('<FocusOut>', doSomething)
              thingEntry.grid(column=0,row=1)
              startButton = ttk.Button(window,text='OK',command=lambda: print('click'))
              startButton.grid(column=0,row=2, sticky='W')
              
              window.mainloop()
              print('Bye Bye')
              

              重要的部分是:

              def doSomething(event):
                  text = event.widget.get()
                  print(text)
              
              testEntry.bind('<FocusOut>', doSomething)
              

              使用Entry.bind(event,function),您可以附加一个事件侦听器,在本例中为doSomething 函数。该函数获取一个参数,一个Event 对象。反过来,您可以使用 event.widget.get() 阅读 Entry 小部件中的内容。

              每次您留下Entry,它的值都会被打印出来。这包括单击按钮时。从测试中可以看到,按钮的事件是在Entry的事件之后触发的,

              【讨论】:

                【解决方案8】:

                我发现使用内置的 TK 验证更适合我的 Python 技能初学者水平。 tkinter 本身没有很好地记录 Entry 元素的验证,您必须使用 register() 方法来设置回调,并且通过 always 从注册的验证回调中返回 True,您可以获得通知。

                def filter_callback(self,new_value):
                    print(new_value)
                    # must return true since we want the validation events to keep coming
                    return(True)
                
                def __init__(self,root)
                    self.edit_filter = ttk.Entry(root)
                    # %d = Type of action (1=insert, 0=delete, -1 for others)
                    # %i = index of char string to be inserted/deleted, or -1
                    # %P = value of the entry if the edit is allowed
                    # %s = value of entry prior to editing
                    # %S = the text string being inserted or deleted, if any
                    # %v = the type of validation that is currently set
                    # %V = the type of validation that triggered the callback
                    #      (key, focusin, focusout, forced)
                    # %W = the tk name of the widget
                    vcmd = (self.edit_filter.register(self.filter_callback), "%P")
                    # notify key presses only
                    self.edit_filter.config(validate = "key", validatecommand = vcmd) 
                

                (仅说明性代码)

                Interactively validating Entry widget content in tkinter 此处为示例答案,https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/entry-validation.html 此处为事件的一些文档。它的代码并不比使用 trace 少,但对于一些初学者来说可能更容易遵循和构建。

                【讨论】:

                  猜你喜欢
                  • 2023-03-22
                  • 2022-12-08
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-03-05
                  • 2016-12-27
                  • 2019-09-27
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多