I am using validatecommand
to observe and validate the input of an entry widget 'dynamically'.
The standard usage of validatecommand
prevents invalid characters from being entered to the observed entry widget. This is not the behaviour I like to have, so I used validatecommand
to pass the string of the entry widget to another function and return True
in any case. floatstr_to_float
validates the string using a regular expression called preg
.
If the regex matches the input is valid, everything is OK and therefore print('approved')
is executed. However, if the user entered invalid input, the regex does not match, print('not approved')
is executed and the appropriate entry widget should be filled red (change of color not implemented properly yet).
What I have done so far is changing the background of the very first entry widget using <widget>.config(bg=<background>)
to check wheather I am able to access every widget by indexing a list of all created entry widgets.
validatecommand
can pass several arguments to the executed function (such as imput/text string and widget/window pathname). So getting a reference to the invalid widget in general is not a problem. However, the pathname passed by validatecommand
does not seem to be python-accessable. How can I get a reference (e.g. unique variable name) from this pathname to change the background by executing <widget>.config(bg=<background>)
on the widget containing (in)valid input?
-- MWE --
#!/usr/bin/env python3
# -*- coding: <utf-8> -*-
# code adapted from:
# http://stackoverflow.com/questions/4140437/python-tkinter-interactively-validating-entry-widget-content
import tkinter as tk
import re
class MyApp():
def __init__(self):
self.root = tk.Tk()
self.parameternames = [
('a', 'U'), ('b', 'U'), ('c', 'U'), ('d', 'U'), ('e', 'U'),
('f', 'U'), ('g', 'U'), ('h', 'U'), ('i', 'U'), ('j', 'U'),
('k', 'U'), ('l', 'U'), ('m', 'U'), ('n', 'U'), ('o', 'U'),
('p', 'U'), ('q', 'U'), ('r', 'U'), ('s', 'U'), ('t', 'U')]
self.vcmd = (self.root.register(self.OnValidate), '%P', '%W')
self.create_widgets()
def create_widgets(self):
self.entries = []
for i in enumerate(self.parameternames):
entry = tk.Entry(self.root, validate="all", validatecommand=self.vcmd)
self.default_bg = entry.cget("bg")
entry.pack()
self.entries.append(entry)
self.root.mainloop()
def OnValidate(self, P, W):
# %P = value of the entry if the edit is allowed
# %W = the tk name of the widget (pathname)
print("OnValidate:")
print("P='%s'" % P )
print("W='%s'" % W )
self.floatstr_to_float(P, W)
# return True to display inserted character, validation is done by a re in 'floatstr_to_float()'
return True
def floatstr_to_float(self, fs, W):
preg = re.compile('^\s*(?P<int>\d*)\s*[\.,]?\s*(?P<dec>\d*)\s*$')
m = preg.match(fs)
if m:
print('approved')
intprt=m.group('int')
frcprt=m.group('dec')
f = 0. if (intprt == '' and frcprt == '') else float('%s.%s' %(intprt, frcprt)) # not needed yet
# currently: just changing the color of the first entry widget (proof of concept)
# aim: pass unique name of edited entry widget to self.change_bg() for changing bg of
# appropriate entry widget
self.change_bg(self.entries[0], 1)
else:
print('not approved')
# see comment in if-statement above
self.change_bg(self.entries[0], 0)
def change_bg(self, name, approved):
if approved == 1:
name.config(bg=self.default_bg)
else:
name.config(bg='#d9534f')
app=MyApp()