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

Mal*_*olm 74 python validation textbox tkinter

在tkinter Entry小部件中交互式验证内容的推荐技术是什么?

我已经阅读了关于使用validate=True和的帖子validatecommand=command,并且看起来这些功能受限于它们在validatecommand命令更新Entry小部件的值时被清除的事实.

鉴于这种行为,我们应该绑定的KeyPress,Cut以及Paste事件和监视/更新我们的Entry小部件的价值,通过这件事情?(以及我可能错过的其他相关事件?)

或者我们是否应该完全忘记交互式验证并仅对FocusOut事件进行验证?

Bry*_*ley 186

正确答案是,使用validatecommand小部件的属性.不幸的是,这个特征在Tkinter世界中严重缺乏记录,尽管在Tk世界中有足够的记录.尽管它没有很好地记录,但它具有您需要进行验证所需的一切,而无需借助绑定或跟踪变量,或者在验证过程中修改小部件.

诀窍是要知道你可以让Tkinter将特殊值传递给validate命令.这些值为您提供了确定数据是否有效所需的所有信息:编辑前的值,编辑后编辑后的值以及其他几个信息.但是,要使用这些,您需要执行一些voodoo以将此信息传递给validate命令.

注:这是很重要的验证命令返回无论是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()
Run Code Online (Sandbox Code Playgroud)

  • 这是正确的方法.它解决了我试图让jmeyer10的答案有效时发现的问题.与我在其他地方找到的相比,这个例子提供了更好的文档验证.我希望我能给这5票. (13认同)
  • “在 Tkinter 世界中记录严重不足”。大声笑——就像几乎所有其他 Tkiinter 世界一样。 (5认同)
  • @Rightleg 该页面不再存在。存档版本:http://web.archive.org/web/20190423043443/http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/entry-validation.html (3认同)
  • 哇!我同意史蒂文的说法 - 这是一种值得多于一票的回复.你应该写一本关于Tkinter的书(你已经发布了足够多的解决方案来制作一个多卷系列).谢谢!!! (2认同)
  • 谢谢你的例子.值得注意的是,validatecommand必须返回一个布尔值(只有True和False).如果没有,验证将被删除. (2认同)
  • 我认为[本页](http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/entry-validation.html)应该脱颖而出. (2认同)
  • @martineau:您可以使用常见的“nametowidget”方法将名称转换为实际的小部件对象。 (2认同)

use*_*793 11

在研究和试验Bryan的代码之后,我制作了一个最小版本的输入验证.以下代码将显示一个Entry框并仅接受数字.

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()
Run Code Online (Sandbox Code Playgroud)

也许我应该补充一点,我仍在学习Python,我很乐意接受任何和所有意见/建议.

  • 通常人们使用 `entry.configure(validatecommand=...)` 并编写 `test_val` 而不是 `testVal`,但这是一个很好的简单示例。 (3认同)

Ste*_*ski 9

使用a Tkinter.StringVar来跟踪Entry小部件的值.您可以StringVar通过设置a 来验证其值trace.

这是一个简短的工作程序,只接受Entry小部件中的有效浮点数.

from Tkinter import *
root = Tk()
sv = StringVar()

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

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

root.mainloop()
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你的帖子。我很高兴看到正在使用的 Tkinter StringVar .trace() 方法。 (2认同)
  • @ArmenSanoyan:这是因为此代码段中未定义“validate”(应该更正)。 (2认同)
  • @Wolf:请参阅我所做的更新,因为答案的作者似乎对自己修复它不感兴趣...... (2认同)
  • @Wolf:在修复这里的问题时,我注意到了一些其他缺陷,并决定发布我自己的[答案](/sf/answers/4814579661/)来解决它们。 (2认同)

Dem*_*olf 7

定义一个返回布尔值的函数,该布尔值指示输入是否有效。
将其注册为 Tcl 回调,并将回调名称作为validatecommand.

例如:

import tkinter as tk


def validator(P):
    """Validates the input.

    Args:
        P (int): the value the text would have after the change.

    Returns:
        bool: True if the input is digit-only or empty, and False otherwise.
    """

    return P.isdigit() or P == ""


root = tk.Tk()

entry = tk.Entry(root)
entry.configure(
    validate="key",
    validatecommand=(
        root.register(validator),
        "%P",
    ),
)
entry.grid()

root.mainloop()
Run Code Online (Sandbox Code Playgroud)

参考


Noc*_*wer 5

在研究Bryan Oakley 的答案时,我发现可以开发一个更通用的解决方案。以下示例介绍了模式枚举、类型字典和用于验证目的的设置函数。请参阅第 48 行,了解示例用法及其简单性演示。

#! /usr/bin/env python3
# /sf/ask/289830621/
import enum
import inspect
import tkinter
from tkinter.constants import *


Mode = enum.Enum('Mode', 'none key focus focusin focusout all')
CAST = dict(d=int, i=int, P=str, s=str, S=str,
            v=Mode.__getitem__, V=Mode.__getitem__, W=str)


def on_validate(widget, mode, validator):
    # http://www.tcl.tk/man/tcl/TkCmd/ttk_entry.htm#M39
    if mode not in Mode:
        raise ValueError('mode not recognized')
    parameters = inspect.signature(validator).parameters
    if not set(parameters).issubset(CAST):
        raise ValueError('validator arguments not recognized')
    casts = tuple(map(CAST.__getitem__, parameters))
    widget.configure(validate=mode.name, validatecommand=[widget.register(
        lambda *args: bool(validator(*(cast(arg) for cast, arg in zip(
            casts, args)))))]+['%' + parameter for parameter in parameters])


class Example(tkinter.Frame):

    @classmethod
    def main(cls):
        tkinter.NoDefaultRoot()
        root = tkinter.Tk()
        root.title('Validation Example')
        cls(root).grid(sticky=NSEW)
        root.grid_rowconfigure(0, weight=1)
        root.grid_columnconfigure(0, weight=1)
        root.mainloop()

    def __init__(self, master, **kw):
        super().__init__(master, **kw)
        self.entry = tkinter.Entry(self)
        self.text = tkinter.Text(self, height=15, width=50,
                                 wrap=WORD, state=DISABLED)
        self.entry.grid(row=0, column=0, sticky=NSEW)
        self.text.grid(row=1, column=0, sticky=NSEW)
        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(0, weight=1)
        on_validate(self.entry, Mode.key, self.validator)

    def validator(self, d, i, P, s, S, v, V, W):
        self.text['state'] = NORMAL
        self.text.delete(1.0, END)
        self.text.insert(END, 'd = {!r}\ni = {!r}\nP = {!r}\ns = {!r}\n'
                              'S = {!r}\nv = {!r}\nV = {!r}\nW = {!r}'
                         .format(d, i, P, s, S, v, V, W))
        self.text['state'] = DISABLED
        return not S.isupper()


if __name__ == '__main__':
    Example.main()
Run Code Online (Sandbox Code Playgroud)


小智 5

Bryan 的回答是正确的,但是没有人提到 tkinter 小部件的“invalidcommand”属性。

一个很好的解释在这里:http : //infohost.nmt.edu/tcc/help/pubs/tkinter/web/entry-validation.html

在链接断开的情况下复制/粘贴文本

Entry 小部件还支持一个 invalidcommand 选项,该选项指定每当 validatecommand 返回 False 时调用的回调函数。此命令可以通过使用小部件关联的文本变量上的 .set() 方法来修改小部件中的文本。设置此选项的工作方式与设置验证命令相同。您必须使用 .register() 方法来包装您的 Python 函数;此方法以字符串形式返回包装函数的名称。然后,您将作为 invalidcommand 选项的值传递该字符串,或者作为包含替换代码的元组的第一个元素。

注意:只有一件事我不知道该怎么做:如果向条目添加验证,并且用户选择文本的一部分并键入新值,则无法捕获原始值并重置入口。这是一个例子

  1. 条目旨在通过实现“validatecommand”仅接受整数
  2. 用户输入 1234567
  3. 用户选择“345”并按“j”。这被注册为两个动作:删除“345”和插入“j”。Tkinter 忽略删除并仅对 'j' 的插入起作用。'validatecommand' 返回 False,传递给 'invalidcommand' 函数的值如下: %d=1, %i=2, %P=12j67, %s=1267, %S=j
  4. 如果代码没有实现'invalidcommand'函数,'validatecommand'函数会拒绝'j',结果为1267。如果代码没有实现'invalidcommand'函数,就没有办法恢复原来的1234567 .