4

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()
albert
  • 8,027
  • 10
  • 48
  • 84

1 Answers1

3

You can use the nametowidget method of Tk. This finds the widget based on its name. So use:

self.change_bg(self.root.nametowidget(W), 1)
fhdrsdg
  • 10,297
  • 2
  • 41
  • 62