Registering a validation callback in tkinter

Hello all,

I’m learning tkinter which has been… interesting. I’m going off of the TkDocs tutorial because the tkinter documentation is intentionally sparse. There is one thing that I can’t seem to find a satisfying answer to in either though.

ttk.Entry widgets support the validatecommand widget option (a keyword argument to the ttk.Entry class constructor), who’s value should be a callable to be invoked in the event that the content of the widget is changed by the user, etc.

When working in TCL itself, and within such a callback, percent substitutions may be used which will be expanded to various values, such as the new value that the entry widget will contain in the event that the validation is successful, a string indicating the condition that triggered the validation, etc.

for reasons I don’t fully understand, in order to use said percent substitutions in the python validation callback, the callback must be ‘registered’ and the specific percent substitutions you wish to be passed to it as arguments named explicitly. This is done with the .register() method of a widget object like so…

def inputValidation(a, b)
    # some validation code

input_validation_wrapper = (root.register(inputValidation), '%P', '%V')
input_widget = ttk.Entry(root, validate='all', validatecommand=input_validation_wrapper)

I don’t understand the syntax of the third line, it appears to me that the right side of the assignment operator is just a triple tuple, which will be stored in the variable. However all the material I’ve seen says that the validatecommand option to the ttk.Entry widget only accepts a reference to a callable, which in the above example is not the case.

I also can’t work out which widget object the .register() method should be called on, in the example I’ve seen it’s just called on the object that symbolizes the root window but TkDocs also says it can be called on other widgets, without elaborating.

relevant section of the TkDocs tutorial can be found here, scroll down a little to the section headed “validation”

Any help would be much appreciated! I find it strange how the tutorial just glosses over this pretty arcane aspect of tkinter.

The validatecommand parameter is a tuple that specifies the callback function and what arguments should be passed to that callback. The root.register(...) part is needed to wrap the Python callback so that it be called by the underlying Tcl code of the Tcl/Tk library.

Some of the percent values are listed towards the bottom of the “Validation” section of TkDocs Tutorial - Basic Widgets.

Here’s an example from there:

import tkinter as tk
import tkinter.ttk as ttk
import re

def check_num(newval):
    print(f'check_num({newval=!a})')
    return re.match('^[0-9]*$', newval) is not None and len(newval) <= 5

root = tk.Tk()
check_num_wrapper = (root.register(check_num), '%P')

num = tk.StringVar()
e = ttk.Entry(root, textvariable=num, validate='key', validatecommand=check_num_wrapper)
e.grid(column=0, row=0, sticky='we')
1 Like

The reason this is so weird is that it exposes the underlying details behind how Tkinter interfaces with Tk. Tk is not actually a Python library, it is for another language Tcl. Tcl’s code is written as a space-separated list of parameters, like ttk:Label nameofmaster -text "blah" -width 8. All the methods in Tkinter convert everything to a nested list/tuple of strings, then pass them over to the Tcl interpreter to execute.

On the Tcl side, the way commands work is that they’d be set to a string of Tcl code. The widget would substitute in the % values to wherever, then run that command. If you pass a Python callable to methods, Tkinter handles this by automatically calls register() on them. That generates a unique name for that callable, then registers that name on the Tcl side as a new command that will execute the callable. register() returns that name, so you can just pass that result anywhere a callable is expected. The place you call register() does matter, when that widget is destroyed it’ll unregister and delete the command, so it makes sense to call it on a relevant widget. If you’re never planning on deleting it of course it doesn’t really matter where it’s called from.

In regards to docs, you should actually look at the Tcl documentation, since that’s the underlying thing you’re interacting with. Here’s the validatecommand docs for example.

1 Like

Thanks, and sorry for the late reply!

So if I am understanding you correctly, tkinter.Widget.register() returns a string and the name of a Tcl routine to which the Python function passed as it’s first agrument is bound, and when that name is given as a callable on the Python side, it’s passed to the Tcl interpreter which then executes it’s associated Python function? (I’m a bit shaky on that very last bit and it’s more something I’ve got in my head from somewhere else than anything I’ve inferred from your answer).

Also, you mention the widget substituting in the % substitutions and then running the command;

The widget would substitute in the % values to wherever, then run that command

I’ve seen this style of language in some of the docs but I still can’t wrap my head around it, e.g in the TkDocs tutorial section on event loops, it is often mentioned that a widget will “redraw itself”. It almost seems to imply widgets are handed control of the programs execution at some point , which I suppose they are when they are initialised, or when one of their methods is called, but it’s a little confusing to me.

And thanks, I’ve been using the Tcl documentation and it’s been invaluable, but I’d say that neither the TkDocs tutorial, the tkinter documentation or the Tk reference manual would suffice on their own (at least for me), I’ve had to flit from one to the other on occasion to seek clarification on certain things.

That’s all roughly right yeah. The command parameter or whatever is really string-typed, but when you pass a callable Tkinter implicitly calls register() for you. The widgets are indeed doing execution. Most of your program’s execution is spent inside mainloop(), which is effectively a while True loop that’s continually checking for events from the operating system (mouse, keyboard, window changes, etc), then calling methods to update the right thing, or invoking your callbacks. Any time you modify a widget, it’s marked as “dirty”, and then later on the main loop calls update() to adjust its dimensions, redraw it, etc.

Perhaps that’s the bit you’re not aware of?