IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Tkinter Python Discussion :

Réception du contenu d'une entry avec .get()


Sujet :

Tkinter Python

  1. #1
    Membre à l'essai
    Profil pro
    Client Solution Developer
    Inscrit en
    Janvier 2011
    Messages
    23
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Client Solution Developer

    Informations forums :
    Inscription : Janvier 2011
    Messages : 23
    Points : 21
    Points
    21
    Par défaut Réception du contenu d'une entry avec .get()
    Bonjour,

    dans un programme, je souhaiterais récupérer le contenu d'une entry et cela à chaque fois qu'une touche est pressée. J'ai donc créé une variable Tk de type StringVar() qui est la variable texte de l'entry:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    varTk = StringVar()
    mon_entry = Entry(fen_parent, textvariable =varTk)
    De même, je réceptionne le contenu de l'entry à l'aide de la commande .get():
    Le souci est que la valeur enregistrée contient tout le contenu de l'entry sauf la dernière touche tapée. (exemple: j'ai tapé "azerty" et le contenu mémorisé est "azert" car le "y" est la dernière touche tapée.
    Je voulais donc savoir s'il existait une manière de mémoriser tout le contenu de l'entry (en "rafraîchissant" le contenu de l'entry par exemple).

    Voici un bout de mon code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    # -*- coding: cp1252 -*-
    from Tkinter import *
     
    class NewEntry(Entry):
        """"""
        def __init__(self, boss =None):
            self.varTk = StringVar()
            Entry.__init__(self, textvariable =self.varTk,
                           validate ='key', vcmd =self.verifClavier)
            self.bind('<Key>', func=self.quelleTouche)
     
        def quelleTouche(self, event):
            """Retiens la touche qui vient d'être tapée."""
            self.touche =event
            self.configure(validate ='key', vcmd =self.verifClavier)
            #print "Le contenu de l'entry est:", self.varTk.get()
     
        def verifClavier(self):
            """Permet uniquement que l'utilisateur tape 1 chiffre dans les entry."""
            try: self.touche    #Si l'entryest modifiée sans avoir appuyé sur...
            except: return True #...une touche, on n'effectue pas la vérification
     
            if self.touche.char in '0123456789' or\
               self.touche.keysym == 'BackSpace':
                print "Le contenu de l'entry est:", self.varTk.get()
                return True
            else:
                self.bell()
                return False
     
    #-----------------Si le programme est le programme principal:-------------------
    if __name__ == "__main__":
        fen = Tk()
        ent = NewEntry(boss =fen)
        ent.pack()
        fen.mainloop()
    Merci d'avance pour votre aide.

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 398
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 398
    Points : 36 957
    Points
    36 957
    Par défaut
    Salut,

    la vcmd est appelé avant d'avoir inséré l'ajout dans 'entry' pour permettre justement de valider le caractère ajouté.

    Ceci dit c'est un fonctionnalité Tk pour laquelle l'interface Tkinter est "weak" car il faut enregistrer les paramètres qui seront reçus (expédiés) par Tk "à la main".

    Inspirez vous du post qui donne quelques pistes sur "l'utilisation" de cette fonctionnalité.

    Cordialement,
    - W

  3. #3
    Membre à l'essai
    Profil pro
    Client Solution Developer
    Inscrit en
    Janvier 2011
    Messages
    23
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Client Solution Developer

    Informations forums :
    Inscription : Janvier 2011
    Messages : 23
    Points : 21
    Points
    21
    Par défaut
    Merci pour le code proposé; je vais lire tout ça et essayer de bien le comprendre.

    En attendant, j'ai trouvé une solution qui me permet de résoudre (presque totalement) mon problème (ce sont les lignes 24 et 28 qui me permettent d'actualiser le contenu de mon entry):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    # -*- coding: cp1252 -*-
    from Tkinter import *
     
    class NewEntry(Entry):
        """"""
        def __init__(self, boss =None):
            self.varTk = StringVar()
            Entry.__init__(self, textvariable =self.varTk,
                           validate ='key', vcmd =self.verifClavier)
            self.bind('<Key>', func=self.quelleTouche)
     
        def quelleTouche(self, event):
            """Retiens la touche qui vient d'être tapée."""
            self.touche =event
            self.configure(validate ='key', vcmd =self.verifClavier)
            #print "Le contenu de l'entry est:", self.varTk.get()
     
        def verifClavier(self):
            """Permet uniquement que l'utilisateur tape 1 chiffre dans les entry."""
            try: self.touche    #Si l'entryest modifiée sans avoir appuyé sur...
            except: return True #...une touche, on n'effectue pas la vérification
     
            if self.touche.char in '0123456789':
                self.event_generate(self.touche.char)
                print "Le contenu de l'entry est:", self.varTk.get()
                return True
            elif self.touche.keysym == 'BackSpace':
                self.event_generate('<BackSpace>')
                print "Le contenu de l'entry est:", self.varTk.get()
                return True
            else:
                self.bell()
                return False
     
    #-----------------Si le programme est le programme principal:-------------------
    if __name__ == "__main__":
        fen = Tk()
        ent = NewEntry(boss =fen)
        ent.pack()
        fen.mainloop()

  4. #4
    Membre à l'essai
    Profil pro
    Client Solution Developer
    Inscrit en
    Janvier 2011
    Messages
    23
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Client Solution Developer

    Informations forums :
    Inscription : Janvier 2011
    Messages : 23
    Points : 21
    Points
    21
    Par défaut
    Je viens de remarquer que mon code ne fonctionne pas dans le cas suivant:

    Je sélectionne une partie de mon entry (ex: si l'entry est 12345, je sélectionne le chiffre 3).
    Je tape ensuite un chiffre (ex: 7).
    Le résultat attendu est que la sélection devrait être remplacée par le chiffre tapé (ex: 12745) mais à la place, la chiffre tapé a été inséré deux fois au lieu d'une (ex: 127745.

    Remarques:
    1°) Le problème ne se pose que lorsque j'ai au préalable sélectionné une partie de mon entry (tout se passe bien lorsque le curseur est entre deux chiffres et que rien n'a été sélectionné).
    2°) C'est d'autant plus étrange que le nombre mémorisé (self.varTk.get()) a lui été correctement écrit et n'a donc pas doublé le chiffre inséré...

    Je ne parviens pas à solutionner ce problème...quelqu'un aurait-il une solution? Merci d'avance.

  5. #5
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 398
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 398
    Points : 36 957
    Points
    36 957
    Par défaut
    Salut,

    Ben oui, je reconnais que l'API de validation du widget Entry est quelque peu austère. Bricoler par dessus l'API dans l'espoir de la faire marcher sans se donner la peine d'apprendre à l'utiliser est une démarche à laquelle j'ai quelques difficultés à souscrire.

    Bon courage,
    - W

  6. #6
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonsoir,

    Juste un détail: Le callback de la classe Variable Tkinter n'est pas suffisant ici ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    from Tkinter import *
     
    def callback(*args):
        lab['text'] = strvar.get()
     
     
    fen = Tk()
    lab = Label(fen)
    lab.pack()
    strvar = StringVar()
    strvar.trace("w", callback)
    ent = Entry(fen, textvariable=strvar)
    ent.pack()
    fen.mainloop()
    @+

  7. #7
    Membre à l'essai
    Profil pro
    Client Solution Developer
    Inscrit en
    Janvier 2011
    Messages
    23
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Client Solution Developer

    Informations forums :
    Inscription : Janvier 2011
    Messages : 23
    Points : 21
    Points
    21
    Par défaut
    Voilà une méthode dont j'ignorais l'existence! Ca fonctionne au poil, merci! Pour ceux qui voudraient, voici le code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    # -*- coding: cp1252 -*-
    from Tkinter import *
     
    class NewEntry(Entry):
        """"""
        def __init__(self, boss =None):
            self.varTk = StringVar()
            self.varTk.trace("w", self.callback)
            Entry.__init__(self, textvariable =self.varTk,
                           validate ='key', vcmd =self.verifClavier)
            self.bind('<Key>', func=self.quelleTouche)
     
        def quelleTouche(self, event):
            """Retiens la touche qui vient d'être tapée."""
            self.touche =event
            self.configure(validate ='key', vcmd =self.verifClavier)
            #print "Le contenu de l'entry est:", self.varTk.get()
     
        def verifClavier(self):
            """Permet uniquement que l'utilisateur tape 1 chiffre dans les entry."""
            try: self.touche    #Si l'entryest modifiée sans avoir appuyé sur...
            except: return True #...une touche, on n'effectue pas la vérification
     
            if self.touche.char in '0123456789' or\
               self.touche.keysym == 'BackSpace': return True
            else:
                self.bell()
                return False
     
        def callback(self, *args):
            print "l'entry est", self.varTk.get()
     
    #-----------------Si le programme est le programme principal:-------------------
    if __name__ == "__main__":
        fen = Tk()
        ent = NewEntry(boss =fen)
        ent.pack()
        fen.mainloop()

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 398
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 398
    Points : 36 957
    Points
    36 957
    Par défaut
    Salut,

    Juste pour info la documentation de Tk dit explicitement:

    In general, the textVariable and validateCommand can be dangerous to mix. Any problems have been overcome so that using the validateCommand will not interfere with the traditional behavior of the entry widget. Using the textVariable for read-only purposes will never cause problems. The danger comes when you try set the textVariable to something that the validateCommand would not accept, which causes validate to become none (the invalidCommand will not be triggered). The same happens when an error occurs evaluating the validateCommand.
    La fonction des textvariable est de permettre d'avoir une variable partagée entre plusieurs widgets et l'application. La mise à jour de l'affichage associé étant "automatique" // et réalisée la plupart du temps en C.

    Mais dans le cas de la validation d'une saisie, tant que cette dernière n'est pas terminée inutile pour le reste de l'application d'aller voir ce qu'il se passe et encore moins déclencher des tas de traitement à chaque modification.

    Pour utiliser un vcmd _et_ un textvariable permettant de valider que les "entrées" sont dans '0123456789' on peut partir du code suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import tkinter as tk
    from tkinter.constants import *
    from string import digits
     
    class ValidateEntry(tk.Entry):
        def __init__(self, master=None, **kwds):
            cvar = kwds.pop('textvariable', None)
            super().__init__(master, validate='key', validatecommand=self._validate, **kwds)
            vcmd = (self.register(self._validate), '%d', '%s', '%S', '%P')
            self.config(vcmd=vcmd)
            self.config(textvariable=cvar)
     
        def _validate(self, action, old, ins, new):
           log.debug('action: %s, old: %s, ins: %s, new: %s' % (action, old, ins, new))
           is_valid = True
           if ins and ins not in digits:
                   self.bell()
                   is_valid = False
           return is_valid
    La vcmd _validate ignore si l'argument passé à Entry est "text" ou "textvariable", son boulot est juste d'assurer que les caractères insérés sont bien dans '0123456789' (ce qui est différent de "être un entier").

    Utilisons la chose...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    if __name__ == "__main__":
        import logging
        logging.basicConfig(level=logging.DEBUG)
        log = logging.getLogger('test')
     
        root = tk.Tk()
     
        cvar = tk.StringVar(value='')
        entry = ValidateEntry(root, textvariable=cvar, name='entry')
        entry.pack(side=TOP, fill=X)
     
        result = tk.Label(root, textvariable=cvar, name='result')
        result.pack(side=TOP, fill=X)
        root.mainloop()
    cvar est un tk.StringVar passé à entry/ValidateEntry et à result/tk.Label comme textvariable. A partir du moment ou ValidateEntry assure que ce qui est dans l'entry est une suite de caractères dans '0123456789', cvar.get et entry.get retournent la même chose.

    Le stringVar et la textvariable font que la mise à jour entre entry et result sont automatiques.

    self.bind('<Key>', ...

    Permet d'attraper le caractère "avant" que le widget ne le traite... (car les binding d'un widget sont "class").

    Un des intérêts pourrait être de forcer la saisie en caractères majuscules i.e. chaque fois qu'on voit passer un ascii lowercase on va le transformer en uppercase.

    Exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import tkinter as tk
    import string
     
    def to_upper(event):
        ch = event.char
        if ch and ch in string.ascii_lowercase:
            event.widget.event_generate('<Key>', keysym=ch.upper(), when='head') 
            return "break"  # cancel current event.
     
    if __name__ == "__main__":
        root = tk.Tk()
        entry = tk.Entry(root)
        entry.bind('<Key>', to_upper)
        entry.pack()
        root.mainloop()
    Bon courage,
    - W

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [C#] Imprimer le contenu d'une fenêtre avec ascenseur
    Par biglong dans le forum Windows Forms
    Réponses: 6
    Dernier message: 06/08/2007, 23h29
  2. Réponses: 4
    Dernier message: 23/07/2006, 20h42
  3. [CSS] Probleme avec le contenu d'une boite avec IE
    Par Seth77 dans le forum Mise en page CSS
    Réponses: 4
    Dernier message: 19/07/2006, 11h52
  4. Réponses: 7
    Dernier message: 13/03/2006, 15h39
  5. modifier le contenu d'une table avec innerHTML
    Par francon81 dans le forum Général JavaScript
    Réponses: 8
    Dernier message: 31/05/2005, 09h02

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo