Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

TOUR DE HANOI AVEC TKINTER


Information sur la source

Catégorie :Jeux Classé sous : hanoi, recursivité Niveau : Débutant Date de création : 15/03/2007 Date de mise à jour : 28/04/2007 18:41:21 Vu / téléchargé: 4 105 / 209

Note :
9,33 / 10 - par 3 personnes
9,33 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (6)
Ajouter un commentaire et/ou une note

Description

Cliquez pour voir la capture en taille normale
Un classique des classiques, mais je tenais à vous soumettre ce code pour me faire part des erreurs ou rectifications qu'il serait bon d'y apporter.
 

Source

  • from Tkinter import *
  • from boite_dialogTk import *
  • from PIL import Image,ImageTk
  • class Hanoi(Frame):
  • def __init__(self):
  • Frame.__init__(self,bg='ivory')
  • self.master.resizable(0, 0)
  • dim = '530x560+247+0'
  • self.master.wm_geometry(newGeometry=dim)
  • #différentes positions pour les boites de dialogue
  • self.pos = ['+280+180','+140+180','+270+180']
  • #--- Icône ---#
  • try:
  • f=open('icone_hanoi.ico','r')
  • f.close()
  • self.icone = 'icone_hanoi.ico'
  • except:
  • message = " L'icône de l'application est manquante !"
  • boite = boite_dialogTk(self,title=' Erreur de chargement !',pos=self.pos[0],
  • mess=message,buttons=['ok'],bg_f='orange',bg_l='grey',fg_l='red',
  • bg_b='black',fg_b='ivory')
  • boite.go()
  • self.icone = None
  • self.master.iconbitmap(self.icone)
  • self.master.title("Tours de Hanoï")
  • self.can = Canvas(self,width=520,height=400,bg='navy',relief="raised", borderwidth=3)
  • #--- Image de fond ---#
  • self.err_chargement = 0
  • try:
  • image = Image.open("fond.bmp")
  • self.fond = ImageTk.PhotoImage(image)
  • except:
  • message = " L'image de fond 'fond.bmp' n'est pas présente \n\
  • dans le répertoire courant de l'application ! \n\n\
  • 'Ignorer' pour continuer sans l'image de fond \n\
  • 'Quitter' pour quitter le programme\n"
  • boite = boite_dialogTk(self,title=' Erreur de chargement !',icone=self.icone,
  • pos=self.pos[0],mess=message,buttons=['Ignorer','Quitter'],
  • bg_f='orange',bg_l='grey',fg_l='red',bg_b='black',fg_b='ivory')
  • if(boite.go()):
  • self.err_chargement = 0
  • self.fond=""
  • else:
  • self.err_chargement = 1
  • return
  • ##------------------------------ L'interface --------------------------------------##
  • #--- le cadre principal --- #
  • cadre_interface = Frame(self,relief="ridge", borderwidth=3,bg='light green')
  • #--- les sous-cadres ---#
  • cadre_nbDisc = Frame(cadre_interface,bg='light blue',relief="ridge", borderwidth=3)
  • cadre_vitesse = Frame(cadre_interface,bg='light green',relief="ridge", borderwidth=3)
  • cadre_bouttons = Frame(cadre_interface,bg='light green',relief="ridge", borderwidth=3)
  • #--- la règle nb disques ---#
  • Label(cadre_nbDisc,text="Nombre de disques :",bg='light blue',
  • font="Arial 11 bold").grid(row=1,column=1)
  • self.nb_disques = IntVar()
  • disques = Scale(cadre_nbDisc,from_=1,to=8,length=420,orient=HORIZONTAL,
  • tickinterval=1,variable=self.nb_disques,command=self.initialisation,
  • bg='light blue',
  • activebackground='blue',
  • troughcolor='ivory')
  • disques.grid(row=2,column=1)
  • #--- le choix de la vitesse ---#
  • Label(cadre_vitesse,text="Vitesse de déplacement :",
  • font="Arial 11 bold",bg='light green').grid(row=1,column=2)
  • self.deux_vitesses = [('Lente',5),('Rapide',10)]
  • self.vitesse = IntVar()
  • self.vitesse.set(5)
  • i=1
  • for texte,vit in self.deux_vitesses:
  • rad_bout = Radiobutton(cadre_vitesse,text=texte,variable=self.vitesse,
  • bg='dark green',fg='ivory',selectcolor='maroon',
  • activebackground='ivory',activeforeground='maroon',
  • value=vit,indicatoron=0,font="Arial 13 bold")
  • rad_bout.grid(row=2,column=i)
  • i += 2
  • #--- les 3 boutons de droite ---#
  • Button(cadre_bouttons,text="Initialiser",
  • command=self.initialisation,font="Arial 11 bold",bg='blue',fg='ivory',
  • activebackground='ivory',activeforeground='blue').grid(row=1,column=1,padx=2
  • ,pady=2, sticky="nsew")
  • self.etat_but_sol = IntVar()
  • self.sol_but = Checkbutton(cadre_bouttons,text='Solution',bg='red',fg='ivory',
  • activebackground='ivory',activeforeground='red',
  • selectcolor='red',
  • variable=self.etat_but_sol,command=self.solution,
  • indicatoron=0,font='Arial 11 bold')
  • self.sol_but.grid(row=2,column=1,padx=2,pady=2,sticky="nsew")
  • Button(cadre_bouttons,text="Quitter",bg='black',fg='ivory',
  • activebackground='ivory',activeforeground='black',
  • command=self.quitter,font="Arial 11 bold").grid(row=3,column=1,padx=2,pady=2,
  • sticky="nsew")
  • Button(cadre_bouttons,text="Règles",bg='dark violet',fg='ivory',
  • activebackground='ivory',activeforeground='dark violet',
  • command=self.regle,font="Arial 11 bold").grid(row=4,column=1,padx=2,pady=2,
  • sticky="nsew")
  • cadre_nbDisc.grid(row=1,column=1)
  • cadre_vitesse.grid(row=2,column=1)
  • cadre_bouttons.grid(row=1,column=2,rowspan=2)
  • cadre_interface.pack()
  • self.can.pack()
  • #--- Création du canevas et des item 'texte'(coups mini, coups effectués)
  • self.can.create_image(264,204,image=self.fond)
  • mess = "Nombre minimum de mouvements : "
  • self.coups_mini = self.can.create_text(250,340,text=mess,font="Arial 13 bold ",
  • fill='light blue')
  • self.coups = self.can.create_text(250,360,text="Nombre de déplacements effectués : "
  • +str(0),font="Arial 13 bold ",fill='light green')
  • #--- Tableau des 3 tours ---#
  • self.tours = [[],[],[]] # chacune d'elle est un tab à 4 dim :
  • t1 = [[],[],[],[]] # 1ere -> l'id de la tour
  • t2 = [[],[],[],[]] # 2eme -> tab d'id des disques
  • t3 = [[],[],[],[]] # 3eme -> id de la tige
  • # 4eme -> tab des coord de la base
  • # tn = [id tn,[idD1,idD2,...],id tige,[coord_tn_xmin,coord_tn_ymin,coord_tn_xmax,...]]
  • self.tours[0] = t1
  • self.tours[1] = t2
  • self.tours[2] = t3
  • #--- Les bases ---#
  • long_base = 120
  • t1[0] = self.can.create_rectangle(40,300,40+long_base,310,fill='gold')
  • t2[0] = self.can.create_rectangle(200,300,200+long_base,310,fill='gold')
  • t3[0] = self.can.create_rectangle(360,300,360+long_base,310,fill='gold')
  • #--- Les tiges ---#
  • t1[2] = self.can.create_rectangle(95,300,105,100,fill='gold')
  • t2[2] = self.can.create_rectangle(255,300,265,100,fill='gold')
  • t3[2] = self.can.create_rectangle(415,300,425,100,fill='gold')
  • #--- Toutes les coordonnées relatives à chaque base ---#
  • self.coord_t1 = self.can.coords(self.tours[0][0])
  • self.coord_t1_xmin = self.coord_t1[0]
  • self.coord_t1_ymin = self.coord_t1[1]
  • self.coord_t1_xmax = self.coord_t1[2]
  • self.coord_t1_ymax = self.coord_t1[3]
  • self.coord_t1_mil = self.coord_t1_xmin+(self.coord_t1_xmax-self.coord_t1_xmin)/2
  • self.tours[0][3] = [self.coord_t1_xmin,self.coord_t1_ymin,
  • self.coord_t1_xmax,self.coord_t1_ymax,self.coord_t1_mil]
  • self.coord_t2 = self.can.coords(self.tours[1][0])
  • self.coord_t2_xmin = self.coord_t2[0]
  • self.coord_t2_ymin = self.coord_t2[1]
  • self.coord_t2_xmax = self.coord_t2[2]
  • self.coord_t2_ymax = self.coord_t2[3]
  • self.coord_t2_mil = self.coord_t2_xmin+(self.coord_t2_xmax-self.coord_t2_xmin)/2
  • self.tours[1][3] = [self.coord_t2_xmin,self.coord_t2_ymin,
  • self.coord_t2_xmax,self.coord_t2_ymax,self.coord_t2_mil]
  • self.coord_t3 = self.can.coords(self.tours[2][0])
  • self.coord_t3_xmin = self.coord_t3[0]
  • self.coord_t3_ymin = self.coord_t3[1]
  • self.coord_t3_xmax = self.coord_t3[2]
  • self.coord_t3_ymax = self.coord_t3[3]
  • self.coord_t3_mil = self.coord_t3_xmin+(self.coord_t3_xmax-self.coord_t3_xmin)/2
  • self.tours[2][3] = [self.coord_t3_xmin,self.coord_t3_ymin,
  • self.coord_t3_xmax,self.coord_t3_ymax,self.coord_t3_mil]
  • ##-----------------------------------------------------------------------------------##
  • #--- Démarage ---#
  • self.initialisation()
  • ##---------------------- Init de toutes les variables ---------------------------------------##
  • def initialisation(self,x=1):
  • self.init = 1 # var qui permet de sortir de la boucle de mouvement si =1
  • self.flag = 1 # var qui met l'animation en pause si le bouton règle est pressé
  • for t in self.tours:
  • for disque in t[1]:
  • self.can.delete(disque) # effacement des disques du canevas
  • t[1] = [] # et du tableau des tours
  • self.nb_disc = self.nb_disques.get() # récupération du nombre de disques
  • self.creer_disques(self.nb_disc) # création des disques
  • self.vit = self.vitesse.get() # récupération de la vitesse de déplacement(5 ou 10 pix)
  • self.dx,self.dy = 0,-self.vit # direction du déplacement
  • self.tour_dep=[] # tableau qui contiendra la tour de depart selectionnée
  • self.tour_arr=[] # pareil pour la tour d'arrivée
  • self.tab = [] # tableau qui contiendra les couples (tour dep,tour arr) pour la solution
  • self.index = 0 # var pour se deplacer dans le ce tableau après chaque mouvement
  • self.sol = 0 # var qui indique que l'on est(=1) ou pas(=0) en mode solution
  • self.mouv = 0 # var qui contient le nombre de mouvements effectués
  • self.can.bind('<Button-1>',self.selection_tour)
  • self.mini_mouv = (2**self.nb_disc)-1 # nombre de mouvements minimum
  • mess = "Nombre minimum de mouvements : %s"%(str(self.mini_mouv))
  • self.can.itemconfigure(self.coups_mini,text=mess) # modif du Label
  • self.can.itemconfigure(self.coups,text="Nombre de déplacements effectués : "+str(0))
  • self.can.itemconfigure(self.tours[0][0],fill='gold')
  • self.can.itemconfigure(self.tours[0][2],fill='gold')
  • self.can.itemconfigure(self.tours[1][0],fill='gold')
  • self.can.itemconfigure(self.tours[1][2],fill='gold')
  • self.can.itemconfigure(self.tours[2][0],fill='gold')
  • self.can.itemconfigure(self.tours[2][2],fill='gold')
  • def creer_disques(self,nb):
  • # Création de nb disques choisi par l'utilisateur
  • for i in range(nb): # i compris entre 0 et nb-1
  • coord_d_xmin = self.coord_t1_mil - (nb-i)*10 # i étant croissant les pos en xmin et
  • coord_d_xmax = self.coord_t1_mil + (nb-i)*10 # xmax diminuent et sont multiple de 10
  • coord_d_y = self.coord_t1_ymin - (i*20) # pos en y = i fois l'épaisseur d'un disque
  • self.tours[0][1].append(self.can.create_rectangle(coord_d_xmin,coord_d_y
  • ,coord_d_xmax,coord_d_y-20
  • ,fill='maroon'))
  • #---------- Départ de l'algorithme de solution ----------#
  • #--- Phase 1 : Initialisation ---#
  • def solution(self):
  • self.initialisation() # Appel à l'initialisation générale
  • self.init = 0
  • self.sol = 1 # mode solution : après chaque pose de disque on reviendra sur la phase 3
  • self.recursivite(self.nb_disc,0,1,2) # 0,1,2 -> les indexs des tours dans self.tours
  • self.deplace_auto()
  • #--- Phase 2 : Remplissage du tableau de couple (t_dep,t_arr) par récursivité ---#
  • def recursivite(self,n,td,tt,ta):
  • if n>0:
  • self.recursivite(n-1,td,ta,tt)
  • self.tab.append((td,ta))
  • self.recursivite(n-1,tt,td,ta)
  • #--- Phase 3 : Appel successif de la fonction mouvement avec chaque couple(t_dep,t_arr) ---#
  • def deplace_auto(self):
  • self.can.unbind('<Button-1>')
  • couple = self.tab[self.index]
  • self.tour_dep = self.tours[couple[0]]
  • self.tour_arr = self.tours[couple[1]]
  • self.vit = self.vitesse.get()
  • self.deplacement()
  • self.index += 1
  • #-----------------------------------------------------#
  • #--- Selection à la souris des tours de départ et d'arrivée ---#
  • def selection_tour(self,event):
  • x,y = event.x,event.y
  • tour_selec = []
  • if((x>self.coord_t1_xmin and x<self.coord_t1_xmax) and
  • (y>100 and y<self.coord_t1_ymax)):
  • tour_selec = self.tours[0]
  • elif((x>self.coord_t2_xmin and x<self.coord_t2_xmax) and
  • (y>100 and y<self.coord_t2_ymax)):
  • tour_selec = self.tours[1]
  • elif((x>self.coord_t3_xmin and x<self.coord_t3_xmax) and
  • (y>100 and y<self.coord_t3_ymax)):
  • tour_selec = self.tours[2]
  • else:
  • return
  • if self.tour_dep == []:
  • self.tour_dep = tour_selec
  • self.can.itemconfigure(self.tour_dep[0],fill='red')
  • self.can.itemconfigure(self.tour_dep[2],fill='red')
  • if self.tour_dep[1] == []: # tour de départ vide : violation d'une règle
  • message = " Cette tour de départ ne contient aucun disque ! \n"
  • boite = boite_dialogTk(self,title='Tours de Hanoï',icone=self.icone,
  • pos=self.pos[2],mess=message,buttons=['ok'],bg_f='orange',
  • bg_l='pink',bg_b='black',fg_b='ivory')
  • boite.go()
  • self.can.itemconfigure(self.tour_dep[0],fill='gold')
  • self.can.itemconfigure(self.tour_dep[2],fill='gold')
  • self.tour_dep = []
  • elif self.tour_dep != []:
  • self.tour_arr = tour_selec
  • self.can.itemconfigure(self.tour_arr[0],fill='green')
  • self.can.itemconfigure(self.tour_arr[2],fill='green')
  • if self.tour_arr == self.tour_dep:
  • self.can.itemconfigure(self.tour_dep[0],fill='gold')
  • self.can.itemconfigure(self.tour_dep[2],fill='gold')
  • self.tour_dep=[]
  • self.tour_arr = []
  • if self.tour_dep != [] and self.tour_arr != []:
  • self.verif()
  • #--- Vérification du respect de la règle pas de disque sur un plus petit ---#
  • def verif(self):
  • self.init = 0
  • if self.tour_arr[1] != []:
  • coord_d_dep = self.can.coords(self.tour_dep[1][-1])
  • diam_d_dep = coord_d_dep[2] - coord_d_dep[0]
  • coord_d_arr = self.can.coords(self.tour_arr[1][-1])
  • diam_d_arr = coord_d_arr[2] - coord_d_arr[0]
  • if diam_d_arr < diam_d_dep:
  • message = " Attention pas de disque sur un plus petit que lui ! \n"
  • boite = boite_dialogTk(self,title="Violation de la règle 2",icone=self.icone,
  • pos=self.pos[2],mess=message,buttons=['ok'],bg_f='orange',
  • bg_l='pink',bg_b='black',fg_b='ivory')
  • if(boite.go()):
  • self.can.itemconfigure(self.tour_dep[0],fill='gold')
  • self.can.itemconfigure(self.tour_dep[2],fill='gold')
  • self.can.itemconfigure(self.tour_arr[0],fill='gold')
  • self.can.itemconfigure(self.tour_arr[2],fill='gold')
  • self.tour_dep = []
  • self.tour_arr = []
  • return
  • self.vit = self.vitesse.get()
  • self.deplacement()
  • #--- Mouvement d'un disque d'une tour à une autre ---#
  • def deplacement(self):
  • # Si appui sur 'initialisation' ou désélection du bonton 'solution'
  • if self.init == 1 or (self.etat_but_sol.get()==0 and self.sol ==1):
  • self.sol_but.deselect()
  • self.initialisation()
  • return
  • self.can.unbind('<Button-1>')
  • # Présence ou non d'un disque sur la tour d'arrivée par defaut : non
  • disc_present = 0
  • # Coord du disque qui se deplace et de son milieu
  • coord_d = self.can.coords(self.tour_dep[1][-1])
  • coord_d_xmin = coord_d[0]
  • coord_d_xmax = coord_d[2]
  • coord_d_ymin = coord_d[1]
  • coord_mil_d = coord_d_xmin+(coord_d_xmax-coord_d_xmin)/2
  • # Coord du dernier disque de la tour d'arrivé si il existe
  • if self.tour_arr[1] != []:
  • disc_present = 1
  • coord_d_arr = self.can.coords(self.tour_arr[1][-1])
  • coord_d_arr_ymin = coord_d_arr[1]
  • # Si le disque est en haut
  • if coord_d_ymin == 60:
  • self.dy=0
  • # le signe de la différence des coord en xmin des tours donne le sens de déplacement
  • # Déplacement sur la droite
  • if self.tour_arr[3][0] - self.tour_dep[3][0] > 0:
  • self.dx = self.vit
  • # Ou déplacement sur la gauche
  • else:
  • self.dx = -self.vit
  • # Si les milieux du disque et de la tour d'arrivée correspondent
  • if coord_mil_d == self.tour_arr[3][4]:
  • self.dx = 0
  • self.dy = self.vit
  • # Descente du disque
  • # jusq'au dernier de la tour d'arrivée
  • if disc_present == 0:
  • if coord_d_ymin+20 == self.tour_arr[3][1]:
  • self.disque_pose() # Que faire quand le disque est posé
  • return
  • # sinon jusqu'à la base
  • elif disc_present == 1:
  • if coord_d_ymin+20 == coord_d_arr_ymin:
  • self.disque_pose() # Que faire quand le disque est posé
  • return
  • self.can.move(self.tour_dep[1][-1],self.dx,self.dy)
  • if self.flag == 1:
  • self.master.after(self.vit,self.deplacement)
  • #--- Actions à réaliser quand un disque est posé ---#
  • def disque_pose(self):
  • self.dy=0
  • self.can.itemconfigure(self.tour_dep[0],fill='gold')
  • self.can.itemconfigure(self.tour_dep[2],fill='gold')
  • self.can.itemconfigure(self.tour_arr[0],fill='gold')
  • self.can.itemconfigure(self.tour_arr[2],fill='gold')
  • # Le disque déplacé est ajouté au tableau des disques de la tour d'arrivée
  • self.tour_arr[1].append(self.tour_dep[1][-1])
  • # Et supprimé du tableau de la tour de départ
  • del(self.tour_dep[1][-1])
  • # Quelques réinitialisations
  • self.tour_dep = []
  • self.tour_arr = []
  • self.dx,self.dy = 0,-self.vit
  • self.can.bind('<Button-1>',self.selection_tour)
  • self.vit = self.vitesse.get()
  • # Mise à jour du label 'déplacements effectués'
  • self.mouv +=1
  • self.can.itemconfigure(self.coups,text="Nombre de déplacements effectués : " +str(self.mouv))
  • # Si la tour doite est complète en mode 'souris'
  • if len(self.tours[2][1]) == self.nb_disc and self.sol == 0:
  • # si il y a trop de déplacements
  • if self.mouv > self.mini_mouv:
  • diff = self.mouv - self.mini_mouv
  • message =" Bien mais il y a %s coups de trop \n\n\
  • Voulez-vous rejouer ? \n"%(str(diff))
  • # si le minimum de déplacement est respecté
  • elif self.mouv == self.mini_mouv:
  • message =" Gagné ! en %s coups \n\n Voulez-vous rejouer ? \n"%(str(self.mouv))
  • boite1 = boite_dialogTk(self,title='Question ?',icone=self.icone,pos=self.pos[2],
  • mess=message,buttons=['Oui','Non'],bg_f='orange',
  • bg_l='gold',bg_b='maroon',fg_b='ivory')
  • if(boite1.go()):
  • self.initialisation()
  • else:
  • self.master.destroy()
  • return
  • # Si la tour doite est complète en mode 'solution'
  • elif len(self.tours[2][1]) == self.nb_disc and self.sol==1:
  • message = " Solution terminée\n %s coups minimum respectés \n\n\
  • Voulez-vous rejouer ? \n"%(str(self.mouv))
  • boite2 = boite_dialogTk(self,title='Question ?',icone=self.icone,pos=self.pos[2],
  • mess=message,buttons=['Oui','Non'],bg_f='orange',
  • bg_l='gold',bg_b='maroon',fg_b='ivory')
  • if(boite2.go()):
  • self.sol_but.deselect()
  • self.initialisation()
  • else:
  • self.master.destroy()
  • return
  • return
  • # Si la tour doite n'est pas complète en mode 'solution'
  • if self.sol == 1:
  • # Retour en phase 3
  • self.deplace_auto()
  • # Si la tour doite n'est pas complète en mode 'souris'
  • # retour dans l'attente d'une sélection
  • return
  • #--- Arret de l'aplication ---#
  • def quitter(self):
  • self.master.destroy()
  • return 0
  • #--- Règles du jeux ---#
  • def regle(self):
  • message = "Règles du jeux :\n\nLe but est de déplacer tous les disques de la tour gauche vers la tour droite\n\
  • et ce sans violer les règles suivantes :\n\n\
  • 1°/ On ne peut déplacer qu'un disque à la fois\n\
  • 2°/ Un disque ne peut se poser sur un disque plus petit que lui\n"
  • self.flag = 0
  • boite = boite_dialogTk(self,title='Tours de Hanoï',icone=self.icone,pos=self.pos[1],
  • mess=message,buttons=['ok'],bg_f='orange',bg_l='gold',
  • bg_b='maroon',fg_b='ivory',justify=LEFT)
  • if(boite.go()):
  • self.flag = 1
  • if self.sol == 1:
  • self.deplacement()
  • if __name__=='__main__':
  • app = Hanoi()
  • if app.err_chargement == 1:
  • app.master.destroy()
  • elif app.err_chargement == 0:
  • app.pack()
  • app.mainloop()
from Tkinter import *
from boite_dialogTk import *
from PIL import Image,ImageTk
       
class Hanoi(Frame):
    def __init__(self):
        Frame.__init__(self,bg='ivory')
        self.master.resizable(0, 0)
        dim = '530x560+247+0'
        self.master.wm_geometry(newGeometry=dim)
        #différentes positions pour les boites de dialogue
        self.pos = ['+280+180','+140+180','+270+180']
        #--- Icône ---#
        try:
            f=open('icone_hanoi.ico','r')
            f.close()
            self.icone = 'icone_hanoi.ico'

        except:
            message = " L'icône de l'application est manquante !"
            boite = boite_dialogTk(self,title=' Erreur de chargement !',pos=self.pos[0],
                               mess=message,buttons=['ok'],bg_f='orange',bg_l='grey',fg_l='red',
                               bg_b='black',fg_b='ivory')
            boite.go()
            self.icone = None
        
        self.master.iconbitmap(self.icone)
        self.master.title("Tours de Hanoï")
        self.can = Canvas(self,width=520,height=400,bg='navy',relief="raised", borderwidth=3)
        
        #--- Image de fond ---#
        self.err_chargement = 0
        try:
            image        = Image.open("fond.bmp")
            self.fond    = ImageTk.PhotoImage(image)
        except:
            message = "   L'image de fond 'fond.bmp' n'est pas présente   \n\
   dans le répertoire courant de l'application !   \n\n\
   'Ignorer' pour continuer sans l'image de fond   \n\
   'Quitter' pour quitter le programme\n"

            boite = boite_dialogTk(self,title=' Erreur de chargement !',icone=self.icone,
                                   pos=self.pos[0],mess=message,buttons=['Ignorer','Quitter'],
                                   bg_f='orange',bg_l='grey',fg_l='red',bg_b='black',fg_b='ivory')
            if(boite.go()):
                self.err_chargement = 0
                self.fond=""
            else:
                self.err_chargement = 1
                return
        

        ##------------------------------ L'interface --------------------------------------##
        #--- le cadre principal --- #
        cadre_interface = Frame(self,relief="ridge", borderwidth=3,bg='light green')
        
        #--- les sous-cadres ---#
        cadre_nbDisc   = Frame(cadre_interface,bg='light blue',relief="ridge", borderwidth=3)
        cadre_vitesse  = Frame(cadre_interface,bg='light green',relief="ridge", borderwidth=3)
        cadre_bouttons = Frame(cadre_interface,bg='light green',relief="ridge", borderwidth=3)
        
        #--- la règle nb disques ---#
        Label(cadre_nbDisc,text="Nombre de disques :",bg='light blue',
              font="Arial 11 bold").grid(row=1,column=1)
        self.nb_disques = IntVar()
        disques = Scale(cadre_nbDisc,from_=1,to=8,length=420,orient=HORIZONTAL,
                        tickinterval=1,variable=self.nb_disques,command=self.initialisation,
                        bg='light blue',
                        activebackground='blue',
                        troughcolor='ivory')
        disques.grid(row=2,column=1)
        
        #--- le choix de la vitesse ---#
        Label(cadre_vitesse,text="Vitesse de déplacement :",
              font="Arial 11 bold",bg='light green').grid(row=1,column=2)
        self.deux_vitesses = [('Lente',5),('Rapide',10)]
        self.vitesse = IntVar()
        self.vitesse.set(5)
        i=1
        for texte,vit in self.deux_vitesses:
            rad_bout = Radiobutton(cadre_vitesse,text=texte,variable=self.vitesse,
                                   bg='dark green',fg='ivory',selectcolor='maroon',
                                   activebackground='ivory',activeforeground='maroon',
                                   value=vit,indicatoron=0,font="Arial 13 bold")
            rad_bout.grid(row=2,column=i)
            i += 2
            
        #--- les 3 boutons de droite ---#
        Button(cadre_bouttons,text="Initialiser",
               command=self.initialisation,font="Arial 11 bold",bg='blue',fg='ivory',
               activebackground='ivory',activeforeground='blue').grid(row=1,column=1,padx=2
                                                                      ,pady=2, sticky="nsew")
        self.etat_but_sol = IntVar()
        self.sol_but = Checkbutton(cadre_bouttons,text='Solution',bg='red',fg='ivory',
                                   activebackground='ivory',activeforeground='red',
                                   selectcolor='red',
                                   variable=self.etat_but_sol,command=self.solution,
                                   indicatoron=0,font='Arial 11 bold')
        self.sol_but.grid(row=2,column=1,padx=2,pady=2,sticky="nsew")
        
        Button(cadre_bouttons,text="Quitter",bg='black',fg='ivory',
               activebackground='ivory',activeforeground='black',
               command=self.quitter,font="Arial 11 bold").grid(row=3,column=1,padx=2,pady=2,
                                                               sticky="nsew")
        Button(cadre_bouttons,text="Règles",bg='dark violet',fg='ivory',
               activebackground='ivory',activeforeground='dark violet',
               command=self.regle,font="Arial 11 bold").grid(row=4,column=1,padx=2,pady=2,
                                                               sticky="nsew")
        cadre_nbDisc.grid(row=1,column=1)
        cadre_vitesse.grid(row=2,column=1)
        cadre_bouttons.grid(row=1,column=2,rowspan=2)
        
        cadre_interface.pack()
        self.can.pack()

        #--- Création du canevas et des item 'texte'(coups mini, coups effectués)
        self.can.create_image(264,204,image=self.fond)
        mess = "Nombre minimum de mouvements  : "
        self.coups_mini = self.can.create_text(250,340,text=mess,font="Arial 13 bold ",
                                               fill='light blue')
        self.coups = self.can.create_text(250,360,text="Nombre de déplacements effectués : "
                                          +str(0),font="Arial 13 bold ",fill='light green')

        #--- Tableau des 3 tours ---#
        self.tours = [[],[],[]]    # chacune d'elle est un tab à 4 dim :
        t1 = [[],[],[],[]] # 1ere -> l'id de la tour
        t2 = [[],[],[],[]] # 2eme -> tab d'id des disques
        t3 = [[],[],[],[]] # 3eme -> id de la tige
                           # 4eme -> tab des coord de la base
        # tn = [id tn,[idD1,idD2,...],id tige,[coord_tn_xmin,coord_tn_ymin,coord_tn_xmax,...]]
        self.tours[0] = t1
        self.tours[1] = t2
        self.tours[2] = t3

        #--- Les bases ---#
        long_base = 120
        t1[0] = self.can.create_rectangle(40,300,40+long_base,310,fill='gold')
        t2[0] = self.can.create_rectangle(200,300,200+long_base,310,fill='gold')
        t3[0] = self.can.create_rectangle(360,300,360+long_base,310,fill='gold')
        #--- Les tiges ---#
        t1[2] = self.can.create_rectangle(95,300,105,100,fill='gold')
        t2[2] = self.can.create_rectangle(255,300,265,100,fill='gold')
        t3[2] = self.can.create_rectangle(415,300,425,100,fill='gold')
        #--- Toutes les coordonnées relatives à chaque base ---#
        self.coord_t1 = self.can.coords(self.tours[0][0])
        self.coord_t1_xmin = self.coord_t1[0]
        self.coord_t1_ymin = self.coord_t1[1]
        self.coord_t1_xmax = self.coord_t1[2]
        self.coord_t1_ymax = self.coord_t1[3]
        self.coord_t1_mil  = self.coord_t1_xmin+(self.coord_t1_xmax-self.coord_t1_xmin)/2
        self.tours[0][3] = [self.coord_t1_xmin,self.coord_t1_ymin,
                              self.coord_t1_xmax,self.coord_t1_ymax,self.coord_t1_mil]
        
        self.coord_t2 = self.can.coords(self.tours[1][0])
        self.coord_t2_xmin = self.coord_t2[0]
        self.coord_t2_ymin = self.coord_t2[1]
        self.coord_t2_xmax = self.coord_t2[2]
        self.coord_t2_ymax = self.coord_t2[3]
        self.coord_t2_mil  = self.coord_t2_xmin+(self.coord_t2_xmax-self.coord_t2_xmin)/2
        self.tours[1][3] = [self.coord_t2_xmin,self.coord_t2_ymin,
                              self.coord_t2_xmax,self.coord_t2_ymax,self.coord_t2_mil]

        self.coord_t3 = self.can.coords(self.tours[2][0])
        self.coord_t3_xmin = self.coord_t3[0]
        self.coord_t3_ymin = self.coord_t3[1]
        self.coord_t3_xmax = self.coord_t3[2]
        self.coord_t3_ymax = self.coord_t3[3]
        self.coord_t3_mil  = self.coord_t3_xmin+(self.coord_t3_xmax-self.coord_t3_xmin)/2
        self.tours[2][3] = [self.coord_t3_xmin,self.coord_t3_ymin,
                              self.coord_t3_xmax,self.coord_t3_ymax,self.coord_t3_mil]
              
        ##-----------------------------------------------------------------------------------##
        #--- Démarage ---#
        self.initialisation()

##---------------------- Init de toutes les variables ---------------------------------------##
    def initialisation(self,x=1):

        self.init = 1  # var qui permet de sortir de la boucle de mouvement si =1
        self.flag = 1  # var qui met l'animation en pause si le bouton règle est pressé      
        for t in self.tours:     
            for disque in t[1]:
                self.can.delete(disque) # effacement des disques du canevas
                t[1] = []               # et du tableau des tours

        self.nb_disc = self.nb_disques.get() # récupération du nombre de disques
        self.creer_disques(self.nb_disc)     # création des disques 

        self.vit = self.vitesse.get() # récupération de la vitesse de déplacement(5 ou 10 pix)
        self.dx,self.dy = 0,-self.vit # direction du déplacement 

        
        self.tour_dep=[] # tableau qui contiendra la tour de depart selectionnée
        self.tour_arr=[] # pareil pour la tour d'arrivée
        
        self.tab   = []  # tableau qui contiendra les couples (tour dep,tour arr) pour la solution
        self.index = 0   # var pour se deplacer dans le ce tableau après chaque mouvement
        self.sol   = 0   # var qui indique que l'on est(=1) ou pas(=0) en mode solution

        self.mouv  = 0   # var qui contient le nombre de mouvements effectués

        self.can.bind('<Button-1>',self.selection_tour)

        self.mini_mouv = (2**self.nb_disc)-1 # nombre de mouvements minimum 
        mess = "Nombre minimum de mouvements  : %s"%(str(self.mini_mouv))
        self.can.itemconfigure(self.coups_mini,text=mess) # modif du Label
        self.can.itemconfigure(self.coups,text="Nombre de déplacements effectués : "+str(0))    
                
        self.can.itemconfigure(self.tours[0][0],fill='gold')
        self.can.itemconfigure(self.tours[0][2],fill='gold')
        self.can.itemconfigure(self.tours[1][0],fill='gold')
        self.can.itemconfigure(self.tours[1][2],fill='gold')
        self.can.itemconfigure(self.tours[2][0],fill='gold')
        self.can.itemconfigure(self.tours[2][2],fill='gold')
        
    def creer_disques(self,nb):
        # Création de nb disques choisi par l'utilisateur
        for i in range(nb): # i compris entre 0 et nb-1
            coord_d_xmin = self.coord_t1_mil - (nb-i)*10 # i étant croissant les pos en xmin et 
            coord_d_xmax = self.coord_t1_mil + (nb-i)*10 # xmax diminuent et sont multiple de 10
            coord_d_y = self.coord_t1_ymin - (i*20) # pos en y = i fois l'épaisseur d'un disque
            self.tours[0][1].append(self.can.create_rectangle(coord_d_xmin,coord_d_y
                                                           ,coord_d_xmax,coord_d_y-20
                                                           ,fill='maroon'))

    #---------- Départ de l'algorithme de solution ----------#
    #--- Phase 1 : Initialisation ---#
    def solution(self):
        self.initialisation() # Appel à l'initialisation générale
        self.init = 0 
        self.sol  = 1 # mode solution : après chaque pose de disque on reviendra sur la phase 3 
        self.recursivite(self.nb_disc,0,1,2) # 0,1,2 -> les indexs des tours dans self.tours
        self.deplace_auto()
        
    #--- Phase 2 : Remplissage du tableau de couple (t_dep,t_arr) par récursivité ---#
    def recursivite(self,n,td,tt,ta):
        if n>0:
            self.recursivite(n-1,td,ta,tt)
            self.tab.append((td,ta))
            self.recursivite(n-1,tt,td,ta)
            
    #--- Phase 3 : Appel successif de la fonction mouvement avec chaque couple(t_dep,t_arr) ---#
    def deplace_auto(self):
        self.can.unbind('<Button-1>')
       
        couple = self.tab[self.index]
        
        self.tour_dep = self.tours[couple[0]]
        self.tour_arr = self.tours[couple[1]]
        self.vit = self.vitesse.get()     
        self.deplacement()
        self.index += 1
    #-----------------------------------------------------#
        
    #--- Selection à la souris des tours de départ et d'arrivée ---#
    def selection_tour(self,event):
        x,y = event.x,event.y
        tour_selec = []
                
        if((x>self.coord_t1_xmin and x<self.coord_t1_xmax) and
           (y>100 and y<self.coord_t1_ymax)):
            tour_selec = self.tours[0]

        elif((x>self.coord_t2_xmin and x<self.coord_t2_xmax) and
           (y>100 and y<self.coord_t2_ymax)):
            tour_selec = self.tours[1]
      
        elif((x>self.coord_t3_xmin and x<self.coord_t3_xmax) and
           (y>100 and y<self.coord_t3_ymax)):
            tour_selec = self.tours[2]
        else:
            return
        
        if self.tour_dep == []:
            self.tour_dep = tour_selec
            self.can.itemconfigure(self.tour_dep[0],fill='red')
            self.can.itemconfigure(self.tour_dep[2],fill='red')
            if self.tour_dep[1] == []: # tour de départ vide : violation d'une règle
                message = "   Cette tour de départ ne contient aucun disque !   \n"
                boite = boite_dialogTk(self,title='Tours de Hanoï',icone=self.icone,
                                       pos=self.pos[2],mess=message,buttons=['ok'],bg_f='orange',
                                       bg_l='pink',bg_b='black',fg_b='ivory')
                boite.go()
                self.can.itemconfigure(self.tour_dep[0],fill='gold')
                self.can.itemconfigure(self.tour_dep[2],fill='gold')
                self.tour_dep = []
                
        elif self.tour_dep != []:
            self.tour_arr = tour_selec
            self.can.itemconfigure(self.tour_arr[0],fill='green')
            self.can.itemconfigure(self.tour_arr[2],fill='green')
            if self.tour_arr == self.tour_dep:
                self.can.itemconfigure(self.tour_dep[0],fill='gold')
                self.can.itemconfigure(self.tour_dep[2],fill='gold')
                self.tour_dep=[]
                self.tour_arr = []
        if self.tour_dep != [] and self.tour_arr != []:
            self.verif()

    #--- Vérification du respect de la règle pas de disque sur un plus petit ---#
    def verif(self):
        self.init = 0
        
        if self.tour_arr[1] != []:
            coord_d_dep = self.can.coords(self.tour_dep[1][-1])
            diam_d_dep  = coord_d_dep[2] - coord_d_dep[0]

            coord_d_arr = self.can.coords(self.tour_arr[1][-1])
            diam_d_arr  = coord_d_arr[2] - coord_d_arr[0]
            
            if diam_d_arr < diam_d_dep: 
                
                message = "   Attention pas de disque sur un plus petit que lui !   \n"
                boite = boite_dialogTk(self,title="Violation de la règle 2",icone=self.icone,
                                       pos=self.pos[2],mess=message,buttons=['ok'],bg_f='orange',
                                       bg_l='pink',bg_b='black',fg_b='ivory')

                if(boite.go()):
                    self.can.itemconfigure(self.tour_dep[0],fill='gold')
                    self.can.itemconfigure(self.tour_dep[2],fill='gold')
                    self.can.itemconfigure(self.tour_arr[0],fill='gold')
                    self.can.itemconfigure(self.tour_arr[2],fill='gold')
                    self.tour_dep = []  
                    self.tour_arr = []
                    return
                
        self.vit = self.vitesse.get()
        self.deplacement()

    #--- Mouvement d'un disque d'une tour à une autre ---#
    def deplacement(self):
        
        # Si appui sur 'initialisation' ou désélection du bonton 'solution'
        if self.init == 1 or (self.etat_but_sol.get()==0 and self.sol ==1):
            self.sol_but.deselect()
            self.initialisation()
            return
        self.can.unbind('<Button-1>')
        
        # Présence ou non d'un disque sur la tour d'arrivée par defaut : non
        disc_present = 0 
        
        # Coord du disque qui se deplace et de son milieu
        coord_d = self.can.coords(self.tour_dep[1][-1])
        coord_d_xmin = coord_d[0]
        coord_d_xmax = coord_d[2]
        coord_d_ymin = coord_d[1]
        coord_mil_d  = coord_d_xmin+(coord_d_xmax-coord_d_xmin)/2
        
        # Coord du dernier disque de la tour d'arrivé si il existe
        if self.tour_arr[1] != []:
            disc_present = 1
            coord_d_arr = self.can.coords(self.tour_arr[1][-1])
            coord_d_arr_ymin = coord_d_arr[1]

        # Si le disque est en haut
        if coord_d_ymin == 60:
            self.dy=0
            # le signe de la différence des coord en xmin des tours donne le sens de déplacement
            # Déplacement sur la droite
            if self.tour_arr[3][0] - self.tour_dep[3][0] > 0:
                self.dx = self.vit
            # Ou déplacement sur la gauche
            else:
                self.dx = -self.vit
                
        # Si les milieux du disque et de la tour d'arrivée correspondent
        if coord_mil_d == self.tour_arr[3][4]:
            self.dx = 0
            self.dy = self.vit
            # Descente du disque
                # jusq'au dernier de la tour d'arrivée
            if disc_present == 0:
                if coord_d_ymin+20 == self.tour_arr[3][1]:
                    self.disque_pose() # Que faire quand le disque est posé
                    return
                # sinon jusqu'à la base
            elif disc_present == 1:
                if coord_d_ymin+20 == coord_d_arr_ymin:
                    self.disque_pose() # Que faire quand le disque est posé
                    return
           
        self.can.move(self.tour_dep[1][-1],self.dx,self.dy)
        if self.flag == 1:
            self.master.after(self.vit,self.deplacement)
        
    #--- Actions à réaliser quand un disque est posé ---#
    def disque_pose(self):
        self.dy=0
        
        self.can.itemconfigure(self.tour_dep[0],fill='gold')
        self.can.itemconfigure(self.tour_dep[2],fill='gold')
        self.can.itemconfigure(self.tour_arr[0],fill='gold')
        self.can.itemconfigure(self.tour_arr[2],fill='gold')

        # Le disque déplacé est ajouté au tableau des disques de la tour d'arrivée
        self.tour_arr[1].append(self.tour_dep[1][-1])
        # Et supprimé du tableau de la tour de départ
        del(self.tour_dep[1][-1])

        # Quelques réinitialisations
        self.tour_dep = []
        self.tour_arr = []
        self.dx,self.dy = 0,-self.vit
        self.can.bind('<Button-1>',self.selection_tour)
        self.vit = self.vitesse.get()
                
        # Mise à jour du label 'déplacements effectués'
        self.mouv +=1
        self.can.itemconfigure(self.coups,text="Nombre de déplacements effectués : " +str(self.mouv))
        
        
        # Si la tour doite est complète en mode 'souris'
        if len(self.tours[2][1]) == self.nb_disc and self.sol == 0:
            # si il y a trop de déplacements
            if self.mouv > self.mini_mouv:
                diff = self.mouv - self.mini_mouv
                message ="   Bien mais il y a %s coups de trop   \n\n\
   Voulez-vous rejouer ?   \n"%(str(diff))
            # si le minimum de déplacement est respecté
            elif self.mouv == self.mini_mouv:
                message ="   Gagné ! en %s coups   \n\n   Voulez-vous rejouer ?   \n"%(str(self.mouv))
 
            boite1 = boite_dialogTk(self,title='Question ?',icone=self.icone,pos=self.pos[2],
                                    mess=message,buttons=['Oui','Non'],bg_f='orange',
                                    bg_l='gold',bg_b='maroon',fg_b='ivory')
            if(boite1.go()):
                self.initialisation()
            else:
                self.master.destroy()
                return
          
        # Si la tour doite est complète en mode 'solution'    
        elif len(self.tours[2][1]) == self.nb_disc and self.sol==1:
            
            message = "   Solution terminée\n   %s coups minimum respectés   \n\n\
   Voulez-vous rejouer ?   \n"%(str(self.mouv))
            boite2 = boite_dialogTk(self,title='Question ?',icone=self.icone,pos=self.pos[2],
                                    mess=message,buttons=['Oui','Non'],bg_f='orange',
                                    bg_l='gold',bg_b='maroon',fg_b='ivory')
            
            if(boite2.go()):
                self.sol_but.deselect()
                self.initialisation()
            else:
                self.master.destroy()
                return
                
            return

        # Si la tour doite n'est pas complète en mode 'solution'
        
        if self.sol == 1:
            # Retour en phase 3
            self.deplace_auto()

        # Si la tour doite n'est pas complète en mode 'souris'
        # retour dans l'attente d'une sélection
        return
    
    #--- Arret de l'aplication ---#
    def quitter(self):
        self.master.destroy()
        return 0

    #--- Règles du jeux ---#
    def regle(self):
        message = "Règles du jeux :\n\nLe but est de déplacer tous les disques de la tour gauche vers la tour droite\n\
et ce sans violer les règles suivantes :\n\n\
1°/ On ne peut déplacer qu'un disque à la fois\n\
2°/ Un disque ne peut se poser sur un disque plus petit que lui\n"

        self.flag = 0
        boite = boite_dialogTk(self,title='Tours de Hanoï',icone=self.icone,pos=self.pos[1],
                               mess=message,buttons=['ok'],bg_f='orange',bg_l='gold',
                               bg_b='maroon',fg_b='ivory',justify=LEFT)
        if(boite.go()):
            self.flag = 1
            if self.sol == 1:
                self.deplacement()
         

    
if __name__=='__main__':
    
    app = Hanoi()
    if app.err_chargement == 1:
        app.master.destroy()
    elif app.err_chargement == 0:
        app.pack()
        app.mainloop()
    
    
    
        

Conclusion

Je tenais à remercier Aéra Group pour sa collaboration à faire évoluer cette application !
 

Fichier Zip

Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip

Historique

15 mars 2007 20:43:45 :
Rajout du fichier .zip contenant code+fond
15 mars 2007 20:45:20 :
Rajout du fichier .zip contenant code+fond
17 mars 2007 22:57:25 :
Rajout de titres de fenêtres et initialisation automatique avec la règle "nombre de disques"
23 mars 2007 19:08:27 :
Addition d'un module de boites de dialogue personnalisables, rajout d'une îcone, et divers détails
23 mars 2007 19:13:41 :
copier/coller du script pour les visiteurs (oublie oups !)
23 mars 2007 19:50:26 :
Rectif dans le texte 'Règles'(rien d'important)
25 mars 2007 16:35:53 :
Correction d'un bug lors de l'appui sur 'règles' en mode sélection.
26 avril 2007 12:03:10 :
Modif de la classe boite_dialogTK pour éviter un bug lors de la fermeture par la 'croix'
28 avril 2007 18:41:21 :
Encore une modif dans le script 'boite_dialogTk' qui rajoute le paramètre 'quit_opt' pour pouvoir choisir le comportement de la boite de dialogue lors de la fermeture par la 'croix'.

Commentaires et avis

signaler à un administrateur
Commentaire de aera group le 16/03/2007 19:31:04

Bonjour,

Pour commencer, bravo pour ton code, il très bien réussi et l'interface est simpatique, ce qui vaut bien un 10/10 largement mérité !

"je tenais à vous soumettre ce code pour me faire part des erreurs ou rectifications qu'il serait bon d'y apporter." <== On peut modifier un peu ton code sur certaint détail mais, ce n'est pas une obligation : c'est un peu quelques plus !



1. Tu peux ajouter un titre au tkMessageBox ça fais mieu
           tkMessageBox.showinfo('Titre','Message') ; Ça fonctionne aussi avec "tkMessageBox.askyesno" et "tkMessageBox.showerror" !

2. Pour que l'initialisation se face automatiquement, il faut que modifie "disques" a la ligne 28 en rajoutant "command=self.initialisation" ce qui donne :

disques = Scale(cadre_nbDisc,from_=1,to=8,length=420,orient=HORIZONTAL,
                        tickinterval=1,variable=self.nb_disques,bg='light blue',
                        activebackground='red',command=self.initialisation,
                        troughcolor='ivory')

il faut également modifier la ligne 137 en marquant :
def initialisation(self,x=1):

3. Enfin à la ligne remplace "image = Image.open("C:\Python25\Projets\Jeux\Hanoi/fond.bmp")" par
"image = Image.open("fond.bmp")"  <== Ansi, ton programme fonctionnera si "fond.bmp" et dans le même répertoire que ton fichier au lieu qu'elle soit obligatoirement dans "C:\Python25\Projets\Jeux\Hanoi/fond.bmp".

Boila, bonne continuation
_______
Aéra

signaler à un administrateur
Commentaire de Mints le 17/03/2007 23:26:22

Tout d'abord je tiens à te remercier Aéra pour ta note et surtout pour tes commentaires très enrichissants !

Je crois avoir rectifié le tire suivant tes conseils, mais pourrais tu me dire comment faire pour que ma fenêtre règle du jeu en 'toplevel' soit prioritaire sur l'application, autrement dit qu'on ne puisse pas interagir sur un bouton du jeu sans que cette toplevel' soit fermée.

Et tant que j'y suis as-tu une autre solution pour radicalement supprimer l'application quand l'image de fond n'est pas trouvée et que l'utilisateur choisi 'ok', j'ai fait un peu de bricolage avec 'self.err_chargement' mais bon ...

Ceci dis je vais surement avoir besoin de ton tuto sur Py2exe dans quelque temps, j'ai fais un tétris que je vais soumettre à la communautée des 'pythonvore' pour pouvoir en faire un éxecutable digne de soit(juste pour le fun) et pour dire qu'avec ce langage on peut vraiment faire des applis utiles agréables et ludiques.

Encore merci


signaler à un administrateur
Commentaire de aera group le 18/03/2007 11:57:47

Salut !!

1. Je n'ai pas très bien compris se que tu veux faire quand tu dis : "radicalement supprimer l'application quand l'image de fond n'est pas trouvée". Si tu veux que a fenêtre se détruise et quelle mette fin à l'application, fait seulement "self.quitter()" ! Si non, je vais essayer de te faire un peu quelque chose qui permet à l'utilisateur soit de quitter soit de lançer le programme sans l'image soit de sélectionner une image présente sur sont disque dure !

2. Je n'en suis pas sur, mais je crois un "toplevel" ne peux pas être plus prioritaire que ta fenêtre principale, mais je crois avoir déja vu sa sur le web, il faut que je regarde (là je suis un peu débordé, mais ne t'inquiet pas, je le ferais dès que possible). Sinon, tout n'est pas perdu : il "suffit" (c'est plus simple à dir qu'a faire) de créé une boite de dialogue (tkMessageBox en gros, mais créé maison, par nous). Par contre je ne sais pas si avec cette méthode on arrivera à obtenir le même styde qu'actuelement. Enfin une troième méthode consisterait à refaire tout le programme sous WxPython ! Mais bon, un gros travail serait nessaicaire et on metterais beaucoup trop de temps,mais si un jour tu n'a rien à faire, c'est un bon défit à ce donné !!

3. Si tu veux vraiment faire une application assez complètre, le mieux serais de faire un petit fichier de sauvegarde des paramètre, des résultats et pouquoi pas des parties ! Libre à toi d'en décider !

Sur ceux, je te souhaite une bonne journée
______
Aéra

signaler à un administrateur
Commentaire de aera group le 21/03/2007 10:53:10

Comme promis :




from Tkinter import _cnfmerge
from Tkinter import *
import tkMessageBox
from PIL import Image,ImageTk



class Dialog(Widget):
    def __init__(self,master=None,cnf= {'title': 'Erreur de chargement',
                      'text':"""Le fichier contenant l'image de fond (fond.bmp) n'est pas dans le même répertoire que votre script
Vous pouvez ne pas mettre d'image en fond ou bien quitter le programme""" ,
                      'bitmap': 'warning',
                      'default': 0,
                      'strings': ('Charger sans fond',
                                  'Quitter')},**kw):
        cnf = _cnfmerge((cnf,kw))
        self.widgetName = '__dialog__'
        Widget._setup(self, master, cnf)
        self.num = self.tk.getint(
                self.tk.call(
                      'tk_dialog', self._w,
                      cnf['title'], cnf['text'],
                      cnf['bitmap'], cnf['default'],
                      *cnf['strings']))

        try: Widget.destroy(self)
        except TclError: pass
    def destroy(self): pass


class Hanoi(Frame):
    def __init__(self):
        Frame.__init__(self,bg='ivory')
        self.master.resizable(0, 0)
        self.master.wm_geometry(newGeometry='530x560+220+0')
        self.master.title("Tours de Hanoï")
        self.can = Canvas(self,width=520,height=400,bg='ivory',relief="raised", borderwidth=3)
        #--- Image de fond ----#
        self.err_chargement = 0
        try:    
            image        = Image.open("fond.bmp")
            self.fond    = ImageTk.PhotoImage(image)
        except:
            dia=Dialog()
            if dia.num==1:
              self.err_chargement = 1
              return
            else :
                self.err_chargement = 0
                self.fond=""
                
        
        ##------------------------------ L'interface --------------------------------------##
        #--- le cadre principal --- #
        cadre_interface = Frame(self,relief="ridge", borderwidth=3,bg='light green')
        
        #--- les sous-cadres ---#
        cadre_nbDisc   = Frame(cadre_interface,bg='light blue',relief="ridge", borderwidth=3)
        cadre_vitesse  = Frame(cadre_interface,bg='light green',relief="ridge", borderwidth=3)
        cadre_bouttons = Frame(cadre_interface,bg='light green',relief="ridge", borderwidth=3)
        
        #--- la règle nb disques ---#
        Label(cadre_nbDisc,text="Nombre de disques :",bg='light blue',
              font="Arial 11 bold").grid(row=1,column=1)
        self.nb_disques = IntVar()
        disques = Scale(cadre_nbDisc,from_=1,to=8,length=420,orient=HORIZONTAL,
                        tickinterval=1,variable=self.nb_disques,command=self.initialisation,
                        bg='light blue',
                        activebackground='blue',
                        troughcolor='ivory')
        disques.grid(row=2,column=1)
        
        #--- le choix de la vitesse ---#
        Label(cadre_vitesse,text="Vitesse de déplacement :",
              font="Arial 11 bold",bg='light green').grid(row=1,column=2)
        self.deux_vitesses = [('Lente',5),('Rapide',10)]
        self.vitesse = IntVar()
        self.vitesse.set(5)
        i=1
        for texte,vit in self.deux_vitesses:
            rad_bout = Radiobutton(cadre_vitesse,text=texte,variable=self.vitesse,
                                   bg='dark green',fg='ivory',selectcolor='maroon',
                                   activebackground='ivory',activeforeground='maroon',
                                   value=vit,indicatoron=0,font="Arial 13 bold")
            rad_bout.grid(row=2,column=i)
            i += 2
            
        #--- les 3 boutons de droite ---#
        Button(cadre_bouttons,text="Initialiser",
               command=self.initialisation,font="Arial 11 bold",bg='blue',fg='ivory',
               activebackground='ivory',activeforeground='blue').grid(row=1,column=1,padx=2
                                                                      ,pady=2, sticky="nsew")
        self.etat_but_sol = IntVar()
        self.sol_but = Checkbutton(cadre_bouttons,text='Solution',bg='red',fg='ivory',
                                   activebackground='ivory',activeforeground='red',
                                   selectcolor='red',
                                   variable=self.etat_but_sol,command=self.solution,
                                   indicatoron=0,font='Arial 11 bold')
        self.sol_but.grid(row=2,column=1,padx=2,pady=2,sticky="nsew")
        
        Button(cadre_bouttons,text="Quitter",bg='black',fg='ivory',
               activebackground='ivory',activeforeground='black',
               command=self.quitter,font="Arial 11 bold").grid(row=3,column=1,padx=2,pady=2,
                                                               sticky="nsew")
        Button(cadre_bouttons,text="Règles",bg='dark violet',fg='ivory',
               activebackground='ivory',activeforeground='dark violet',
               command=self.regle,font="Arial 11 bold").grid(row=4,column=1,padx=2,pady=2,
                                                               sticky="nsew")
        cadre_nbDisc.grid(row=1,column=1)
        cadre_vitesse.grid(row=2,column=1)
        cadre_bouttons.grid(row=1,column=2,rowspan=2)
        
        cadre_interface.pack()
        self.can.pack()

        #--- Création du canevas et des item 'texte'(coups mini, coups effectués)
        self.can.create_image(264,204,image=self.fond)
        mess = "Nombre minimum de mouvements : "
        self.coups_mini = self.can.create_text(250,340,text=mess,font="Arial 13 bold ",fill='white')
        self.coups = self.can.create_text(250,360,text="Nombre de déplacements effectué : "
                                          +str(0),font="Arial 13 bold ",fill='white')

        #--- Tableau des 3 tours ---#
        self.tours = [[],[],[]]    # chacune d'elle est un tab à 4 dim :
        t1 = [[],[],[],[]] # 1ere -> l'id de la tour
        t2 = [[],[],[],[]] # 2eme -> tab d'id des disques
        t3 = [[],[],[],[]] # 3eme -> id de la tige
                           # 4eme -> tab des coord de la base
        # tn = [id tn,[idD1,idD2,...],id tige,[coord_tn_xmin,coord_tn_ymin,coord_tn_xmax,...]]
        self.tours[0] = t1
        self.tours[1] = t2
        self.tours[2] = t3

        #--- Les bases ---#
        long_base = 120
        t1[0] = self.can.create_rectangle(40,300,40+long_base,310,fill='gold')
        t2[0] = self.can.create_rectangle(200,300,200+long_base,310,fill='gold')
        t3[0] = self.can.create_rectangle(360,300,360+long_base,310,fill='gold')
        #--- Les tiges ---#
        t1[2] = self.can.create_rectangle(95,300,105,100,fill='gold')
        t2[2] = self.can.create_rectangle(255,300,265,100,fill='gold')
        t3[2] = self.can.create_rectangle(415,300,425,100,fill='gold')
        #--- Toutes les coordonnées relatives à chaque base ---#
        self.coord_t1 = self.can.coords(self.tours[0][0])
        self.coord_t1_xmin = self.coord_t1[0]
        self.coord_t1_ymin = self.coord_t1[1]
        self.coord_t1_xmax = self.coord_t1[2]
        self.coord_t1_ymax = self.coord_t1[3]
        self.coord_t1_mil  = self.coord_t1_xmin+(self.coord_t1_xmax-self.coord_t1_xmin)/2
        self.tours[0][3] = [self.coord_t1_xmin,self.coord_t1_ymin,
                              self.coord_t1_xmax,self.coord_t1_ymax,self.coord_t1_mil]
        
        self.coord_t2 = self.can.coords(self.tours[1][0])
        self.coord_t2_xmin = self.coord_t2[0]
        self.coord_t2_ymin = self.coord_t2[1]
        self.coord_t2_xmax = self.coord_t2[2]
        self.coord_t2_ymax = self.coord_t2[3]
        self.coord_t2_mil  = self.coord_t2_xmin+(self.coord_t2_xmax-self.coord_t2_xmin)/2
        self.tours[1][3] = [self.coord_t2_xmin,self.coord_t2_ymin,
                              self.coord_t2_xmax,self.coord_t2_ymax,self.coord_t2_mil]

        self.coord_t3 = self.can.coords(self.tours[2][0])
        self.coord_t3_xmin = self.coord_t3[0]
        self.coord_t3_ymin = self.coord_t3[1]
        self.coord_t3_xmax = self.coord_t3[2]
        self.coord_t3_ymax = self.coord_t3[3]
        self.coord_t3_mil  = self.coord_t3_xmin+(self.coord_t3_xmax-self.coord_t3_xmin)/2
        self.tours[2][3] = [self.coord_t3_xmin,self.coord_t3_ymin,
                              self.coord_t3_xmax,self.coord_t3_ymax,self.coord_t3_mil]
        
        ##-----------------------------------------------------------------------------------##
        #--- Démarage ---#
        self.initialisation()

##---------------------- Init de toutes les variables ---------------------------------------##
    def initialisation(self,x=1):

        self.init = 1  # var qui permet de sortir de la boucle de mouvement si =1
                
        for t in self.tours:    
            for disque in t[1]:
                self.can.delete(disque) # effacement des disques du canevas
                t[1] = []               # et du tableau des tours

        self.nb_disc = self.nb_disques.get() # récupération du nombre de disques
        self.creer_disques(self.nb_disc)     # création des disques

        self.vit = self.vitesse.get() # récupération de la vitesse de déplacement(5 ou 10 pix)
        self.dx,self.dy = 0,-self.vit # direction du déplacement

        
        self.tour_dep=[] # tableau qui contiendra la tour de depart selectionnée
        self.tour_arr=[] # pareil pour la tour d'arrivée
        
        self.tab   = []  # tableau qui contiendra les couples (tour dep,tour arr) pour la solution
        self.index = 0   # var pour se deplacer dans le ce tableau après chaque mouvement
        self.sol   = 0   # var qui indique que l'on est(=1) ou pas(=0) en mode solution

        self.mouv  = 0   # var qui contient le nombre de mouvements effectués

        self.can.bind('<Button-1>',self.selection_tour)

        self.mini_mouv = (2**self.nb_disc)-1 # nombre de mouvements minimum
        mess = "Nombre minimum de mouvements : %s"%(str(self.mini_mouv))
        self.can.itemconfigure(self.coups_mini,text=mess) # modif du Label
        self.can.itemconfigure(self.coups,text="Nombre de déplacements effectué : "
                                          +str(0))
                
        self.can.itemconfigure(self.tours[0][0],fill='gold')
        self.can.itemconfigure(self.tours[0][2],fill='gold')
        self.can.itemconfigure(self.tours[1][0],fill='gold')
        self.can.itemconfigure(self.tours[1][2],fill='gold')
        self.can.itemconfigure(self.tours[2][0],fill='gold')
        self.can.itemconfigure(self.tours[2][2],fill='gold')
        
    def creer_disques(self,nb):
        # Création de nb disques choisi par l'utilisateur
        for i in range(nb): # i compris entre 0 et nb-1
            coord_d_xmin = self.coord_t1_mil - (nb-i)*10 # i étant croissant les pos en xmin et
            coord_d_xmax = self.coord_t1_mil + (nb-i)*10 # xmax diminuent et sont multiple de 10
            coord_d_y = self.coord_t1_ymin - (i*20) # pos en y = i fois l'épaisseur d'un disque
            self.tours[0][1].append(self.can.create_rectangle(coord_d_xmin,coord_d_y
                                                           ,coord_d_xmax,coord_d_y-20
                                                           ,fill='maroon'))

    #---------- Départ de l'algorithme de solution ----------#
    #--- Phase 1 : Initialisation ---#
    def solution(self):
        self.initialisation() # Appel à l'initialisation générale
        self.init = 0
        self.sol  = 1 # mode solution : après chaque pose de disque on reviendra sur la phase 3
        self.recursivite(self.nb_disc,0,1,2) # 0,1,2 -> les indexs des tours dans self.tours
        self.deplace_auto()
        
    #--- Phase 2 : Remplissage du tableau de couple (t_dep,t_arr) par récursivité ---#
    def recursivite(self,n,td,tt,ta):
        if n>0:
            self.recursivite(n-1,td,ta,tt)
            self.tab.append((td,ta))
            self.recursivite(n-1,tt,td,ta)
            
    #--- # Phase 3 : Appel successif de la fonction mouvement avec chaque couple(t_dep,t_arr) ---#
    def deplace_auto(self):
        self.can.unbind('<Button-1>')
      
        couple = self.tab[self.index]
        
        self.tour_dep = self.tours[couple[0]]
        self.tour_arr = self.tours[couple[1]]
            
        self.deplacement()
        self.index += 1
    #-----------------------------------------------------#
        
    #--- Selection à la souris des tours de départ et d'arrivée ---#
    def selection_tour(self,event):
        x,y = event.x,event.y
        tour_selec = []
                
        if((x>self.coord_t1_xmin and x<self.coord_t1_xmax) and
           (y>100 and y<self.coord_t1_ymax)):
            tour_selec = self.tours[0]

        elif((x>self.coord_t2_xmin and x<self.coord_t2_xmax) and
           (y>100 and y<self.coord_t2_ymax)):
            tour_selec = self.tours[1]
      
        elif((x>self.coord_t3_xmin and x<self.coord_t3_xmax) and
           (y>100 and y<self.coord_t3_ymax)):
            tour_selec = self.tours[2]
        else:
            return
        
        if self.tour_dep == []:
            self.tour_dep = tour_selec
            self.can.itemconfigure(self.tour_dep[0],fill='red')
            self.can.itemconfigure(self.tour_dep[2],fill='red')
            if self.tour_dep[1] == []: # tour de départ vide : violation d'une règle
                tkMessageBox.showerror(title="Tour de départ invalide",
                                       message="Cette tour de départ ne contient aucun disque")
                self.can.itemconfigure(self.tour_dep[0],fill='gold')
                self.can.itemconfigure(self.tour_dep[2],fill='gold')
                self.tour_dep = []
        elif self.tour_dep != []:
            self.tour_arr = tour_selec
            self.can.itemconfigure(self.tour_arr[0],fill='green')
            self.can.itemconfigure(self.tour_arr[2],fill='green')
            if self.tour_arr == self.tour_dep:
                self.can.itemconfigure(self.tour_dep[0],fill='gold')
                self.can.itemconfigure(self.tour_dep[2],fill='gold')
                self.tour_dep=[]
                self.tour_arr = []
        if self.tour_dep != [] and self.tour_arr != []:
            self.verif()

    #--- Vérification du respect de la règle pas de disque sur un plus petit ---#
    def verif(self):
        self.init = 0
        
        if self.tour_arr[1] != []:
            coord_d_dep = self.can.coords(self.tour_dep[1][-1])
            diam_d_dep  = coord_d_dep[2] - coord_d_dep[0]

            coord_d_arr = self.can.coords(self.tour_arr[1][-1])
            diam_d_arr  = coord_d_arr[2] - coord_d_arr[0]
            
            if diam_d_arr < diam_d_dep:
                self.can.itemconfigure(self.tour_dep[0],fill='gold')
                self.can.itemconfigure(self.tour_dep[2],fill='gold')
                self.can.itemconfigure(self.tour_arr[0],fill='gold')
                self.can.itemconfigure(self.tour_arr[2],fill='gold')
                self.tour_dep = []  
                self.tour_arr = []
                tkMessageBox.showerror(title="Déplacement non autorisé",
                                       message="Attention pas de disque sur un plus petit que lui !")
                return
        
        self.deplacement()

    #--- Mouvement d'un disque d'une tour à une autre ---#
    def deplacement(self):
        
        # Si appui sur 'initialisation' ou désélection du bonton 'solution'
        if self.init == 1 or (self.etat_but_sol.get()==0 and self.sol ==1):
            self.sol_but.deselect()
            self.initialisation()
            return
        self.can.unbind('<Button-1>')
        
        # Présence ou non d'un disque sur la tour d'arrivée par defaut : non
        disc_present = 0
        
        # Coord du disque qui se deplace et de son milieu
        coord_d = self.can.coords(self.tour_dep[1][-1])
        coord_d_xmin = coord_d[0]
        coord_d_xmax = coord_d[2]
        coord_d_ymin = coord_d[1]
        coord_mil_d  = coord_d_xmin+(coord_d_xmax-coord_d_xmin)/2
    
        # Coord du dernier disque de la tour d'arrivé si il existe
        if self.tour_arr[1] != []:
            disc_present = 1
            coord_d_arr = self.can.coords(self.tour_arr[1][-1])
            coord_d_arr_ymin = coord_d_arr[1]

        # Si le disque est en haut
        if coord_d_ymin == 60:
            self.dy=0
            # le signe de la différence des coord en xmin des tours donne le sens de déplacement
            # Déplacement sur la droite
            if self.tour_arr[3][0] - self.tour_dep[3][0] > 0:
                self.dx = self.vit
            # Ou déplacement sur la gauche
            else:
                self.dx = -self.vit
                
        # Si les milieux du disque et de la tour d'arrivée correspondent
        if coord_mil_d == self.tour_arr[3][4]:
            self.dx = 0
            self.dy = self.vit
            # Descente du disque
                # jusq'au dernier de la tour d'arrivée
            if disc_present == 0:
                if coord_d_ymin+20 == self.tour_arr[3][1]:
                    self.disque_pose() # Que faire quand le disque est posé
                    return
                # sinon jusqu'à la base
            elif disc_present == 1:
                if coord_d_ymin+20 == coord_d_arr_ymin:
                    self.disque_pose() # Que faire quand le disque est posé
                    return
          
        self.can.move(self.tour_dep[1][-1],self.dx,self.dy)
        self.master.after(self.vit,self.deplacement)
        
    #--- Actions à réaliser quand un disque est posé ---#
    def disque_pose(self):
        self.dy=0
        
        self.can.itemconfigure(self.tour_dep[0],fill='gold')
        self.can.itemconfigure(self.tour_dep[2],fill='gold')
        self.can.itemconfigure(self.tour_arr[0],fill='gold')
        self.can.itemconfigure(self.tour_arr[2],fill='gold')

        # Le disque déplacé est ajouté au tableau des disques de la tour d'arrivée
        self.tour_arr[1].append(self.tour_dep[1][-1])
        # Et supprimé du tableau de la tour de départ
        del(self.tour_dep[1][-1])

        # Quelques réinitialisations
        self.tour_dep = []
        self.tour_arr = []
        self.dx,self.dy = 0,-self.vit
        self.can.bind('<Button-1>',self.selection_tour)
        self.vit = self.vitesse.get()
                
        # Mise à jour du label 'déplacements effectués'
        self.mouv +=1
        self.can.itemconfigure(self.coups,text="Nombre de déplacements effectués : " +str(self.mouv))
        
        
        # Si la tour doite est complète en mode 'souris'
        if len(self.tours[2][1]) == self.nb_disc and self.sol == 0:
            # si il y a trop de déplacements
            if self.mouv > self.mini_mouv:
                diff = self.mouv - self.mini_mouv
                message ="Bien mais il y a %s coups de trop\nVoulez-vous rejouer ?"%(str(diff))
            # si le minimum de déplacement est respecté
            elif self.mouv == self.mini_mouv:
                message ="Gagné ! en %s coups\nVoulez-vous rejouer ?"%(str(self.mouv))
            
            if tkMessageBox.askyesno(title="              !! BRAVO !!",message=message):
                self.initialisation()
            else:
                self.master.destroy()
                return
            
        # Si la tour doite est complète en mode 'solution'    
        elif len(self.tours[2][1]) == self.nb_disc and self.sol==1:
            if tkMessageBox.askyesno(title="              Terminé",
                                     message="Solution terminée\n%s coups minimum respectés\nVoulez-vous rejouer ?"
                                     %(str(self.mouv))):
                self.sol_but.deselect()
                self.initialisation()
            else:
                self.master.destroy()
                return

        # Si la tour doite n'est pas complète en mode 'solution'
        if self.sol == 1:
            # Retour en phase 3
            self.deplace_auto()

        # Si la tour doite n'est pas complète en mode 'souris'
        # retour dans l'attente d'une sélection
        return
    
    #--- Arret de l'aplication ---#
    def quitter(self):
        self.master.destroy()

    #--- Règle du jeux ---#
    def regle(self):
        regle = Toplevel(self,bg='pink',width=200,height=200)
        regle.resizable(0,0)
        regle.transient(self.master) # Pour n'avoir qu'une icone dans la barre des tâches
        regle.grab_set() # Pour que l'utilisateur n'a plus accès à la fenetre parente
        regle.wm_geometry(newGeometry='700x200+120+180')
        mess = "Règle du jeux\n\n Le but est de déplacer tous les disques\
de la tour gauche vers la tour droite\n\
et ce sans violer les règles suivantes :\n\n\
1°/ On ne peut déplacer qu'un disque à la fois\n\
2°/ Un disque ne peut se poser sur un disque plus petit que lui\n"
        Label(regle,text=mess,font='Century 13 bold',bg='light blue',relief='ridge',borderwidth=5).pack()
        Button(regle,text='OK',command=regle.destroy).pack()
        


        
        
    
if __name__=='__main__':
    
    app = Hanoi()
    if app.err_chargement == 1:
        app.master.destroy()
    elif app.err_chargement == 0:
        app.pack()
        app.mainloop()

________
Aéra

signaler à un administrateur
Commentaire de Mints le 23/03/2007 19:29:26

Encore merci Aéra pour ton aide !

J'ai finalement opté pour la solution de faire un module qui permette de faire des boites de dialogue personnalisées(customisées)... Bon, des améliorations sont encore possibles, mais c'est déja mieux que les tkMessageBox un peu hostaires.

J'aimerai avoir votre avis (Aéra et vous les autres pythonvores) sur ce module et le programme aussi tant qu'on y est.

Merci d'avance.

signaler à un administrateur
Commentaire de aera group le 24/03/2007 19:22:50

C'est un très bon programme que tu a créé : l'interface est très simpatique, le programme ne contient aparament pas de buge et le "module qui permette de faire des boites de dialogue personnalisées" est très bien construit !!! Encore bravo pour ton travail qui mérite bien la note que je lui est mis. Il ne reste plus qu'a faire un "À propos de ..." pour que ton programme soit complet, mais c'est un détail. Enfin, on pourrait également créé une fonction pour l'enregistrement des paramètres du jeux et pourquoi pas une fonction pour la sauvegarde des parties (ça serais interressant pour réaliser celui à 8 disques).
Bonne continuation
_____
Aéra

Ajouter un commentaire



Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Comparez les prix Nouvelle version

Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,343 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.