[Tkinter-discuss] tkinter Entry validation: insert instead of replace

Michael Lange klappnase at web.de
Fri Feb 22 20:24:01 CET 2013


On Fri, 22 Feb 2013 21:59:50 +0600
Teodor the Thinker <doctorlans at gmail.com> wrote:

> Thank you. It was so simple! I changed function return to
> return P.isdigit() or (P == "")
> and it seems work.
> 
> But it allows empty string not only during editing. I want to make:
> when i clear the Entry and switch to another GUI element Entry
> rollbacks to last valid value.
> 
> But obvious code to achieve this goal doesnt work:
> if P == "" and V == 'focusout':
>     return False
> else:
>     return (P.isdigit() or P == "")
> 
> I found when "return False" is executed, *s* variable is empty. So i
> need to rollback not for last but to *before last* value. Is there a
> nice bultin Tkinter way to achieve that or i should declare some
> variable with "before last value" by my own?

There is no (or at least I don't know of) any built-in way to do this.

One possible way to work around this is to store the old value and restore
it if required, as in this example:

##############################################################
import Tkinter

class IntEntry(Tkinter.Entry):
    def __init__(self, master=None, value=0, **kw):
        Tkinter.Entry.__init__(self, master, **kw)
        self._oldvalue = value
        self.insert('end', value)
        vcmd = (self.register(self._validate), '%s', '%P')
        self.configure(validate='all', vcmd=vcmd)
        self.bind('<FocusOut>', self._focus_out)

    def _focus_out(self, event):
        self.get()

    def _validate(self, old, new):
        if old != '':
            self._oldvalue = old
        if new == '':
            return True
        return new.isdigit()

    def get(self):
        value = Tkinter.Entry.get(self)
        if value == '':
            self.insert('end', self._oldvalue)
            value = self._oldvalue
        return int(value)

    def set(self, value):
        self.delete(0, 'end')
        self.insert('end', value)

root = Tkinter.Tk()
e1 = IntEntry(root, value=111)
e1.pack(side='top', padx=100, pady=20)
e2 = IntEntry(root, value=222)
e2.pack(side='bottom', padx=100, pady=20)
def test(event):
    print 'test'
e2.bind('<FocusOut>', test)
root.mainloop()
##############################################################

This appears to work rather robust, it takes even care that get()
will always return a decent value.
However you see the weak point of this approach in the second Entry where
a new binding to <FocusOut> events stops the validation from working
properly.
Using the "%V" substitution in the _validate() callback seems not to be
exactly an alternative, because calling insert() from within the
validation callback will again start validation and the results are hard
to predict; sooner or later it may happen that Tk decides to turn off
validation at all (at least I never managed to get such setups working).

An alternative may (or may not ;) be to use the modified get() as above
but to leave the empty Entries alone until some procedure is started by
the user that calls get() which will automagically restore the last sane
value (that's in fact what I did in one of my programs where I took the
above example from).

Regards

Michael

.-.. .. ...- .   .-.. --- -. --.   .- -. -..   .--. .-. --- ... .--. . .-.

Another war ... must it always be so?  How many comrades have we lost
in this way? ...  Obedience.  Duty.  Death, and more death ...
		-- Romulan Commander, "Balance of Terror", stardate 1709.2


More information about the Tkinter-discuss mailing list