validar solo numeros python validation tkinter textbox

python - solo - Validación interactiva del contenido del widget de entrada en tkinter



validar numeros en python (4)

¿Cuál es la técnica recomendada para validar interactivamente el contenido en un widget de Entry tkinter?

He leído las publicaciones sobre el uso de validate=True y validatecommand=command , y parece que estas funciones están limitadas por el hecho de que se borran si el comando validatecommand actualiza el valor del widget Entry .

Dado este comportamiento, ¿debemos unirnos a KeyPress , Cut y Paste eventos y monitorear / actualizar el valor de nuestro widget de Entry través de estos eventos? (¿Y otros eventos relacionados que podría haber perdido?)

¿O deberíamos olvidar la validación interactiva por completo y solo validar en los eventos de FocusOut ?


Después de estudiar y experimentar con el código de Bryan, produje una versión mínima de validación de entrada. El siguiente código colocará un cuadro de Entrada y solo aceptará dígitos numéricos.

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

Quizás debería añadir que todavía estoy aprendiendo Python y con mucho gusto aceptaré todos los comentarios / sugerencias.


La respuesta correcta es usar el atributo validatecommand del widget. Lamentablemente, esta característica está muy poco documentada en el mundo Tkinter, aunque está suficientemente documentada en el mundo Tk. Aunque no está bien documentado, tiene todo lo que necesita para hacer la validación sin recurrir a enlaces o variables de seguimiento, o modificar el widget desde el procedimiento de validación.

El truco está en saber que puede hacer que Tkinter pase valores especiales a su comando de validación. Estos valores le brindan toda la información que necesita saber para decidir si los datos son válidos o no: el valor anterior a la edición, el valor después de la edición, si la edición es válida, y varios otros bits de información. Para usar estos, sin embargo, debe hacer un poco de vudú para que esta información pase a su comando de validación.

Nota: es importante que el comando de validación devuelva True o False . Cualquier otra cosa hará que la validación se desactive para el widget.

Aquí hay un ejemplo que solo permite minúsculas (e imprime todos esos valores originales):

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


Mientras estudiaba la respuesta de Bryan Oakley , algo me dijo que se podría desarrollar una solución mucho más general. El siguiente ejemplo presenta una enumeración de modo, un diccionario de tipo y una función de configuración para fines de validación. Vea la línea 48 para ejemplos de uso y una demostración de su simplicidad.

#! /usr/bin/env python3 # https://.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()


Use un Tkinter.StringVar para rastrear el valor del widget de Entrada. Puede validar el valor de StringVar estableciendo un trace en él.

Aquí hay un breve programa de trabajo que acepta solo flotantes válidos en el widget de Entrada.

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