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 :

Problème lors de création de menus.


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 Problème lors de création de menus.
    Bonjour,

    lorsque je créé un menu avec sa barre de menu, je désire transmettre un argument vers une fonction avec l'instruction "lambda".

    Aux lignes 19 à 24 de mon code, l'argument est transmis correctement à la fonction "action".
    Aux lignes 13 à 16 de mon code (qui est une écriture plus concise que celle des lignes 19 à 24), l'argument n'est pas transmis correctement à la fonction "action".

    Comment se fait-il qu'une méthode transmette l'argument correctement et pas l'autre?

    Merci d'avance pour vos réponses.

    Voici mon code (attention, j'utilise la version 3.x de Python):

    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
    from tkinter import *
     
    class Application(Frame):
        def __init__(self):
            Frame.__init__(self)
            self.pack()
     
            menuPrincipal = Menubutton(self, text='Fichier')
            menuPrincipal.pack(side =LEFT)
     
            me1 = Menu(menuPrincipal)
            #Méthode concise pour créér la barre de menu mais avec erreur:
            for lab, param in ( ('Débutant 1',(9,9,10)), ('Interm 1',(16,16,40)),
                             ('Expert 1',(30,16,99)) ):
                me1.add_command(label =lab,
                                    command = lambda: self.action(param))
     
            #Méthode plus longue pour créér la barre de menu mais sans erreur:
            me1.add_command(label ='Débutant 2',
                            command = lambda: self.action((9,9,10)))
            me1.add_command(label ='Interm 2',
                            command = lambda: self.action((16,16,40)))
            me1.add_command(label ='Expert 2',
                            command = lambda: self.action((30,16,99)))
     
            menuPrincipal.configure(menu = me1)
     
        def action(self, param):
            print (param)
     
    if __name__ == '__main__':
        Application().mainloop()

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 442
    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 442
    Points : 37 034
    Points
    37 034
    Par défaut
    Salut,
    Le problème est du au manque de "nesting"(*) de la boucle for. De loin, vous voulez associez au lambda le "param" de chaque itération, mais à la sortie on se retrouve avec le "même".

    (*) nom barbare que vous allez comprendre

    Démonstration (sans Tkinter qui n'y est pour rien!)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    items = ["stack", "over", "flow"] 
    map = { } 
     
    for item in items: 
        def new_command(): 
            print(item) 
        map[item] = new_command
     
    map["stack"]() 
    map["over"]() 
    map["flow"]()
    Le manque de "nesting" fait que l'exécution de ce code produit:
    En gros, c'est comme si 'new_command' était une variable "globale" au block "for...next" et non "local" à chaque itération... *
    Donc "variabilisons":
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    for item in items: 
        def item_command(name): 
            def new_command(): 
                print(name) 
            return new_command 
        map[item] = item_command(item)
    item_command est ici une fonction "nested" qui à chaque tour fabriquera une nouvelle fonction stockée dans map... et donc survivra sorti de la boucle.

    Si mes explications ne sont pas trop mauvaises, vous devriez pouvoir écrire vous même la solution dans le cas de la déclaration des actions/fonctions du menu Tkinter.

    Bon courage,
    - 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
    Pas évident pour moi de comprendre en détail tes explications, mais d'après ce que j'ai compris, voici mon code modifié:

    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
    from tkinter import *
     
    class Application(Frame):
        def __init__(self):
            Frame.__init__(self)
            self.pack()
     
            menuPrincipal = Menubutton(self, text='Fichier')
            menuPrincipal.pack(side =LEFT)
     
            me1 = Menu(menuPrincipal)
     
            for lab, param in ( ('Débutant 1',(9,9,10)), ('Interm 1',(16,16,40)),
                                ('Expert 1',(30,16,99)) ):
                def fct(param):
                    return me1.add_command(label =lab,
                                           command = lambda: self.action(param))
                fct(param)
     
            menuPrincipal.configure(menu = me1)
     
        def action(self, param):
            print (param)
     
    if __name__ == '__main__':
        Application().mainloop()
    L'argument est transmis correctement! Mais à ton avis, y aurait-il moyen de faire plus simplement, sans créér une nouvelle fonction 'fct' à l'intérieur de la boucle?

    En tout cas merci beaucoup pour l'éclairage apporté!

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 442
    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 442
    Points : 37 034
    Points
    37 034
    Par défaut
    Citation Envoyé par pieral85 Voir le message
    L'argument est transmis correctement! Mais à ton avis, y aurait-il moyen de faire plus simplement, sans créér une nouvelle fonction 'fct' à l'intérieur de la boucle?
    Ben c'est une fonction.... on peut la mettre ou on veut pourvu qu'on sache lui transporter le "contexte" (param, self, action).
    Tant que cette fonction n'a pas d'utilité à l'extérieur de la boucle... , je préfère l'écrire ainsi.
    -W

  5. #5
    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
    Bonjour,

    C'est effectivement un problème de contexte, que ce soit avec une fonction classique ou lambda. Vous devez donner à votre fonction param au moment de la création (/enregistrement dans command).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
                me1.add_command(label=lab,
                                command=lambda param=param: self.action(param))
                # comprendre lambda p=param: self.action(p)
    lambda est fabuleux avec command, vous devriez tester cela plus en avant.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    fns = []
    for i in range(0, 3):
        fns.append(lambda:i*2)
    for fn in fns:
        print(fn())
    del(fns[:])
     
    for i in range(0, 3):
        fns.append(lambda i=i:i*2)
    for fn in fns:
        print(fn())
    output
    De même vous devriez regarder (il y a des discutions sur le forum à ce sujet) ce qu'il en est pour les valeurs des variables lorsque une fonction est évaluée et lors de son appel. C'est très instructif sur la mécanique interne de la bête.

    @+

  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
    Petit supplément puisque j'ai le temps.

    Dans votre code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
            for lab, param in ( ('Débutant 1',(9,9,10)), ('Interm 1',(16,16,40)),
                             ('Expert 1',(30,16,99)) ):
                me1.add_command(label =lab,
                                    command = lambda: self.action(param))
    Lorsque le code est évalué, à chaque passage dans la boucle for une variable param remplace la précédente avec des valeurs différentes. C'est le but non ?
    On stocke dans command une fonction lambda self.action(param)
    Lors de l'appel de la fonction lambda celle ci utilise la variable param qui est... la dernière de la boucle for.

    Par contre si vous donnez à lambda la valeur de param au moment ou vous stockez la fonction dans command
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    lambda p=param: self.action(p)
    vous aurez bien le résultat recherché.

    Dans le cadre de label=lab c'est bien au moment ou le menu est créé (dans la boucle for) que le string lab est stocké.

  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
    Merci pour les explications!

    Je comprends maintenant l'intérêt (et surtout la sécurité) de mettre les valeurs des paramètres par défaut lorsqu'on utilise l'instruction lambda!

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

Discussions similaires

  1. Problème lors de création d'un .exe sous Borland6
    Par klad13 dans le forum C++Builder
    Réponses: 2
    Dernier message: 23/01/2009, 11h53
  2. problème lors de création d'un projet ASP.Net
    Par fifidante dans le forum ASP.NET
    Réponses: 2
    Dernier message: 16/02/2008, 00h33
  3. [SQL Server 2005]problème lors du création d'un nouvel utilisateur
    Par etoile_de_vie dans le forum MS SQL Server
    Réponses: 4
    Dernier message: 08/08/2007, 17h05
  4. [10G] Problème lors de création de tables et champs
    Par keiserjo dans le forum Oracle
    Réponses: 7
    Dernier message: 28/09/2006, 13h14
  5. problème lors de création d'une vue
    Par gapse dans le forum Oracle
    Réponses: 7
    Dernier message: 26/07/2006, 13h59

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