Quiero que si un campo tiene una entrada no válida, que se muestre un mensaje al salir de ese campo y que el foco permanezca en ese campo. Pero en el siguiente código, la validación del siguiente campo también se activa al salir del primer campo. He comentado establecer foco, de lo contrario, entra en un bucle infinito.
from tkinter import * from tkinter import ttk, messagebox root = Tk() def callback1(): if (len(e1.get()) <4): messagebox.showinfo("error", "Field 1 length < 4") #e1.focus_set() return False else: return True def callback2(): if (len(e2.get()) <4): messagebox.showinfo("error", "Field 2 length < 4") #e2.focus_set() return False else: return True e1 = Entry(root, validate="focusout", validatecommand=callback1) e1.grid() e2 = Entry(root, validate="focusout", validatecommand=callback2) e2.grid() root.mainloop()
Cuando coloca el cursor en e1
, escribe algo que no satisface la condición del comando de validatecommand
y luego intenta colocar el cursor en e2
, tiene lugar la siguiente secuencia de eventos:
e1
pierde el foco y llama callback1
e2
y e2
se enfocae1
se enfoca y está lista para aparecer justo cuando...e2
pierde el foco y llama callback2
e2
e1
que estaba esperando en la colaLa causa raíz del problema es que la ventana del cuadro de mensaje toma el foco, lo que a su vez activa el otro cuadro de entrada. Este enfoque parece ser muy frágil debido a la fuerte interacción de los acontecimientos.
Tenga en cuenta que si solo imprime la información en la terminal, todo funciona perfectamente ya que la terminal no se enfoca.
Por lo tanto, le recomendaría que muestre la información utilizando un método alternativo en el que el widget que muestra la información no robe el foco. Una opción es mostrar la información mediante una etiqueta.
Ahora, llegando a su segundo problema, si desea que la entrada mantenga el foco si el texto ingresado no es válido, puede usar una variable global ( hanging
) para realizar un seguimiento de si el usuario está en el proceso de completar una entrada con éxito.
Si el usuario está en el proceso de completar una entrada, no podrá colocar el cursor en la otra entrada porque FocusIn1
y FocusIn2
devuelven "break" cuando hanging
igual a True.
Puede reemplazar las declaraciones de impresión en el siguiente código de trabajo usando una etiqueta.
from tkinter import * from tkinter import ttk, messagebox root = Tk() hanging = False #True implies that the user is in the process of filling an entry successfully def onFocusOut1(): global hanging hanging = True if (len(e1.get()) <4): print("error", "Field 1 length < 4") e1.focus_set() return False else: hanging = False return True def onFocusOut2(): global hanging hanging = True if (len(e2.get()) <4): print("error", "Field 2 length < 4") e2.focus_set() return False else: hanging = False return True def onFocusIn1(): if hanging: return "break" e1.configure(validate="focusout", validatecommand=onFocusOut1) def onFocusIn2(): if hanging: return "break" e2.configure(validate="focusout", validatecommand=onFocusOut2) e1 = Entry(root) e1.grid() e2 = Entry(root) e2.grid() #Binding both the entries to FocusIn e1.bind("<FocusIn>", lambda e: onFocusIn1()) e2.bind("<FocusIn>", lambda e: onFocusIn2()) root.mainloop()
PD: Resulta que en realidad puedes usar messagebox.showinfo
en lugar de print
en el código de trabajo. El primer problema se resolvió automáticamente junto con el segundo. Entonces, esto le da la solución completa a su problema.