If you do not need any of the special arguments, you don't need to call register
. For example, the following code will correctly call do_validation
without any arguments:
import tkinter as tk
def do_validation():
value = entry.get()
if value == "" or value.isnumeric():
return True
return False
root = tk.Tk()
entry = tk.Entry(root, validate='key', validatecommand=do_validation)
entry.pack(fill="x", padx=20, pady=20)
root.mainloop()
However, in the above case the validation will always be one character behind. This is because validation happens before the character is inserted into the entry widget (ie: the first time it is called, entry.get()
will return an empty string). The whole point of validation is to prevent illegal characters, so it makes sense to call the command before the character is inserted rather than after.
This is why the special arguments are useful -- they provide sufficient context in order to decide whether the character or characters are legal before they are inserted into the widget.
How register
is useful
Tkinter is a wrapper around a Tcl interpreter, because tk (the core technology behind tkinter) is implemented in Tcl. Tcl is a programming language just like python.
In a Tcl program, you would use the validation features like this:
proc do_validation(new_value) {
return $new_value == "" || [string is integer $new_value]
}
entry .entry -validate key --validatecommand [list do_validation %P]
When Tcl detects a change, it performs substitution on the arguments, and then calls the defined procedure with the substituted arguments. For example, if you type "A", %P
is converted to "A"
and the function is called with "A"
as the only argument.
In the case of Tkinter, there is no direct corollary for defining the function with arguments to be substituted at runtime.
Unfortunately, the validation feature wasn't implemented very well in the tkinter wrapper, so to properly use the validation we have to use a small workaround in order to get these special arguments passed to our function.
What register
does is to create a new Tcl command that calls your python command. It also creates a new python command that is a reference to this new Tcl command. You can use this reference exactly like any other python command.
A simple example
In the simplest case, all you need is what the string would look like if the edit was to be allowed. You can then decide whether the edit is something that will result in valid input, or if it will result in invalid input. If the edit won't result in valid input, you can reject the edit before it actually happens.
The special argument that represents the value if the edit is allowed is %P
1. We can modify the function we are registering to accept this argument, and we can add this argument when we do the registering:
def do_validation(new_value):
return new_value == "" or new_value.isnumeric()
...
vcmd = (root.register(do_validation), '%P')
entry = tk.Entry(..., validatecommand=vcmd)
With that, when the underlying Tcl code detects a change it will call the new Tcl command which was created, passing in one argument corresponding to the special %P
substitution.
1All of the mechanics of the validation are described thoroughly in the tcl documentation here: http://tcl.tk/man/tcl8.5/TkCmd/entry.htm#M7