How do I get an event callback when a Tkinter Entry widget is modified?

Add a Tkinter StringVar to your Entry widget. Bind your callback to the StringVar using the trace method.

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

At the time of writing (2017, Python 3.6, tkinter version 8.6.6) the docs suggest that .trace is deprecated. The suggested form now seems to be:

sv.trace_add("write", callback)

This works very well if you want notification whenever the variable is changed. However, my application just wants notification when he user finishes editing the text. I found piggy-backing on the "validation" mechanism to work well here:

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

This will invoke callback whenever the entry widget loses focus (I added a 2nd entry widget so the 1st one can actually lose focus!)


Thanks Steven! Russell Owen's Tkinter Folklore explains how to get the StringVar value directly from the name argument (PY_VAR#) using globalgetvar(), but not how to map the name to a widget. Your lambda method of changing the callback args is like magic (to us Python newbies, at least).

When there is more than one Entry, it is often necessary to know not just the value, but which Entry was changed. Expanding on Steven's example slightly, the following (Python3) passes an index which can be used to keep track of multiple Entries.

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

Tags:

Python

Tkinter