Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interactively validating Entry widget content in tkinter

What is the recommended technique for interactively validating content in a tkinter Entry widget?

I've read the posts about using validate=True and validatecommand=command, and it appears that these features are limited by the fact that they get cleared if the validatecommand command updates the Entry widget's value.

Given this behavior, should we bind on the KeyPress, Cut, and Paste events and monitor/update our Entry widget's value through these events? (And other related events that I might have missed?)

Or should we forget interactive validation altogether and only validate on FocusOut events?

like image 292
Malcolm Avatar asked Nov 10 '10 01:11

Malcolm


People also ask

Which of the following is valid Tkinter widgets?

Tkinter widgets - Checkbutton, Label, Scale, Listbox.

How do I get an event callback when a Tkinter entry widget is modified?

Callback functions in Tkinter are generally used to handle a specific event happening in a widget. We can add an event callback function to the Entry widget whenever it gets modified. We will create an event callback function by specifying the variable that stores the user input.


2 Answers

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

like image 84
Bryan Oakley Avatar answered Sep 24 '22 10:09

Bryan Oakley


After studying and experimenting with Bryan's code, I produced a minimal version of input validation. The following code will put up an Entry box and only accept numeric digits.

from tkinter import *  root = Tk()  def testVal(inStr,acttyp):     if acttyp == '1': #insert         if not inStr.isdigit():             return False     return True  entry = Entry(root, validate="key") entry['validatecommand'] = (entry.register(testVal),'%P','%d') entry.pack()  root.mainloop() 

Perhaps I should add that I am still learning Python and I will gladly accept any and all comments/suggestions.

like image 20
user1683793 Avatar answered Sep 24 '22 10:09

user1683793