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

Pradelles -  
mamiemando Messages postés 33228 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

  1. mamiemando Messages postés 33228 Date d'inscription   Statut Modérateur Dernière intervention   7 943
     

    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
  2. xHaMaz Messages postés 123 Date d'inscription   Statut Membre Dernière intervention   18
     

    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
  3. Pradelles
     

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

    0
  4. Pradelles
     

    De même pour les chiffres négatifs

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

    Posez votre question
  6. Phil_1857 Messages postés 1883 Date d'inscription   Statut Membre Dernière intervention   169
     

    Bonjour, 

    Au lieu de isdigit(), utilise isinstance() 

    0
  7. Phil_1857 Messages postés 1883 Date d'inscription   Statut Membre Dernière intervention   169
     

    ou plutôt ça:

    def check_input(s):
        try:
            float(s)
            return True
        except:
            return False
    
    if check_input(entry.get()):
    	.......
    0