Validating a text entry
Typically, text inputs represent fields that follow certain validation rules, such as having a maximum length or matching a specific format. Some applications allow typing any kind of content into these fields and trigger the validation when the whole form is submitted.
Under some circumstances, we want to prevent users from typing invalid content into a text field. We will take a look at how to implement this behavior using the validation options of the Entry widget.
How to do it...
The following application shows how to validate an entry using regular expressions:
import re import tkinter as tk class App(tk.Tk): def __init__(self): super().__init__() self.pattern = re.compile("^\w{0,10}$") self.label = tk.Label(self, text="Enter your username") vcmd = (self.register(self.validate_username), "%i", "%P") self.entry = tk.Entry(self, validate="key", validatecommand=vcmd, invalidcommand=self.print_error) self.label.pack() self.entry.pack(anchor=tk.W, padx=10, pady=10) def validate_username(self, index, username): print("Modification at index " + index) return self.pattern.match(username) is not None def print_error(self): print("Invalid username character") if __name__ == "__main__": app = App() app.mainloop()
If you run this script and type a non-alphanumeric character in the Entry widget, it will keep the same content and print the error message. This will also happen when you try to type more than 10 valid characters since the regular expression also limits the content's length.
How it works...
With the validate
option set to "key"
, we will activate the entry validation that gets triggered on any content modification. The value is "none"
by default, which means that there is no validation.
Other possible values are "focusin"
and "focusout"
, which validate when the widget gets or loses the focus, respectively, or simply "focus"
to validate in both cases. Alternatively, we can use the "all"
value to validate in all situations.
The validatecommand
function is called each time the validation is triggered, and it should return true
if the new content is valid, and false
otherwise.
Since we need more information to determine whether the content is valid or not, we create a Tcl wrapper around our Python function using theregister
method of theWidget
class. Then, you can add the percent substitution for each parameter that will be passed to the Python function. Finally, we will group these values as a Python tuple. This corresponds to the following line from our example:
vcmd = (self.register(self.validate_username), "%i", "%P")
In general, you can use any of the following substitutions:
%d
: Type of action; 1 for insertion, 0 for deletion, and -1 otherwise%i
: Index of the string being inserted or deleted%P
: Value of the entry if the modification is allowed%s
: Value of the entry before the modification%S
: String content that is being inserted or deleted%v
: The type of validation currently set%V
: Type of validation that triggered the action%W
: The name of the Entry widget
The invalidcommand
option takes a function that is invoked when validatecommand
returns false
. The same percent substitutions can be applied to this option, but in our example, we directly passed the print_error()
method of our class.
There's more...
The Tcl/Tk documentation suggests not mixing the validatecommand
and the textvariable
options since setting an invalid value to the Tk
variable will turn off validation. The same occurs if the validatecommand
function do not return a Boolean value.
In case you are not familiar with the re
module, you can check out the detailed introduction to regular expressions in the official Python documentation at https://docs.python.org/3.6/howto/regex.html.
See also
- The Creating text entries recipe