在tkinter中交互validationEntry widget的内容
交互式validationtkinter Entry
小部件中的内容的推荐技术是什么?
我已经阅读了关于使用validate=True
和validatecommand=command
,并且似乎这些function受限于事实,即如果validatecommand
命令更新Entry
小部件的值,它们将被清除。
鉴于此行为,我们是否应该绑定KeyPress
, Cut
和Paste
事件,并通过这些事件监视/更新Entry
元素的值? (和我可能错过的其他相关事件?)
或者,我们应该忘记交互式validation,只有在FocusOut
事件上validation?
正确的答案是,使用小部件的validatecommand
属性。 不幸的是,这个特性在Tkinter世界中被严重地logging在案,尽pipe它在Tk世界中被充分logging。 尽pipe它没有很好的logging,但它拥有所有你需要做的validation,而不需要绑定或跟踪variables,或者在validation过程中修改小部件。
诀窍是知道你可以让Tkinter将特殊值传递给你的validate命令。 这些值为您提供了所有需要了解的信息,以确定数据是否有效:编辑前的值,编辑后的编辑后的值以及其他几位信息。 但是,为了使用这些信息,你需要做一点巫术来将这些信息传递给你的validate命令。
注意:validation命令返回True
或False
非常重要。 其他任何事情都会导致对小部件的validationclosures。
这是一个只允许小写的例子(并打印所有这些时髦的值):
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()
使用Tkinter.StringVar
来跟踪Entry小部件的值。 您可以通过在其上设置trace
来validationStringVar
的值。
这是一个简短的工作程序,只接受入口小部件中的有效浮点数。
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()
在对Bryan的代码进行研究和实验后,我制作了一个最小版本的inputvalidation。 下面的代码将放置一个input框,只接受数字。
from tkinter import * root = Tk() def testVal(inStr,i,acttyp): ind=int(i) if acttyp == '1': #insert if not inStr[ind].isdigit(): return False return True entry = Entry(root, validate="key") entry['validatecommand'] = (entry.register(testVal),'%P','%i','%d') entry.pack() root.mainloop()
也许我应该补充一点,我仍然在学习Python,我会很乐意接受任何和所有的意见/build议。
在研究布莱恩·奥克利的回答时 ,有人告诉我可以开发出更为一般的解决scheme。 下面的例子介绍了一个模式枚举,一个types字典和一个用于validation的设置函数。 请参见第48行的例子使用和简单的演示。
#! /usr/bin/env python3 # https://stackoverflow.com/questions/4140437 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()