<p>正确答案是,使用小部件的<code>validatecommand</code>属性。不幸的是,这个特性在Tkinter世界中的文档记录严重不足,尽管在Tk世界中的文档记录相当充分。即使它没有很好的文档记录,它也具备了进行验证所需的一切,而无需借助绑定或跟踪变量,也无需在验证过程中修改小部件</p>
<p>诀窍是让Tkinter将特殊值传递给validate命令。这些值为您提供了决定数据是否有效所需的所有信息:编辑前的值、编辑后的值(如果编辑有效)以及其他一些信息。不过,要使用这些命令,您需要执行一些巫术操作,以将这些信息传递给validate命令</p>
<p>注意:validation命令返回<code>True</code>或<code>False</code>非常重要。任何其他操作都会导致关闭小部件的验证</p>
<p>下面是一个只允许小写的示例。为了便于说明,它还打印所有特殊值的值。它们并不都是必要的;你很少需要超过一两个</p>
<pre><code>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()
</code></pre>
<p>有关调用<code>register</code>方法时引擎盖下发生的情况的更多信息,请参阅<a href="https://stackoverflow.com/questions/55184324/input-validation-tkinter/55231273#55231273">Why is calling register() required for tkinter input validation?</a></p>
<p>有关规范文档,请参见<a href="http://tcl.tk/man/tcl8.6/TkCmd/entry.htm#M16" rel="noreferrer">Validation section of the Tcl/Tk Entry man page</a></p>