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

Python Discussion :

Tkinter - widget Button : Fonction à appeler lorsque le bouton est cliqué. [Python 3.X]


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé Avatar de scalpel
    Homme Profil pro
    Gestionnaire de parc micro-informatique
    Inscrit en
    Novembre 2008
    Messages
    157
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France, Gard (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Gestionnaire de parc micro-informatique
    Secteur : Service public

    Informations forums :
    Inscription : Novembre 2008
    Messages : 157
    Par défaut Tkinter - widget Button : Fonction à appeler lorsque le bouton est cliqué.
    Bonjour à tous.tes,

    Je ne comprends pas bien l'option "command" du widget "Button"

    • Pour appeler ma fonction (qui n'a pas besoin de paramètre) drawline5(), il me suffit de l'indiquer sans les parenthèses.
      Pour appeler ma fonction (qui a besoin d'un paramètre) drawline1(n), elle s'exécute automatiquement au lancement du programme sans attendre que je clique sur le bouton

    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
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    # charge la bibliothèque graphique tkinter
    from tkinter import *
     
    # Court programme qui dessine les 5 anneaux olympiques dans un rectangle
    # de fond blanc (white). Un bouton « Quitter » doit permettre de fermer la fenêtre.
    # Modifiez le programme ci-dessus en y ajoutant 5 boutons.
    # Chacun de ces boutons provoquera le tracé de chacun des 5 anneaux
     
    # --- définition des fonctions gestionnaires d'événements : ---
     
    def drawline5():
        global x1, y1, x2, y2, coul
        i = 0
        # "Trace les anneaux olympiques dans le canevas can1"
        while i < 3 :
            can1.create_oval(x1,y1,x2,y2,width=4, outline=coul[i])
            x1, x2 = x1+120, x2+120 # modification des coordonnées horizontales pour l'anneau suivant
            i=i+1
        y1, y2 = y1+50, y2+50 # modification des coordonnées verticales pour les anneaux suivants
        x1, x2 = x1-180, x2-180 # modification des coordonnées horizontales pour les anneaux suivants
        while i < 5 :
            can1.create_oval(x1,y1,x2,y2,width=4, outline=coul[i])
            x1, x2 = x1-120, x2-120 # modification des coordonnées horizontales pour l'anneau suivant
            i=i+1
        x1, y1, x2, y2 = 50 ,50 ,150 , 150 # repositionne les coordonnées de départ des anneaux
     
    def drawline1(n):
        global x1, y1, x2, y2, coul
        can1.create_oval(x1,y1,x2,y2,width=4, outline=coul[0])
        # fonction à finaliser
        # ...
     
    #------ Programme principal -------
     
    # les variables suivantes seront utilisées de manière globale :
    coul = ['blue','black','red','green','yellow'] # couleur de contour des anneaux
     
    # Création du widget principal ("maître") :
    x1, y1, x2, y2 = 50 ,50 ,150 , 150 # coordonnées de départ des anneaux
    fen1 = Tk(className='\Anneaux olympiques')
     
    # création des widgets "esclaves" :
    can1 = Canvas(fen1,bg='white',height=250,width=440)
    can1.pack(side=LEFT)
    bou1 = Button(fen1,text='           Quitter            ',command=fen1.quit)
    bou1.pack(side=BOTTOM)
    bou2 = Button(fen1,text='Trace les 5 anneaux',command=drawline5)
    bou2.pack()
    bou3 = Button(fen1,text='   Trace l\'anneau 1   ',command=drawline1(1))
    bou3.pack()
    bou4 = Button(fen1,text='   Trace l\'anneau 2   ',command=drawline1(2))
    bou4.pack()
    bou5 = Button(fen1,text='   Trace l\'anneau 3   ',command=drawline1(3))
    bou5.pack()
    bou6 = Button(fen1,text='   Trace l\'anneau 4   ',command=drawline1(4))
    bou6.pack()
    bou7 = Button(fen1,text='   Trace l\'anneau 5   ',command=drawline1(5))
    bou7.pack()
     
    fen1.mainloop() # démarrage du réceptionnaire d'événements
     
    fen1.destroy() # destruction (fermeture) de la fenêtre
    Merci pour votre aide.

  2. #2
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 743
    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 743
    Par défaut
    Salut,

    Les parenthèses () forcent l'appel à une fonction.
    Exemple (avec un objet qui n'est pas fonction):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    >>> a = 1
    >>> a()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'int' object is not callable
    >>>
    Vous, ce que vous voulez, c'est passer un paramètre à la fonction drawline1 et vous espérez que ça va marcher en écrivant command=drawline1(1). Ce qui provoque l'appel immédiat de la fonction avec le paramètre 1.
    La solution simple est de créer une fonction anonyme avec lambda: command=lambda: drawline1(1). C'est cette fonction anonyme qui sera appelée et qui exécutera alors drawline1(1).

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Expert confirmé
    Avatar de jurassic pork
    Homme Profil pro
    Bidouilleur
    Inscrit en
    Décembre 2008
    Messages
    4 228
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Bidouilleur
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2008
    Messages : 4 228
    Par défaut
    Hello,
    tu peux aussi utiliser la fonction partial du module functools.
    Exemple d'utilisation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    from functools import partial
    bou3 = Button(fen1,text='   Trace l\'anneau 1   ',command=partial(drawline1,1))
    Ami calmant, J.P

  4. #4
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 062
    Par défaut
    On peut aussi éviter des blocs qui visuellement sont redondants,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    bou3 = Button(fen1,text='   Trace l\'anneau 1   ',command=drawline1(1))
    bou3.pack()
    bou4 = Button(fen1,text='   Trace l\'anneau 2   ',command=drawline1(2))
    bou4.pack()
    bou5 = Button(fen1,text='   Trace l\'anneau 3   ',command=drawline1(3))
    bou5.pack()
    bou6 = Button(fen1,text='   Trace l\'anneau 4   ',command=drawline1(4))
    bou6.pack()
    bou7 = Button(fen1,text='   Trace l\'anneau 5   ',command=drawline1(5))
    bou7.pack()
    par une boucle,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for i in range(5):
        Button(fen1, text=f'   Trace l\'anneau {i+1}   ', command=lambda i=i: drawline1(i+1)).pack()

  5. #5
    Membre Expert
    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    1 545
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 1 545
    Par défaut
    Et si, pour une raison, on veut conserver une référence sur les boutons:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    liste_boutons=[]
    for i in range(5):
        liste_boutons.append(Button(fen1, text=f'   Trace l\'anneau {i+1}   ', command=lambda i=i: drawline1(i+1))
        liste_boutons[i].pack()

  6. #6
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 743
    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 743
    Par défaut
    Salut,

    Dans cette histoire, il y a ce qu'on peut faire côté python mais il ne faut pas oublier qu'on peut aussi faire des choses côté GUI.

    Rien n'oblige, par exemple, à utiliser le paramètre command.
    Si on utilise un bind de ButtonPress à la place, on va pouvoir récupérer l'objet Button associé dans l'event passé à la fonction de rappel:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import tkinter as tk
     
    def on_click(w):
        slaves = w.master.pack_slaves()
        print(slaves.index(w))
     
    root = tk.Tk()
    root.bind('<ButtonPress-1>', lambda e: on_click(e.widget))
    for c in 'ABCD':
        tk.Button(root, text=c).pack()
    tk.mainloop()
    Et pour ce qui est de récupérer l'indice du Button dans une liste, "pack" maintient déjà une liste.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  7. #7
    Membre confirmé Avatar de scalpel
    Homme Profil pro
    Gestionnaire de parc micro-informatique
    Inscrit en
    Novembre 2008
    Messages
    157
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France, Gard (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Gestionnaire de parc micro-informatique
    Secteur : Service public

    Informations forums :
    Inscription : Novembre 2008
    Messages : 157
    Par défaut
    Vraiment merci à tous,

    Vos explications on toutes été pour moi complémentaires, j'ai pratiquement réussit à tout comprendre.
    Seule celle qui aborde "l'utilisation d'un bind de ButtonPress" et encore bien trop compliquée pour moi.

    J'ai terminé l'exercice, mon petit programme fonctionne et il est nettement simplifié.
    J'ai utilisé la solution de la fonction anonyme avec lambda: c'est celle qui ma parut la plus simple et surtout la plus abordable pour mon niveau.
    J'ai également utilisé la boucle pour économiser quelques lignes de code, mais je ne suis pas sur d'être capable de la reproduire plus tard sans un exemple à disposition.

    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
    # Programme qui dessine 1 ou les 5 anneaux olympiques dans un rectangle
    # de fond blanc, un bouton « Quitter » permet de fermer la fenêtre et
    # 5 boutons provoquent le tracé de chacun des 5 anneaux.
     
    # charge la bibliothèque graphique tkinter
    from tkinter import *
     
    # fonction qui trace 1 ou les 5 anneaux olympiques dans le canevas can1
    def dessinAnneau(n):  
        if n==1 or n==6 : can1.create_oval(50 ,50 ,150 ,150,width=4, outline='blue') # anneau 1
        if n==2 or n==6 : can1.create_oval(170 ,50 ,270 ,150,width=4, outline='black') # anneau 2
        if n==3 or n==6 : can1.create_oval(290 ,50 ,390 ,150,width=4, outline='red') # anneau 3
        if n==4 or n==6 : can1.create_oval(230 ,100 ,330 ,200,width=4, outline='green') # anneau 4
        if n==5 or n==6 : can1.create_oval(110 ,100 ,210 ,200,width=4, outline='yellow') # anneau 5
     
    #----------------------- Programme principal -----------------------------
     
    # Création du widget principal ("maître")
    fen1 = Tk()
    fen1.title('Anneaux olympiques')
     
    # création des widgets "esclaves"
    can1 = Canvas(fen1,bg='white',height=250,width=440)
    can1.pack(side=LEFT)
    boutQuit = Button(fen1,width=15, text='Quitter',command=fen1.quit)
    boutQuit.pack(side=BOTTOM)
    # boutons de 1 à 5
    for i in range(5):
        Button(fen1,width=15, text=f'Trace l\'anneau {i+1}', command=lambda i=i: dessinAnneau(i+1)).pack()
     
    bout6 = Button(fen1,width=15, text='Trace les 5 anneaux',command=lambda:dessinAnneau(6))
    bout6.pack()
    boutDel = Button(fen1,width=15, text='Efface tout',command=lambda:can1.delete('all'))
    boutDel.pack()
     
    fen1.mainloop() # démarrage du réceptionnaire d'événements
    fen1.destroy() # fermeture de la fenêtre
    PS. Je suis très surpris de ce que l'on peu obtenir avec aussi peu de code, c'est très motivant.

  8. #8
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 062
    Par défaut
    Bonjour,

    Rapidement un petite amélioration algorithmique de votre code pourrait être,

    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
    41
    42
    43
    from tkinter import *
     
    ANNEAUX = [
            { 'x1': 50, 'y1': 50, 'x2': 150, 'y2': 150, 'couleur': 'blue' },
            { 'x1': 170, 'y1': 50, 'x2': 270, 'y2': 150, 'couleur': 'black' },
            { 'x1': 290, 'y1': 50, 'x2': 390, 'y2': 150, 'couleur': 'red' },
            { 'x1': 230, 'y1': 100, 'x2': 330, 'y2': 200, 'couleur': 'green' },
            { 'x1': 110, 'y1': 100, 'x2': 210, 'y2': 200, 'couleur': 'yellow' }
        ]
     
    def dessinAnneaux(n):
        if n == 6:
            for anneau in ANNEAUX:
                dessinAnneau(anneau)
     
        else:
            anneau = ANNEAUX[n-1]
            dessinAnneau(anneau)
     
    def dessinAnneau(anneau):
        can1.create_oval(anneau['x1'], anneau['y1'], anneau['x2'], anneau['y2'], width=4, outline=anneau['couleur'])
     
    fen1 = Tk()
    fen1.title('Anneaux olympiques')
     
    # création des widgets "esclaves"
    can1 = Canvas(fen1,bg='white',height=250,width=440)
    can1.pack(side=LEFT)
    boutQuit = Button(fen1,width=15, text='Quitter',command=fen1.quit)
    boutQuit.pack(side=BOTTOM)
     
    # boutons de 1 à 5
    for i in range(5):
        Button(fen1,width=15, text=f'Trace l\'anneau {i+1}', command=lambda i=i: dessinAnneaux(i+1)).pack()
     
    bout6 = Button(fen1,width=15, text='Trace les 5 anneaux',command=lambda:dessinAnneaux(6))
    bout6.pack()
    boutDel = Button(fen1,width=15, text='Efface tout',command=lambda:can1.delete('all'))
    boutDel.pack()
     
    fen1.mainloop() # démarrage du réceptionnaire d'événements
     
    # fen1.destroy() # fermeture de la fenêtre INUTILE

  9. #9
    Membre confirmé Avatar de scalpel
    Homme Profil pro
    Gestionnaire de parc micro-informatique
    Inscrit en
    Novembre 2008
    Messages
    157
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France, Gard (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Gestionnaire de parc micro-informatique
    Secteur : Service public

    Informations forums :
    Inscription : Novembre 2008
    Messages : 157
    Par défaut
    Citation Envoyé par fred1599 Voir le message
    Bonjour,
    Rapidement un petite amélioration algorithmique de votre code pourrait être,

    Merci c'est très formateur pour moi ce type de retour, mais j'ai eu du mal à comprendre.
    Je l'ai exécuté pour me persuader que cela fonctionnait.

    Super l'idée des paramètres dans une liste

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

Discussions similaires

  1. [E-03] Fonction ChDir lorsque le disque est protege ?
    Par shadok6 dans le forum Macros et VBA Excel
    Réponses: 7
    Dernier message: 18/03/2009, 13h50
  2. Répéter un événement lorsqu'un bouton est enfoncé
    Par Zaki_SDwin dans le forum Langage
    Réponses: 5
    Dernier message: 02/06/2008, 21h29
  3. Repetion d'une fonction lorsqu'un bouton est cliqué
    Par zmatz dans le forum Général Dotnet
    Réponses: 7
    Dernier message: 29/05/2007, 17h21
  4. Réponses: 7
    Dernier message: 19/04/2007, 18h54
  5. Réponses: 3
    Dernier message: 27/03/2006, 10h15

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