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 :

Récupérer le bouton qui vient d'être clické


Sujet :

Tkinter Python

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2009
    Messages : 8
    Par défaut Récupérer le bouton qui vient d'être clické
    Bonjour à tous,

    Avant tout, je précise que je ne suis qu'un amateur débutant avec python et Tkinter mais je me permet de poster sur ce site dont j'apprécie l'efficacité.

    Alors voilà, j'ai commencé un petit jeu de morpion en guise d'exercice. je ne fais qu'un mode 2 joueurs, pas d'IA.
    Au niveau de la structure du code, je trace la grille de 3X3 boutons dans le main du programme, et je crée un objet d'une classe "joueurs" qui représente les 2 joueurs.
    mais quand j'initialise les 9 boutons:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    for num in range(9):
      caseBouton[num] = Button(frame, text =' ', width = 4, height = 4, command = joueur.clicked)
      caseBouton[num].grid(row = a, column = b)
      b = b + 1
      if b == 3:
        b = 0
        a = a+1
    on peut voir que renvois la méthode clicked(event) de la classe joueurs au niveau de la commande du bouton. Mais voilà, je ne sais pas du tout comment, dans la méthode clicked(event), récupérer le bouton qui a été clické. Par exemple si le paramètre "event" représentait ce bouton, je pourrais faire dans cette méthode quelque chose du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class joueurs:
      def __init__(self):
        self.joueur = [1,2]
      def clicked(event):
        event.configure(text ="X") #simplifié par rapport à ce que je dois vraiment faire
    j'ai du mal à m'expliquer clairement, j'espère que vous comprenez.

    merci d'avance,

    merodrem

  2. #2
    Membre émérite
    Homme Profil pro
    heu...
    Inscrit en
    Octobre 2007
    Messages
    648
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : heu...

    Informations forums :
    Inscription : Octobre 2007
    Messages : 648
    Par défaut
    Je viens juste d'en parler dans le précédent thread .

    Plusieurs choses, a part de rare cas (explicitement indiqué comme tels dans le code) , le premier argument d'une méthode (fonction dans une classe) sera toujours une reference de l'instance, ce paramètre est nommé self par convention, mais pourrait avoir un tout autre nom que ca ne changerait rien... par exemple dans ta méthode clicked, l'instance se passera elle-même automatiquement en tant que premier argument (ici event, qui est d'ailleurs le seul argument).

    Première problématique : tu stockes tes boutons dans un liste et c'est très biens. mais chacun d'entre eux doit appeler la même méthode, comment alors cette dernière peut savoir qui fait appel à elle ?
    La solution est est simple, une fois qu'on la connais. Le but est qu'un bouton appelle la méthode clicked en lui passant un paramètre qui ici serait l'index de sa place dans la liste de boutons.
    "Oui, mais normalement on peut pas !"
    En fait si, mais il faut ruser grâce à lambda, lambda permet de créer de petites fonction ex :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    >>> mul=lambda a,b: a*b
    >>> mul(3,2)
    6
    On utilisera donc lambda pour parvenir à identifier quel est le bouton qui appelle clicked

    Deuxième problématique : comment savoir quel joueurs à cliqué sur un bouton ?
    Je comprend ta démarche en mettant clicked en tant que méthode de la classe Joueur, c'est un raisonnement assez naturel, mais qui malheureusement ne convient pas le mieux pour cette situation... La solution que je te propose c'est d'avoir un variable globale JOUEUR, qui indique qui est en train de jouer, comme ça quand clicked est appelé, il sait quel est le bouton qui l'appelle et lequel des deux à cliqué sur un bouton...

    donc voici une solution possible :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    JOUEURS=False #False indiquera le joueur n°1 et True le n°2
    caseBouton=[]
    nrows,ncols=3,3
    for num in range(9):
      caseBouton[num] = Button(frame, text =' ', width = 4, height = 4, command = lambda idx=num: clicked(num))
      caseBouton[num].grid(row = num/nrows, column = num%ncols)
     
    def clicked(idx):
        global JOUEUR
        caseBouton[idx]['text']='X' if JOUEUR else 'O'
        JOUEUR=not JOUEUR

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2009
    Messages : 8
    Par défaut
    salut!
    ok ok merci de ton aide. Je ne connaissais pas lambda, comme ça je n'ai pas perdu ma journée
    pour la deuxième problématique, je pensais faire un attribut servant de "flag" qui altèrne entre 2 valeurs selon le le joueur à qui est le tour. Mais finalement, ce n'est pas si différent de ta variable globale.
    Sinon, n'y a-t-il pas moyen que clicked reste une méthode de la classe "joueurs"?
    si je fais ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
      def clicked(self, idx):
        caseBouton[idx].configure(text = "X")#test...
    idx prend la valeur 8 quoi qu'il arrive. Je suppose que le problème vient de l'argument par défaut self?

    Remarque: si je fais clicked sous forme de fonction comme tu l'as fait tout marche parfaitement, mais c'est juste que par principe je préfère la POO à l'impérative.

  4. #4
    Membre émérite
    Homme Profil pro
    heu...
    Inscrit en
    Octobre 2007
    Messages
    648
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : heu...

    Informations forums :
    Inscription : Octobre 2007
    Messages : 648
    Par défaut
    Passer par une classe pour cette situation ne peut convenir que si effectivement, l'instance de la classe joueur regroupe les deux joueurs, et non une instance par joueur - c'est possible, mais il faudrait garder une variable globale indiquant à qui est le tour de jouer, et le check de cette variable ne serait donc plus dans la fonction clicked mais dans la fonction lambda, c'est une question de goût-.

    pour la deuxième problématique, je pensais faire un attribut servant de "flag" qui altèrne entre 2 valeurs selon le le joueur à qui est le tour. Mais finalement, ce n'est pas si différent de ta variable globale.
    En effet

    Sinon, n'y a-t-il pas moyen que clicked reste une méthode de la classe "joueurs"?
    Bien sûr

    si je fais ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    def clicked(self, idx):
        caseBouton[idx].configure(text = "X")#test...
    idx prend la valeur 8 quoi qu'il arrive. Je suppose que le problème vient de l'argument par défaut self?
    Ca viendrait plutôt des paramètres de lambda, si je voyais la boucle dans laquelle tu crée tes boutons (et donc la fonction lambda), je devrais pouvoir te dire ce qui cloche

    Si tu tiens à réaliser ce code plus POO, je verrais plus une classe Bouton, qu'une classe joueur, je trouve que la structure est moins alambiquée :
    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
     
    class Main(Frame):
        def __init__(self, num_cases, parent=None)
            Frame.__init__(self,parent)
            self.cases=[]
            self.joueur=False
            racine=int(num_case**0.5) #en partant du principe que la grille carrée (3x3,4x4, etc...)
            r=c=0
            for idx in xrange(num_cases):
                if idx : r,c=idx/racine, idx%racine
                self.cases.append(Bouton(idx,self))
                self.cases[-1].grid(row=r, column=c)
            self.pack()
     
    class Bouton(Button):
        def __init__(self, idx, parent=None):
            Button.__init__(self,parent, command=self.clicked)
            self.idx=idx
        def clicked(self):
            self['text']='X' if self.parent.joueur else 'O'
            self.parent.joueur= not self.parent.joueur
     
    fen=Tk()
    main=Main(9,fen)
    fen.mainloop()
    Si tu comprend ce que j'ai fait avec les appels d'__init__ des classes parent depuis la méthode __init__ de l'objet défini, tu peux passer, sinon ça devrait t'aider à comprendre :
    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
    >>> classe A(object):
        def foo(self): print 'foo'
     
    >>> class B(A):
        def foo(self):
            A.foo(self)
            print 'c\'est vraiment fou !'
     
    >>> a=A()
    >>> a.foo()
    foo
    >>> b=B()
    >>> b.foo()
    foo
    c'est vraiment fou !

Discussions similaires

  1. Réponses: 1
    Dernier message: 13/10/2008, 10h59
  2. Réponses: 10
    Dernier message: 30/06/2008, 21h04
  3. [MySQL] Récupérer l'ID d'une entrée qui vient d'être créée
    Par yongblood dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 23/06/2007, 06h14
  4. Réponses: 2
    Dernier message: 05/06/2006, 17h17
  5. Récupérer l'id d'un élément qui vient d'être inséré
    Par sg-40 dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 18/06/2004, 10h30

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