Contrôler le contenu d'une entrée tkinter à la saisie

Pradelles -  
mamiemando Messages postés 33769 Date d'inscription   Statut Modérateur Dernière intervention   -

Bonjour,

Je dois faire un programme python avec tkinter et je cherche comment empêcher certains caractères d'être saisis dans la barre d'entrée (caractères spéciaux, lettres) (uniquement les chiffres pour ne pas qu'il y ai de message d'erreurs avec les calculs).

Merci

6 réponses

mamiemando Messages postés 33769 Date d'inscription   Statut Modérateur Dernière intervention   7 878
 

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

1
xHaMaz Messages postés 120 Date d'inscription   Statut Membre Dernière intervention   17
 

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

Merci, mais ducoup je ne peux pas taper de chiffres à virgule, y a t il un moyen pour le faire ?

0
Pradelles
 

De même pour les chiffres négatifs

0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Phil_1857 Messages postés 1872 Date d'inscription   Statut Membre Dernière intervention   168
 

Bonjour, 

Au lieu de isdigit(), utilise isinstance() 

0
Phil_1857 Messages postés 1872 Date d'inscription   Statut Membre Dernière intervention   168
 

ou plutôt ça:

def check_input(s):
    try:
        float(s)
        return True
    except:
        return False

if check_input(entry.get()):
	.......
0