Interactively Validating Entry Widget Content in Tkinter

Interactively validating Entry widget content in tkinter

The correct answer is, use the validatecommand attribute of the widget. Unfortunately this feature is severely under-documented in the Tkinter world, though it is quite sufficiently documented in the Tk world. Even though it's not documented well, it has everything you need to do validation without resorting to bindings or tracing variables, or modifying the widget from within the validation procedure.

The trick is to know that you can have Tkinter pass in special values to your validate command. These values give you all the information you need to know to decide on whether the data is valid or not: the value prior to the edit, the value after the edit if the edit is valid, and several other bits of information. To use these, though, you need to do a little voodoo to get this information passed to your validate command.

Note: it's important that the validation command returns either True or False. Anything else will cause the validation to be turned off for the widget.

Here's an example that only allows lowercase. It also prints the values of all of the special values for illustrative purposes. They aren't all necessary; you rarely need more than one or two.

import tkinter as tk  # python 3.x
# import Tkinter as tk # python 2.x

class Example(tk.Frame):

def __init__(self, parent):
tk.Frame.__init__(self, parent)

# valid percent substitutions (from the Tk entry man page)
# note: you only have to register the ones you need; this
# example registers them all for illustrative purposes
#
# %d = Type of action (1=insert, 0=delete, -1 for others)
# %i = index of char string to be inserted/deleted, or -1
# %P = value of the entry if the edit is allowed
# %s = value of entry prior to editing
# %S = the text string being inserted or deleted, if any
# %v = the type of validation that is currently set
# %V = the type of validation that triggered the callback
# (key, focusin, focusout, forced)
# %W = the tk name of the widget

vcmd = (self.register(self.onValidate),
'%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
self.entry = tk.Entry(self, validate="key", validatecommand=vcmd)
self.text = tk.Text(self, height=10, width=40)
self.entry.pack(side="top", fill="x")
self.text.pack(side="bottom", fill="both", expand=True)

def onValidate(self, d, i, P, s, S, v, V, W):
self.text.delete("1.0", "end")
self.text.insert("end","OnValidate:\n")
self.text.insert("end","d='%s'\n" % d)
self.text.insert("end","i='%s'\n" % i)
self.text.insert("end","P='%s'\n" % P)
self.text.insert("end","s='%s'\n" % s)
self.text.insert("end","S='%s'\n" % S)
self.text.insert("end","v='%s'\n" % v)
self.text.insert("end","V='%s'\n" % V)
self.text.insert("end","W='%s'\n" % W)

# Disallow anything but lowercase letters
if S == S.lower():
return True
else:
self.bell()
return False

if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()

For more information about what happens under the hood when you call the register method, see Why is calling register() required for tkinter input validation?

For the canonical documentation see the Validation section of the Tcl/Tk Entry man page

Interactively validating Entry widget content in tkinter (part 2 - change the properties of the entry object)

I can't pass the entry pointer to the validation function

You can pass the name of the entry widget, and use tkinter's nametowidget method to convert that name into the instance of the widget.

Could you please explain ... why my validatecommand usage only works once?

You are improperly configuring the validate command. Consider this code:

self.entry['validatecommand'] = self.onValidate2(self.entry)

The above code is functionally identical to this:

result = self.onValidate2(self.entry)
self.entry['validatecommand'] = result

In other words, you are immediately calling your validation function and then setting the validatecommand option to None. the validationcommand option must be set to a callable.


Here is a working example of what you are trying to achieve. Normally the validate command must return True for a valid entry and False for an invalid entry, but I'm guessing you actually want to allow invalid entries and instead just want to turn the background red.

This example creates multiple entry widgets so you can see that the single validation function works for multiple widgets, by passing the name of the widget to the validation function.

import tkinter as tk

class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)

vcmd = self.register(self.onValidate)

for i in range(4):
entry = tk.Entry(self, validate="all")
entry.configure(validatecommand=(vcmd, "%W", "%P"))
entry.pack(side="top", fill="x")

def onValidate(self, entry_name, new_value):
entry = self.nametowidget(entry_name)
entry.configure(background="white")
try:
int(new_value)
except ValueError:
entry.configure(background="red")
return True

if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()

How to validate tkinter entry

Use <KeyRelease> instead of <Key>

A better way would be to use .trace

self.entry_str = tk.StringVar()
self.entry_str.set('')
self.entry_str.trace('w', self.validate_entry)

...

def validate_entry(self, *event):
try:
_ = int(self.entry_str.get())
...

An alternative is to disable the user from entering anything but integer.

self.entry = tk.Entry(self.frame, textvariable=self.entry_str, validate='key', validatecommand=(master.register(self.validate), "%P"))
...
def validate(self, char): # this function must return only True or False
return char.isdigit() or char==''

Do note self.entry_str will always be up-to date

How to validate entry widgets in python tkinter

There are 2 problems with your code.

  1. The validation function should always return a boolean.

    From this answer:

    It's important that the validation command returns either True or False. Anything else will cause the validation to be turned off for the widget.

    Your test_input function doesn't do that - there's a branch in which it returns None.

    def test_input(value, action):
    valid_input = ["7", "8", "9", "+", "4", "5", "6", "-", "1", "2", "3", "*", "0", ".", "/"]
    if action == "1":
    if value not in valid_input:
    return False
    return True
    # None is being returned here!

    This is why the validation is disabled after your program deletes text from the Entry. The fix is simple: return True instead of None.

    def test_input(value, action):
    valid_input = ["7", "8", "9", "+", "4", "5", "6", "-", "1", "2", "3", "*", "0", ".", "/"]
    if action == "1":
    if value not in valid_input:
    return False
    return True

    # if action != 1, allow it
    return True
  2. The validation function needs to handle multi-character input.

    You've assumed that the validation function is called for every single character that's entered. This is true when the user types a formula with their keyboard, but not when copy/pasting or setting the entry's text with .insert(...). Your function needs to handle these cases.

    def test_input(value, action):
    valid_input = ["7", "8", "9", "+", "4", "5", "6", "-", "1", "2", "3", "*", "0", ".", "/"]
    if action == "1":
    return all(char in valid_input for char in value)

    # if action != 1, allow it
    return True

validation of tkinter entry widget

This accepts valid decimal numbers not greater than 1000:

def acceptNumber(inp):
try:
return True if inp == '' else float(inp) <= 1000
except:
return False

How to validate a Tkinter entry widget to only accept string?

Here is a small snippet to actually make you understand better

from tkinter import * 
from tkinter import messagebox

root = Tk()

def check():
sel = e.get()

if not sel.isalpha():
messagebox.showerror('Only letters','Only letters are allowed!')

e = Entry(root)
e.pack(pady=10)

b = Button(root,text='Click Me',command=check)
b.pack(padx=10,pady=10)

root.mainloop()

Here we are checking if sel.isalpha() returns False or not, if it does, then show a messagebox saying only letters are allowed. Simple as that.

Do let me know if any errors. Happy coding

Here is more on isalpha() method

Cheers

Tkinter Entry validatation

The proper way to do entry validation is with the validatecommand option rather than using trace. With the validation feature built into the widget you don't need a reference to the widget itself (though you can use it if you want).

When the validatecommand is run, you can have it pass in what the new value will be if the input is valid. You only need to check this value and then return True or False, without having to know which widget it applies to.

For example:

import tkinter as tk

def validate_input(new_value):
valid = new_value .isdigit() and len(new_value) <= 5
return valid

root = tk.Tk()

validate = root.register(validate_input)
for i in range(10):
entry = tk.Entry(root, validate="key", validatecommand=(validate, "%P"))
entry.pack(side="top", fill="x")

root.mainloop()

For information about what %P represents, and what else can be used as arguments to the command, see this question: Interactively validating Entry widget content in tkinter



Related Topics



Leave a reply



Submit