Search code examples
pythonpython-3.xtkintercallbacktkinter-entry

Calling tk.StringVar.set() on a tk.Entry textvariable causes validate="focusout" to stop getting called


The question's in the title, essentially: how do I get the validatecommand callback to continue being called after setting the Entry's textvariable? Here's the Minimum Working Example (MWE):

import tkinter as tk

root = tk.Tk()
sv = tk.StringVar()


def callback():
    print(sv.get())
    sv.set('Set Text.')
    return True


e = tk.Entry(root, textvariable=sv, validate="focusout",                 
             validatecommand=callback)
e.grid()
e = tk.Entry(root)
e.grid()
root.mainloop()

Note that the second tk.Entry widget is there to allow the first one to lose focus, which is the event we're trying to capture.

As the code is now, when you run it, you can change the top Entry widget's text once. It'll correctly get set to Set Text. Then, if you try to change the Entry's text again, the new text will be in the widget, but the callback doesn't happen.

On the other hand, if you comment out the sv.set('Set Text.') code, this behavior completely disappears, and the callback gets called as many times as you wish.

How can I have the sv.set() functionality, while still maintaining the callback getting called every time the Entry widget loses focus?


Solution

  • This is discussed in the Tk manual page for entry:

    The validate option will also set itself to none when you edit the entry widget from within either the validateCommand or the invalidCommand. Such editions will override the one that was being validated.

    Presumably, this is done to avoid infinite recursion.

    You can run this (instead of the given Tcl code, after idle {%W config -validate %v})

    root.after_idle(lambda: e.config(validate="focusout"))
    

    from the callback to schedule a reconfiguration of the widget to enable validation again (after changing your sources so that e is the right Entry widget, i.e. not the second one).