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 :

Exercice pédagogique POO [Python 3.X]


Sujet :

Tkinter Python

  1. #1
    Membre à l'essai
    Homme Profil pro
    Urbaniste
    Inscrit en
    Février 2020
    Messages
    19
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Bâtiment Travaux Publics

    Informations forums :
    Inscription : Février 2020
    Messages : 19
    Points : 19
    Points
    19
    Par défaut Exercice pédagogique POO
    Bonjour,

    J'ai d'un côté un programme qui fonctionne (pendu), et de l'autre une GUI qui fonctionne également (code ci-dessous, avec un dessin qui ultérieurement sera un pendu).
    Question classique (de ce que j'en ai vu) : comment relier les deux ?


    J'ai lu qu'il y avait l'option MVC, mais c'est un peu une recette toute faite, et ce n'est pas très pédagogique (pour moi en tout cas). J'ai trouvé sur le forum :
    https://www.developpez.net/forums/d9...es-distinctes/

    Ou ces post, mais qui fonctionnent (si j'ai compris) comme de la programmation fonctionnelle :
    https://www.developpez.net/forums/d2...u-tkinter-isn/
    https://www.developpez.net/forums/d2...fichage-entry/

    L'exemple du tuto est également dans une logique de programmation fonctionnelle :
    https://python.developpez.com/cours/.../?page=page_10


    Avant de regarder ce côté "organisation à trois" de la VMC, j'aimerai pouvoir faire communiquer la GUI et le Core de manière bidirectionnelle (et ensuite étendre le fonctionnement à un modèle type MVC). Je pense que c'est une base de révision/progrès sur la POO.

    Il me semble avoir compris que les classes doivent communiquer entre elles. Et j'ai l'impression d'avoir raté quelque chose : les instances recoivent et donnent des données, mais je ne comprend pas comment (comment créer les liens entre les instances ?).

    Dans le sens GUI->Core je récupère les attributs de l'instance de la classe Core. Mais celà ne me parait pas bien "propre" ?
    Dans l'autre sens, je sèche... à part réaliser une programmation fonctionnelle qui ne dit pas son nom (mais je souhaite rester dans une logique POO).

    J'ai un bouton command dans la GUI qui appelle une fonction :
    - dois-je prendre à chaque fois la méthode de la classe Core ?
    - dois-je instancier un objet de la classe Core (ou rendre "Core" en temps que classe mère, l'héritage devant suffire) ?
    - bref, comment créer une fonction "command=input" qui renvoie la valeur dans la bonne case (voir code ci-dessus) ?

    J'aimerai éviter de bricoler en gérant la classe Core comme un module. Comment le faire en POO proprement (ou à la mode Python) ?
    Si je dois être synthétique après ce long message : quelle architecture et quels liens ?

    Merci pour votre aide.

    Code python : 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
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
     
    #!/usr/bin/python3.5
    # -*-coding:utf-8 -*
     
    import tkinter
    from turtle import *
    from couleurs import COLORS
    from random import choice
    import pickle
     
     
    class Jeu():
        def __init__(self, Scores_recuperes = {}, Scores_charges = 0):
            self.Scores_recuperes = Scores_recuperes
            self.Scores_charges = Scores_charges
            self.nom = ''
            self.score_joueur = 0
            self.message = 'En attente'
            self.mot=''
            self.mot_partiel = list(8 * '*')
            self.compteur = 0
            self.lettre = ''
            self.tentativesmax = 0
            self.entree = ''
     
        def identifiant(self):
            if self.nom in self.Scores_recuperes.keys():
                self.score_joueur = self.Scores_recuperes[self.nom]
     
                self.message = 'Votre précédent score était de :' + str(score_joueur)
     
            else:
                self.Scores_recuperes[self.nom] = self.score_joueur
                self.message = 'Vous êtes enregistré' + str(self.nom)
                with open('score', 'wb') as Scores:             # Enregistrement de l'identifiant dans le dictionnaire et dans scores
                    self.Scores_charges = pickle.dump(self.Scores_recuperes, Scores)
            return self.Scores_recuperes, self.score_joueur
     
        def chercher_lettre_dans_mot(self):
            nb_occurences = self.mot.count(self.lettre)
            if nb_occurences == 0 :
                self.message = str(self.lettre) + ' pas dans le mot cherché.'
                return False
            return True
     
        def afficher_mot_partiel(self):
            for i,x in enumerate(self.mot):
                if x == self.lettre : self.mot_partiel[i] = self.lettre
            return self.mot_partiel
     
     
        def chargement_donnees(self):
            with open("donnees.py", "r") as fichier:
                donnees = fichier.read()
            self.mot = choice(donnees.split()[:-1]).lower()
            self.tentativesmax = int(donnees.split()[-1])
            self.compteur = self.tentativesmax + self.score_joueur
            return
     
        def core(self):
     
            while self.compteur >= 0 and self.mot != ''.join(self.mot_partiel):
                self.lettre = '' # Il faut rentrer qqch ici depuis la GUI : str(input("Choix d'une lettre : "))
                self.message = "Il reste {} tentatives.".format(self.compteur)
     
                if self.chercher_lettre_dans_mot() == False:
                    self.compteur -= 1
                    continue
     
                else:
                    self.mot_partiel = self.afficher_mot_partiel()
                    self.message = str(''.join(self.mot_partiel))
     
            if self.mot == ''.join(self.mot_partiel):
                self.message = "Mot trouvé : {} ! Score = {}".format(''.join(mot_partiel), compteur)
                self.Scores_recuperes[self.nom] = compteur
     
            else:
                self.message = "Perdu. Le mot était : " + str(self.mot)
                self.Scores_recuperes[self.nom] = self.compteur+1
     
            with open('score', 'wb') as Scores:
                self.Scores_charges = pickle.dump(self.Scores_recuperes, Scores)
     
     
        def jeux(self, nom):
            # Identification et initialisation du score
            self.nom = nom # il faut rentrer qqch ici depuis la GUI : input("Quel votre nom ? ")
            self.Scores_recuperes, self.score_joueur = self.identifiant()
     
            # Chargement des donnees
            self.chargement_donnees()
     
            # Début du jeu
            self.core()
     
     
    class InterfaceGraphique(tkinter.Tk, Jeu):
        def __init__(self,parent):
            tkinter.Tk.__init__(self)
            Jeu.__init__(self)
            self.a = Jeu() #Instanciation avant l'ouverture de la fenêtre?
            self.fenetre()
     
     
        def fenetre(self, largeur=200, hauteur=200) :
            '''Une zone graphique et plusieurs boutons'''
            self.largeur = largeur
            self.hauteur = hauteur
     
            #Deux frames : frame1 à gauche pour le dessin, et frame2 à droite pour les autres widgets
            #Les frames sont positionnées avec grid
     
     
            #Frame contenant la zone de dessin et initialisation d'un canvas
            #Positionnement de la zone de dessin avec la méthode grid et une seule case (extension pour plus tard)
     
            self.frame1 = tkinter.Frame()
            self.frame1.grid(column=0,row=0)
     
            self.zone_dessin = tkinter.Canvas(self.frame1, width=self.largeur, height=self.hauteur)
            self.zone_dessin.grid(column=0,row=0)
     
     
            #Frame contenant les boutons et labels
            #Positionnement des widgets avec une deuxième méthode grid dans frame2
     
            #frame
            self.frame2 = tkinter.Frame()
            self.frame2.grid(column=1,row=0)
     
            #Boutons
            b_start = tkinter.Button(self.frame2, command = self.RAZ, text = "Rejouer une partie", fg="green")
            b_start.grid(column=0,row=0)
            b_quit = tkinter.Button(self.frame2, command = self.destroy, text = "Quitter", fg="red")
            b_quit.grid(column=0,row=7)       
     
            #Champ texte
                #Variable de contrôle
            self.value = tkinter.StringVar(self)
            self.value.set("Quel est votre nom ?")
                #Champ
            self.entree = tkinter.Entry(self.frame2, textvariable=self.value, width=30)
            self.entree.grid(column=0,row=4)
                #Enregistrement de la saisie
            self.entree.bind("<Return>", self.affichersaisie) 
                #Description du champ
            lab1 = tkinter.Label(self.frame2, text='Résultat de la saisie')
            lab1.grid(column=0,row=5)
                 #Affichage de la saisie
            self.lab2 = tkinter.Label(self.frame2, text=self.value.get())
            self.lab2.grid(column=0,row=6)
     
     
        def affichersaisie(self,event):
            '''Affiche la saisie du widget "entree" dans le label "lab2"'''
            self.lab2["text"] = self.entree.get() # les widgets se comportent comme des dictionnaires ou, sinon, utiliser self.lab2.configure(text = self.entree.get())
            #self.a.jeux('Damien')
            return
     
     
        def RAZ(self):
            ''' Remise à zéro pour une nouvelle partie'''
            pass
     
        def dessiner(self):
            '''Fonction dessiner avec Turtle'''
     
            #Nettoyage de la zone de dessin : supression du canvas et création d'un nouveau
     
            self.zone_dessin.grid_forget()
            self.zone_dessin = tkinter.Canvas(self.frame1, width=self.largeur, height=self.hauteur)
            self.zone_dessin.grid(column=0,row=0)
     
            #Module Turtle
     
            t=RawPen(self.zone_dessin)
     
            #Début du dessin Turtle
     
            x = 0
            L = self.largeur
            H = self.hauteur
            t.up()
            t.goto(-L, H)
            t.pensize(4)
            while x < 2*self.hauteur :
                t.down()
                t.goto(-L, H)
                t.up()
                x += 15
                L = -L
                if L >0 : H = -self.hauteur+x
                else :
                    H = self.hauteur-x
                t.pencolor(choice(COLORS))
     
     
     
     
    if __name__ == "__main__":
        # Création du fichier scores ou chargement si existant
        try:
            with open("score", "rb") as Scores:
                Scores_recuperes = pickle.load(Scores)
        except :
            Scores_recuperes = {}
            with open('score', 'wb') as Scores:
                Scores_charges = pickle.dump(Scores_recuperes, Scores)
     
        #Lancement du jeu ici ?
        '''a = Jeu()
        a.jeux()'''
     
        #Ouverture fenêtre
        app = InterfaceGraphique(None)
        app.title('Appli Pendu')
     
        app.mainloop()

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 333
    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 333
    Points : 36 853
    Points
    36 853
    Par défaut
    Salut,

    Citation Envoyé par Damien_38 Voir le message
    Question classique (de ce que j'en ai vu) : comment relier les deux ?
    La rubrique ALM est le bon endroit pour y chercher des cours sur la POO et apprendre les différents patterns de conceptions, d'architecture,...

    Citation Envoyé par Damien_38 Voir le message
    Il me semble avoir compris que les classes doivent communiquer entre elles. Et j'ai l'impression d'avoir raté quelque chose : les instances recoivent et donnent des données, mais je ne comprend pas comment (comment créer les liens entre les instances ?)
    .

    Dans votre code vous créez une instance de Jeux à l'initialisation de l'interface. Vous auriez pu la créer avant et la passer en paramètre.

    Si vous voulez que l'instance de Jeux puisse appeler les méthodes de l'instance d'interface, vous appelez une méthode de Jeux avec l'instance d'interface en paramètre qui enregistrera l'instance à appeler dans l'autre sens.

    Les autres méthodes sont décrites dans les tutos (ou vous trouverez exemples et exercices corrigés)

    De toutes façons, avec la POO, la réalisation technique est un détail qui vient après la définition des rôles et responsabilités de chaque objet/composant.... qui est arbitraire (c'est vous qui décidez et qui justifiez vos choix) même si vous décidez de le faire suivant un pattern connu.

    - W

  3. #3
    Membre à l'essai
    Homme Profil pro
    Urbaniste
    Inscrit en
    Février 2020
    Messages
    19
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Bâtiment Travaux Publics

    Informations forums :
    Inscription : Février 2020
    Messages : 19
    Points : 19
    Points
    19
    Par défaut
    Bonsoir,

    Merci pour votre réponse.

    Citation Envoyé par wiztricks Voir le message
    La rubrique ALM est le bon endroit pour y chercher des cours sur la POO et apprendre les différents patterns de conceptions, d'architecture,...
    En parcourant cette section ALM, je comprends qu'il faut organiser le squelette du programme avant de taper du code (c'est d'ailleurs indiqué dans les tutos Python). Il y a des méthodes pour cela.
    La conséquence est qu'on peut tout faire, du moment que les liens sont établis correctement entre les objets... ce qui ne m'avance guère à court terme.


    Citation Envoyé par wiztricks Voir le message
    Dans votre code vous créez une instance de Jeux à l'initialisation de l'interface. Vous auriez pu la créer avant et la passer en paramètre.
    Si vous voulez que l'instance de Jeux puisse appeler les méthodes de l'instance d'interface, vous appelez une méthode de Jeux avec l'instance d'interface en paramètre qui enregistrera l'instance à appeler dans l'autre sens.
    Dois-je comprendre qu'il n'y a donc pas de lien d'héritage entre Jeu et InterfaceGraphique ?
    Par ailleurs : instancier Jeu préalablement et appeler une méthode de Jeux avec l'instance d'InterfaceGraphique en paramètre ne risque-t-il pas de créer un "serpent qui se mord la queue" ?
    Pour la fonction "Input" du Core, je devrais appeler une méthode de l'instance d'InterfaceGraphique (qui ne sera pas défini lors de l'instanciation de Jeu, par définition).

    Citation Envoyé par wiztricks Voir le message
    De toutes façons, avec la POO, la réalisation technique est un détail qui vient après la définition des rôles et responsabilités de chaque objet/composant.... qui est arbitraire (c'est vous qui décidez et qui justifiez vos choix) même si vous décidez de le faire suivant un pattern connu.
    J'aurai beau définir que la GUI envoi des infos au Core, et que le Core renvoie les données traitées à la GUI, si je bute sur la technique, cela ne changera rien. Il faut un minimum de savoir faire technique, qu'à l'évidence je n'ai pas.
    Votre réponse m'invite à penser que je fais fausse route... je vais donc suivre plus sagement les tutos MVC.

    Merci tout de même d'avoir tenté de m'éclairer !

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 333
    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 333
    Points : 36 853
    Points
    36 853
    Par défaut
    Salut,

    Citation Envoyé par Damien_38 Voir le message
    J'aurai beau définir que la GUI envoi des infos au Core, et que le Core renvoie les données traitées à la GUI, si je bute sur la technique, cela ne changera rien. Il faut un minimum de savoir faire technique, qu'à l'évidence je n'ai pas.
    Votre réponse m'invite à penser que je fais fausse route... je vais donc suivre plus sagement les tutos MVC.
    Si vous voulez jouer avec la POO, vous devriez ajouter des contraintes à votre code.

    Par exemple, poussez votre classe Jeu dans un module et rendez cette classe utilisable à la fois depuis une interface console et depuis une interface graphique.
    Et pour ajouter un peu de piquant, rendez Jeu asynchrone (via un Thread).

    L'intérêt étant de vous rendre compte quels problèmes cela pose et quelles solutions apportent POO et patterns.

    - W

  5. #5
    Membre à l'essai
    Homme Profil pro
    Urbaniste
    Inscrit en
    Février 2020
    Messages
    19
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Bâtiment Travaux Publics

    Informations forums :
    Inscription : Février 2020
    Messages : 19
    Points : 19
    Points
    19
    Par défaut
    Bonsoir,

    Citation Envoyé par wiztricks Voir le message
    Salut,
    Par exemple, poussez votre classe Jeu dans un module et rendez cette classe utilisable à la fois depuis une interface console et depuis une interface graphique.
    - W
    Merci du conseil, en effet, cela permettra probablement de bien séparer les différentes composantes et de faciliter l'interaction entre tout ceci !

    Citation Envoyé par wiztricks Voir le message
    L'intérêt étant de vous rendre compte quels problèmes cela pose et quelles solutions apportent POO et patterns.
    - W
    C'est certain, c'est formateur : 'il vaut mieux apprendre à pêcher, que de donner du poisson" !

    Alors que je voulais "simplement" faire une pause révision en codant un pendu graphiquement... Je me trouve embarqué dans la découverte d'un pan entier de programmation (et c'est passionnant).
    Il va falloir que je me discipline sur un objectif simple sur ce jeu afin d'aboutir rapidement et de poursuivre la formation (et y revenir ensuite).

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 333
    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 333
    Points : 36 853
    Points
    36 853
    Par défaut
    Salut,

    Citation Envoyé par Damien_38 Voir le message
    C'est certain, c'est formateur : 'il vaut mieux apprendre à pêcher, que de donner du poisson" !
    Pour comprendre les solutions/procédés de construction qu'apporte la POO, il faut comprendre les problèmes qu'on cherche à résoudre.

    Et ce sont des problèmes non techniques qu'on cherche à résoudre: faire travailler plusieurs développeurs sur un même projet, faciliter l'extensibilité et la maintenabilité du code,...

    Alors que je voulais "simplement" faire une pause révision en codant un pendu graphiquement... Je me trouve embarqué dans la découverte d'un pan entier de programmation (et c'est passionnant).
    Quand on débute en programmation, on imagine que çà se résume à écrire des lignes de codes dans un langage quelconque. En fait cette partie là, c'est 20 à 30% du temps d'un projet un peu touffu: on parle plutôt de quelques centaines voire de quelques milliers de lignes de code. Un truc que vous ne pouvez pas écrire en quelques heures/jours sans trop réfléchir en amont.

    Pour y arriver, il va falloir commencer par écrire un (ou plusieurs) plan(s) plus ou moins détaillé(s): il s'agit de programmer la construction de livrables (les applications).
    C'est là que les patterns sont importants: ce sont des objets techniques qui mettent des mots sur ce qu'on veut réaliser, des concepts, des abstractions qui permettront à une équipe de programmation (ou a vous même) de partager, mettre en forme.

    Vous débutez, donc vous avez déjà touché aux concepts que sont listes, dictionnaires, chaines de caractères, boucles, fonctions,.... Çà n'existe pas dans la nature et si vous essayer d'expliquer çà à quelqu'un qui n'a pas fait l'effort de les apprendre, il vous écoutera poliment mais n'y pigera pas grand chose.

    Citation Envoyé par Damien_38 Voir le message
    Il va falloir que je me discipline sur un objectif simple sur ce jeu afin d'aboutir rapidement et de poursuivre la formation (et y revenir ensuite).
    A priori, vous avez un jeu qui fonctionne en mode "console". Si vous voulez avoir la même chose avec une interface graphique, partez en vous disant que vous allez écrire un autre programme en repartant à zéro.

    Vous y arriverez plus vite qu'à essayer d'apprendre MVC et autres patterns et vous allez vous frotter à la programmation évènementielle qui est à voir côté technique à apprendre.

    Ceci dit vous faites un peu comme vous voulez mais sachez qu'apprendre à programmer, c'est 3 à 5 ans de formation histoire de savoir de quoi on parle et la chance de travailler ensuite sur des projets intéressants pour mettre en pratique (et comprendre).

    Ne soyez pas "pressé" et émerveillez vous.

    - W

  7. #7
    Membre à l'essai
    Homme Profil pro
    Urbaniste
    Inscrit en
    Février 2020
    Messages
    19
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Bâtiment Travaux Publics

    Informations forums :
    Inscription : Février 2020
    Messages : 19
    Points : 19
    Points
    19
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Ne soyez pas "pressé" et émerveillez vous.
    - W
    J'ai donc pris mon temps... et ça en a valu la peine !

    J'arrive à quelque chose qui me convienne, par tatonnements successifs :
    1- Mise des lignes du coeur de programme dans la fonction appelée par "bind" (donc dans la GUI);
    2- Dans un second temps, un fois que j'avais les idées plus claires, intégration de ce coeur dans une fonction de ma classe "Jeu", avec passage des variables en paramètre puis récupération des données en sortie.

    Même si ce n'est pas Byzance, la deuxième solution me se semble plus propre (en tout cas plus conforme avec ce que je souhaitais initialement).
    Petit bémol, je ne suis pas allé au bout de la logique pour les paramètres des méthodes (exemple : def chercher_lettre_dans_mot(self)). J'ai en effet travaillé avec les variables de la classe... Que font usuellement les programmeurs plus chevronés ?

    Petit bonus perso pour la fin : la mise en pratique de l'itérateur. Je n'y avais pas pensé au début. Cela a été plus simple que je ne l'imaginais.

    Merci pour vos conseils

    Ci-dessous mon code:

    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
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    #!/usr/bin/python3.5
    # -*-coding:utf-8 -*
     
    import tkinter
    from turtle import *
    from couleurs import COLORS
    from random import choice
    import pickle
     
     
    class Jeu():
        def __init__(self, Scores_recuperes = {}, Scores_charges = 0):
            self.Scores_recuperes = Scores_recuperes
            self.Scores_charges = Scores_charges
            self.nom = ''
            self.score_joueur = 0
            self.message = ''
            self.message_lettre = ''
            self.mot=''
            self.mot_partiel = list(8 * '*')
            self.compteur = 0
            self.lettre = ''
            self.tentativesmax = 0  
     
     
        def identifiant(self,nom):
            self.nom = nom
            if self.nom in self.Scores_recuperes.keys():
                self.score_joueur = self.Scores_recuperes[self.nom]
                self.message = 'Votre précédent score était de : ' + str(self.score_joueur)
     
            else:
                self.Scores_recuperes[self.nom] = self.score_joueur
                self.message = 'Vous êtes enregistré ' + str(self.nom)
                with open('score', 'wb') as Scores:             # Enregistrement de l'identifiant dans le dictionnaire et dans scores
                    self.Scores_charges = pickle.dump(self.Scores_recuperes, Scores)
     
     
        def chercher_lettre_dans_mot(self):
            nb_occurences = self.mot.count(self.lettre)
            if nb_occurences == 0 :
                self.message_lettre = str(self.lettre) + ' pas dans le mot cherché.'
                return False
            self.message_lettre = str(self.lettre) + ' dans dans le mot cherché.'
            return True
     
     
        def afficher_mot_partiel(self):
            for i,x in enumerate(self.mot):
                if x == self.lettre : self.mot_partiel[i] = self.lettre
            return self.mot_partiel
     
     
        def chargement_donnees(self):
            with open("donnees.py", "r") as fichier:
                donnees = fichier.read()
            self.mot = choice(donnees.split()[:-1]).lower()
            self.tentativesmax = int(donnees.split()[-1])
            self.compteur = self.tentativesmax + self.score_joueur
            return
     
     
        def dessiner(self, canvas_tkinter, hight, width):
            '''Fonction dessiner avec Turtle'''
            #Module Turtle
            self.t=RawPen(canvas_tkinter)
            self.t.pensize(4)
            self.t.pencolor(choice(COLORS))
            self.t.hideturtle()
            self.t.speed('fastest')
            self.X0 = -width/2
            self.Y0 = -hight/2
     
            #Début du dessin Turtle
            self.t.up()
            self.t.goto(self.X0+175, self.Y0)
            self.t.down()
            #pilier
            self.t.goto(self.X0+175, self.Y0+180)
            yield # utilisé avec next comme itérateur sur gibet=dessiner()
            #potence
            self.t.goto(self.X0+90, self.Y0+180)
            yield
            #corde
            self.t.goto(self.X0+90, self.Y0+150)
            self.t.up()
            yield
            #tête
            self.t.goto(self.X0+90, self.Y0+130)
            self.t.down()
            self.t.circle(10)
            self.t.up()
            yield
            #buste
            self.t.goto(self.X0+75, self.Y0+130)
            self.t.down()
            self.t.forward(30)
            self.t.right(90)
            self.t.forward(60)
            self.t.right(90)
            self.t.forward(30)
            self.t.right(90)
            self.t.forward(60)
            self.t.up()
            yield
            #bras gauche
            self.t.goto(self.X0+75, self.Y0+120)
            self.t.down()
            self.t.goto(self.X0+55, self.Y0+75)
            self.t.up()
            yield
            #bras droit
            self.t.goto(self.X0+105, self.Y0+120)
            self.t.down()
            self.t.goto(self.X0+125, self.Y0+75)
            self.t.up()
            yield
            #jambe droite
            self.t.goto(self.X0+100, self.Y0+70)
            self.t.down()
            self.t.goto(self.X0+125, self.Y0+30)
            self.t.up()
            yield
            #jambe gauche
            self.t.goto(self.X0+80, self.Y0+70)
            self.t.down()
            self.t.goto(self.X0+55, self.Y0+30)
            self.t.up()
            yield
            if self.compteur == 0 : yield
     
     
        def core(self, lettre = ''):
            self.lettre = lettre
     
            if self.chercher_lettre_dans_mot() == True:
                self.mot_partiel = self.afficher_mot_partiel()
                if self.mot == ''.join(self.mot_partiel):
                    self.message = "Mot trouvé : {} ! Score = {}".format(''.join(self.mot_partiel), self.compteur)
                    return (self.message, self.mot_partiel, self.message_lettre, 1) # Envoi un tuple (lab3, lab3, lab4, réponse 0/1)
                else:
                    return (self.message, self.mot_partiel, self.message_lettre, 1)
     
            else:
                self.compteur -= 1
                if self.compteur == 0:
                    self.message = "Perdu. Le mot était : " + str(self.mot)
                    return (self.message, self.mot_partiel, self.message_lettre, 0)
                else:
                    self.message = "Il reste {} tentatives.".format(self.compteur)
                    return (self.message, self.mot_partiel, self.message_lettre, 0)
     
            # Mise à jour des scores (0 ou nombre de tentative restante
            self.Scores_recuperes[self.nom] = self.compteur
            with open('score', 'wb') as Scores:
                    Scores_charges = pickle.dump(Scores_recuperes, Scores)
     
     
     
    class InterfaceGraphique(tkinter.Tk):
        def __init__(self):
            tkinter.Tk.__init__(self)
     
            #Ouverture fenêtre
            self.fenetre()
            self.title('Appli Pendu')
            self.mainloop()
     
     
        def fenetre(self, largeur=200, hauteur=200) :
            '''Une zone graphique et plusieurs boutons'''
            self.largeur = largeur
            self.hauteur = hauteur
     
            #Deux frames : frame1 à gauche pour le dessin, et frame2 à droite pour les autres widgets
            #Les frames sont positionnées avec grid
     
            #Frame contenant la zone de dessin et initialisation d'un canvas
            self.frame1 = tkinter.Frame()
            self.frame1.grid(column=0,row=0)
     
            #Positionnement de la zone de dessin avec la méthode grid et une seule case (extension pour plus tard)
            self.zone_dessin = tkinter.Canvas(self.frame1, width=self.largeur, height=self.hauteur)
            self.zone_dessin.grid(column=0,row=0)
            # Instanciation de dessiner() pour itérer
            self.gibet = a.dessiner(self.zone_dessin, self.largeur, self.hauteur)
     
     
            #Frame contenant les boutons et labels
            #Positionnement des widgets avec une deuxième méthode grid dans frame2
     
            #frame
            self.frame2 = tkinter.Frame()
            self.frame2.grid(column=1,row=0)
     
            #Boutons
     
            b_quit = tkinter.Button(self.frame2, command = self.destroy, text = "Quitter", fg="red")
            b_quit.grid(column=0,row=11)       
     
            #Champ texte nom
                #Variable de contrôle
            self.value = tkinter.StringVar(self)
            self.value.set("Quel est votre nom ?")
                #Champ
            self.entree = tkinter.Entry(self.frame2, textvariable=self.value, width=30)
            self.entree.grid(column=0,row=1)
                #Enregistrement de la saisie
            self.entree.bind("<Return>", self.affichersaisie) 
                #Description du champ
            #self.lab1 = tkinter.Label(self.frame2, text='Résultat de la saisie')
            #self.lab1.grid(column=0,row=2)
                 #Affichage de la saisie
            self.lab2 = tkinter.Label(self.frame2, text=self.value.get())
            self.lab2.grid(column=0,row=3)
     
            #Champ texte lettre
                #Variable de contrôle
            self.value2 = tkinter.StringVar(self)
            self.value2.set("Choix d'une lettre ?")
                #Champ
            self.entree2 = tkinter.Entry(self.frame2, textvariable=self.value2, width=30)
            self.entree2.grid(column=0,row=4)
                #Enregistrement de la saisie
            self.entree2.bind("<Return>", self.affichersaisie2) 
                #Description du champ
            self.lab3 = tkinter.Label(self.frame2, text='')
            self.lab3.grid(column=0,row=5)
                 #Affichage de la saisie
            self.lab4 = tkinter.Label(self.frame2, text='')
            self.lab4.grid(column=0,row=6)
     
     
        def affichersaisie(self,event):
            #Charge les identifiants et compare si joueur déjà inscrit (précédent score)
            a.identifiant(self.entree.get())
     
            #Charge les données pour un nouveau jeu (y compris ancien score)
            a.chargement_donnees()
     
            #Affiche le précdent score ou message (0 si nouveau ou dernière partie perdue)
            self.lab2["text"] = a.message # les widgets se comportent comme des dictionnaires ou, sinon, utiliser self.lab2.configure(text = self.entree.get())
            return
     
     
        def affichersaisie2(self,event):
            '''Envoie la lettre saisie dans le programme pour traitement'''
            #Permet de déclencher l'appel à la fonction gibet si mauvaise réponse
            self.reponse = 0
     
            #Affecte le tuple de réponse aux labels
            (self.lab2["text"],self.lab3["text"],self.lab4["text"], self.reponse) = a.core(self.entree2.get())
     
            #Si mauvaise réponse (valeur self.reponse = 0) alors itérer sur dessiner() pour afficher le gibet 
            if self.reponse == 0 : next(self.gibet)
     
     
     
    if __name__ == "__main__":
        # Création du fichier scores ou chargement si existant
        # Instanciation de la classe "Jeu" pour pouvoir être manipuler dans la GUI
     
        try:
            with open("score", "rb") as Scores:
                Scores_recuperes = pickle.load(Scores)
                a = Jeu(Scores_recuperes, Scores_charges=0)
        except :
            Scores_recuperes = {}
            with open('score', 'wb') as Scores:
                Scores_charges = pickle.dump(Scores_recuperes, Scores)
                a = Jeu(Scores_recuperes, Scores_charges=0)
     
        #Lancement InterfaceGraphique ici
        GUI = InterfaceGraphique()

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 333
    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 333
    Points : 36 853
    Points
    36 853
    Par défaut
    Salut,

    Une petite lecture en diagonale de votre code: vous avez juste 2 classes (Jeu et Interface) et une instance de chacune de ces classes.
    Ce n'est pas interdit, mais les premiers pas de la POO, c'est fabriquer un moule (la classe) qui va permettre de fabriquer autant d'objets qu'on veut ayant un état initial et un comportement identique.

    Jeu a une méthode dessine qui met à jour l'affichage. Ca serait mieux qu'il ignore le type d'interface avec laquelle "interface" interagit. Si demain vous remplacez l'interface tkinter par Qt, ce sera à ré-écrire.

    - W

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

Discussions similaires

  1. [Débutant] Exercices corrigés POO en C#
    Par amhana13 dans le forum C#
    Réponses: 0
    Dernier message: 14/05/2019, 14h34
  2. exercice java poo
    Par Amenimiya dans le forum Débuter avec Java
    Réponses: 1
    Dernier message: 08/01/2017, 20h22
  3. [Débutant] Quelques question exercice prog POO
    Par Seb2913 dans le forum C#
    Réponses: 2
    Dernier message: 27/03/2015, 23h04
  4. Besoin d'aide sur un exercice en POO
    Par scriptkiddie dans le forum Débuter
    Réponses: 4
    Dernier message: 24/06/2013, 09h56

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