Contrôler le contenu d'une entrée tkinter à la saisie
Fermémamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 - 6 mars 2023 à 17:11
- Tkinter validatecommand
- Tkinter récupérer valeur entry ✓ - Forum Python
- Morpion python tkinter - Forum Python
- Tkinter canvas rotate - Forum Python
- Tkinter mac os ✓ - Forum Python
- Itemconfig tkinter ✓ - Forum Python
6 réponses
Modifié le 6 mars 2023 à 17:22
Bonjour,
Retour sur les réponses précédentes
La solution #1 ne contrôle que la valeur du dernier caractère saisi (%S). Si cette approche marche parfaitement pour valider un entier non signé (car toute sous-chaîne non vide d'un entier non signé est un entier non signé) , ceci ne marchera ni pour un entier signé (car "-" à lui seul n'est pas un entier signé) ou pour un flottant (car ni ".", ni "-" ne constituent à eux seuls un float valide).
La solution #5 s'applique si la validation doit être faite à la soumission (par exemple en déclenchant check_input au moment de cliquer sur un bouton), mais pas au moment de la saisie.
Pour contourner le problème, il faut donc adapter la solution #1 de sorte à ce que l'on puisse contrôle le contenu du tk.Entry dans sa totalité (comme proposé dans #5).
Dans un monde idéal...
Selon ce lien, on peut définir ce qui sera passé à la callback de validation. On aimerait passer la totalité de la valeur saisie dans l'Entry et donc utiliser %P au lieu de %S dans la solution #1.
Malheureusement, dans mon cas, je n'obtiens que le dernier caractère saisie. Il va donc falloir reconstruire la valeur que l'Entry s'apprête à accueillir à la main. Pour cela nous allons avoir besoin de la valeur actuellement stockée dans l'Entry (%s), l'endroit où le nouveau caractère est saisi (%i), et le caractère nouvellement saisi.
Solution préliminaire
Afin de dissocier cette étape de reconstruction et celle de vérification, je sépare les deux fonctionnalités dans le code qui suit. En termes techniques, la reconstruction est réalisée par un foncteur, et la validation est implémentée dans une callback passée à ce foncteur.
- Pour rappel, un foncteur est une classe qui implémente la méthode __call__ et qui une fois instancié, se comporte comme une fonction. C'est donc une manière d'implémenter ce qu'en mathématiques on appelle une fonction paramétrée (par exemple : f_t(x, y) où t paramètre la fonction, x et y sont ses arguments). Dans le cas d'un foncteur, t est un paramètre du constructeur (la méthode __init__) ; x et y sont des paramètres de la méthode __call__.
import tkinter as tk class Validator: def __init__(self, check: callable): self.check = check def __call__(self, action :str, before: str, i: str, what: str) -> bool: i = int(i) action = int(action) print(f"before = {before} i = {i}, what = {what}") if action == 1: # Insertion text = before[:i] + what + before[i:] elif action == 0: # Deletion text = before[:i] + before[i+1:] else: # Focus change, etc. return True print(f"before = {before} i = {i}, what = {what}") return self.check(text) def is_float(s: str) -> bool: try: float(s) return True except: return False root = tk.Tk() entry = tk.Entry(root) check_float = Validator(is_float) entry.pack() entry.config(validate="key") # See https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/entry-validation.html entry.config(validatecommand=(entry.register(check_float), "%d", "%s", "%i", "%S")) root.mainloop()
Remarque : les prints sont uniquement là pour illustrer le programme/
Solution proposée
La solution préliminaire a quelques limitations :
- Si l'on veut saisir "-1.23" et qu'on commence par saisir "-", à ce stade on n'aurait que "-" et donc is_float renverra False. On peut donc décider d'autoriser exceptionnellement la chaîne "-" à être valide.
- De la même manière on peut autoriser la chaîne vide pour permettre à l'utilisateur d'effacer toute sa saisie.
Pour répondre à ces limitations, je relâche dans le code ci-dessous le test is_float pour rendre cela possible (voir fonction is_float_relaxed).
import tkinter as tk class Validator: def __init__(self, check: callable): self.check = check def __call__(self, action :str, before: str, i: str, what: str) -> bool: i = int(i) action = int(action) if action == 1: # Insertion text = before[:i] + what + before[i:] elif action == 0: # Deletion text = before[:i] + before[i+1:] else: # Focus change, etc. return True return self.check(text) def is_float(s: str) -> bool: try: float(s) return True except: return False def is_float_relaxed(s: str) -> bool: return s in {"", "-"} or is_float(s) root = tk.Tk() entry = tk.Entry(root) check_float = Validator(is_float_relaxed) entry.pack() entry.config(validate="key") # See https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/entry-validation.html entry.config(validatecommand=(entry.register(check_float), "%d", "%s", "%i", "%S")) root.mainloop()
Bonne chance
3 mars 2023 à 18:46
Bonjour, tu peux utiliser la méthode register() de l'objet entry comme ceci :
import tkinter as tk def validate_input(input_str): return input_str.isdigit() root = tk.Tk() entry = tk.Entry(root) entry.pack() entry.config(validate="key") entry.config(validatecommand=(entry.register(validate_input), '%S')) root.mainloop()
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre questionModifié le 4 mars 2023 à 11:10
Bonjour,
Au lieu de isdigit(), utilise isinstance()
Modifié le 6 mars 2023 à 16:29
ou plutôt ça:
def check_input(s): try: float(s) return True except: return False if check_input(entry.get()): .......