Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
874 views
in Technique[技术] by (71.8m points)

python 3.x - Dont understand Tkinter entry box validatiom

I have a tkinter GUI with an entry box I want to allow only numbers. Can someone explain to me what each command / line of code in validation does. I don't understand the vcmd variable and all the '%i' '%s' stuff. Thanks :)

UPDATE: I have a different application to use this vcmd command with and dont understand it entirely. Here is my validation code:

def validate(self, action, index, value_if_allowed, prior_value, text, validation_type, trigger_type, widget_name):
    if not int(action):
        return True
    elif text in '0123456789.-+':
        try:
            float(value_if_allowed)
            return True
        except ValueError:
            return False
    else:
        return False

I dont get why in this code i need all of these:

action, index, value_if_allowed, prior_value, text, validation_type, trigger_type, widget_name

Why do i need all of these specific to my validation code for it to function correctly and what use are they? The documentation you provided made sense but some of those '%s', '%i' stuff seemed unnecessary for my specific code yet it only works with all of them included like so:

vcmd = (self.master.register(self.validate), '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')

I also want to know what self.master.register does please, i still cant figure that one out.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...