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 14/08/2020, à 10:22

Plagioclase

[Résolu] Boutons Tkinter Python

Bonjour,
Je suis en train de coder un jeu à choix multiples personnalisable depuis quelques jours. J'ai quasiment fini, mais voilà je suis tombé sur un bogue coriace que je n'arrive pas à résoudre !

Le jeu utilise la bibliothèque graphique Tkinter. En bas de l'écran j'affiche jusqu'à 4 boutons de choix, au minimum il peut y en avoir un seul. À chaque bouton j'associe une commande qui permet d'afficher une page. En fait, chaque page pointe vers 1 à 4 page(s). Pour information, une page est un objet Page. J'ai vérifié et ce n'a pas l'air d'être un problème de référence. Le bogue est le suivant : quand je clique sur un des choix la page qui s'affiche est toujours la page du dernier des choix !

Ci-dessous, le code qui me paraît incriminé et que je simplifie pour y voir plus clair :

class Graphic(Tk):
    def __init__(self):
        # Boutons des différents choix (jusqu'à 4 choix)
        # Liste des boutons de choix pour plus de praticité
        self.choices_button = [Button(self), Button(self), Button(self), Button(self)]
        # Lors du redimensionnement de la fenêtre le texte
        # doit également être redimensionné dans les boutons
        for choice in self.choices_button:
            choice.bind('<Configure>', lambda e: choice.config(wraplength = choice.winfo_width()))

def __choices(self, choices_data, **kwargs):
        """Configure les boutons de choix de la page.
        :param choices_data: Ensemble de couples (message, target_page)
        :type choices_data: set
        """
        
        # Suppression visuelle de tous les boutons
        for choice in self.choices_button: choice.pack_forget()
        
        # Affichage des boutons en fonction des données
        for index, choice in enumerate(choices_data):
            button_msg, target_page = choice
            # Pour débogage
            print("__choices", index, button_msg, target_page.title, target_page)
            self.choices_button[index].config(text=button_msg, command=lambda: self.f(index, target_page))
            self.choices_button[index].pack(fill=X, expand=True)
    
    # Pour débogage
    def f(self, index, target_page):
        print(index)
        self.print_page(target_page)

Le code complet se trouve sur GitHub : Code source du jeu sur GitHub

Le bogue en image...
[img]Écran d'accueil du jeu[/img]
[img]Deuxième page du jeu[/img]
Le résultat en console est le suivant :

$ python3 Graphic.py 
__choices 0 Oui, allons-y. Voici toute l'histoire <Page.Page object at 0x7f72c0fef9d0>
0
__choices 0 Je le tue Une défaite cuisante <Page.Page object at 0x7f72c0fefbb0>
__choices 1 Je le laisse en vie Une belle réussite <Page.Page object at 0x7f72c0fefac0>
__choices 2 Je rentre chez moi Le commencement d'une belle aventure <Page.Page object at 0x7f72c0fef880>
2
__choices 0 Oui, allons-y. Voici toute l'histoire <Page.Page object at 0x7f72c0fef9d0>

Sur la deuxième page, j'ai cliqué sur le choix 0 et pourtant c'est comme si c'était le choix 2 qui avait été exécuté.
Sur la page d'accueil il n'y a pas eu de problème puisqu'il n'y avait qu'un choix.

Toute aide est la bienvenue,
même si cela ne résout pas le problème.

Dernière modification par Plagioclase (Le 14/08/2020, à 16:54)

Hors ligne

#2 Le 14/08/2020, à 16:27

Plagioclase

Re : [Résolu] Boutons Tkinter Python

Conscient que le code donné ci-dessus est un peu trop complet, j'ai isolé le problème dans un code beaucoup plus simple. Maintenant, vous pouvez essayer d'exécuter le programme et le bogue persiste malgré tout dans ces quelques lignes :

from tkinter import *

class Page:
    def __init__(self, title):
        self.title = title
        self.choices = list()
        
class Graphic(Tk):
    def __init__(self, page=None):
        super().__init__()
        self.title("Bogue des boutons")
        self.label = Label(self)
        self.label.pack()
        self.buttons = [Button(self), Button(self), Button(self), Button(self)]
        self.print_page(page)
    def print_page(self, page):
        self.label.config(text=page.title)
        self.draw_buttons(page.choices)
    def draw_buttons(self, choices):
        for button in self.buttons: button.pack_forget()
        for index, choice in enumerate(choices):
            button_msg, target_page = choice
            print(index, button_msg, target_page)
            self.buttons[index].config(text=button_msg, command=lambda: self.print_page(target_page))
            self.buttons[index].pack()
            
page0 = Page("Page 0")
page1 = Page("Page 1")
page2 = Page("Page 2")
page3 = Page("Page 3")

page0.choices.append(("Page 1", page1))
page0.choices.append(("Page 2", page2))
page0.choices.append(("Page 3", page3))

page1.choices.append(("Page 0", page0))
page2.choices.append(("Page 0", page0))
page3.choices.append(("Page 0", page0))

graphic = Graphic(page0)
graphic.mainloop()

Tout se passe comme si c'était le dernier choix qui était pris en compte uniquement et que tous les boutons suivaient la même commande que le dernier choix...
J'avoue que je suis perplexe. En plus, ce ne doit pas être si compliqué à résoudre.

Dernière modification par Plagioclase (Le 14/08/2020, à 16:27)

Hors ligne

#3 Le 14/08/2020, à 16:53

Plagioclase

Re : [Résolu] Boutons Tkinter Python

À force de persévérance j'ai finalement réussi à déboguer ce fichu programme !

self.buttons[index].config(text=button_msg, command=lambda page=target_page: self.print_page(page))

Le problème venait du fait qu'il fallait passer un argument à la fonction anonyme de la commande... Pas facile à détecter. D'ailleurs, j'ai du mal à comprendre pourquoi c'est indispensable.

Dernière modification par Plagioclase (Le 14/08/2020, à 16:54)

Hors ligne