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

Fermé
Pradelles - Modifié le 6 mars 2023 à 17:13
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

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 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 7 812
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

1
xHaMaz Messages postés 108 Date d'inscription mardi 3 janvier 2023 Statut Membre Dernière intervention 23 avril 2023 14
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()
0

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

0

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 lundi 23 mars 2020 Statut Membre Dernière intervention 28 février 2024 168
Modifié le 4 mars 2023 à 11:10

Bonjour, 

Au lieu de isdigit(), utilise isinstance() 

0
Phil_1857 Messages postés 1872 Date d'inscription lundi 23 mars 2020 Statut Membre Dernière intervention 28 février 2024 168
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()):
	.......
0