在 tkinter 中交互式验证 Entry 小部件内容

116 投票
11 回答
116139 浏览
提问于 2025-04-16 06:49

在tkinter的Entry控件中,怎样才能有效地进行内容的实时验证呢?

我看到有些帖子提到可以用validate=Truevalidatecommand=command,但是这些功能有个问题,就是如果validatecommand命令更新了Entry控件的值,这些验证就会被清空。

考虑到这个情况,我们是不是应该在KeyPress(按键)、Cut(剪切)和Paste(粘贴)这些事件上绑定一些操作,来监控和更新Entry控件的值呢?(还有其他我可能遗漏的相关事件?)

或者我们干脆不做实时验证,只在FocusOut(失去焦点)事件时进行验证呢?

11 个回答

14

使用一个 Tkinter.StringVar 来跟踪 Entry 小部件的值。你可以通过在这个 StringVar 上设置一个 trace 来验证它的值。

下面是一个简单的程序,它只接受在 Entry 小部件中输入的有效浮点数。

try:
    from tkinter import *
except ImportError:
    from Tkinter import *  # Python 2


root = Tk()
sv = StringVar()

def validate_float(var):
    new_value = var.get()
    try:
        new_value == '' or float(new_value)
        validate_float.old_value = new_value
    except:
        var.set(validate_float.old_value)

validate_float.old_value = ''  # Define function attribute.

# trace wants a callback with nearly useless parameters, fixing with lambda.
sv.trace('w', lambda nm, idx, mode, var=sv: validate_float(var))
ent = Entry(root, textvariable=sv)
ent.pack()
ent.focus_set()

root.mainloop()

29

在研究和尝试Bryan的代码后,我写出了一个简单的输入验证版本。下面的代码会显示一个输入框,只接受数字。

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()

也许我应该补充一下,我还在学习Python,欢迎大家提出任何意见或建议。

288

正确的做法是使用小部件的 validatecommand 属性。不过,这个功能在 Tkinter 的文档中介绍得很少,但在 Tk 的文档中却有详细说明。尽管文档不够完善,它实际上提供了你进行数据验证所需的所有信息,而不需要使用绑定、追踪变量,或者在验证过程中修改小部件。

关键是要知道,你可以让 Tkinter 将一些特殊值传递给你的验证命令。这些值会告诉你是否数据有效所需的所有信息:编辑前的值、如果编辑有效则编辑后的值,以及其他一些信息。不过,要使用这些信息,你需要做一些小技巧来将这些信息传递给你的验证命令。

注意:验证命令必须返回 TrueFalse。如果返回其他值,小部件的验证功能就会被关闭。

下面是一个只允许小写字母的例子。为了说明,这个例子还打印了所有特殊值的内容。并不是所有的值都是必须的;通常你只需要一两个就够了。

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()

想了解调用 register 方法时发生了什么,可以查看 为什么在 tkinter 输入验证中需要调用 register()?

有关官方文档,请参见 Tcl/Tk Entry 手册的验证部分

撰写回答