﻿#!/usr/bin/python
# -*-coding:utf-8 -*

import tkinter
import random
import threading
import time

random.seed()







class Window(tkinter.Tk):
   """Fenêtre graphique pour l'affichage des méthodes de tri"""

   def __init__(self, methodeDeTri, parent = None, largeur = 800, hauteur = 400, marge = 30, title = "Tris et complexité", longueurParDefaut = 50, dureeParDefaut = 100, longueurMax = 500, dureeMax = 1000, valeurMin = 1, valeurMax = 200):
      """Constructeur"""
      tkinter.Tk.__init__(self, parent)
      self.parent = parent


      # Initialisation des propriétés
      self.methodeDeTri = methodeDeTri
      self.largeur = largeur
      self.hauteur = hauteur
      self.marge = marge
      self.longueurParDefaut = longueurParDefaut
      self.dureeParDefaut = dureeParDefaut
      self.longueurMax = longueurMax
      self.dureeMax = dureeMax
      self.valeurMin = valeurMin
      self.valeurMax = valeurMax
      
      self.longueurListe = longueurParDefaut     
      self.dureeAttente = dureeParDefaut     
      
      self.liste = []
      self.derniereListeAffichee = []
      self._creerNouvelleListe()
      
      self.affichageEnCours = False
      
      
      # Création de la fenêtre graphique
      self.title(title)

      self.labelLongueurListe = tkinter.Label(self, text="Longueur de la liste", justify="left", font=("Helvetica", 10))
      self.labelLongueurListe.grid(row = 0, column = 0, sticky = "W", padx=10, pady=10)

      self.entryLongueurListe = tkinter.Entry(self, font=("Helvetica", 10))
      self.entryLongueurListe.insert(0, str(self.longueurListe))
      self.entryLongueurListe.grid(row = 0, column = 1, sticky = "W", padx=10, pady=10)

      self.labelDureeAttente = tkinter.Label(self, text="Durée d'attente (en ms)", justify="right", font=("Helvetica", 10))
      self.labelDureeAttente.grid(row = 0, column = 5, sticky = "E", padx=10, pady=10)

      self.entryDureeAttente = tkinter.Entry(self, font=("Helvetica", 10))
      self.entryDureeAttente.insert(0, str(self.dureeAttente))
      self.entryDureeAttente.grid(row = 0, column = 6, sticky = "E", padx=10, pady=10)

      self.canvasPrincipal = tkinter.Canvas(self, width = largeur, height = hauteur, bg="white")
      self.canvasPrincipal.grid(row = 1, columnspan = 7)
            
      self.boutonNouvelleListe = tkinter.Button(self, text="Nouvelle liste", command = self.creerNouvelleListe, font=("Helvetica", 12))
      self.boutonNouvelleListe.grid(row = 2, column = 0, sticky = "W", padx=10, pady=10)

      self.boutonTrier = tkinter.Button(self, text="Trier la liste", command = self.lancer, font=("Helvetica", 12))
      self.boutonTrier.grid(row = 2, column = 3, padx=10, pady=10)

      self.boutonQuitter = tkinter.Button(self, text="Quitter", command = self.quitter, font=("Helvetica", 12))
      self.boutonQuitter.grid(row = 2, column = 6, sticky = "E", padx=10, pady=10)
            

      # Gel des dimensions de la fenetre
      self.resizable(width=False, height=False)
            

      # Affichage de la liste courante
      self._calculerParametresAffichage()
      self._afficherListe()


      # Lancement de la fenêtre graphique
      self.mainloop()
               

               
   def __repr__(self):
      """Fonction utilisée pour l'affichage sous forme de chaîne de caractères (lorsqu'on tape directement le nom de l'objet)"""
      s = "Liste courante : {0}".format(self.liste)
      return s
 
 

   def __str__(self):
      """Fonction utilisée pour l'affichage sous forme de chaîne de caractères (via la fonction print)"""
      s = "Liste courante : {0}".format(self.liste)
      return s  
      
      
   def creerNouvelleListe(self):
      """Creer une nouvelle liste et l'afficher"""
      self._recupererParametres()
      self._creerNouvelleListe()    
      self._calculerParametresAffichage()
      self._afficherListe()      
      self.boutonTrier["state"] = "active"

      
   def lancer(self):
      """Lancer la fonction de tri"""
      self._recupererParametres()

      self._calculerParametresAffichage()
      self._afficherListe()      

      self.boutonNouvelleListe["state"] = "disabled"
      self.entryLongueurListe["state"] = "disabled"
      self.entryDureeAttente["state"] = "disabled"
      

      self.affichageEnCours = True
      self._afficherEnBoucle();

      self.Resolve_Thread = _Resolve_Thread(self)
      self.Resolve_Thread.start()


   def quitter(self):
      """Quitter la fenêtre graphique"""
      self.affichageEnCours = False
      self.quit()
     
      
      
   # # FONCTIONS PRIVEES  
   
   def _recupererParametres(self):
      """Récupérer les paramètres entrés par l'utilisateur (fonction privée)"""
   
      # Longueur de la liste
      longueur = self.entryLongueurListe.get()
      
      try:
         nouvelleLongueur = int(longueur)
         if(nouvelleLongueur> self.longueurMax):
            raise ValueError()
         self.longueurListe = nouvelleLongueur
      except ValueError:
         self.longueurListe = self.longueurParDefaut
      
      self.entryLongueurListe.delete(0, tkinter.END)
      self.entryLongueurListe.insert(0, str(self.longueurListe))
   
      # Duree d'attente
      duree = self.entryDureeAttente.get()
      
      try:
         nouvelleDuree = int(duree)
         if(nouvelleDuree>self.dureeMax):
            raise ValueError()
         self.dureeAttente = nouvelleDuree
      except ValueError:
         self.dureeAttente = self.dureeParDefaut
      
      self.entryDureeAttente.delete(0, tkinter.END)
      self.entryDureeAttente.insert(0, str(self.dureeAttente))
      
      
   def _creerNouvelleListe(self):
      """Créer une nouvelle liste (fonction privée)"""
      
      self.liste.clear()
      
      for i in range(self.longueurListe):
         self.liste.append(random.randint(self.valeurMin, self.valeurMax))

      self.derniereListeAffichee = list(self.liste)

      
      
   def _calculerParametresAffichage(self):
      """Calculer les parametres utilises pour l'affichage (fonction privée)"""

      largeurDispo = self.largeur - self.marge*2
      hauteurDispo = self.hauteur - self.marge*2
      
      self.largeurBarre = largeurDispo // self.longueurListe
      largeurUtilisee = self.longueurListe * self.largeurBarre
      self.x0 = self.marge + (largeurDispo - largeurUtilisee) // 2

      self.ratioHauteur = hauteurDispo / (self.valeurMax)
      self.y0 = self.marge

      
   def _afficherListe(self):
      """Afficher l'état courant de la liste dans la fenetre graphique (fonction privée)"""
      
      self.canvasPrincipal.delete("all")

      self.derniereListeAffichee = list(self.liste)

      for i in range(self.longueurListe):
         valeur = self.liste[i]
         x1 = self.x0 + i*self.largeurBarre
         y1 = self.hauteur - self.y0
         x2 = x1 + self.largeurBarre
         y2 = y1 - int(valeur*self.ratioHauteur)
         self.canvasPrincipal.create_rectangle(x1, y1, x2, y2, fill="red")


   def _afficherEnBoucle(self):
      """Met à jour l'affichage en boucle dans la fenêtre graphique (fonction privée)"""
      if(self.affichageEnCours or (self.liste != self.derniereListeAffichee)):
         self._afficherListe()
         self.after(self.dureeAttente, self._afficherEnBoucle)
      

    
                
         
class _Resolve_Thread(threading.Thread):
   """Thread dédié à la méthode de tri"""
  
   def __init__(self, gui):
      threading.Thread.__init__(self)
      self.gui = gui
      self.methodeDeTri = self.gui.methodeDeTri
     
   def run(self):
      self.methodeDeTri(self.gui.liste)
      self.gui.affichageEnCours = False
      self.gui.boutonNouvelleListe["state"] = "active"
      self.gui.entryLongueurListe["state"] = "normal"
      self.gui.entryDureeAttente["state"] = "normal"
      self.gui.boutonTrier["state"] = "disabled"

