Reputation: 1475
I'm trying to use the validation procedure from the answer here: Interactively validating Entry widget content in tkinter
I want to change the background color by checking the input type. Unfortunately, I can't pass the entry pointer to the validation function: vcmd = (self.register(self.onValidate), '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
. Thus, I tried this:
self.entry = tk.Entry(self, validate="all")
self.entry['validatecommand'] = self.onValidate2(self.entry)
But this only works once. Could you please explain me what is the best way to access to the object from which the validation function (or other one) is called and why my validatecommand
usage only works once?
Here is the full code from the above link with some corrections:
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.entry = tk.Entry(self, validate="all")
# self.entry['validatecommand'] = vcmd
self.entry['validatecommand'] = self.onValidate2(self.entry)
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)
self.text.insert("end","W='%s'\n" % W)
W.config({"background": "Red"})
# Disallow anything but lowercase letters
if S == S.lower():
return True
else:
self.bell()
return False
def onValidate2(self, entry):
try:
entry.config({"background": "White"})
value = int(entry.get())
print("Int Value=",value)
# return int(value)
except ValueError:
entry.config({"background": "Red"})
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
Upvotes: 2
Views: 708
Reputation: 386342
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()
Upvotes: 2