#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 )
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
Hors ligne
#4 Le 21/11/2019, à 10:07
- pingouinux
Re : [résolu] tkinter : création dynamique de boutons
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