Advertisement
Guest User

Untitled

a guest
Jan 19th, 2017
154
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 34.86 KB | None | 0 0
  1. ##imports
  2. from tkinter import Tk, Toplevel, Listbox, Canvas, IntVar, CENTER, END, LEFT, RIGHT, BOTH, W, E, N, S, ACTIVE, Y, ALL # GUI
  3. from tkinter import messagebox
  4. from tkinter.ttk import Frame, Combobox, Button, Label, Entry, Checkbutton, Scrollbar # Widgets avec thèmes
  5. import csv
  6. from datetime import *
  7. from os.path import join, exists
  8. from os import makedirs
  9.  
  10. ##liste_de_var
  11. #L'ensemble des variables de chaque fichier est représenté par une liste, dont chaque élément est une variable dudit fichier.
  12. #Cette variable est elle-même une liste, de la forme suivante :
  13. #["nom de la variable", "type de la variable", **paramètres supplémentaires, suivant le type de la variable**, "booleen:variable importante"]
  14. #Les différents types sont les suivants :
  15.  
  16. #"d" : date ; on précisera en paramètre supplémentaire si la date est une date de début ("dd"),
  17. #si c'est une date de fin ("df"), ou si c'est une simple date ("d")
  18. #"int" : entier
  19. #"str" : chaîne de caractères
  20. #"ld" : liste déroulante
  21.  
  22. #Par exemple, la variable "Situation de la demande" sera codée de la façon suivante :
  23. #["Situation de la demande", "ld", ["", "Active", "Annulée", "Fermée", "Pourvue"]]
  24. #Ici, le troisième élément de la liste représente donc les modalités de la variable.
  25.  
  26. #En revanche, la variable "Nom du stagiaire" sera codée de la façon suivante :
  27. #["Nom du stagiaire", "str"]
  28. #Ici, il n'y a pas de paramètre supplémentaire à ajouter, la liste ne comprend donc que 2 éléments et non 3.
  29.  
  30. Var_stages=[["Situation de la demande","ld",["","Active","Annulée","Fermée","Pourvue"],True],
  31. ["Nombre stagiaire","int",True],
  32. ["Nom du stagiaire","str",True],
  33. ["Prénom du stagiaire","str",True],
  34. ["Rémunération","ld",["","Oui","Non"],True],
  35. ["Site","ld",["","Paris","Arcueil","Angers"],True],
  36. ["Structure","str",True],
  37. ["Unité","str",True],
  38. ["Niveau études","ld",["","Collège","Lycée","Bac +1","Bac +2","Licence","Master 1","Master 2","Master Spécialisé"],True],
  39. ["Domaine études","ld", ["", "Actuariat", "Comptabilité", "Contrôle de gestion", "Audit/Contrôle interne", "Assurance", "Juridique", "Finance de marché/Risques", "RH", "Communication/Digital", "Informatique/MOA", "Achats", "Secrétariat", "Statistiques", "Immeuble/Logistique", "Commercial/marketing", "Autre"],True],
  40. ["Durée","int",True],
  41. ["Date de début","d","dd",True],
  42. ["Date de fin","d","df",True],
  43. ["CHR","str",False],
  44. ["Indemnités de stage (brut)","int",False],
  45. ["N°TS","str",False],
  46. ["Nom responsable hierarchique","str",False],
  47. ["Nom tuteur","str",False],
  48. ["Demande validée","str",False],
  49. ["Transmission Dossier Paie/GA","str",False],
  50. ["Convention","str",False],
  51. ["Centre de formation","str",False],
  52. ["Adresse","str",False],
  53. ["Demande de badge","str",False],
  54. ["Commentaires","str",False]]
  55.  
  56.  
  57. Var_CDD=[["N° Demande", "str", True],
  58. ["Situation", "ld", ["", "Active", "Annulée", "Fermée", "Pourvue"], True],
  59. ["CRH", "str", True],
  60. ["Intitulé du poste", "str", True],
  61. ["Zone géographique", "ld", ["", "Paris", "Arcueil", "Angers"], True],
  62. ["Nombre de CDD", "int", True],
  63. ["Famille","ld", ["", "EC","AS","IL","FC","GC","JU","CM","RH","CO","SA","MO"], True],
  64. ["Fonction", "str", True],
  65. ["Classe","ld",["","1","2","3","4","5","6","7"], True],
  66. ["Origine", "ld", ["", "Remplacement", "Surcroît", "Autre", "CDD Senior"], True],
  67. ["Personne remplacée", "str", True],
  68. ["Entité", "str", True],
  69. ["Structure","str", True],
  70. ["Hiérarchique", "str", True],
  71. ["C/NC", "ld", ["", "C", "NC"], True],
  72. ["Demande hiérarchique", "str", False],
  73. ["Réception RH12", "str", False],
  74. ["Présentation 1er candidat", "str", False],
  75. ["Présentation du candidat retenu", "str", False],
  76. ["Clôture de mission", "d", False],
  77. ["Date annulation", "d", False],
  78. ["Date arrivée prise de fonction", "d", False],
  79. ["Indicateur budget", "ld", ["","0","1"], False],
  80. ["Mat. candidat ou N° recrut.", "str", False],
  81. ["Date arrivée CNP ou date début contrat", "d", "dd", False],
  82. ["Date départ CNP ou date fin contrat", "d", "df", False],
  83. ["Candidat retenu", "str", False],
  84. ["Âge", "int", False],
  85. ["Sexe", "ld", ["", "H", "F"], False],
  86. ["Niveau études", "ld", ["", "Bac Professionnel", "Bac + 1", "Bac + 2", "DUT", "BTS", "DCG", "Licence", "DSCG", "Master 1", "Master 2", "Master Spécialisé"], False],
  87. ["Diplôme", "str", False],
  88. ["Origine candidature", "str", False],
  89. ["Date RDV manager", "d", False],
  90. ["Délai", "str", False],
  91. ["Durée du CDD (en mois)", "int", False],
  92. ["Prolongation (en mois)", "int", False],
  93. ["Actions", "str", False],
  94. ["Commentaires FH11", "str", False],
  95. ["Nbre candidat presente H", "str", False],
  96. ["Nbre candidat presente F", "str", False]]
  97.  
  98.  
  99. Var_alternants=[["Situation de la demande", "ld", ["", "Active", "Annulée", "Fermée", "Pourvue"], True],
  100. ["CHR", "str", True],
  101. ["N° RH", "str", True],
  102. ["Matricule", "str", True],
  103. ["NOM", "str", True],
  104. ["Prénom", "str", True],
  105. ["BU", "str", True],
  106. ["UNITE", "str", True],
  107. ["Site", "ld", ["", "Paris", "Arcueil", "Angers"], True],
  108. ["Date début de contrat", "d", "dd", True],
  109. ["Date fin de contrat", "d", "df", True],
  110. ["Type de contrat", "str", True],
  111. ["Diplôme préparé", "ld", ["", "Bac Professionnel", "Bac + 1", "Bac + 2", "DUT", "BTS", "DCG", "Licence", "DSCG", "Master 1", "Master 2", "Master Spécialisé"], True],
  112. ["Domaine métier", "str", True],
  113. ["Genre", "ld", ["", "H", "F"], False],
  114. ["Âge", "int", False],
  115. ["Tuteur", "str", False],
  116. ["Responsable hiérarchique", "str", False],
  117. ["Organisme de formation", "str", False],
  118. ["Adresse", "str", False],
  119. ["Date validation demande", "d", False],
  120. ["Commentaire", "str", False],
  121. ["Date d'envoi à la Gestion Administrative", "d", False],
  122. ["Date d'envoi à la CCI ou l'OPCA", "d", False],
  123. ["Date d'envoi fiche période d'essai", "d", False]]
  124.  
  125.  
  126. Var_interim=[["Agence", "ld", ["", "ADECCO", "HAYS", "KELLY", "MANPOWER", "RANDSTAD"], True],
  127. ["NOM", "str", True],
  128. ["Prénom", "str", True],
  129. ["Sexe", "ld", ["", "H", "F"], True],
  130. ["Date de début", "d", "dd", True],
  131. ["Date de fin", "d", "df", True],
  132. ["Nature", "str", True],
  133. ["Mois", "ld", [str(i) for i in range(13)], True],
  134. ["Jours travaillés", "int", True],
  135. ["Jours payés", "int", True],
  136. ["Motif", "str", True],
  137. ["Description du motif", "str", True],
  138. ["Fonction", "str", True],
  139. ["Famille professionnelle", "str", True],
  140. ["Statut", "ld", ["", "C", "NC"], True],
  141. ["Nouvelle entité", "str", True],
  142. ["BU/Direction Groupe", "str", True],
  143. ["Site", "ld", ["", "Paris", "Arcueil", "Angers"], True],
  144. ["Observations", "str", True],
  145. ["Nbre d'interim", "int", True],
  146. ["Montant facturé", "float", True],
  147. ["Avoir", "float", True],
  148. ["N° Facture", "str", True],
  149. ["N° Engagement", "str", True],
  150. ["Centre budgétaire", "str", True],
  151. ["N° Contrat", "str", True],
  152. ["Taux horaire", "float", True],
  153. ["Coeff", "float", True],
  154. ["Taux facturé", "float", True]]
  155.  
  156.  
  157. ##Définition des classes
  158. class Menu_principal(Tk):
  159. dic_var = {"CDD":Var_CDD, "Alternants":Var_alternants, "Stages":Var_stages, "Intérim":Var_interim }
  160. dic_code_fichier = {"CDD":"CDD_", "Alternants":"alternants_", "Stages":"stages_", "Intérim":"Int_"}
  161. annees = [str(i) for i in range(2016, 2051)]
  162. """Affiche le menu principal"""
  163. dev = False
  164. def __init__(self, dev = False):
  165. self.dev = dev
  166. self.root = Tk()
  167. self.root.title("Menu principal")
  168. self.root.geometry("260x150")
  169.  
  170. #Widgets
  171. self.canvas = Canvas(self.root, width = 260, height = 150, highlightthickness = 0)
  172. self.canvas.place(relx = 0.5, rely = 0.5, anchor = CENTER)
  173.  
  174. self.frame = Frame(self.canvas)
  175. self.frame.pack()
  176. self.choix_fichier = Combobox(self.frame, exportselection=0, state="readonly", width = 10)
  177. self.choix_fichier["values"] = list(self.dic_var.keys())
  178. self.choix_fichier.set("Stages")
  179. self.choix_fichier.grid(row = 0, column = 0, padx=5, pady=4, sticky = E)
  180.  
  181. self.choix_annee = Combobox(self.frame, exportselection = 0, state = "readonly", width = 10)
  182. self.choix_annee["values"] = self.annees
  183. self.choix_annee.set(2017)
  184. self.choix_annee.grid(row = 0, column = 1, padx=5, pady=4, sticky = W)
  185.  
  186. Button(self.canvas, text="Ajouter un individu", width = 27, command = lambda : self.bouton_ig_ajout()).pack(padx=5, pady=4)
  187. Button(self.canvas, text="Modifier un individu", width = 27, command = lambda : self.bouton_ig_rech()).pack(padx=5, pady=4)
  188.  
  189. self.canvas.update()
  190. self.root.mainloop()
  191.  
  192. def init_var(self):
  193. """charge les variables sous la forme d'une liste"""
  194. self.liste_de_var = self.dic_var[self.choix_fichier.get()]
  195. self.filename = self.file_name()
  196. self.filepath = self.file_path()
  197.  
  198.  
  199. def code_fichier(self):
  200. """renvoie le code fichier en fonction du choix de l'utilisateur ; exemple : CDD_"""
  201. return self.dic_code_fichier[self.choix_fichier.get()]
  202.  
  203. def file_path(self):
  204. """renvoie le path du fichier associé à la saisie"""
  205. debut_path = self.choix_annee.get() + "\CSV"
  206. if not exists(debut_path):
  207. makedirs(debut_path)
  208. return join(debut_path, self.filename)
  209.  
  210. def file_name(self):
  211. """renvoie le nom du fichier associé à la saisie"""
  212. annee = self.choix_annee.get()
  213. return (self.code_fichier() + annee + ".csv")
  214.  
  215. def bouton_ig_ajout(self):
  216. self.init_var()
  217. utilisateur_veut_nouveau_fichier = self.pop_up_fichier_inexistant()
  218. if utilisateur_veut_nouveau_fichier == None:
  219. self.root.withdraw() #fait disparaître self.root (et tous ses widgets) ; pour le faire réapparaître, utiliser self.deiconify() ; le pb : quand utiliser self.deiconify() ??
  220. Interface_graphique(parent = self, which = "a")
  221. elif utilisateur_veut_nouveau_fichier:
  222. self.nouveau_fichier()
  223. self.root.withdraw() #fait disparaître self.root
  224. Interface_graphique(parent = self, which = "a")
  225.  
  226.  
  227. def bouton_ig_rech(self):
  228. self.init_var()
  229. utilisateur_veut_nouveau_fichier = self.pop_up_fichier_inexistant()
  230. if utilisateur_veut_nouveau_fichier == None:
  231. self.root.withdraw() #fait disparaître self.root
  232. Interface_graphique(parent = self, which = "r")
  233. elif utilisateur_veut_nouveau_fichier:
  234. self.nouveau_fichier()
  235.  
  236. def fichier_existe(self):
  237. try:
  238. with open(self.filepath):
  239. return True
  240. except IOError:
  241. return False
  242.  
  243. def pop_up_fichier_inexistant(self):
  244. if self.fichier_existe():
  245. return None
  246. elif not(self.fichier_existe()):
  247. return messagebox.askyesnocancel("Information", "Le fichier sélectionné n'existe pas. Voulez-vous en initialiser un nouveau ?")
  248.  
  249. def nouveau_fichier(self):
  250. """crée fichier csv vide (avec variables en 1e ligne)"""
  251. with open(self.filepath,'w') as file:
  252. writer = csv.writer(file, delimiter=";",lineterminator='\n')
  253. writer.writerow([self.liste_de_var[i][0] for i in range(len(self.liste_de_var))])
  254. return messagebox.showinfo("Information", "Fichier créé.")
  255.  
  256.  
  257. class Interface_graphique(Tk): #dans la suite : ig correspondra à interface graphique
  258. dic_texte_infobulle = {"int":"Saisissez un entier.", "d":"Saisissez une date au format jj/mm/aaaa.", "float":"Saisissez un nombre décimal.", "duree_stage":"Saisissez un entier (unité : jour).", "indicateurbudget_cdd":"0 = à ne pas prendre dans le budget ; 1 = à prendre en compte."}
  259. """Affiche une interface graphique"""
  260. dev = False
  261. def __init__(self, parent, which, line_id_a_modif = None, index_liste = None):
  262. """Interface de recherche ou interface de modification"""
  263. self.type = which
  264. self.parent = parent #permet d'appeler le Tk "master"
  265. self.dev = self.parent.dev
  266. self.root = Toplevel(self.parent.root)
  267.  
  268. self.root.grab_set() #empêche de modifer les fenêtres parentes
  269. self.root.focus_set() #passe le focus (1e action du clavier par ex)
  270.  
  271. self.liste_de_var = self.parent.liste_de_var
  272. self.filename = self.parent.filename
  273. self.filepath = self.parent.filepath
  274. self.afficher_scrollbar = (self.filename[:-9] == "CDD")
  275.  
  276. if which == "r" : #charge ig de recherche
  277. self.ig_rech()
  278. elif which == "a" : #charge ig d'ajout
  279. self.ig_ajout()
  280. elif which == "m" : #charge ig de modification
  281. self.ig_modif(line_id_a_modif, index_liste)
  282.  
  283. for i in range(len(self.liste_de_var)): #on a déjà grid les labels avec self.creer_Tkobjets()
  284. self.Tkobjets[i].grid(row=i,column=1, pady=3, padx=5)
  285.  
  286. if not(self.afficher_scrollbar):
  287. self.frame.pack()
  288. else:
  289. self.frame.update()
  290. self.canvas.create_window(0, 0, window=self.frame, anchor = N + W)
  291. self.canvas.configure(scrollregion=self.canvas.bbox(ALL))
  292. self.root.bind('<Return>', lambda e: self.raccourci_entree())
  293. self.root.mainloop()
  294.  
  295.  
  296. def creer_Tkobjets(self):
  297. """Retourne les différents objets de saisie + grid les labels associes"""
  298. if not(self.afficher_scrollbar):
  299. self.canvas = Canvas(self.root, highlightthickness = 0)
  300. self.canvas.pack(side=LEFT, fill = BOTH)
  301.  
  302. else:
  303. self.scrollbar = Scrollbar(self.root, orient = "vertical")
  304. self.scrollbar.grid(row = 0, column = 1, sticky = N + S)
  305.  
  306. self.canvas = Canvas(self.root, yscrollcommand=self.scrollbar.set, width = 550, height = 750, highlightthickness = 0)
  307. self.canvas.grid(row = 0, column = 0, sticky=W+E+N+S)
  308. self.scrollbar.config(command=self.canvas.yview)
  309.  
  310. self.frame = Frame(self.canvas)
  311. L = []
  312. for i in range(len(self.liste_de_var)):
  313. #intitules des variables
  314. label = Label(self.frame,text=self.liste_de_var[i][0])
  315. label.grid(row=i,column=0,sticky=W, padx = 5)
  316.  
  317. #saisie texte ou listes deroulantes
  318. if self.liste_de_var[i][1]=="ld":
  319. L.append(Combobox(self.frame,exportselection=0, width = 17 ,state="readonly"))
  320. L[i]["values"] = self.liste_de_var[i][2]
  321. L[i].set(self.liste_de_var[i][2][0])
  322. else:
  323. L.append(Entry(self.frame, width = 20))
  324.  
  325. #infobulles
  326. if (self.filename[:-9] == "stages" and self.liste_de_var[i][0] == "Durée"): #"en mois"
  327. infoBulle(label, self.dic_texte_infobulle["duree_stage"])
  328. infoBulle(L[i], self.dic_texte_infobulle["duree_stage"])
  329. elif (self.filename[:-9] == "CDD" and self.liste_de_var[i][0] == "Indicateur budget"):
  330. infoBulle(label, self.dic_texte_infobulle["indicateurbudget_cdd"])
  331. infoBulle(L[i], self.dic_texte_infobulle["indicateurbudget_cdd"])
  332. elif est_dans(self.liste_de_var[i][1], ["int","float","d"]):
  333. infoBulle(label, self.dic_texte_infobulle[self.liste_de_var[i][1]])
  334. infoBulle(L[i], self.dic_texte_infobulle[self.liste_de_var[i][1]])
  335.  
  336. return L
  337.  
  338.  
  339. def saisie(self): #fusion de criteres et saisie_modif
  340. """Renvoie valeurs saisies par l'utilisateur sous le format [val1, ...]"""
  341. return [objet.get() for objet in self.Tkobjets]
  342.  
  343. def afficher_erreur(self,erreur):
  344. """Affiche message d'erreur si saisie incohérente ; retourne True si erreur, False sinon"""
  345. msg = ""
  346. if erreur == "type":
  347. Erreurs, dates_inversees = controle_type(self.saisie(), self.liste_de_var)
  348. if dates_inversees: #on remet dans le bon sens
  349. messagebox.showerror("Erreur","La date de fin est antérieure à la date de début.")
  350. return True
  351. elif erreur =="rempli":
  352. Erreurs = controle_est_vide(self.saisie(),self.liste_de_var)
  353. n_erreurs = sum(Erreurs) #Erreurs = [True/False, ... , True/False] avec : True = 1, False = 0
  354. if n_erreurs == 0:
  355. return False
  356. elif n_erreurs == 1:
  357. msg = "La variable " + self.liste_de_var[Erreurs.index(True)][0] + " est mal renseignée."
  358. elif n_erreurs > 1:
  359. msg_var = "" #stocke les noms des variables présentant des valeurs incohérentes
  360. for i in range(len(self.liste_de_var)):
  361. if Erreurs[i]:
  362. if n_erreurs > 1:
  363. msg_var = msg_var + " " + self.liste_de_var[i][0] + ","
  364. n_erreurs -= 1 #après cette ligne, il reste encore n_erreurs variables à ajouter à msg_var
  365. elif n_erreurs == 1: # il reste une seule variable
  366. msg_var = msg_var + " et " + self.liste_de_var[i][0]
  367. msg = "Les variables" + msg_var + " sont mal renseignées."
  368. if erreur == "rempli":
  369. n_erreurs = sum(Erreurs)
  370. if n_erreurs == 1:
  371. msg = msg + " (Cette variable ne peut être laissée vide.)"
  372. elif n_erreurs > 1:
  373. msg = msg + " (Ces variables ne peuvent être laissées vides.)"
  374. messagebox.showerror('Erreur',msg)
  375. return True
  376.  
  377. def raccourci_entree(self):
  378. """Doit déterminer s'il faut lancer bouton_modif ou bouton_rech ou bouton_ajout lorsque l'utilisateur appuie sur la touche entree"""
  379. if self.root.title() == "Saisie de recherche":
  380. self.bouton_rech()
  381. elif self.root.title() == "Saisie de modification":
  382. self.bouton_modif()
  383. elif self.root.title() == "Saisie d'ajout":
  384. self.bouton_ajout()
  385.  
  386. def fermer_ig(self):
  387. self.parent.root.deiconify()
  388. self.root.destroy()
  389.  
  390.  
  391. ##interface de recherche
  392. def ig_rech(self):
  393. self.root.title("Saisie de recherche")
  394. self.root.protocol("WM_DELETE_WINDOW",lambda : self.fermer_ig()) #s'exécute quand on appuie sur la croix de fermeture
  395. self.root.bind('<Escape>', lambda e: self.fermer_ig()) #s'execute lorsqu'on appuie sur Echap
  396.  
  397. #Widgets
  398. self.Tkobjets = self.creer_Tkobjets()
  399. Button(self.frame, text="Rechercher",command = lambda : self.bouton_rech()).grid(row=0,column=3, padx = 5, pady = 5)
  400.  
  401. def bouton_rech(self):
  402. erreur_types = self.afficher_erreur(erreur = "type")
  403. if not(erreur_types):
  404. self.root.withdraw() #fait disparaître self.root
  405. Resultats_recherche(saisie_recherche = self)
  406.  
  407. def elem_list(self):
  408. """Retourne iterable pour remplir la listbox de Resultats_recherche, recalcule resultats recherche"""
  409. a_afficher = reponse_recherche(self.saisie(), self.filepath,self.liste_de_var) #est une liste dont les éléments sont de la forme [[val1, ... , val n], nligne]
  410. elem_list=[]
  411. for i in range(len(a_afficher)):
  412. elem="| "
  413. for j in range(len(self.liste_de_var)):
  414. if a_afficher[i][0][j] != "" and self.liste_de_var[j][-1]:
  415. elem=elem+a_afficher[i][0][j]+" | " # a_afficher[i][0][j] = val(j+1)
  416. elem_list.append(elem)
  417. return elem_list
  418.  
  419.  
  420. ##interface d'ajout
  421. def ig_ajout(self):
  422. self.root.title("Saisie d'ajout")
  423. self.root.bind('<Escape>', lambda e: self.fermer_ig())
  424. self.root.protocol("WM_DELETE_WINDOW",lambda : self.fermer_ig()) #s'exécute quand on appuie sur la croix de fermeture
  425.  
  426. self.vider_champs_apres_saisie = IntVar() #création de "variable-retour" pour Checkbutton
  427.  
  428. #Widgets
  429. self.Tkobjets = self.creer_Tkobjets()
  430. Checkbutton(self.frame, variable = self.vider_champs_apres_saisie, text = "Vider les champs après la saisie").grid(row = 1, column = 3, padx = 5)
  431. Button(self.frame, text="Ajouter",command = lambda : self.bouton_ajout()).grid(row=0,column=3, padx = 5, pady = 5)
  432.  
  433.  
  434. def bouton_ajout(self):
  435. """Si confirmation, rend effectif la saisie de l'utilisateur"""
  436. erreur_rempli = self.afficher_erreur(erreur = "rempli")
  437. if not(erreur_rempli):
  438. erreur_types = self.afficher_erreur(erreur = "type")
  439. if not(erreur_types):
  440. if pop_up_confirmer():
  441. add_line(self.saisie(), self.filepath)
  442. if self.vider_champs_apres_saisie.get():
  443. self.vider_formulaire()
  444. messagebox.showinfo("Information", "L'individu a bien été ajouté à la base de données.")
  445.  
  446. def vider_formulaire(self):
  447. """Vide le formulaire"""
  448. for i in range(len(self.liste_de_var)):
  449. if self.liste_de_var[i][1]=="ld":
  450. self.Tkobjets[i].set("")
  451. else:
  452. self.Tkobjets[i].delete(0,END)
  453.  
  454.  
  455. ##interface de modification
  456. def ig_modif(self, line_id_a_modif, index_liste):
  457. #ces valeurs sont constantes dans interface de modif
  458. self.line_id_a_modif = line_id_a_modif
  459. self.index_liste = index_liste
  460. self.root.title("Saisie de modification")
  461.  
  462. self.root.bind('<Escape>', lambda e: self.root.confirmer_sortie_saisie_modifiee())
  463. self.root.protocol("WM_DELETE_WINDOW",lambda : self.confirmer_sortie_saisie_modifiee())
  464.  
  465. #Widgets
  466. self.Tkobjets = self.creer_Tkobjets()
  467. self.preremplir_formulaire() #charge les données du csv quant à l'individu ciblé
  468. Button(self.frame, text="Modifier",command = lambda : self.bouton_modif()).grid(row=0,column=3, padx = 5, pady = 5)
  469.  
  470. self.hardcore = IntVar() #création de "variable-retour" pour Checkbutton
  471. Checkbutton(self.frame, variable = self.hardcore, text = "Ne pas demander de confirmation").grid(row = 1, column = 3, padx = 5)
  472.  
  473.  
  474. def bouton_modif(self):
  475. """Si confirmation, rend effectif la saisie de l'utilisateur"""
  476. erreur_rempli = self.afficher_erreur(erreur = "rempli")
  477. if not(erreur_rempli):
  478. erreur_types = self.afficher_erreur(erreur = "type")
  479. if not(erreur_types):
  480. if self.hardcore.get() or pop_up_confirmer():
  481. mod_line(self.line_id_a_modif, self.saisie(), self.filepath)
  482. self.parent.update_liste()
  483. self.parent.liste.selection_set(self.index_liste)
  484.  
  485. def preremplir_formulaire(self):
  486. """Preremplit avec valeurs du csv"""
  487. if self.type == "m":
  488. ligne_select = read_line(self.line_id_a_modif, self.filepath)
  489. elif self.type == "a" and self.dev == True:
  490. ligne_select = creer_exemples(1, self.liste_de_var, self.parent.choix_annee.get())[0]
  491. for i in range(len(self.liste_de_var)):
  492. if self.liste_de_var[i][1]=="ld":
  493. for item in self.liste_de_var[i][2]:
  494. if est_dans(ligne_select[i],self.liste_de_var[i][2]): #pour éviter bug (sur Listbox) si données mal renseignées dans le csv
  495. self.Tkobjets[i].set(ligne_select[i]) #on fixe à la valeur du csv
  496. else:
  497. self.Tkobjets[i].insert(0,ligne_select[i]) #idem
  498.  
  499.  
  500. def confirmer_sortie_saisie_modifiee(self):
  501. if self.saisie() != read_line(self.line_id_a_modif, self.filepath):
  502. if messagebox.askokcancel("Confirmation", "Êtes-vous sûr de vouloir quitter sans enregistrer les modifications ?"):
  503. self.root.destroy()
  504. else:
  505. self.root.destroy()
  506.  
  507.  
  508. class Resultats_recherche(Tk):
  509. """Affiche les resultats de la recherche dans une Listbox"""
  510. dev = False
  511. def __init__(self, saisie_recherche, dev = False):
  512. "Listbox + 2 boutons si resultats, messagebox sinon"
  513. self.parent = saisie_recherche
  514. self.dev = self.parent.dev
  515.  
  516. if self.parent.elem_list() == []:
  517. messagebox.showinfo("Résultats de la recherche","Aucun individu ne répond à la requête.")
  518. self.parent.root.deiconify()
  519.  
  520. else: #ne se lance que si des individus correspondent à la requête
  521. self.root = Toplevel(self.parent.root)
  522. self.root.title("Résultats de la recherche")
  523.  
  524. self.root.grab_set() #empêche de modifer les fenêtres parentes
  525. self.root.focus_set() #passe le focus (1e action du clavier par ex)
  526.  
  527. self.liste_de_var = self.parent.liste_de_var
  528. self.filename = self.parent.filename
  529. self.filepath = self.parent.filepath
  530.  
  531. #creation de la Listbox
  532. self.liste = self.remplir_listbox()
  533. self.liste.pack()
  534.  
  535. #Autres widgets
  536. Button(self.root, text="Modifier",command=lambda:self.bouton_ig_modif()).pack()
  537. Button(self.root, text="Supprimer",command=lambda:self.bouton_suppr()).pack()
  538.  
  539. self.root.protocol("WM_DELETE_WINDOW",lambda : self.fermer_fenetre())
  540. self.root.bind('<Escape>', lambda e: self.fermer_fenetre())
  541. self.root.mainloop()
  542.  
  543.  
  544. def remplir_listbox(self):
  545. """Cree la Listbox"""
  546. elem_list = self.parent.elem_list()
  547. listbox = Listbox(self.root, height=len(elem_list), width = max(len(ligne) for ligne in elem_list), exportselection=0, activestyle = 'none')
  548. for item in elem_list:
  549. listbox.insert(END,item)
  550. return listbox
  551.  
  552. def line_id(self):
  553. """Renvoie line_id dans csv de la selection"""
  554. choisi = self.liste.get(ACTIVE)
  555. n0 = self.parent.elem_list().index(choisi) #indice dans la listbox de la sélection
  556. line_id = reponse_recherche(self.parent.saisie(), self.filepath, self.liste_de_var)[n0][1] #indice dans le csv
  557. return line_id
  558.  
  559. def index_liste(self):
  560. """Renvoie index dans la liste de la selection"""
  561. return self.liste.curselection()
  562.  
  563. def bouton_ig_modif(self):
  564. """Ouvre interface graphique pour modifier individu selectionne et renvoie line_id, index_liste"""
  565. Interface_graphique(parent = self, which = "m", line_id_a_modif = self.line_id(), index_liste = self.index_liste())
  566.  
  567. def bouton_suppr(self):
  568. """Si confirmation, supprime la selection de la Listbox"""
  569. if pop_up_confirmer():
  570. del_line(self.line_id(),self.filepath)
  571. self.update_liste()
  572.  
  573. def update_liste(self):
  574. """Actualise la Listbox"""
  575. elem_list = self.parent.elem_list()
  576. self.liste.delete(0,END)
  577. for item in elem_list:
  578. self.liste.insert(END,item)
  579.  
  580. def fermer_fenetre(self):
  581. self.root.destroy()
  582. self.parent.root.destroy()
  583. self.parent.parent.root.deiconify()
  584.  
  585.  
  586. class infoBulle(Toplevel):
  587. def __init__(self,parent=None,texte='', temps=500):
  588. Toplevel.__init__(self,parent,bd=1,bg='black')
  589. self.tps=temps
  590. self.parent=parent
  591. self.withdraw()
  592. self.overrideredirect(1)
  593. self.transient()
  594. l=Label(self,text=texte,background="white",justify='left')
  595. l.update_idletasks()
  596. l.pack()
  597. l.update_idletasks()
  598. self.tipwidth = l.winfo_width()
  599. self.tipheight = l.winfo_height()
  600. self.parent.bind('<Enter>',self.delai)
  601. self.parent.bind('<Button-1>',self.efface)
  602. self.parent.bind('<Leave>',self.efface)
  603. def delai(self,event):
  604. self.action=self.parent.after(self.tps,self.affiche)
  605. def affiche(self):
  606. self.update_idletasks()
  607. posX = self.parent.winfo_rootx()+self.parent.winfo_width()
  608. posY = self.parent.winfo_rooty()+self.parent.winfo_height()
  609. if posX + self.tipwidth > self.winfo_screenwidth():
  610. posX = posX-self.winfo_width()-self.tipwidth
  611. if posY + self.tipheight > self.winfo_screenheight():
  612. posY = posY-self.winfo_height()-self.tipheight
  613. #~ print posX,print posY
  614. self.geometry('+%d+%d'%(posX,posY))
  615. self.deiconify()
  616. def efface(self,event):
  617. self.withdraw()
  618. self.parent.after_cancel(self.action)
  619.  
  620.  
  621. ##outils csv
  622. def read_line(line_id,file_name):
  623. with open(file_name,'r') as myfile:
  624. r = csv.reader(myfile, delimiter=';', quotechar='"', lineterminator='\n')
  625. lines=[l for l in r]
  626. return lines[line_id]
  627.  
  628. def add_line(new_line, file_name):
  629. """ajoute ligne à la fin du csv"""
  630. #on lit le fichier en mode "append"
  631. with open(file_name,'a') as myfile:
  632. w = csv.writer(myfile, delimiter=';', quotechar='"', lineterminator='\n')
  633. w.writerow(new_line)
  634.  
  635. def mod_line(line_id, new_line,file_name):
  636. """remplace la ligne d'indice line_id par new_line"""
  637. #on lit le fichier en mémoire
  638. with open(file_name,'r') as myfile:
  639. r = csv.reader(myfile, delimiter=';', quotechar='"', lineterminator='\n')
  640. lines=[l for l in r]
  641. lines[line_id]=new_line
  642. # puis on le modifie (toujours en mémoire), avant d'en réécrire toutes les lignes.
  643. with open(file_name,'w') as myfile:
  644. w = csv.writer(myfile, delimiter=';', quotechar='"', lineterminator='\n')
  645. w.writerows(lines)
  646.  
  647. def del_line(line_id,file_name):
  648. """supprime la ligne d'indice line_id"""
  649. # on lit le fichier en mémoire
  650. with open(file_name,'r') as myfile:
  651. r = csv.reader(myfile, delimiter=';', quotechar='"', lineterminator='\n')
  652. lines=[l for l in r]
  653. lines.pop(line_id)
  654. # puis on le modifie (toujours en mémoire), avant d'en réécrire toutes les lignes.
  655. with open(file_name,'w') as myfile:
  656. w = csv.writer(myfile, delimiter=';', quotechar='"', lineterminator='\n')
  657. w.writerows(lines)
  658.  
  659.  
  660. ##fonctions
  661. def est_inclus(str1, str2):
  662. """True si str1 est inclus dans str2, False sinon"""
  663. diff = len(str2) - len(str1)
  664. if diff < 0:
  665. return False
  666. elif diff == 0:
  667. return (str1 == str2)
  668. elif diff > 0:
  669. return(est_inclus(str1,str2[1:]) or est_inclus(str1,str2[:-1]))
  670.  
  671. def est_dans(elem_candidat,list):
  672. """True si elem_candidat est dans list, False sinon"""
  673. reponse = False
  674. for elem in list:
  675. if elem == elem_candidat:
  676. reponse = True
  677. break
  678. return reponse
  679.  
  680. def reponse_recherche(criteres, file_name, liste_de_var):
  681. """Renvoie les résultats de la recherche"""
  682. "Liste dont les éléments sont de la forme [[val1, ... , val25], line_id]"
  683. Lindiv_et_nligne = []
  684. with open(file_name,'r') as myfile:
  685. reader = csv.reader(myfile, delimiter=';', quotechar='"', lineterminator='\n')
  686. lines=[l for l in reader]
  687. for i in range(1,len(lines)): #on commence à 1 pour écarter la ligne des intitulés de variables
  688. for j in range(len(liste_de_var)): #j comptera presque le nombre de critères remplis
  689. if not(est_inclus(criteres[j],lines[i][j])) and (criteres[j] != ""): #(match critère ou critère inactif)
  690. j = 0
  691. break
  692. if j == len(liste_de_var) - 1:
  693. Lindiv_et_nligne.append([lines[i],i])
  694. return Lindiv_et_nligne
  695.  
  696. def pop_up_confirmer():
  697. """renvoie True si l'utilisateur clique sur 'OK', False sinon"""
  698. return messagebox.askokcancel("Demande de confirmation", "Êtes-vous sûr de vouloir continuer ?")
  699.  
  700. def est_float(s):
  701. """Verifier qu'un string est un float ou est vide"""
  702. if s == "":
  703. return True
  704. else:
  705. try:
  706. float(s)
  707. return True
  708. except Exception:
  709. return False
  710.  
  711. def est_entier(s):
  712. """Verifier qu'un string est un entier ou est vide"""
  713. if s == "":
  714. return True
  715. else:
  716. try:
  717. int(s)
  718. return True
  719. except Exception:
  720. return False
  721.  
  722. def est_date(d):
  723. """Verifier qu'un string est soit vide soit un date sous la forme jour/mois/annee"""
  724. if d == "":
  725. return True
  726. else:
  727. try:
  728. datetime.strptime(d, '%d/%m/%Y')
  729. return True
  730. except Exception:
  731. return False
  732.  
  733. def controle_type(indiv, liste_de_var):
  734. """vérifie la cohérence des valeurs de l'individu 'indiv' conformément à la liste de variables 'liste_var'"""
  735. Erreurs=[] #liste de booléens : Erreurs[i] vaut True s'il y a une erreur de saisie sur indiv[i], False sinon.
  736. dates_inversees = False #vaut True si la date de fin est antérieure à la date de début
  737. #on teste toutes les variables suivant leur type :
  738. for i in range(len(liste_de_var)):
  739. if est_dans(liste_de_var[i][1], ["str", "ld"]):
  740. Erreurs.append(False)
  741. elif liste_de_var[i][1]=="int":
  742. Erreurs.append(not(est_entier(indiv[i])))
  743. elif liste_de_var[i][2] == "float":
  744. Erreurs.append(not(est_float(indiv[i])))
  745. elif liste_de_var[i][1]=="d":
  746. Erreurs.append(not(est_date(indiv[i])))
  747. if liste_de_var[i][2]=="dd":
  748. date_debut=indiv[i]
  749. elif liste_de_var[i][2]=="df":
  750. date_fin=indiv[i]
  751. #on teste ensuite si la date de début est bien antérieure à la date de fin
  752. try:
  753. dates_inversees = datetime.strptime(date_debut, "%d/%m/%Y") > datetime.strptime(date_fin, "%d/%m/%Y")
  754. except ValueError:
  755. pass
  756. return (Erreurs,dates_inversees)
  757.  
  758. def controle_est_vide(indiv, liste_de_var):
  759. """vérifie que les variables importantes ne sont pas vides"""
  760. est_vide = [] #est_vide[i] = True si indiv[i] = "" (champ laissé vide)
  761. for i in range(len(liste_de_var)):
  762. if liste_de_var[i][-1]:
  763. est_vide.append(indiv[i] == "")
  764. elif not(liste_de_var[i][-1]):
  765. est_vide.append(False) #on considère que pour les variables non importantes, laisser vide leurs champs respectifs n'est pas une erreur
  766. return est_vide
  767.  
  768.  
  769. ##exécution
  770. f = Menu_principal()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement