def askResetBind(): """ Dialogue qui demande quelle binding retrouver @return binding : <str> "defaut" -> cherche le binding par défaut "custom" -> cherche l'ancien binding de l'utilisateur "vide" -> retire le binding "" <None> -> annule l'opération """ binding = None def onClose(btn): nonlocal binding if btn == "Annuler": pass elif btn == "Par défaut": binding = "defaut" elif btn == "Précédent": binding = "custom" elif btn == "Vide": binding = "vide" fen.destroy() fen = Dialog(title="Choix du raccourci", buttons=("Précédent", "Par défaut", "Vide", "Annuler"), command=onClose, exitButton=("Précédent", "Par défaut", "Vide", "Annuler")) Label(fen, text="Quelle est le raccourci que vous voulez retrouver ?").pack( expand=YES, fill=BOTH, padx=2, pady=2) fen.activateandwait() return binding
def askRestart(): """ Dialog qui demande si l'utilisateur veut redemarrer l'application """ restart = None def Onclose(button): nonlocal restart if button == "Redémarrer": restart = True elif button == "Redémarrer plus tard" or button == 'WM_DELETE_WINDOW': restart = False else: restart = False f = Dialog(master=None, title="Redémarrage requis", buttons=("Redémarrer", "Redémarrer plus tard"), defaultbutton="Redémarrer", exitButton=("Redémarrer", "Redémarrer plus tard", 'WM_DELETE_WINDOW'), command=Onclose) l = Label( f, text= "Une ou plusieurs de vos modifications nécéssitent un redémarrage\nde l'applicationpour être correctement appliquées.\n\nVoulez-vous redémarrer l'application maintenant ?" ) l.pack() f.activateandwait() return restart
def askGroup(periode): """ Permet de demander à l'utilisateur les informations nécessaires pour créer un groupe. @param periode: La période dans laquelle sera le groupe. """ # Variables : groupe = None nom = None desc = None color = None # Callback : def onClose(button): nonlocal groupe, nom, desc, color nom = nomWidget.get() desc = descWidget.get("0.0", END) color = colorWidget.get() if button == "Ok": if not nom: showerror("Nom incorrect", "Vous devez nommer le groupe.") else: groupe = Groupe(nom, periode, desc, color) fen.destroy() # Dialogue : fen = Dialog(title="Ajouter un groupe", buttons=("Ok", "Annuler"), exitButton=("Annuler", "WM_DELETE_WINDOW"), command=onClose) # Widgets : nomGroupe = LabelFrame(fen, text="Nom") descGroupe = LabelFrame(fen, text="Description") nomWidget = Entry(nomGroupe) colorWidget = ColorButton(nomGroupe) descWidget = Text(descGroupe, wrap="word") # Placements : descGroupe.pack(side=BOTTOM, expand=YES, fill=BOTH) nomGroupe.pack(side=TOP, expand=YES, fill=X) nomWidget.pack(side=LEFT, fill=X, expand=YES) colorWidget.pack(side=RIGHT) descWidget.pack(expand=YES, fill=BOTH) # Activation : fen.activateandwait() # Retour : return groupe
def askDureeJours(): duree = None def onClose(button): nonlocal duree if button == "Ok": duree = datetime.timedelta(**{{"Semaines":"weeks", "Jours": "days"}[unit.get()]:int(delta.get())}) fen.destroy() fen = Dialog(title = "Décaler des périodes", buttons = ("Ok", "Annuler"), command = onClose) delta = Spinbox(fen, from_ = 0, to = 1000) delta.pack(side = LEFT, expand = YES, fill = X) delta.set(0) unit = Combobox(fen, values = ["Jours", "Semaines"]) unit.set(unit.cget("values")[0]) unit.pack(side = RIGHT, expand = YES, fill = X) fen.activateandwait() return duree
def askEditGroupe(groupe): """ Dialogue qui édite le groupe sur ses paramètres @param groupe : <Groupe> le groupe à modifier """ def onClose(button): if button == "Ok": paramGroupe.onClose() elif button == "Supprimer": groupe.delete() # Pendant qu'on a encore le droit de l'utiliser groupe.getApplication().getDonneeCalendrier().updateAffichage(True) fen.destroy() fen = Dialog(title="Édition de \"%s\"" % groupe.getNom(), buttons=("Ok", "Supprimer", "Annuler"), exitButton=("Ok", "Supprimer", "Annuler"), command=onClose) paramGroupe = GroupeParametre(fen, groupe) paramGroupe.pack(fill=BOTH, expand=YES) fen.activateandwait()
def askAfficherMasquer(periodManager): """ Dialogue qui permet de gérer la visibilitée des schedulables @param periodManager : <PeriodManager> celui de l'app @return masquage : <bool> True si au moins une tache n'est pas visible """ masquage = False def onClose(b): nonlocal masquage if b == "Ok": masquage = gestion.onClose(b) fen.destroy() fen = Dialog(title="Afficher ou masquer des taches", buttons=("Ok", "Annuler"), command=onClose) gestion = AfficherMasquer(fen, periodManager) gestion.pack(fill=BOTH, expand=YES) fen.activateandwait() return masquage
def askdate(): """ Permet d'ouvrir une boîte de dialogue usuelle pour demander la date (et pas l'heure) à l'utilisateur. @return datetime.date() choisie par l'utilisateur ou None """ # Variable qui contient la valeur de retour : dateretour = None # Callback quand on ferme la fenêtre par l'un des boutons : def onClose(a): nonlocal dateretour # Si on annule : if a == 'Annuler' or a == 'WM_DELETE_WINDOW': fen.destroy() return # Si on accepte : if cal.selection is not None: dateretour = cal.selection.date() # on ferme la fenêtre. fen.destroy() # Construction du dialogue : fen = Dialog(title="Choix de la date", buttons=("Ok", "Annuler"), command=onClose) # création des widgets : cal = Calendar(master=fen, firstweekday=calendar.MONDAY) # placement des widgets : cal.pack(expand=1, fill='both') # et on active le dialogue. fen.activateandwait() return dateretour
def askEditTask(task): """ Dialogue qui édite la tache sur ses paramètres @param task : <Task> la tache à modifier """ def onClose(button): if button == "Ok": taskUndo = task.saveByDict() parametrage.onClose() UndoRedoTaskEditing(taskUndo, task) elif button == "Supprimer": task.delete() # Pendant qu'on a encore le droit de l'utiliser task.getApplication().getDonneeCalendrier().updateAffichage(True) fen.destroy() fen = Dialog(title="Édition de \"%s\"" % task.getNom(), buttons=("Ok", "Supprimer", "Annuler"), exitButton=("Ok", "Supprimer", "Annuler"), command=onClose) parametrage = TaskParametre(fen, task) parametrage.pack(fill=BOTH, expand=YES) fen.activateandwait()
def askDupliquerPeriode(periodManager, taskEditor, from_=None): from affichages.periode.Periode import Periode from ..PeriodAdder import PeriodAdder per = from_ def onClose(button): if button == "Ajouter": periode = p.createPeriode() ## Ajout des schedulables et autres : # Il faut en faire une copie, # Mais pour éviter tout les soucis de non copies en profondeur # (je pense notamment aux dépendances qui pourrait alors se trouver dans une autre période...) # on va faire une "sauvegarde-lecture" dans la RAM. data = per.saveByDict() newPeriode = Periode.load(data, periodManager) # Puis on remet les attributs : newPeriode.setNom(periode.getNom()) newPeriode.setDebut(periode.getDebut()) newPeriode.setFin(periode.getFin()) newPeriode.setDescription(periode.getDescription()) newPeriode.setColor(periode.getColor()) newPeriode.setUniqueID() periodManager.ajouter(newPeriode) fen = Dialog(command=onClose, buttons=("Ajouter", "Fermer"), exitButton=("Fermer", "WM_DELETE_WINDOW")) fen.redessiner = taskEditor.redessiner fen.ajouter = taskEditor.ajouter fen.getApplication = taskEditor.getApplication p = PeriodAdder(periodManager, fen) p.pack(expand=YES, fill=BOTH) p.boutonValider.grid_forget() # Ajouter les champs déjà rempli si on a une période : if per is not None: p.debut = per.getDebut() p.fin = per.getFin() p.champNom.insert(END, per.nom) p.champDebut.config(text=p.debut) p.champFin.config(text=p.fin) p.champDescription.insert(END, per.desc) p.boutonColor.set(per.color) fen.activateandwait()
def askModifierPeriode(periodManager, taskEditor, from_=None): from ..PeriodAdder import PeriodAdder per = from_ def onClose(button): if button == "Modifier": # La création d'une période permettra de récupérer ses attributs. # Cependant, cette période est temporaire au calcul et n'est pas # ajoutée au PeriodManager. periode = p.createPeriode() # Puis on remet les attributs : per.setNom(periode.getNom()) per.setDebut(periode.getDebut()) per.setFin(periode.getFin()) per.setDescription(periode.getDescription()) per.setColor(periode.getColor()) fen = Dialog(command=onClose, buttons=("Modifier", "Annuler"), exitButton=("Modifier", "Annuler", "Fermer", "WM_DELETE_WINDOW")) # Pour faire croire au PeriodAdder que le Dialog est le TaskEditor. fen.redessiner = taskEditor.redessiner fen.ajouter = taskEditor.ajouter fen.getApplication = taskEditor.getApplication p = PeriodAdder(periodManager, fen) p.pack(expand=YES, fill=BOTH) p.boutonValider.grid_forget() # Ajouter les champs déjà rempli si on a une période : if per is not None: p.debut = per.getDebut() p.fin = per.getFin() p.champNom.insert(END, per.nom) p.champDebut.config(text=p.debut) p.champFin.config(text=p.fin) p.champDescription.insert(END, per.desc) p.boutonColor.config(bg=per.color) fen.activateandwait()
def askComplicationjour(tache, periodeManager): choix = None periode = None def onClose(bouton): # Permet de modifier les valeurs des variables nonlocal choix, periode if bouton == 'Ok': choix = varRadio.get() if choix == "changer": for p in periodeManager.getPeriodes(): if p.nom == combo.get(): periode = p fen.destroy() def stateCombobox(): if varRadio.get() == "changer": combo.config(state=ACTIVE) else: combo.config(state=DISABLED) fen = Dialog(title="%s n'est pas dans la période active" % tache.getNom(), buttons=("Ok", "Annuler"), command=onClose, exitButton=('Ok', 'Annuler', "WM_DELETE_WINDOW")) # Binding des touches fen.bind_all("<Return>", lambda e: fen.execute("Ok")) fen.bind_all("<Escape>", lambda e: fen.execute("Annuler")) l = Label( fen, text= "La tache \"%s\" se trouve maintenant hors de la période. Que voulez-vous faire ?" % tache.getNom()) frameRadio = LabelFrame(fen, text="Options") varRadio = StringVar() r1 = Radiobutton(frameRadio, text="Agrandir la période", value="agrandir", variable=varRadio, command=stateCombobox) r2 = Radiobutton(frameRadio, text="Faire de %s une tache indépendante" % tache.getNom(), value="independante", variable=varRadio, command=stateCombobox) r3 = Radiobutton(frameRadio, text="Supprimer %s." % tache.getNom(), value="supprimer", variable=varRadio, command=stateCombobox) r4 = Radiobutton(frameRadio, text="Changer la période de %s." % tache.getNom(), value="changer", variable=varRadio, command=stateCombobox) # valeur par défaut : varRadio.set("agrandir") r1.grid(sticky="w") r2.grid(row=1, sticky="w") r3.grid(row=2, sticky="w") r4.grid(row=3, sticky="w") periodesExistantes = periodeManager.getPeriodes() pp = Periode(periodeManager, "", tache.getDebut().date(), tache.getFin().date(), "") periodesExistantes = [ p.nom for p in periodesExistantes if p.intersectWith(pp) ] combo = Combobox(frameRadio, values=periodesExistantes) combo.grid(row=3, column=1, sticky="we") if periodesExistantes: combo.set(combo.cget("values")[0]) else: combo.config(state=DISABLED) r4.config(state=DISABLED) l.pack() frameRadio.pack(expand=YES, fill=BOTH, pady=4, padx=4) # Active et attend (un peu comme une mainloop) fen.activateandwait() return choix, periode
def askDecalJour(debut, fin, totBloque, tarBloque): """ Dialog pour demander comment décaler les tâches @param debut : (date) jour de début de la période @param fin : (date) jour de fin de la période @param totBloque : (int) nombre de jours à partir duquel le blocage devient utile @param tarBloque : (int) nombre de jours à partir duquel le blocage devient utile """ nbJour = None position = None param = None def onClose(bouton): # Permet de modifier les valeurs des variables nonlocal nbJour, position, param if bouton == 'Ok': position = varRadioGestion.get() param = varRadioParam.get() nbJour = int(sb.get()) if varRadioGestion.get() == "tot": nbJour = nbJour * -1 fen.destroy() # Pour adapter le nombre d'jour max du spinBoc def adapteSpinbox(): newVar = 0 if varRadioGestion.get() == "tot" and varRadioParam.get() == "bloquer": newVar = totBloque elif varRadioGestion.get() == "tard" and varRadioParam.get( ) == "bloquer": newVar = tarBloque elif varRadioGestion.get() == "tot" or varRadioGestion.get() == "tard": newVar = "+inf" # On config sb.config(to=newVar) # Si on dépasse, on reset au max if isinstance(newVar, str): return if int(sb.get()) > newVar: sb.set(int(newVar)) fen = Dialog(title="Nombre de jours à déplacer", buttons=("Ok", "Annuler"), command=onClose, exitButton=('Ok', 'Annuler', "WM_DELETE_WINDOW")) # Binding des touches fen.bind_all("<Return>", lambda e: fen.execute("Ok")) fen.bind_all("<Escape>", lambda e: fen.execute("Annuler")) # Mettre les widgets framePos = Frame(fen) varRadioGestion = StringVar() rG1 = Radiobutton(framePos, text="Déplacer plus tot", variable=varRadioGestion, value="tot", command=adapteSpinbox) rG2 = Radiobutton(framePos, text="Déplacer plus tard", variable=varRadioGestion, value="tard", command=adapteSpinbox) rG1.grid(row=0, sticky="w") rG2.grid(row=1, sticky="w") frameJour = Labelframe(fen, text="Déplacer de combien de jours ?") lb = Label(frameJour, text="Nombre de jours :") sb = Spinbox(frameJour, from_=0, to="+inf") sb.set(0) lb.pack(side=LEFT, fill=BOTH) sb.pack(side=LEFT, fill=X, expand=YES) frameParametre = Frame(fen) varRadioParam = StringVar() rP1 = Radiobutton(frameParametre, text="Garder la même durée entre chaque tache", variable=varRadioParam, value="duree", command=adapteSpinbox) rP2 = Radiobutton( frameParametre, text="Garder les tâches entre le %02i/%02i et le %02i/%02i" % (debut.day, debut.month, fin.day, fin.month), variable=varRadioParam, value="bloquer", command=adapteSpinbox) rP1.grid(row=0, sticky="w") rP2.grid(row=1, sticky="w") framePos.pack(side=TOP, expand=YES, fill=BOTH) frameJour.pack(side=TOP, expand=YES, fill=BOTH) frameParametre.pack(side=TOP, expand=YES, fill=BOTH) varRadioGestion.set("tard") varRadioParam.set("duree") # Active et attend (un peu comme une mainloop) fen.activateandwait() return nbJour, position, param
def askDureeTache(master, longueurPeriode): """ Permet de demander la durée de la tâche à l'utilisateur. @param longueurPeriode = longueur de la periode @return la durée choisie par l'utilisateur. """ # Fonction quand on ferme le dialogue : duree = None def onClose(bouton): """Exécutée quand l'un des boutons du dialogue est pressé.""" nonlocal duree if bouton == "Ok": duree = datetime.timedelta(days=int(d.get()), hours=int(h.get()), minutes=int(m.get())) if duree > datetime.timedelta(): fen.quit() fen.destroy() else: showerror("Durée invalide", "Veuillez mettre une durée plus grande que 0.") else: fen.destroy() def adapteHeureJour(): """Adaptation des heures et des jours quand on augmente ou diminue trop les heures ou les minutes.""" minutes = int(m.get()) heures = int(h.get()) jours = int(d.get()) while minutes >= 60: minutes -= 60 heures += 1 while minutes < 0: minutes += 60 heures -= 1 while heures >= 24: heures -= 24 jours += 1 while heures < 0: heures += 24 jours -= 1 jours = max(0, min(jours, longueurPeriode.days)) m.set(minutes) h.set(heures) d.set(jours) # Création du dialogue : fen = Dialog(master, title="Choix de la durée de la tâche", buttons=("Ok", "Annuler"), command=onClose, exitButton=('Annuler', )) # Widgets du dialogue : Label(fen, text="Choisissez la durée de la Tâche").pack(side=TOP, fill=X) d = Spinbox(fen, from_=0, to=longueurPeriode.days, increment=1, width=4) d.pack(side=LEFT) Label(fen, text="Jours").pack(side=LEFT) h = Spinbox(fen, from_=-1, to=24, increment=1, width=4, command=adapteHeureJour) h.pack(side=LEFT) Label(fen, text="Heures").pack(side=LEFT) m = Spinbox(fen, from_=-1, to=60, increment=1, width=4, command=adapteHeureJour) m.pack(side=LEFT) Label(fen, text="Minutes").pack(side=LEFT) d.set(0) h.set(1) m.set(0) # lancement du dialogue: fen.activateandwait() return duree
def askScinderPeriode(periodManager, taskEditor, periode=None): """ """ # Vérifications : if periode.getDebut() == periode.getFin(): showerror( "Longueur de période invalide", "Il est impossible de scinder une période qui dure un seul jour.") raise ValueError( "Impossible de scinder une période qui dure un seul jour") def getDebut(): return periode.getDebut() + datetime.timedelta(days=1) # Une période de 2 jours ne peut être coupé qu'entre les 2 jours, # donc le début de la 2ème période dure à partir du 2ème jour, # soit la fin de cette période. (on notera que la fonction permet justement d'ignorer le début, # étant donné qu'il est impossible de scinder le même jour que ça commence). if getDebut() == periode.getFin(): return periode.getFin() # Variables à retourner : dateScindage = None # Fonction quand on appuie sur un bouton : def onClose(button): """ Fonction quand on appuie sur un bouton. @param button: le texte du bouton appuyé. """ nonlocal dateScindage if button == "Aujourd'hui": date = datetime.datetime.now().date() jour.set(date.day) if mois is not None: mois.set(MOIS[date.month - 1]) if annee is not None: annee.set(date.year) return if button == "Ok": dateScindage = datetime.date(getAnnee(), getMois(), getJour()) fen.destroy() def setJourCalendrier(): date = cal.selection if date is not None: date = date.date() if date < getDebut() or date > periode.getFin(): showerror( "Date invalide", "La date choisie est en dehors des limites de la période à scinder." ) return jour.set(date.day) if mois is not None: mois.set(MOIS[date.month - 1]) if annee is not None: annee.set(date.year) def adapteJours(): min = 1 max = tailleMois(getMois(), getAnnee()) if getMois() == getDebut().month and getAnnee() == getDebut().year: min = getDebut().day if getMois() == periode.getFin().month and getAnnee( ) == periode.getFin().year: max = periode.getFin().day jour.config(from_=min, to=max) if getJour() < min: jour.set(min) if getJour() > max: jour.set(max) def adapteMois(): if mois is not None: lesMois = set() for m in rangeMois(getDebut(), periode.getFin()): lesMois.add(m) lesVraisMois = [] for indice, m in enumerate(MOIS): if indice + 1 in lesMois: lesVraisMois.append(m) mois.config(values=lesVraisMois) if mois.get() not in lesVraisMois: mois.set(lesVraisMois[0]) def adapte(): adapteMois() adapteJours() def getJour(): if not jour.get(): jour.set(1) return int(jour.get()) def getMois(): if mois is None: return datetime.datetime.now().month return MOIS.index(mois.get()) + 1 def getAnnee(): if annee is None: return datetime.datetime.now().year if not annee.get(): jour.set(getDebut().year) return int(annee.get()) # Dialogue : hasAujourdhui = datetime.datetime.now().date() >= getDebut( ) and datetime.datetime.now().date() < periode.getFin() fen = Dialog(title="Scinder une période", buttons=("Ok", "Annuler", "Aujourd'hui") if hasAujourdhui else ("Ok", "Annuler"), command=onClose) # Widgets : cal = Calendar(master=fen) cal.pack(expand=YES, fill=BOTH, side=TOP) cal._calendar.bind("<Button-1>", lambda e: setJourCalendrier(), add=True) Label(fen, text="Le :").pack(side=LEFT, fill=X) jour = Spinbox(fen, from_=1, to=31, command=adapte, width=4) jour.pack(side=LEFT, fill=X) if getDebut().year != periode.getFin().year or getDebut( ).month != periode.getFin().month: mois = Combobox(fen, values=MOIS, state="readonly", width=11) mois.bind("<<ComboboxSelected>>", lambda e: adapte()) mois.pack(side=LEFT, fill=X) else: mois = None Label(fen, text=MOIS[getMois() - 1]).pack(side=LEFT, fill=X) if getDebut().year != periode.getFin().year: annee = Spinbox(fen, from_=getDebut().year, to=periode.getFin().year, command=adapte, width=6) annee.pack(side=LEFT, fill=X) else: annee = None Label(fen, text=getAnnee()).pack(side=LEFT, fill=X) adapte() # Activation du dialogue : fen.activateandwait() # renvoie des valeurs return dateScindage
def askHeureExacte(master, region): """ Permet de faire un dialogue à l'utilisateur lui demandant de confirmer l'heure exacte à laquelle la tâche rajoutée via Drag&Drop doit-être rajoutée. @param region: L'heure auparavant calculée. @return la nouvelle heure calculée. """ heure1 = region.hour minute1 = region.minute def onClose(bouton): nonlocal region if bouton == "Reset": h.set(heure1) m.set(minute1) return if bouton == "Ok": heure2 = int(h.get()) minute2 = int(m.get()) region += datetime.timedelta(minutes=heure2 * 60 - heure1 * 60 + minute2 - minute1) else: region = None fen.destroy() def minutePres(): if var.get(): m.config(increment=1) else: m.config(increment=5) def adapteHeure(): """Adapte les heures quand on augmente (ou diminue) trop les minutes.""" minutes = int(m.get()) heures = int(h.get()) while minutes < 0: minutes += 60 heures -= 1 while minutes >= 60: minutes -= 60 heures += 1 heures += 24 heures %= 24 m.set(minutes) h.set(heures) fen = Dialog(master, "Confirmez l'heure exacte", ("Ok", "Annuler", "Reset"), command=onClose) Label(fen, text="Veuillez entrer l'heure exacte").pack(side=TOP, expand=YES, fill=BOTH) var = BooleanVar(value=False) c = Checkbutton(fen, text="Précision à la minute près ?", command=minutePres, variable=var) c.pack(side=TOP, fill=X) Label(fen, text="Heure :").pack(side=LEFT) h = Spinbox(fen, from_=-1, to=24, increment=1, command=adapteHeure) h.pack(side=LEFT, fill=X, expand=YES) m = Spinbox(fen, from_=-5, to=64, increment=5, command=adapteHeure) m.pack(side=RIGHT, fill=X, expand=YES) Label(fen, text="Minute :").pack(side=RIGHT) onClose("Reset") fen.activateandwait() return region
def askSeparateRepetitiveTask(task): """ Dialogue qui gère les taches à répétitions (les jours à ne pas afficher et autres @param task : <Task> tache dont on gère les répétition """ from ..Task import Task # Import circulaire... def makeListDissociated(): """ Fonction qui génère la liste des répétition avec un jolie texte @return <list> un truc parfait pour le StringVar de la listeDate """ listTemp = [] # variable temporaire pour améliorer la visibilité for numero in range(task.getNbRep()): if numero in task.getDissociated(): # on rajoute * si dissocié listTemp.append("*" + adaptDatetime(numero * task.getRep() + task.getDebut())) else: listTemp.append( adaptDatetime(numero * task.getRep() + task.getDebut())) return listTemp def manageOption(e=None): """ Fonction qui gère les options des boutons Active/désactive scinder (on ne peut pas scinder le 1 jour de la répétition) Dissocier/associer en fonction de si la répétition à été dissocié ou pas @param e : <tkinter.event> inutilisé """ selected = listBoxRepet.curselection() # Scinder if 0 in selected: btnScinder.config(state=DISABLED) else: btnScinder.config(state=ACTIVE) # Dissocier # Permet d'avoir accès à l'index sinon c'est un tuple for index in selected: if index in task.getDissociated(): # La tache est dissocié, on peut donc la rassocier btnSeparate.config(text="Associer", command=associate, state=ACTIVE) else: # La tache est dans la liste de répétition, on peut donc l'associer btnSeparate.config(text="Dissocier", command=dissociate, state=ACTIVE) def scinder(): """ Fonction qui scinde la répétition en une nouvelle répétition à partir de la répétition selectionné """ selected = listBoxRepet.curselection() # On passe par une boucle pour avoir des int for iteration in selected: newTask = task.scinder(iteration) # On rajoute la tache task.getPeriode().addCopiedTask(newTask, task) # On met à jour la liste manageOption() listeDate.set(makeListDissociated()) def associate(): """ Fonction qui remet la tache à répétition dans celles à faire En pratique, retire l'itération en question de la liste "setDissociated" """ selected = listBoxRepet.curselection() for iteration in selected: task.removeDissociated(iteration) # On met à jour la liste manageOption() listeDate.set(makeListDissociated()) def dissociate(): """ Fonction qui ajoute une répétition à ne pas faire + crée une autre tache à la date ou la précédante est annulé """ selected = listBoxRepet.curselection() # Parcours du tuple pour avoir l'index de la ligne for iteration in selected: task.addDissociated(iteration) # On met à jour la liste manageOption() listeDate.set(makeListDissociated()) # Variable listeDate = StringVar() # Affctation listeDate.set(makeListDissociated()) fen = Dialog(title="Répétition de \"%s\"" % task.getNom(), buttons=("Ok", "Annuler"), exitButton=("Ok", "Annuler", "WM_DELETE_WINDOW")) # Liste de widget lbTop = Label( fen, text= "Gestion des répétitions :\n\t- (*)Dissocier : retire une tache des répétition\n\t- Scinder : crée une nouvelle tache à répétition", justify=LEFT) listBoxRepet = Listbox(fen, listvariable=listeDate, selectmode="single") listBoxRepet.bind("<<ListboxSelect>>", manageOption) # Bind associé frameBtn = Frame(fen) btnSeparate = Button( frameBtn, text="Dissocier", state=DISABLED) # Pour les commandes, voir manageOption btnScinder = Button(frameBtn, text="Scinder", command=scinder, state=DISABLED) # Pour l'état voir manageOption # Affichage lbTop.grid(row=0, column=0, columnspan=2) listBoxRepet.grid(row=1, column=0, sticky="we", padx=3) frameBtn.grid(row=1, column=1, sticky="nsew") btnSeparate.grid(row=0, column=0, sticky="we") btnScinder.grid(row=1, column=0, sticky="we") fen.activateandwait()
def askProfil(obligatoire, app, listeProfil): """ Dialogue qui demande a créer un profil @param obligatoire : <bool> True = création d'un profil, pour un user si il en a 0 False = création d'un profil, facultatif @param app : <Application> pour quitter en cas de Bool True et croix @param listeProfil : <dict> de tout les profils avec leurs paths, on dois la passer car la premiere fois on peut pas passer par l'app car le profil manager n'est pas encore crée """ listeFolder = listeProfil.values( ) # <dict_values> contient tous les paths des profils nom = None folder = None def onClose(bouton): # Permet de modifier les valeurs des variables nonlocal nom, folder # Récupération des valeurs nom = entryNom.get() folder = varEntryPath.get() if bouton == "Ok": # On regarde si le dossier est vide testVide(folder) # Ne cherche que dans les "keys" qui sont les noms des profils ☺ if nom in listeProfil: showerror(title="Erreur", message="Ce nom est déjà pris pour un autre profil") return elif folder in listeFolder: showerror( title="Erreur", message="Ce dossier est déjà pris pour un autre profil") return # Dans tous les cas quand c'est annulé : else: if obligatoire: if not askyesnowarning( title="Erreur", message= "Vous n'avez pas encore de profil, vous devez un créer un pour commencer.\nCliquez sur \"non\" pour fermer l'application" ): # Si vraiment il est conscient et qu'il veut quitter... app.destroy() raise SystemExit(0) # Pour finir le programme sans soucis return else: nom = folder = None fen.destroy() def parcourir(): """ fonction qui demande où stocker les fichier ET vérifie si le dossier est bien vide """ path = askFolder(vide=True) # on set le nouveau path if path is not None: varEntryPath.set(path) fen = Dialog(master=app, title="Création d'un profil", buttons=("Ok", "Annuler"), command=onClose, exitButton=[]) # Binding des touches fen.bind_all("<Return>", lambda e: fen.execute("Ok")) fen.bind_all("<Escape>", lambda e: fen.execute("Annuler")) # Widgets lbDebut = Label(fen, text="Créez un profil pour sauvegarder vos données") lbNom = Label(fen, text="Nom :") entryNom = Entry(fen) framePath = Frame(fen) lbPathCustomFile = Label( framePath, text="Chemin d'enregistrement de vos fichiers de préférences") varEntryPath = StringVar() entryPathCustomFile = Entry(framePath, state="normal", textvariable=varEntryPath) btnParcourir = Button(framePath, text="...", command=parcourir, width=3) # Affichage lbDebut.grid(column=0, row=0, columnspan=2, sticky="wens", pady=3) lbNom.grid(column=0, row=1, sticky="w") entryNom.grid(column=1, row=1, sticky="we") framePath.grid(column=0, row=2, columnspan=2, sticky="wens") lbPathCustomFile.grid(column=0, row=0, sticky="w") entryPathCustomFile.grid(column=0, row=1, sticky="we") btnParcourir.grid(column=1, row=1, sticky="w") # preset entryNom.insert(END, os.getlogin()) varEntryPath.set((os.path.expanduser("~/.taskManager/")).replace( "/", os.sep).replace("\\", os.sep)) fen.activateandwait() return nom, folder
def askAjouterJour(totalJour): nbJour = None position = None def onClose(bouton): # Permet de modifier les valeurs des variables nonlocal nbJour, position if bouton == 'Ok': position = varRadioBouton.get() nbJour = int(sb.get()) if varRadioGestion.get() == "Retirer": nbJour = nbJour*-1 fen.destroy() # Pour adapter le nombre de jour max du spinBox def adapteSpinbox(): newVar = 0 if varRadioGestion.get() == "Retirer": newVar = totalJour # Si on dépasse, on reset au max if int(sb.get()) > newVar: sb.set(int(newVar)) elif varRadioGestion.get() == "Ajouter": newVar = "+inf" # On config sb.config(to = newVar) fen = Dialog(title = "Nombre de jours à ajouter", buttons = ("Ok", "Annuler"), command = onClose, exitButton = ('Ok', 'Annuler', "WM_DELETE_WINDOW")) # Binding des touches fen.bind_all("<Return>", lambda e: fen.execute("Ok")) fen.bind_all("<Escape>", lambda e: fen.execute("Annuler")) # Mettre les widgets frameGestion = Frame(fen) varRadioGestion = StringVar() rG1= Radiobutton(frameGestion, text = "Ajouter les jours", variable = varRadioGestion, value = "Ajouter", command = adapteSpinbox) rG2= Radiobutton(frameGestion, text = "Retirer les jours", variable = varRadioGestion, value = "Retirer", command = adapteSpinbox) rG1.grid(row=0, sticky="w") rG2.grid(row=1, sticky="w") framejour = Labelframe(fen, text="Combien de jours ajouter ?") lb = Label(framejour,text="Nombre d'jour :") sb = Spinbox(framejour, from_ = 0, to="+inf") sb.set(0) lb.pack(side=LEFT, fill=BOTH) sb.pack(side=LEFT, fill=X, expand=YES) framePos = Labelframe(fen, text="Où rajouter les jours ?") varRadioBouton = StringVar() r1 = Radiobutton(framePos, text = "Au début de la période", variable = varRadioBouton, value = "Avant", command = adapteSpinbox) r2 = Radiobutton(framePos, text = "À la fin de la période", variable = varRadioBouton, value = "Apres", command = adapteSpinbox) r1.grid(row=0, sticky="w") r2.grid(row=1, sticky="w") frameGestion.pack(side = TOP, expand = YES, fill = BOTH) framejour.pack( side = TOP, expand = YES, fill = BOTH) framePos.pack( side = TOP, expand = YES, fill = BOTH) varRadioGestion.set("Ajouter") varRadioBouton.set("Apres") # Active et attend (un peu comme une mainloop) fen.activateandwait() return nbJour, position
def askDecalHeure(heureRetirerMax, heureAjoutMax, debut, fin, totBloque, tarBloque): """ Dialog pour demander comment décaler les tâches @param heureRetirerMax : (int) nombre d'heure que l'on peut retirer maximum à la tache qui commence le plus tardivement @param heureAjoutMax : (int) nombre d'heure que l'on peut ajouter maximum à la tache qui termine le plus tôt @param debut : (time) heure de début d'affichage @param fin : (time) heure de fin d'affichage @param totBloque : (int) nombre d'heure à partir duquel le blocage devient utile @param tarBloque : (int) nombre d'heure à partir duquel le blocage devient utile """ nbHeure = None position = None param = None def onClose(bouton): # Permet de modifier les valeurs des variables nonlocal nbHeure, position, param if bouton == 'Ok': position = varRadioGestion.get() param = varRadioParam.get() nbHeure = int(sb.get()) if varRadioGestion.get() == "tot": nbHeure = nbHeure * -1 fen.destroy() # Pour adapter le nombre d'heure max du spinBoc def adapteSpinbox(): newVar = 0 if varRadioGestion.get() == "tot" and varRadioParam.get() == "bloquer": newVar = totBloque elif varRadioGestion.get() == "tot": newVar = heureRetirerMax elif varRadioGestion.get() == "tard" and varRadioParam.get( ) == "bloquer": newVar = tarBloque elif varRadioGestion.get() == "tard": newVar = heureAjoutMax # On config sb.config(to=newVar) # Si on dépasse, on reset au max if int(sb.get()) > newVar: sb.set(int(newVar)) fen = Dialog(title="Nombre d'heure à déplacer", buttons=("Ok", "Annuler"), command=onClose, exitButton=('Ok', 'Annuler', "WM_DELETE_WINDOW")) # Binding des touches fen.bind_all("<Return>", lambda e: fen.execute("Ok")) fen.bind_all("<Escape>", lambda e: fen.execute("Annuler")) # Mettre les widgets framePos = Frame(fen) varRadioGestion = StringVar() rG1 = Radiobutton(framePos, text="Déplacer plus tot", variable=varRadioGestion, value="tot", command=adapteSpinbox) rG2 = Radiobutton(framePos, text="Déplacer plus tard", variable=varRadioGestion, value="tard", command=adapteSpinbox) rG1.grid(row=0, sticky="w") rG2.grid(row=1, sticky="w") frameHeure = Labelframe(fen, text="Déplacer de combien d'heure ?") lb = Label(frameHeure, text="Nombre d'heure :") sb = Spinbox(frameHeure, from_=0, to=heureAjoutMax) sb.set(0) lb.pack(side=LEFT, fill=BOTH) sb.pack(side=LEFT, fill=X, expand=YES) frameParametre = Frame(fen) varRadioParam = StringVar() rP1 = Radiobutton(frameParametre, text="Garder la même durée entre chaque tache", variable=varRadioParam, value="duree", command=adapteSpinbox) rP2 = Radiobutton(frameParametre, text="Garder les tâches entre %s:%02i et %s:%02i" % (debut.hour, debut.minute, fin.hour, fin.minute), variable=varRadioParam, value="bloquer", command=adapteSpinbox) rP1.grid(row=0, sticky="w") rP2.grid(row=1, sticky="w") framePos.pack(side=TOP, expand=YES, fill=BOTH) frameHeure.pack(side=TOP, expand=YES, fill=BOTH) frameParametre.pack(side=TOP, expand=YES, fill=BOTH) varRadioGestion.set("tard") varRadioParam.set("duree") # Active et attend (un peu comme une mainloop) fen.activateandwait() return nbHeure, position, param
def askdatetime(style): """ """ # Callback quand on ferme la fenêtre par l'un des boutons : date = "" heure = "" dateheure = None EXIT = False prevHWas0 = prevMWas0 = False def onClose(a): nonlocal EXIT, date, heure, dateheure EXIT = True # Si on annule : if a == 'Annuler' or a == 'WM_DELETE_WINDOW': fen.destroy() elif a == 'Ok': # Si on accepte : # on met en forme la date date = cal.selection if date == None: date = datetime.datetime.now().date() else: date = date.date() # et l'heure : dateheure = datetime.datetime.combine( date, datetime.time(hor.heure, hor.minute)) # et on ferme la fenêtre. fen.destroy() def heureChange(): try: hor.setAuto(False) hh = int(h.get() if h.get() else 0) mm = int(m.get() if m.get() else 0) while mm < 0: mm += 60 hh -= 1 while mm >= 60: mm -= 60 hh += 1 while hh < 0: hh += 24 # cal.date -= 1 while hh >= 24: hh -= 24 # cal.date += 1 m.set(mm) h.set(hh) hor.set(hh, mm) except TclError: # Il peut y avoir une exception si on ferme la fenêtre : on s'en fiche. pass except Exception as e: showerror("Symbol Interdit", err(e)) m.set(0) h.set(0) hor.set(0, 0) def heureChange2(): try: print("I'm using it") if prevHWas0: h.delete(INSERT, END) if prevMWas0: m.delete(INSERT, END) hor.setAuto(False) hh = int(h.get() if h.get() else 0) mm = int(m.get() if m.get() else 0) while mm < 0: mm += 60 hh -= 1 while mm >= 60: mm -= 60 hh += 1 if hh < 0: hh = 0 mm = 0 if hh >= 24: hh = 23 mm = 59 m.set(mm) h.set(hh) hor.set(hh, mm) #except TclError: # Il peut y avoir une exception si on ferme la fenêtre : on s'en fiche. # pass except Exception as e: showerror("Symbol Interdit", err(e)) m.set(0) h.set(0) hor.set(0, 0) def prechange(): nonlocal prevHWas0, prevMWas0 prevHWas0 = int(h.get() if h.get() else 0) == 0 prevMWas0 = int(m.get() if m.get() else 0) == 0 print("pre", prevHWas0, prevMWas0) # Construction du dialogue : fen = Dialog(title="Choix de la date et l'heure", buttons=("Ok", "Annuler"), command=onClose) cadre = Frame(fen) cadre.pack(side=RIGHT) # création des widgets : cal = Calendar(master=fen, firstweekday=calendar.MONDAY) hor = Horloge(cadre, number=style) # placement des widgets : cal.pack(side=LEFT, expand=1, fill='both') hor.pack(side=TOP) # choix de l'heure : h = Spinbox(cadre, from_=-1, to=24, increment=1, width=4, command=heureChange) h.bind("<Key>", lambda e: prechange(), add=1) h.bind("<Key>", lambda e: h.after(10, heureChange2), add=1) h.pack(side=LEFT, expand=YES, fill=X) # choix de la minute : TODO : in-/décrémenter les minutes ou les heures au delà de leurs périodes de définition doit augmenter les heures/minutes m = Spinbox(cadre, from_=-1, to=60, increment=1, width=4, command=heureChange) m.bind("<Key>", lambda e: prechange(), add=1) m.bind("<Key>", lambda e: m.after(10, heureChange2), add=1) m.pack(side=LEFT, expand=YES, fill=X) # valeurs de bases : h.set(time.localtime().tm_hour) m.set(time.localtime().tm_min) prechange() # print(fen.master.master.slaves()[0].taskEditor.slaves()[1].slaves()) # et on active le dialogue. fen.activateandwait() print(dateheure) return dateheure
def create(self, master=None): """ Méthode pour demander à l'utilisateur les informations nécéssaires pour créer et instancier un objet quelquonque. @param master : Objet contenant l'objet créé. Si il est null, il ne sera pas passé au constructeur. @return l'objet tout juste créé. """ # Créer l'espace du dialogue : dialogue = Dialog(None, "Ajouter %s" % self.__unNom, ("Ok", "Annuler")) # Mettre des composants dessus : # Pour commencer, on demmande le nom (obligatoire). cadreNom = LabelFrame(dialogue, text="Nom de %s" % self.__leNom) cadreNom.pack(side=TOP, expand=YES, fill=BOTH, padx=5, pady=5) etr = Entry(cadreNom) etr.pack(side=LEFT, expand=YES, fill=BOTH, padx=5, pady=5) # Mettre le choix de la visibilité: (facultatif) if len(self.__visibilitees): # Séparateur : Separator(dialogue, orient=HORIZONTAL).pack(side=TOP, fill=X) # On crée le cadre et les variable de calcul : cadreVisibilitee = LabelFrame(dialogue, text="Visibilité de %s" % self.__leNom) cadreVisibilitee.pack(side=TOP, fill=X, padx=5, pady=5) varV = StringVar(value=self.__defautVisibilitee) # Calcul du nombre de lignes et colonnes pour garder une grille à peu près carrée : rows = math.ceil(math.sqrt(len(self.__visibilitees)) / 2) cols = math.floor(math.sqrt(len(self.__visibilitees)) * 2) if cols * rows < len(self.__visibilitees): cols += 1 while (cols - 1) * rows >= len(self.__visibilitees): cols -= 1 # on crée/place les widgets : for i, v in enumerate(self.__visibilitees): Radiobutton(cadreVisibilitee, text=v, value=v, variable=varV).grid(row=i // cols, column=i % cols, sticky="w") varV.set(self.__defautVisibilitee) # Mettre le choix des modifiers if len(self.__modifiers): # Séparateur : Separator(dialogue, orient=HORIZONTAL).pack(side=TOP, fill=X) # On crée le cadre et les variable de calcul : cadreModifiers = LabelFrame(dialogue, text="Modifiers de %s" % self.__leNom) cadreModifiers.pack(side=TOP, fill=X, padx=5, pady=5) varsM = {} # Calcul du nombre de lignes et colonnes pour garder une grille à peu près carrée : rows = math.ceil(math.sqrt(len(self.__modifiers)) / 2) cols = math.floor(math.sqrt(len(self.__modifiers)) * 2) if cols * rows < len(self.__modifiers): cols += 1 while (cols - 1) * rows >= len(self.__modifiers): cols -= 1 # on crée/place les widgets : for i, v in enumerate(self.__modifiers): var = BooleanVar(value=False) Checkbutton(cadreModifiers, text=v, variable=var).grid(row=i // cols, column=i % cols, sticky="w") varsM[v] = var # Mettre le choix du type: if self.__typable: # Séparateur : Separator(dialogue, orient=HORIZONTAL).pack(side=TOP, fill=X) # On crée le cadre et les variable de calcul : cadreType = LabelFrame(dialogue, text="Type de %s" % self.__leNom) cadreType.pack(side=TOP, fill=X, padx=5, pady=5) cadreTypeNatifs = Frame(cadreType) cadreTypeNatifs.pack(expand=YES, fill=BOTH, side=TOP) varT = StringVar(value=self.__defautType) # Calcul du nombre de lignes et colonnes pour garder une grille à peu près carrée : rows = math.ceil(math.sqrt(len(self.__types)) / 2) cols = math.floor(math.sqrt(len(self.__types)) * 2) if cols * rows < len(self.__types): cols += 1 while (cols - 1) * rows >= len( list(inter(self.__types, DialogBuilder.__nativeTypes))): cols -= 1 # on crée/place les widgets : for i, t in enumerate( inter(self.__types, DialogBuilder.__nativeTypes)): Radiobutton(cadreTypeNatifs, text=t, value=t, variable=varT).grid(row=i // cols, column=i % cols, sticky="w") # TODO : Mettre les types custom. Avec un combobox ? cbbTypes = Combobox(cadreType, values=[ i for i in self.__types if not i in DialogBuilder.__nativeTypes ], state="readonly", textvariable=varT) Label(cadreType, text="Autre objet :").pack(side=LEFT) cbbTypes.pack(side=LEFT, fill=X) varT.set(self.__defautType) # Activer puis récupérer le résultat pour le renvoyer if dialogue.activateandwait() == "Ok": options = {"nom": etr.get(), "style": self.__style} if master is not None: options["master"] = master if len(self.__visibilitees): options["visibilitee"] = varV if len(self.__modifiers): options["modifiers"] = varsM if self.__typable: options["type"] = varT result = self.__cls(**options) else: result = None dialogue.destroy() return result