tkinter 中的小部件验证

Widget validation in tkinter

我希望用户能够在 Spinbox 小部件中输入整数值。如果输入的值不是整数或超出 Spinbox 限制的整数,一旦 Spinbox 失去焦点,Spinbox 内容中的值必须恢复为默认值。

在示例代码中,我使用 Entry 小部件只是因为 Spinbox 可能会失去焦点。

如果用户返回 Spinbox 输入新值,他的输入将不会被验证。 我确认 Malcolm 在 Interactively validating Entry widget content in tkinter 中的评论,即一旦此命令更新小部件的值,validatecommand=command 功能就会被清除。

有没有办法让在 Spinbox 中输入的值反复验证而不是只验证一次?

from tkinter import *


class GUI:

    def __init__(self):
        # root window of the whole program
        self.root = Tk()
        self.root.title('Validate Spinbox')

        # registering validate and invalid commands
        validate_cmd = (self.root.register(self.validate), '%P')
        invalid_cmd = (self.root.register(self.invalid))

        # creating a Label
        items_lbl = Label(self.root, text="# of items (5-10):")
        items_lbl.grid(row=0, column=0)

        # creating a Spinbox widget
        self.items_var = StringVar()
        self.items_var.set(7)
        items_count = Spinbox(self.root, textvariable=self.items_var,
                              from_=5, to=10, width=4, validate='focusout',
                              validatecommand=validate_cmd,
                              invalidcommand=invalid_cmd)
        items_count.grid(row=0, column=1)

        # creating an Entry widget
        self.entry_var = StringVar()
        self.entry_var.set("Input some text here")
        text_entry = Entry(self.root, textvariable=self.entry_var)
        text_entry.grid(row=1, column=0)

    def validate(self, entry):
        try:
            value = int(entry)
            valid = value in range(5, 11)
        except ValueError:
            valid = False
        if not valid:
            self.root.bell()
        return valid

    def invalid(self):
        self.items_var.set(7)


if __name__ == '__main__':
    main_window = GUI()
    mainloop()

我在这里找到了很好的解释(在验证一章的最后一段):

http://stupidpythonideas.blogspot.fr/2013/12/tkinter-validation.html

If your validatecommand (or invalidcommand) modifies the Entry directly or indirectly (e.g., by calling set on its StringVar), the validation will get disabled as soon as your function returns. (This is how Tk prevents an infinite loop of validate triggering another validate.) You have to turn it back on (by calling config). But you can't do that from inside the function, because it gets disabled after your function returns.

但您需要应用一些更改才能使用此技巧。

您需要使 Spinbox 成为一个实例属性, self :

self.items_count = Spinbox(self.root, textvariable=self.items_var,
                      from_=5, to=10, width=4, validate='focusout',
                      validatecommand=validate_cmd,
                      invalidcommand=invalid_cmd)
self.items_count.grid(row=0, column=1)

然后你可以在 validate 方法中调用 self.items_count.after_idle(...) :

def validate(self, entry):
    try:
        value = int(entry)
        valid = value in range(5, 11)
    except ValueError:
        valid = False
    if not valid:
        self.root.bell()
        self.items_count.after_idle(lambda: self.items_count.config(validate='focusout'))
    return valid