Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 20/11/2019, à 23:01

mandeb

[résolu] tkinter : création dynamique de boutons

Bonsoir,
Je prépare un petit jeu qui créera une fenêtre remplie de boutons dont un seul permettra de quitter l'appli  ! Le niveau de difficulté sera le nombre de boutons affichés (10 ça ira vite, 300 ça permettra de tester les nerfs du joueur tongue)
On est bien d'accord, c'est pas le jeu du siècle, ça me sert juste de prétexte pour manipuler la création de widgets "à la volée" pour apprendre.
la partie création fonctionne (à la mise en page prêt, à voir plus tard) mais je bloque sur la récupération de l'identité du bouton cliqué. J'utilise une fonction lambda pour passer le nom du bouton en paramètre et j'ai un message d'erreur (cf ci-dessous).

Dans le code ci dessous : création de 20 boutons en dynamique (0 à 19) et pour test un bouton n°25 lui codé "en dur" pour vérification de ma syntaxe. Le numéro 25 fonctionne comme prévu, mais pour les autres il y a "un loup" quelque part avec un message d'erreur qui ne montre pas clairement où ça pêche (un self non reconnu?). J'ai testé diverses syntaxes mais rien ne fonctionne. La chaîne construite pour exec est conforme à celle du bouton 25 qui elle passe bien. Je ne sais pas si c'est un problème de syntaxe à la construction de la commande (genre cote mal placées) ou une erreur d'attributs POO.

Si ça inspire quelqu'un... merci d'avance.

#!/usr/bin/env python3
#!-*- coding: UTF8 -*-


import tkinter as tk
#---------------------------------------------------------------------------------------------------
class App(tk.Tk):## FENETRE PRINCIPALE
    def __init__(self):
        super().__init__()
               
        window_height = 700
        window_width = 100
        
        #widgets  
        nb=20
        for i in range(nb):
            n=str(i)
            exec("self.btn"+n+"=tk.Button(self,text="+n+")")
            exec("self.btn"+n+".pack()")
            exec("self.btn"+n+".configure(command=lambda b=self.btn"+n+": self.QuiClick(b))")
#            print("self.btn"+n+".configure(command=lambda b=self.btn"+n+": self.QuiClick(b))")
            
        self.btn25=tk.Button(self,text="25", command=lambda: self.QuiClick("25"))
        self.btn25.pack()
             
        self.btn100=tk.Button(self,text="Quitter", command=self.sortie)
        self.btn100.pack()

    def QuiClick(self,button_click):#oriente selon le bouton clické
        print("click n° : "+ button_click)

                
    def sortie(self):
        self.destroy()

#===================================================================================================  
if __name__ == "__main__":## BOUCLE PRINCIPALE
    app=App()
    app.mainloop()

avec le message suivant lorsqu'on clique sur les boutons 0 à 19.

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1705, in __call__
    return self.func(*args)
  File "<string>", line 1, in <lambda>
NameError: name 'self' is not defined

Dernière modification par mandeb (Le 29/11/2019, à 21:57)

Hors ligne

#2 Le 21/11/2019, à 08:42

pingouinux

Re : [résolu] tkinter : création dynamique de boutons

Bonjour,
Je ne peux pas te dire pourquoi le self n'est pas connu dans le exec, mais tu peux t'en sortir en définissant QuiClick en dehors de la classe App :

#!/usr/bin/env python3
#!-*- coding: UTF8 -*-


import tkinter as tk
#---------------------------------------------------------------------------------------------------
def QuiClick(button_click):#oriente selon le bouton clické
    print("click n° : %s"%button_click)

class App(tk.Tk):## FENETRE PRINCIPALE
    def __init__(self):
        super().__init__()

        window_height = 700
        window_width = 100

        #widgets  
        nb=20
        for i in range(nb):
            txt1="self.btn%d=tk.Button(self,text=%d, command=lambda: QuiClick(%d))"%(i,i,i)
            txt2="self.btn%d.pack()"%(i)
            exec(txt1)
            exec(txt2)

        self.btn25=tk.Button(self,text="25", command=lambda: QuiClick("25"))
        self.btn25.pack()

        self.btn100=tk.Button(self,text="Quitter", command=self.sortie)
        self.btn100.pack()

    def sortie(self):
        self.destroy()

#===================================================================================================  
if __name__ == "__main__":## BOUCLE PRINCIPALE
    app=App()
    app.mainloop()

Édité : Autre solution, en ne modifiant que les lignes 20 et 30 de ton script en #1

#!/usr/bin/env python3
#!-*- coding: UTF8 -*-


import tkinter as tk
#---------------------------------------------------------------------------------------------------
class App(tk.Tk):## FENETRE PRINCIPALE
    def __init__(self):
        super().__init__()
               
        window_height = 700
        window_width = 100
        
        #widgets  
        nb=20
        for i in range(nb):
            n=str(i)
            exec("self.btn"+n+"=tk.Button(self,text="+n+")")
            exec("self.btn"+n+".pack()")
            exec("self.btn"+n+".configure(command=lambda: app.QuiClick("+n+"))")
#            print("self.btn"+n+".configure(command=lambda b=self.btn"+n+": self.QuiClick(b))")
            
        self.btn25=tk.Button(self,text="25", command=lambda: self.QuiClick("25"))
        self.btn25.pack()
             
        self.btn100=tk.Button(self,text="Quitter", command=self.sortie)
        self.btn100.pack()

    def QuiClick(self,button_click):#oriente selon le bouton clické
        print("click n° : %s"%button_click)

                
    def sortie(self):
        self.destroy()

#===================================================================================================  
if __name__ == "__main__":## BOUCLE PRINCIPALE
    app=App()
    app.mainloop()

Dernière modification par pingouinux (Le 21/11/2019, à 09:02)

Hors ligne

#3 Le 21/11/2019, à 09:52

mandeb

Re : [résolu] tkinter : création dynamique de boutons

Merci pingouinux, ça fonctionne.
Je note au passage la syntaxe de construction de chaines mode python3, j'ai pas encore le réflexe étant resté à python2.7 jusqu'à présent hmm

Hors ligne

#4 Le 21/11/2019, à 10:07

pingouinux

Re : [résolu] tkinter : création dynamique de boutons

mandeb #3 a écrit :

Je note au passage la syntaxe de construction de chaines mode python3, j'ai pas encore le réflexe étant resté à python2.7 jusqu'à présent

Cette syntaxe est valable aussi en python2.

Hors ligne

#5 Le 29/11/2019, à 21:55

mandeb

Re : [résolu] tkinter : création dynamique de boutons

J'ai (enfin !) trouvé la solution en fouinant sur stackoverflow.com
la ligne

exec("self.btn"+n+".configure(command=lambda b=self.btn"+n+": self.QuiClick(b))")

doit s'écrire :

exec("self.btn{}.configure(command=lambda self=self : self.QuiClick({},{})))".format(i,i,_exit)

C'est le self=self qui débloque tout (les autres différence sont juste une construction "modernisée" de la ligne de commande pour exec().

Le jeu (idiot) fonctionne parfaitement maintenant.

Hors ligne