diff --git a/projet_final/interface.py b/projet_final/interface.py new file mode 100644 index 0000000..0ea1e03 --- /dev/null +++ b/projet_final/interface.py @@ -0,0 +1,266 @@ + +# IMPORTATIONS + +import tkinter as tk +from PIL import Image as Img +from PIL import ImageTk + + +# CONSTANTES + +TAILLE_IMG = 350 +FOND_IMAGE = "#222222" + +VISUEL = { + 'nb caractères':80, + 'nb lignes':9, + 'fond':"#EE7722", + 'écriture':"black", + 'police': ("Courier", 16) + } + +# DECLARATION DES CLASSES PUBLIQUES + +class IHM(): + '''IHM basique permettant de gérer le projet Manoir Hanté + + Idée générale + ------------- + Interface graphique interactive permettant d'afficher : + -> Trois zones de texte : description, actions possibles et caractéristiques ? + -> Deux images : une pour le lieu et une pour un monstre ou autre ? + + Classe IHM + ---------- + La Classe générant l'interface graphique (GUI en anglais) + L'interaction avec le programme gérant les données se fait + via la méthode signaler_evenement() + + Méthodes disponibles + -------------------- + signaler_evenement(self, f) + afficher_txt_1(self, texte:str) + afficher_txt_2(self, texte:str) + afficher_txt_3(self, texte:str) + afficher_img_1(self, fichier_image:str) + afficher_img_2(self, fichier_image:str) + ''' + + def __init__(self): + self.interne = IHM_interne() + + def signaler_evenement(self, f): + self.interne.signaler_evenement(f) + + def afficher_txt_1(self, texte:str): + self.interne.afficher_txt_1(texte) + + def afficher_txt_2(self, texte:str): + self.interne.afficher_txt_2(texte) + + def afficher_txt_3(self, texte:str): + self.interne.afficher_txt_3(texte) + + def afficher_img_1(self, fichier_image:str): + self.interne.afficher_img_1(fichier_image) + + def afficher_img_2(self, fichier_image:str): + self.interne.afficher_img_2(fichier_image) + + +# DECLARATION DES CLASSES PRIVEES (mais que je n'ai pas caché plus que cela) +# Vous pouvez d'ailleurs interagir direction avec elle en notant directement +# ihm =IHM_interne() plutôt que ihm = IHM() si vous voulez. + +class IHM_interne(tk.Tk): + + def __init__(self): + + # Création de l'application en elle-même + tk.Tk.__init__(self) + xmax, ymax = self.obtenir_taille_ecran() + self.geometry(f"{xmax}x{ymax}") + self.title("MON SUPER JEU") + self.configure(bg=FOND_IMAGE) + + + self.frame_g = self.creer_frame_g(FOND_IMAGE) + self.frame_d = self.creer_frame_d(FOND_IMAGE) + + # Création des widgets-labels + self.IMAGE_VIDE = ImageTk.PhotoImage(Img.new("RGB", (TAILLE_IMG, TAILLE_IMG), (50, 50, 50))) + self.image_1 = self.IMAGE_VIDE + self.image_2 = self.IMAGE_VIDE + self.label_img_1 = self.creer_label_img_1(self.frame_g, None) + self.label_img_2 = self.creer_label_img_2(self.frame_g, None) + self.label_txt_1 = self.creer_label_txt_1(self.frame_d, "-", VISUEL) + self.label_txt_3 = self.creer_label_txt_3(self.frame_d, "valeurs", VISUEL) + self.label_txt_2 = self.creer_label_txt_2(self.frame_d, "-", VISUEL) + + # Configuration intiale des widgets (à commenter si vous ne voulez pas d'images au départ + config_depart = ("Ici, on décrit la scène\nSur plusieurs lignes si nécessaire.", "Ici, on donne les choix possibles", None, None, "Caractéristiques aventurier et monstre") + self.configuration_scene(config_depart) + + # Gestion des événements + self.fonction = None + self.bind('', self.gestion_actions) + + # DECLARATION DES METHODES INTERNES : ne pas utiliser depuis l'extérieur du module + + def obtenir_taille_ecran(self) -> tuple: + xmax = self.winfo_screenwidth() + ymax = self.winfo_screenheight() + return xmax, ymax + + def creer_frame_g(self, fond): + zone = tk.Frame(self, bg=fond) + zone.pack(side=tk.LEFT, fill=tk.Y, padx=0, pady=0) + return zone + + def creer_frame_d(self, fond): + zone = tk.Frame(self, bg=fond) + zone.pack(side=tk.RIGHT, expand=True, fill=tk.BOTH, padx=0, pady=0) + return zone + + def creer_label_txt_1(self:tk.Tk, conteneur, texte:str, visuel:dict) -> tk.Label: + auDessus = tk.Label(conteneur, text="DESCRIPTION", fg="#CCCCCC", bg="black", height=3) + auDessus.configure(font=visuel['police']) + auDessus.pack(side=tk.TOP, fill=tk.X, padx=10, pady=10) + + monLabel = tk.Label(conteneur, text=texte) + monLabel.configure(fg=visuel['écriture']) + monLabel.configure(bg=visuel['fond']) + monLabel.configure(font=visuel['police']) + monLabel.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=10, pady=10) + + return monLabel + + def creer_label_txt_2(self:tk.Tk, conteneur, texte:str, visuel:dict) -> tk.Label: + + monLabel = tk.Label(conteneur, text=texte) + monLabel.configure(fg=visuel['écriture']) + monLabel.configure(bg=visuel['fond']) + monLabel.configure(width=visuel['nb caractères']) + monLabel.configure(height=visuel['nb lignes']) + monLabel.configure(font=visuel['police']) + monLabel.pack(side=tk.BOTTOM,fill=tk.BOTH, expand=True, padx=10, pady=10) + + auDessus = tk.Label(conteneur, text="ACTIONS POSSIBLES", fg="#CCCCCC", bg="black", height=3) + auDessus.configure(font=visuel['police']) + auDessus.pack(side=tk.BOTTOM,fill=tk.X, padx=10, pady=10) + + return monLabel + + def creer_label_txt_3(self:tk.Tk, conteneur, texte:str, visuel:dict) -> tk.Label: + monLabel = tk.Label(conteneur, text=texte) + monLabel.configure(fg=visuel['écriture']) + monLabel.configure(bg=visuel['fond']) + monLabel.configure(width=visuel['nb caractères']) + monLabel.configure(height=2) + monLabel.configure(font=visuel['police']) + monLabel.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=10) + + auDessus = tk.Label(conteneur, text="Caractéristiques", fg="#CCCCCC", bg="black", height=3) + auDessus.configure(font=visuel['police']) + auDessus.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=10) + + return monLabel + + def creer_label_img_1(self:tk.Tk, conteneur, fichier_image:str) -> tk.Label: + + if fichier_image: + objet_image_PIL = Img.open(fichier_image) + objet_image_PIL = objet_image_PIL.resize( (TAILLE_IMG, TAILLE_IMG) ) + objet_image_tk = ImageTk.PhotoImage(objet_image_PIL) + self.image_1 = objet_image_tk + else: + self.image_1 = self.IMAGE_VIDE + + zone_image = tk.Label(conteneur, image=self.image_1) + zone_image.configure(bg=FOND_IMAGE) + zone_image.pack(side=tk.TOP, ipadx=10, ipady=10) + + return zone_image + + def creer_label_img_2(self:tk.Tk, conteneur, fichier_image:str) -> tk.Label: + + if fichier_image: + objet_image_PIL = Img.open(fichier_image) + objet_image_PIL = objet_image_PIL.resize( (TAILLE_IMG, TAILLE_IMG) ) + objet_image_tk = ImageTk.PhotoImage(objet_image_PIL) + self.image_2 = objet_image_tk + else: + self.image_2 = self.IMAGE_VIDE + + zone_image = tk.Label(conteneur, image=self.image_2) + zone_image.configure(bg=FOND_IMAGE) + zone_image.pack(side=tk.BOTTOM, ipadx=10, ipady=10) + + return zone_image + + def gestion_actions(self, event): + if self.fonction: + self.fonction(event) + + # DECLARATION DES METHODES D'INTERFACE + + def signaler_evenement(self, f): + '''Lance un appel à la fonction f transmise lors de l'appui sur une touche''' + self.fonction = f + + def configuration_scene(self, configuration_voulue:list): + '''Modifie l'affichage sur le GUI''' + self.afficher_txt_1(configuration_voulue[0]) + self.afficher_txt_2(configuration_voulue[1]) + self.afficher_img_1(configuration_voulue[2]) + self.afficher_img_2(configuration_voulue[3]) + self.afficher_txt_3(configuration_voulue[4]) + + def afficher_txt_1(self, texte:str): + '''Modifie le texte visible dans la zone de texte en haut à gauche''' + #largeur_max_ligne = max(map(len, texte.split('\n'))) + #zone_texte.configure(width=largeur_max_ligne) + self.label_txt_1.configure(text=texte) + + def afficher_txt_2(self, texte:str): + '''Modifie le texte visible dans la zone de texte en haut à gauche''' + #largeur_max_ligne = max(map(len, texte.split('\n'))) + #zone_texte.configure(width=largeur_max_ligne) + self.label_txt_2.configure(text=texte) + + def afficher_txt_3(self, texte:str): + '''Modifie le texte visible dans la zone de texte en haut à gauche''' + #largeur_max_ligne = max(map(len, texte.split('\n'))) + #zone_texte.configure(width=largeur_max_ligne) + self.label_txt_3.configure(text=texte) + + def afficher_img_1(self, fichier_image:str): + '''Modifie le texte visible dans la zone de texte en haut à gauche''' + if fichier_image: + objet_image_PIL = Img.open(fichier_image) + objet_image_PIL = objet_image_PIL.resize( (TAILLE_IMG, TAILLE_IMG) ) + objet_image_tk = ImageTk.PhotoImage(objet_image_PIL) + self.image_1 = objet_image_tk + else: + self.image_1 = self.IMAGE_VIDE + + self.label_img_1.configure(image=self.image_1) + + def afficher_img_2(self, fichier_image:str): + '''Modifie le texte visible dans la zone de texte en haut à gauche''' + if fichier_image: + objet_image_PIL = Img.open(fichier_image) + objet_image_PIL = objet_image_PIL.resize( (TAILLE_IMG, TAILLE_IMG) ) + objet_image_tk = ImageTk.PhotoImage(objet_image_PIL) + self.image_2 = objet_image_tk + else: + self.image_2 = self.IMAGE_VIDE + + self.label_img_2.configure(image=self.image_2) + + + +# PROGRAMME PRINCIPAL + +if __name__ == '__main__': + application = IHM() \ No newline at end of file diff --git a/projet_final/jeu.py b/projet_final/jeu.py new file mode 100644 index 0000000..5e22de3 --- /dev/null +++ b/projet_final/jeu.py @@ -0,0 +1,105 @@ +# Importations + +import interface # Dépendances : PIL(Pillow) et tkinter +import donnees +import sys +import pickle + +# Déclaration des fonctions pour la sauvegarde + +def sauvegarder_jeu(manoir, fichier): + with open(fichier, 'wb') as f: + pickle.dump(manoir, f) + +def charger_jeu(fichier): + with open(fichier, 'rb') as f: + return pickle.load(f) + +# Déclaration des fonctions pour le fichier texte + +def rediriger_sortie_vers_fichier(fichier_sortie: str): + """Redirige la sortie de la console vers un fichier texte.""" + sys.stdout = open(fichier_sortie, "a") + sys.stderr = sys.stdout + +def restaurer_sortie_console(): + """Restaure la sortie de la console après la redirection vers un fichier.""" + sys.stdout.close() + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + +# Déclaration des fonctions + +def ihm_signale_evenement(evenement:'tkinter.Event', manoir:'Manoir', ihm:'IHM') -> None: + """Fonction où on récupère des informations sur l'événément reçu via l'IHM""" + + # On récupère la touche sur laquelle on vient d'appuyer + touche = evenement.char + print(f"\nEVENEMENT RECU : {evenement}") # Permet de voir le vrai événement + print(f"CODE PERSO : {touche}") # L'IHM permet de connaitre la touche utilisée + + # Il faut maintenant gérer l'événement : modifier les données et redemander une affichage à l'IHM + + h = manoir.heros + + if touche == 'X' or touche == 'x': + sauvegarder_jeu(manoir, "sauvegarde_jeu.pkl") + print("Jeu sauvegardé.") + + elif touche == 'V' or touche == 'v': + try: + manoir = charger_jeu("sauvegarde_jeu.pkl") + modifier_affichage(manoir, ihm) + print("Jeu chargé.") + except FileNotFoundError: + print("Aucune sauvegarde trouvée.") + + h = manoir.heros + + if touche == 'N' or touche == 'n': + if h.aller_nord(): # Si aller au nord est possible et c'est bien passé + modifier_affichage(manoir, ihm) + if touche == 'S' or touche == 's': + if h.aller_sud(): # Si aller au sud est possible et c'est bien passé + modifier_affichage(manoir, ihm) + if touche == 'Q' or touche == 'q': + if h.aller_ouest(): # Si aller a l'ouest est possible et c'est bien passé + modifier_affichage(manoir, ihm) + if touche == 'D' or touche == 'd': + if h.aller_est(): # Si aller a l'est est possible et c'est bien passé + modifier_affichage(manoir, ihm) + if touche == 'C' or touche == 'c': + if h.combattre_monstre_actuel(): # Si combattre est possible et c'est bien passé + modifier_affichage(manoir, ihm) + if touche == 'F' or touche == 'f': + if h.fuire(): # Si fuire est possible + modifier_affichage(manoir, ihm) + if touche == 'U' or touche == 'u': + if h.ouvrir(manoir): # Si l'utilisation de la cle fonctionne + modifier_affichage(manoir, ihm) + if touche == 'R' or touche == 'r': + modifier_affichage(manoir, ihm) + +def modifier_affichage(manoir:'Manoir', ihm:'IHM') -> None: + """Modifie les 5 champs de l'interface graphique""" + h = manoir.heros + ihm.afficher_txt_1( h.observer() ) # affiche le texte descriptif + ihm.afficher_txt_2( h.reflechir() ) # affiche les actions possibles + ihm.afficher_txt_3( h.get_carac() ) # affiche les caractéristiques du héros + ihm.afficher_img_1( h.lieu.img ) # affiche l'image éventuelle du lieu + if h.lieu.occupant: + ihm.afficher_img_2( h.lieu.occupant.img) # affiche l'image éventuelle du monstre + + +# PROGRAMME PRINCIPAL + +#rediriger_sortie_vers_fichier("sortie_console.txt") + +# Création des données et de l'interface graphique +m = donnees.peupler_manoir() # Création des données du manoir +ihm = interface.IHM() # Création de l'interface graphique + +# ihm_signale_evenement va récupérer les événements sur l'ihm +ihm.signaler_evenement(lambda event: ihm_signale_evenement(event, m, ihm)) + +modifier_affichage(m, ihm) \ No newline at end of file diff --git a/projet_final/limace.jpg b/projet_final/limace.jpg new file mode 100644 index 0000000..fc9aa94 Binary files /dev/null and b/projet_final/limace.jpg differ diff --git a/projet_final/manoir-hante.jpg b/projet_final/manoir-hante.jpg new file mode 100644 index 0000000..626bd6f Binary files /dev/null and b/projet_final/manoir-hante.jpg differ diff --git a/projet_final/piscine.jpg b/projet_final/piscine.jpg new file mode 100644 index 0000000..3a4c9eb Binary files /dev/null and b/projet_final/piscine.jpg differ