def initialize(self): self.master.title("NENR DZ5 - Miljenko Šuflaj") self.pack(fill="both", expand=True) notebook = Notebook(self) notebook.pack() record_gestures_frame = Frame(self) classify_gestures_frame = Frame(self) notebook.add(record_gestures_frame, text="Zabilježi geste") notebook.add(classify_gestures_frame, text="Klasificiraj geste") notebook.enable_traversal() notebook.select(record_gestures_frame) gesture_classification_frame = Frame(classify_gestures_frame) gesture_record_frame_2 = Frame(classify_gestures_frame) gesture_classification_frame.pack(side="top", fill="x", expand=True) gesture_record_frame_2.pack(fill="both") # region Record gesture tab gesture_info_frame = Frame(record_gestures_frame) gesture_record_frame = Frame(record_gestures_frame) gesture_end_frame = Frame(record_gestures_frame) gesture_info_frame.pack(side="top", fill="x") gesture_record_frame.pack(fill="both") gesture_end_frame.pack(side="bottom", fill="x") gesture_name_menu = Menubutton(gesture_info_frame, text="Odaberi gestu za bilježenje...") gesture_name_menu.menu = Menu(gesture_name_menu, tearoff=0) gesture_name_menu["menu"] = gesture_name_menu.menu for i, label in enumerate(GestureWindow.labels): gesture_name_menu.menu.add_command(label=label, command=self.switch_record(i)) self.__recorded_gesture_label = Label(gesture_info_frame) gesture_record_canvas = Canvas(gesture_record_frame, width=700, height=475, bg="white") gesture_record_canvas.current_coords = None gesture_record_canvas.records = list() gesture_record_save_button = Button(gesture_end_frame, text="Pohrani u .tsv", command=self.save_data) gesture_record_delete_button = Button(gesture_end_frame, text="Zaboravi učitane podatke", command=self.delete_internal) gesture_name_menu.pack(side="left", fill="x", padx=5, pady=5, expand=True) self.__recorded_gesture_label.pack(side="left", fill="x", padx=5, pady=5) gesture_record_canvas.pack(fill="both", padx=5, pady=5, expand=True) gesture_record_delete_button.pack(side="left", fill="x", padx=5, pady=5) gesture_record_save_button.pack(side="right", fill="x", padx=5, pady=5) self.switch_record(self.__recorded_gesture_index)() # endregion # region Classify gesture tab gesture_classification_frame = Frame(classify_gestures_frame) gesture_record_frame_2 = Frame(classify_gestures_frame) gesture_classification_frame.pack(side="top", fill="x", expand=True) gesture_record_frame_2.pack(fill="both") classification_labels = list() self.__classification_outputs = list() for category in GestureWindow.labels: classification_labels.append( Label(gesture_classification_frame, text=category)) self.__classification_outputs.append( Label(gesture_classification_frame, text=f"{0.:.01f}%", font=("helvetica", 8))) classification_blank = Label(gesture_classification_frame) classification_labels[-1].pack(side="left", fill="x", padx=5, pady=5) self.__classification_outputs[-1].pack(side="left", fill="x", padx=5, pady=5) classification_blank.pack(side="left", fill="x", padx=50, pady=5) gesture_record_canvas_2 = Canvas(gesture_record_frame_2, width=700, height=525, bg="white") gesture_record_canvas_2.current_coords = None gesture_record_canvas_2.records = list() gesture_record_canvas_2.pack(side="left", fill="both", padx=5, pady=5, expand=True) # endregion # region Functionality for record_canvas in [gesture_record_canvas, gesture_record_canvas_2]: draw_function = self.get_draw_on_canvas_function(record_canvas) record_canvas.bind("<B1-Motion>", draw_function) record_canvas.bind("<ButtonRelease-1>", draw_function) self.__root_window.bind( "<BackSpace>", self.get_delete_currently_drawn(gesture_record_canvas)) self.__root_window.bind( "<Return>", self.get_record_gesture_function(gesture_record_canvas)) gesture_record_canvas_2.bind( "<Leave>", self.get_evaluate_gesture_function(gesture_record_canvas_2)) self.__root_window.bind( "<Delete>", self.get_delete_currently_drawn(gesture_record_canvas_2))
def __init__(self, master): """Create note manager to easily delete multiple notes.""" Toplevel.__init__(self, master, class_='MyNotes') self.title(_("Note Manager")) self.minsize(width=546, height=200) self.grab_set() categories = CONFIG.options("Categories") categories.sort() self.im_del = PhotoImage(file=IM_DELETE, master=self) self.im_change = PhotoImage(file=IM_CHANGE, master=self) self.im_visible = PhotoImage(file=IM_VISIBLE_24, master=self) self.im_hidden = PhotoImage(file=IM_HIDDEN_24, master=self) tooltipwrapper = TooltipWrapper(self) self.notebook = Notebook(self) self.notebook.pack(fill='both', expand=True) self.texts = {} self.frames = {} self.notes = {} # to change notes category menu_cat = Menu(self, tearoff=False) self.category = StringVar(self) # create one tab per category for cat in categories: menu_cat.add_radiobutton(label=cat.capitalize(), value=cat, variable=self.category, command=self.change_cat_selection) self.notes[cat] = {} frame = Frame(self.notebook, padding=2) self.texts[cat] = Text(frame, width=1, height=1, bg=self.cget('bg'), relief='flat', highlightthickness=0, padx=0, pady=0, cursor='arrow') frame.columnconfigure(0, weight=1) frame.rowconfigure(1, weight=1) self.texts[cat].grid(row=1, column=0, sticky='ewsn') scrolly = Scrollbar(frame, orient='vertical', command=self.texts[cat].yview) scrolly.grid(row=1, column=1, sticky='ns', pady=(2, 0)) scrollx = Scrollbar(frame, orient='horizontal', command=self.texts[cat].xview) scrollx.grid(row=2, column=0, sticky='ew') self.texts[cat].configure(xscrollcommand=scrollx.set, yscrollcommand=scrolly.set) self.frames[cat] = Frame(self.texts[cat], style='bg.TFrame', padding=1, height=29, width=523) self.frames[cat].columnconfigure(0, weight=1, minsize=170) headings = Frame(frame, padding=(1, 0, 1, 0)) headings.columnconfigure(0, weight=0, minsize=20) headings.columnconfigure(1, weight=1, minsize=198) headings.columnconfigure(2, weight=1, minsize=198) headings.columnconfigure(3, weight=0, minsize=84) headings.columnconfigure(4, weight=0, minsize=22) Heading(headings, cat, 'title', command=self.sort_column, text=_('Title')).grid(row=0, column=1, sticky='ew') Heading(headings, cat, 'text', command=self.sort_column, text=_('Text')).grid(row=0, column=2, sticky='ew') Heading(headings, cat, 'date', command=self.sort_column, text=_('Date')).grid(row=0, column=3, sticky='ew') Heading(headings, cat, 'select_all', style='select.heading.TLabel', padding=0, command=self.toggle_selectall).place(x=0, y=0, anchor='nw', relheight=1, width=20) Heading(headings, cat, 'visibility', command=self.sort_column).place(relx=1, y=0, anchor='ne', bordermode='outside', width=23, relheight=1) headings.place(x=0, y=2, anchor='nw') self.update_idletasks() frame.rowconfigure(0, minsize=headings.winfo_reqheight()) self.texts[cat].window_create('1.0', window=self.frames[cat]) b_frame = Frame(frame) b_frame.grid(row=3, columnspan=2) m = Menubutton(b_frame, image=self.im_change, text=_('Change category'), compound='right', menu=menu_cat, padding=1) m.pack(side='left', padx=4, pady=4, fill='y') tooltipwrapper.add_tooltip(m, _('Change category of selected notes')) b_show = Button(b_frame, image=self.im_visible, padding=1, command=self.show_selection) b_show.pack(side='left', padx=4, pady=4) tooltipwrapper.add_tooltip(b_show, _('Show selected notes')) b_hide = Button(b_frame, image=self.im_hidden, padding=1, command=self.hide_selection) b_hide.pack(side='left', padx=4, pady=4) tooltipwrapper.add_tooltip(b_hide, _('Hide selected notes')) b_del = Button(b_frame, image=self.im_del, command=self.del_selection, padding=1) b_del.pack(side='left', padx=4, pady=4, fill='y') tooltipwrapper.add_tooltip(b_del, _('Delete selected notes')) self.notebook.add(frame, text=cat.capitalize(), sticky="ewsn", padding=0) # display notes by category for key, note_data in self.master.note_data.items(): self.display_note(key, note_data) for txt in self.texts.values(): txt.configure(state='disabled') self.geometry('410x450') self.bind("<Button-4>", lambda e: self.scroll(-1)) self.bind("<Button-5>", lambda e: self.scroll(1)) self.notebook.bind('<<NotebookTabChanged>>', self.on_change_tab)
class Timer(Tk): """ Chronométre de temps de travail pour plus d'efficacité """ def __init__(self): Tk.__init__(self, className="WorkHourGlass") self.on = False # is the timer on? if not CONFIG.options("Tasks"): CONFIG.set("Tasks", _("Work"), CMAP[0]) # colors self.background = { _("Work"): CONFIG.get("Work", "bg"), _("Break"): CONFIG.get("Break", "bg"), _("Rest"): CONFIG.get("Rest", "bg") } self.foreground = { _("Work"): CONFIG.get("Work", "fg"), _("Break"): CONFIG.get("Break", "fg"), _("Rest"): CONFIG.get("Rest", "fg") } # window configuration if PL[0] == "w": self.iconbitmap(ICON_WIN, default=ICON_WIN) else: self.icon = PhotoImage(master=self, file=ICON) self.iconphoto(True, self.icon) self.title("WorkHourGlass") self.protocol("WM_DELETE_WINDOW", self.exit) self.rowconfigure(1, weight=1) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) self.minsize(181, 190) self.geometry("200x190+%i+%i" % ((self.winfo_screenwidth() - 200) // 2, (self.winfo_screenheight() - 190) // 2)) self.configure(background=self.background[_("Work")]) # style self.style = Style(self) self.style.theme_use(STYLE) self.style.configure('fen.TLabel', foreground=self.foreground[_("Work")], background=self.background[_("Work")]) # nombre de séquence de travail effectuées d'affilée (pour # faire des pauses plus longues tous les 4 cycles) self.nb_cycles = 0 self.pomodori = IntVar(self, 0) # images self.im_go = PhotoImage(master=self, file=GO) self.im_stop = PhotoImage(master=self, file=STOP) self.im_plus = PhotoImage(master=self, file=PLUS) self.im_moins = PhotoImage(master=self, file=MOINS) self.im_params = PhotoImage(master=self, file=PARAMS) self.im_tomate = PhotoImage(master=self, file=TOMATE) self.im_graph = PhotoImage(master=self, file=GRAPH) # tasks list tasks_frame = Frame(self) tasks_frame.grid(row=3, column=0, columnspan=3, sticky="wnse") tasks = [t.capitalize() for t in CONFIG.options("Tasks")] self.task = StringVar(self, tasks[0]) self.menu_tasks = Menu(tasks_frame, tearoff=False) for task in tasks: self.menu_tasks.add_radiobutton(label=task, value=task, variable=self.task) self.menu_tasks.add_command(label=_("New task"), image=self.im_plus, compound="left", command=self.add_task) self.menu_tasks.add_command(label=_("Remove task"), image=self.im_moins, compound="left", command=self.del_task) self.menu_tasks.add_command(label=_("Statistics"), image=self.im_graph, compound="left", command=self.display_stats) self.choose_task = Menubutton(tasks_frame, textvariable=self.task, menu=self.menu_tasks) Label(tasks_frame, text=_("Task: "), font="CMU\ Sans\ Serif\ Demi\ Condensed 12", width=6, anchor="e").pack(side="left") self.choose_task.pack(side="right", fill="x") # display self.tps = [CONFIG.getint("Work", "time"), 0] # time: min, sec self.activite = StringVar(self, _("Work")) self.titre = Label(self, textvariable=self.activite, font='CMU\ Sans\ Serif\ Demi\ Condensed 14', style='fen.TLabel', anchor="center") self.titre.grid(row=0, column=0, columnspan=2, sticky="we") self.temps = Label( self, text="{0:02}:{1:02}".format(self.tps[0], self.tps[1]), font="%s %i" % (CONFIG.get( "General", "font"), CONFIG.getint("General", "fontsize")), style='fen.TLabel', anchor="center") self.temps.grid(row=1, column=0, columnspan=2, sticky="nswe", pady=(0, 10)) self.aff_pomodori = Label(self, textvariable=self.pomodori, image=self.im_tomate, compound="left", style='fen.TLabel', font='CMU\ Sans\ Serif\ Demi\ Condensed 14') self.aff_pomodori.grid(row=2, columnspan=2, sticky="e", padx=20) # buttons self.b_go = Button(self, image=self.im_go, command=self.go) self.b_go.grid(row=4, column=0, sticky="ew") self.b_params = Button(self, image=self.im_params, command=self.params) self.b_params.grid(row=4, column=1, sticky="ew") # --- make window sticky self.update_idletasks() e = EWMH() try: for w in e.getClientList(): if w.get_wm_name() == self.title(): e.setWmState(w, 1, '_NET_WM_STATE_STICKY') e.display.flush() except ewmh.display.error.BadWindow: pass def set_config(self): self.background = { _("Work"): CONFIG.get("Work", "bg"), _("Break"): CONFIG.get("Break", "bg"), _("Rest"): CONFIG.get("Rest", "bg") } self.foreground = { _("Work"): CONFIG.get("Work", "fg"), _("Break"): CONFIG.get("Break", "fg"), _("Rest"): CONFIG.get("Rest", "fg") } act = self.activite.get() self.configure(background=self.background[act]) self.style.configure('fen.TLabel', foreground=self.foreground[act], background=self.background[act]) self.temps.configure(font="%s %i" % (CONFIG.get("General", "font"), CONFIG.getint("General", "fontsize"))) def add_task(self): def ajoute(event=None): task = nom.get() if task and not CONFIG.has_option("Tasks", task): index = len(CONFIG.options("Tasks")) self.menu_tasks.insert_radiobutton(index, label=task, value=task, variable=self.task) CONFIG.set("Tasks", task, CMAP[index % len(CMAP)]) top.destroy() with open(PATH_CONFIG, "w") as file: CONFIG.write(file) self.menu_tasks.invoke(index) else: nom.delete(0, "end") top = Toplevel(self) top.title(_("New task")) top.transient(self) top.grab_set() nom = Entry(top, width=20) nom.grid(row=0, columnspan=2, sticky="ew") nom.focus_set() nom.bind('<Key-Return>', ajoute) Button(top, text=_("Cancel"), command=top.destroy).grid(row=1, column=0) Button(top, text=_("Ok"), command=ajoute).grid(row=1, column=1) top.wait_window(top) def del_task(self): """ Suppression de tâches """ def supprime(): rep = askyesno(_("Confirmation"), _("Are you sure you want to delete these tasks?")) if rep: for i in range(len(boutons) - 1, -1, -1): # l'ordre de parcours permet de supprimer les derniers # éléments en premier afin de ne pas modifier les index des # autres éléments lors des suppressions task = tasks[i] if "selected" in boutons[i].state(): # suppression de la tâche de la liste des tâches CONFIG.remove_option("Tasks", task) tasks.remove(task) # suppression de l'entrée correspondante dans le menu self.menu_tasks.delete(i) if not tasks: CONFIG.set("Tasks", _("Work"), CMAP[0]) tasks.append(_("Work")) self.menu_tasks.insert_radiobutton( 0, label=_("Work"), value=_("Work"), variable=self.task) if self.task.get() == task: self.task.set(tasks[0]) # suppression des stats associées chemin = PATH_STATS + "_" + "_".join(task.split(" ")) if os.path.exists(chemin): os.remove(chemin) top.destroy() with open(PATH_CONFIG, "w") as file: CONFIG.write(file) else: top.destroy() tasks = [t.capitalize() for t in CONFIG.options("Tasks")] top = Toplevel(self) top.title(_("Remove task")) top.transient(self) top.grab_set() style = Style(top) style.theme_use(STYLE) boutons = [] for i, task in enumerate(tasks): boutons.append(Checkbutton(top, text=task)) boutons[-1].grid(row=i, columnspan=2, sticky="w") Button(top, text=_("Cancel"), command=top.destroy).grid(row=i + 1, column=0) Button(top, text=_("Delete"), command=supprime).grid(row=i + 1, column=1) def stats(self): """ Enregistre la durée de travail (en min) effectuée ce jour pour la tâche qui vient d'être interrompue. Seul les pomodori complets sont pris en compte. """ # TODO: translate, correct date/time format pom = self.pomodori.get() if pom: # la tâche en cours a été travaillée, il faut enregistrer les stats date = dt.date.today() task = self.task.get() chemin = PATH_STATS + "_" + "_".join(task.split(" ")) if not os.path.exists(chemin): with open(chemin, 'w') as fich: fich.write( "# tâche : %s\n# jour\tmois\tannée\ttemps de travail (min)\n" % task) with open(chemin, 'r') as fich: stats = fich.readlines() if len(stats) > 2: last = stats[-1][:10], stats[-1][:-1].split("\t")[-1] else: last = "", 0 if last[0] != date.strftime("%d\t%m\t%Y"): with open(chemin, 'a') as fich: fich.write("%s\t%i\n" % (date.strftime("%d\t%m\t%Y"), pom * CONFIG.getint("Work", "time"))) else: # un nombre a déjà été enregistré plus tôt dans la journée # il faut les additioner with open(chemin, 'w') as fich: fich.writelines(stats[:-1]) fich.write( "%s\t%i\n" % (date.strftime("%d\t%m\t%Y"), pom * CONFIG.getint("Work", "time") + int(last[1]))) def display_stats(self): """ affiche les statistiques """ plt.figure("Statistiques") tasks = [t.capitalize() for t in CONFIG.options("Tasks")] coul = [CONFIG.get("Tasks", task) for task in tasks] stats_x = [] stats_y = [] demain = dt.date.today().toordinal() + 1 min_x = demain # récupération des données no_data = True for i, task in enumerate(tasks): chemin = PATH_STATS + "_" + "_".join(task.split(" ")) if os.path.exists(chemin): no_data = False stat = loadtxt(chemin, dtype=int) if len(stat.shape) == 1: stat = stat.reshape(1, 4) x = [ dt.date(an, mois, jour).toordinal() for jour, mois, an in stat[:, :3] ] y = stat[:, -1] / 60 # temps de travail min_x = min(x[0], min_x) stats_x.append(x) stats_y.append(y) else: # la taĉhe n'a jamais été travaillée stats_x.append([demain - 1]) stats_y.append(array([0])) # plots xx = arange(min_x, demain, dtype=float) yy0 = zeros_like(xx) # pour empiler les stats if not no_data: for (i, task), x, y in zip(enumerate(tasks), stats_x, stats_y): ax0 = plt.subplot(111) plt.ylabel(_("time (h)")) plt.xlabel(_("date")) yy = array([], dtype=int) # comble les trous par des 0 # ainsi, les jours où une tâche n'a pas été travaillée correspondent # à des 0 sur le graph xxx = arange(min_x, x[0]) yy = concatenate((yy, zeros_like(xxx, dtype=int))) for j in range(len(x) - 1): xxx = arange(x[j], x[j + 1]) yy = concatenate((yy, [y[j]], zeros(len(xxx) - 1, dtype=int))) xxx = arange(x[-1], demain) yy = concatenate((yy, [y[-1]], zeros(len(xxx) - 1, dtype=int))) plt.bar(xx - 0.4, yy, bottom=yy0, width=0.8, label=task, color=coul[i]) yy0 += yy axx = array( [int(xt) for xt in ax0.get_xticks() if xt.is_integer()]) ax0.set_xticks(axx) ax0.set_xticklabels( [dt.date.fromordinal(i).strftime("%x") for i in axx]) plt.gcf().autofmt_xdate() ax0.set_xlim(min_x - 0.5, demain - 0.5) lgd = plt.legend(fontsize=10) lgd.draggable() plt.subplots_adjust(top=0.95) max_y = yy0.max() ax0.set_ylim(0, max_y + 0.1 * max_y) plt.show() def go(self): if self.on: self.on = False if self.activite.get() == _("Work"): # rep = askyesno(title=_("Confirmation"), # message=_("You should not interrupt your work if you want to be efficient. Do you still want to suspend the timer?"), # icon="warning") # else: # rep = True self.stop() # if rep: # self.b_go.configure(image=self.im_go) # else: # self.on = True # self.affiche() else: self.on = True self.choose_task.config(state="disabled") self.b_go.configure(image=self.im_stop) self.after(1000, self.affiche) def stop(self, confirmation=True): """ Arrête le décompte du temps et le réinitialise, demande une confirmation avant de le faire si confirmation=True """ self.on = False if confirmation: rep = askyesno( title=_("Confirmation"), message=_( "Are you sure you want to give up the current session?")) else: rep = True if rep: self.stats() self.pomodori.set(0) self.nb_cycles = 0 self.b_go.configure(image=self.im_go) self.tps = [CONFIG.getint("Work", "time"), 0] self.temps.configure( text="{0:02}:{1:02}".format(self.tps[0], self.tps[1])) act = _("Work") self.activite.set(act) self.style.configure('fen.TLabel', background=self.background[act], foreground=self.foreground[act]) self.configure(background=self.background[act]) self.choose_task.config(state="normal") else: self.on = True self.affiche() def ting(self): """ joue le son marquant le changement de période """ if not CONFIG.getboolean("Sound", "mute", fallback=False): if PL[0] == "w": Popen([ "powershell", "-c", '(New-Object Media.SoundPlayer "%s").PlaySync();' % (CONFIG.get("Sound", "beep")) ]) else: Popen([ CONFIG.get("Sound", "player"), CONFIG.get("Sound", "beep") ]) def affiche(self): if self.on: self.tps[1] -= 1 if self.tps[1] == 0: if self.tps[0] == 0: self.ting() if self.activite.get() == _("Work"): self.pomodori.set(self.pomodori.get() + 1) self.nb_cycles += 1 if self.nb_cycles % 4 == 0: # pause longue self.activite.set(_("Rest")) self.tps = [CONFIG.getint("Rest", "time"), 0] else: # pause courte self.activite.set(_("Break")) self.tps = [CONFIG.getint("Break", "time"), 0] else: self.activite.set(_("Work")) self.tps = [CONFIG.getint("Work", "time"), 0] act = self.activite.get() self.style.configure('fen.TLabel', background=self.background[act], foreground=self.foreground[act]) self.configure(background=self.background[act]) elif self.tps[1] == -1: self.tps[0] -= 1 self.tps[1] = 59 self.temps.configure( text="{0:02}:{1:02}".format(self.tps[0], self.tps[1])) self.after(1000, self.affiche) def params(self): on = self.on self.on = False self.b_go.configure(image=self.im_go) p = Params(self) self.wait_window(p) if on: self.on = True self.choose_task.config(state="disabled") self.b_go.configure(image=self.im_stop) self.after(1000, self.affiche) def exit(self): self.stats() plt.close() self.destroy()
def __init__(self, parent, **options): """ créer le Toplevel permettant de modifier les paramètres """ Toplevel.__init__(self, parent, **options) self.grab_set() self.transient(parent) self.title(_("Settings")) self.resizable(0, 0) self.onglets = Notebook(self) self.onglets.grid(row=0,column=0,columnspan=2) self.im_color = PhotoImage(master=self, file=COLOR) self.style = Style(self) self.style.theme_use(STYLE) self.style.configure('title.TLabel', font='CMU\ Sans\ Serif\ Demi\ Condensed 12') self.okfct = self.register(valide_entree_nb) self.nb_task = len(CONFIG.options("Tasks")) # Général (temps, police et langue) self.general = Frame(self.onglets, padding=10) self.general.pack(fill="both", expand=True, padx=10, pady=10) self.onglets.add(self.general, text=_("General")) # Temps Label(self.general, text=_("Times:"), style='title.TLabel').grid(row=0, pady=4, sticky="w") self.time_frame = Frame(self.general) self.time_frame.grid(row=1, sticky="ew") Label(self.time_frame, text=_("Work: ")).grid(row=0, column=0) self.travail = Entry(self.time_frame, width=4, justify='center', validatecommand=(self.okfct, '%d', '%S'), validate='key') self.travail.insert(0, CONFIG.get("Work", "time")) self.travail.grid(row=0, column=1, padx=(0,10)) Label(self.time_frame, text=_("Break: ")).grid(row=0, column=2) self.pause = Entry(self.time_frame, width=4, justify='center', validatecommand=(self.okfct, '%d', '%S'), validate='key') self.pause.insert(0, CONFIG.get("Break", "time")) self.pause.grid(row=0, column=3, padx=(0,10)) Label(self.time_frame, text=_("Rest: ")).grid(row=0, column=4) self.rest = Entry(self.time_frame, width=4, justify='center', validatecommand=(self.okfct, '%d', '%S'), validate='key') self.rest.insert(0, CONFIG.get("Rest", "time")) self.rest.grid(row=0, column=5) Separator(self.general, orient='horizontal').grid(row=2, sticky="ew", pady=10) # Police self.font_frame = Frame(self.general) self.font_frame.grid(row=3, pady=4, sticky="ew") Label(self.font_frame, text=_("Font:"), style='title.TLabel').pack(anchor="n", side="left") self.exemple = Label(self.font_frame, text="02:17", anchor="center", font="%s %i" % (CONFIG.get("General", "font"), CONFIG.getint("General", "fontsize")), relief="groove") self.exemple.pack(side="right") self.font_frame2 = Frame(self.general) self.font_frame2.grid(row=4) Label(self.font_frame2, text=_("Family: ")).grid(row=0, column=0, sticky="e") self.font = Entry(self.font_frame2, justify='center') self.font.insert(0, CONFIG.get("General", "font")) self.font.grid(row=0, column=1, padx=(0,10), sticky="ew") self.font.bind('<FocusOut>', self.actualise_police) self.font.bind('<Key-Return>', self.actualise_police, True) Label(self.font_frame2, text=_("Size: ")).grid(row=0, column=2, sticky="e") self.size = Entry(self.font_frame2, width=4, justify='center', validatecommand=(self.okfct, '%d', '%S'), validate='key') self.size.insert(0, CONFIG.getint("General", "fontsize")) self.size.grid(row=0, column=3, pady=2, sticky="w") self.size.bind('<FocusOut>', self.actualise_police) self.size.bind('<Key-Return>', self.actualise_police, True) Separator(self.general, orient='horizontal').grid(row=5, sticky="ew", pady=10) # Langues self.lang_frame = Frame(self.general) self.lang_frame.grid(row=6, pady=4, sticky="ew") Label(self.lang_frame, text=_("Language:"), style='title.TLabel').pack(side="left") self.lang = StringVar(self.lang_frame, LANGUES[CONFIG.get("General", "language")]) b_lang = Menubutton(self.lang_frame, textvariable=self.lang) menu = Menu(b_lang, tearoff=False) menu.add_radiobutton(label="English", variable=self.lang, value="English", command=self.change_langue) menu.add_radiobutton(label="Français", variable=self.lang, value="Français", command=self.change_langue) b_lang.configure(menu=menu) b_lang.pack(side="right") # Son self.im_son = PhotoImage(master=self, file=SON) self.im_mute = PhotoImage(master=self, file=MUTE) self.son = Frame(self.onglets, padding=10) self.son.pack(fill="both", expand=True, padx=10, pady=10) self.son.columnconfigure(1, weight=1) self.onglets.add(self.son, text=_("Sound")) Label(self.son, text=_("Sound:"), style='title.TLabel').grid(row=0, pady=4, sticky="w") self.mute = BooleanVar(self) self.mute.set(not CONFIG.get("Sound", "mute")) def mute_unmute(): if self.mute.get(): self.mute.set(False) b_son.configure(image=self.im_son) else: self.mute.set(True) b_son.configure(image=self.im_mute) b_son = Button(self.son, command=mute_unmute) mute_unmute() b_son.grid(row=0, column=1, sticky="e", pady=4) self.son_frame = Frame(self.son) self.son_frame.grid(row=1, sticky="ew", columnspan=2) self.bip = Entry(self.son_frame, justify='center') self.bip.insert(0, CONFIG.get("Sound", "beep")) self.bip.pack(side="left", fill="both", expand=True) Button(self.son_frame, text="...", width=2, command=self.choix_son).pack(side="right", padx=(2,0)) if PL[0] != "w": Separator(self.son, orient='horizontal').grid(row=2, columnspan=2, sticky="ew", pady=10) son_frame2 = Frame(self.son) son_frame2.grid(row=3, sticky="ew", columnspan=2) Label(son_frame2, text=_("Audio player: "), style='title.TLabel').pack(side="left") self.player = Entry(son_frame2, justify='center') self.player.insert(0, CONFIG.get("Sound", "player")) self.player.pack(side="right", fill="both", expand=True) # Couleurs self.couleurs = Frame(self.onglets, padding=10) self.couleurs.pack(fill="both", expand=True, padx=10, pady=10) self.onglets.add(self.couleurs, text=_("Colors")) # style des boutons de choix des couleurs self.style.configure("fond_w.TButton", background=CONFIG.get("Work", "bg")) self.style.configure("fond_p.TButton", background=CONFIG.get("Break", "bg")) self.style.configure("fond_r.TButton", background=CONFIG.get("Rest", "bg")) self.style.configure("texte_w.TButton", background=CONFIG.get("Work", "fg")) self.style.configure("texte_p.TButton", background=CONFIG.get("Break", "fg")) self.style.configure("texte_r.TButton", background=CONFIG.get("Rest", "fg")) self.couleurs.grid_columnconfigure(3, weight=3) self.couleurs.grid_rowconfigure(0, weight=1) Label(self.couleurs, text=_("Work: "), style='title.TLabel').grid(row=0, column=0, pady=4, padx=(2,10), sticky="w") Label(self.couleurs, text=_("Background: ")).grid(row=0, column=1, sticky="e", pady=(6,4)) Button(self.couleurs, width=2, command=lambda: self.choix_couleur("fond_w"), style='fond_w.TButton').grid(row=0, column=2, pady=4) Label(self.couleurs, text=_("Text: ")).grid(row=1, column=1, sticky="e") Button(self.couleurs, width=2, command=lambda: self.choix_couleur("texte_w"), style='texte_w.TButton').grid(row=1, column=2, pady=4) Separator(self.couleurs, orient='horizontal').grid(row=2, sticky="ew", pady=10, columnspan=4) Label(self.couleurs, text=_("Break: "), style='title.TLabel').grid(row=3, column=0, pady=4, padx=(2,10), sticky="w") Label(self.couleurs, text=_("Background: ")).grid(row=3, column=1, sticky="e", pady=(6,4)) Button(self.couleurs, width=2, command=lambda: self.choix_couleur("fond_p"), style='fond_p.TButton').grid(row=3, column=2, pady=4) Label(self.couleurs, text=_("Text: ")).grid(row=4, column=1, sticky="e") Button(self.couleurs, width=2, command=lambda: self.choix_couleur("texte_p"), style='texte_p.TButton').grid(row=4, column=2, pady=4) Separator(self.couleurs, orient='horizontal').grid(row=5, sticky="ew", pady=10, columnspan=4) Label(self.couleurs, text=_("Rest: "), style='title.TLabel').grid(row=6, column=0, pady=4, sticky="w", padx=(2,10)) Label(self.couleurs, text=_("Background: ")).grid(row=6, column=1, sticky="e", pady=(6,4)) Button(self.couleurs, width=2, command=lambda: self.choix_couleur("fond_r"), style='fond_r.TButton').grid(row=6, column=2, pady=4) Label(self.couleurs, text=_("Text: ")).grid(row=7, column=1, sticky="e") Button(self.couleurs, width=2, command=lambda: self.choix_couleur("texte_r"), style='texte_r.TButton').grid(row=7, column=2, pady=4) # Stats self.stats = Frame(self.onglets, padding=10) self.stats.pack(fill="both", expand=True, padx=10, pady=10) self.stats.grid_columnconfigure(2, weight=1) self.onglets.add(self.stats, text=_("Statistics")) Label(self.stats, text=_("Statistics:"), style='title.TLabel').grid(row=0, column=0, pady=4, sticky="w") tasks = [t.capitalize() for t in CONFIG.options("Tasks")] cmap = [CONFIG.get("Tasks", task) for task in tasks] for i, coul, task in zip(range(self.nb_task), cmap , tasks): Label(self.stats, text=task).grid(row=i + 1, column=0, sticky="e", padx=4, pady=4) self.style.configure("t%i.TButton" % i, background=coul) Button(self.stats, style="t%i.TButton" % i, width=2, command=lambda j=i: self.coul_stat(j)).grid(row=i + 1, column=1, pady=4) # Validation Button(self, text="Ok", command=self.valide).grid(row=1,column=1, sticky="we") Button(self, text=_("Cancel"), command=self.destroy).grid(row=1,column=0, sticky="we")
def __init__(self, master, note_data): """Create export dialog.""" Toplevel.__init__(self, master, class_='MyNotes') self.title(_("Export")) self.minsize(350, 250) self.grab_set() self.columnconfigure(0, weight=1) self.rowconfigure(3, weight=1) self.note_data = note_data self.categories = CONFIG.options("Categories") self.categories.sort() self.notes_to_export = [] self.export_type = None self.export_data = False # export type self.type = StringVar(self, _("Notes (.notes)")) type_frame = Frame(self) menu_type = Menu(self, tearoff=False) for etype in EXT_DICT: menu_type.add_radiobutton(label=etype, value=etype, variable=self.type) mb = Menubutton(type_frame, menu=menu_type, textvariable=self.type, width=max([int(len(key) * 0.8) for key in EXT_DICT])) Label(type_frame, text=_('Export to')).pack(side='left', padx=4) mb.pack(side='left', padx=4) type_frame.grid(row=0, columnspan=2, sticky='w', pady=4) Separator(self).grid(columnspan=2, sticky="ew", padx=4, pady=4) # export only visible notes checkbutton self.ch_only_visible = Checkbutton(self, text=_("Only visible notes"), command=self.select_only_visible) self.ch_only_visible.grid(columnspan=2, sticky="w", padx=4, pady=4) # note selection self.tree = CheckboxTreeview(self, show='tree') self.tree.grid(row=3, sticky="nsew", padx=4, pady=4) scroll = Scrollbar(self, orient='vertical', command=self.tree.yview) self.tree.configure(yscrollcommand=scroll.set) scroll.grid(row=3, column=1, sticky='ns') self.tree.insert('', 'end', 'root', text=_('Categories')) for cat in self.categories: self.tree.insert('root', 'end', cat, text=cat.capitalize()) for key, data in self.note_data.items(): self.tree.insert(data['category'], 'end', key, text='{} - {}'.format(data['title'], data.get('date', '??')), tags=['visible'] if data['visible'] else []) for cat in self.categories: if not self.tree.get_children(cat): self.tree.detach(cat) self.tree.bind('<<Checked>>', self.toggle_select_visible) self.tree.bind('<<Unchecked>>', self.toggle_select_visible) Separator(self).grid(sticky="ew", columnspan=2, padx=4, pady=4) self.ch_export_data = Checkbutton(self, text=_('Export data (pictures and linked files)')) self.ch_export_data.grid(sticky="w", columnspan=2, padx=4, pady=4) frame = Frame(self) frame.grid(columnspan=2) Button(frame, text="Ok", command=self.ok).grid(row=0, column=0, sticky="w", padx=4, pady=4) Button(frame, text=_("Cancel"), command=self.destroy).grid(row=0, column=1, sticky="e", padx=4, pady=4) self.tree.check_item('root') self.tree.expand_all() self.toggle_select_visible()
class Pomodoro(BaseWidget): """ Chronometre de temps de travail pour plus d'efficacité """ def __init__(self, master): BaseWidget.__init__(self, 'Pomodoro', master) def create_content(self, **kw): self.minsize(190, 190) self.on = False # is the timer on? if not CONFIG.options("Tasks"): CONFIG.set("Tasks", _("Work"), CMAP[0]) self._stats = None # --- colors self.background = { _("Work"): CONFIG.get("Pomodoro", "work_bg"), _("Break"): CONFIG.get("Pomodoro", "break_bg"), _("Rest"): CONFIG.get("Pomodoro", "rest_bg") } self.foreground = { _("Work"): CONFIG.get("Pomodoro", "work_fg"), _("Break"): CONFIG.get("Pomodoro", "break_fg"), _("Rest"): CONFIG.get("Pomodoro", "rest_fg") } self.rowconfigure(1, weight=1) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) # nombre de séquence de travail effectuées d'affilée (pour # faire des pauses plus longues tous les 4 cycles) self.nb_cycles = 0 self.pomodori = IntVar(self, 0) # --- images self.im_go = PhotoImage(master=self, file=IM_START) self.im_stop = PhotoImage(master=self, file=IM_STOP) self.im_tomate = PhotoImage(master=self, file=IM_POMODORO) self.im_graph = PhotoImage(master=self, file=IM_GRAPH) # --- tasks list tasks_frame = Frame(self, style='pomodoro.TFrame') tasks_frame.grid(row=3, column=0, columnspan=3, sticky="wnse") tasks = [t.capitalize() for t in CONFIG.options("PomodoroTasks")] tasks.sort() self.task = StringVar(self, tasks[0]) self.menu_tasks = Menu(tasks_frame, relief='sunken', activeborderwidth=0) self.choose_task = Menubutton(tasks_frame, textvariable=self.task, menu=self.menu_tasks, style='pomodoro.TMenubutton') Label(tasks_frame, text=_("Task: "), style='pomodoro.TLabel', font="TkDefaultFont 12", width=6, anchor="e").pack(side="left", padx=4) self.choose_task.pack(side="right", fill="x", pady=4) # --- display self.tps = [CONFIG.getint("Pomodoro", "work_time"), 0] # time: min, sec self.activite = StringVar(self, _("Work")) self.titre = Label(self, textvariable=self.activite, font='TkDefaultFont 14', style='timer.pomodoro.TLabel', anchor="center") self.titre.grid(row=0, column=0, columnspan=2, sticky="we", pady=(4, 0), padx=4) self.temps = Label(self, text="{0:02}:{1:02}".format(self.tps[0], self.tps[1]), style='timer.pomodoro.TLabel', anchor="center") self.temps.grid(row=1, column=0, columnspan=2, sticky="nswe", padx=4) self.aff_pomodori = Label(self, textvariable=self.pomodori, anchor='e', padding=(20, 4, 20, 4), image=self.im_tomate, compound="left", style='timer.pomodoro.TLabel', font='TkDefaultFont 14') self.aff_pomodori.grid(row=2, columnspan=2, sticky="ew", padx=4) # --- buttons self.b_go = Button(self, image=self.im_go, command=self.go, style='pomodoro.TButton') self.b_go.grid(row=4, column=0, sticky="ew") self.b_stats = Button(self, image=self.im_graph, command=self.display_stats, style='pomodoro.TButton') self.b_stats.grid(row=4, column=1, sticky="ew") self._corner = Sizegrip(self, style="pomodoro.TSizegrip") self._corner.place(relx=1, rely=1, anchor='se') # --- bindings self.bind('<3>', lambda e: self.menu.tk_popup(e.x_root, e.y_root)) tasks_frame.bind('<ButtonPress-1>', self._start_move) tasks_frame.bind('<ButtonRelease-1>', self._stop_move) tasks_frame.bind('<B1-Motion>', self._move) self.titre.bind('<ButtonPress-1>', self._start_move) self.titre.bind('<ButtonRelease-1>', self._stop_move) self.titre.bind('<B1-Motion>', self._move) self.temps.bind('<ButtonPress-1>', self._start_move) self.temps.bind('<ButtonRelease-1>', self._stop_move) self.temps.bind('<B1-Motion>', self._move) self.b_stats.bind('<Enter>', self._on_enter) self.b_stats.bind('<Leave>', self._on_leave) def update_style(self): self.menu_tasks.delete(0, 'end') tasks = [t.capitalize() for t in CONFIG.options('PomodoroTasks')] tasks.sort() for task in tasks: self.menu_tasks.add_radiobutton(label=task, value=task, variable=self.task) if self.task.get() not in tasks: self.stop(False) self.task.set(tasks[0]) self.attributes('-alpha', CONFIG.get(self.name, 'alpha', fallback=0.85)) bg = CONFIG.get('Pomodoro', 'background') fg = CONFIG.get('Pomodoro', 'foreground') active_bg = active_color(*self.winfo_rgb(bg)) self.style.configure('pomodoro.TMenubutton', background=bg, relief='flat', foreground=fg, borderwidth=0, arrowcolor=fg) self.style.configure('pomodoro.TButton', background=bg, relief='flat', foreground=fg, borderwidth=0) self.style.configure('pomodoro.TLabel', background=bg, foreground=fg) self.style.configure('pomodoro.TFrame', background=bg) self.style.configure('pomodoro.TSizegrip', background=bg) self.style.map('pomodoro.TSizegrip', background=[('active', active_bg)]) self.style.map('pomodoro.TButton', background=[('disabled', bg), ('!disabled', 'active', active_bg)]) self.style.map('pomodoro.TMenubutton', background=[('disabled', bg), ('!disabled', 'active', active_bg)]) self.configure(bg=bg) self.menu.configure(bg=bg, fg=fg, selectcolor=fg, activeforeground=fg, activebackground=active_bg) self.menu_pos.configure(bg=bg, fg=fg, selectcolor=fg, activeforeground=fg, activebackground=active_bg) self.menu_tasks.configure(bg=bg, activebackground=active_bg, fg=fg, selectcolor=fg, activeforeground=fg) self.background = { _("Work"): CONFIG.get("Pomodoro", "work_bg"), _("Break"): CONFIG.get("Pomodoro", "break_bg"), _("Rest"): CONFIG.get("Pomodoro", "rest_bg") } self.foreground = { _("Work"): CONFIG.get("Pomodoro", "work_fg"), _("Break"): CONFIG.get("Pomodoro", "break_fg"), _("Rest"): CONFIG.get("Pomodoro", "rest_fg") } act = self.activite.get() self.style.configure('timer.pomodoro.TLabel', font=CONFIG.get("Pomodoro", "font"), foreground=self.foreground[act], background=self.background[act]) def _on_enter(self, event=None): self._corner.state(('active', )) def _on_leave(self, event=None): self._corner.state(('!active', )) def _start_move(self, event): self.x = event.x self.y = event.y def _stop_move(self, event): self.x = None self.y = None self.configure(cursor='arrow') def _move(self, event): if self.x is not None and self.y is not None: self.configure(cursor='fleur') deltax = event.x - self.x deltay = event.y - self.y x = self.winfo_x() + deltax y = self.winfo_y() + deltay self.geometry("+%s+%s" % (x, y)) def hide(self): if self._stats is not None: self._stats.destroy() BaseWidget.hide(self) def stats(self, time=None): """Save stats.""" if time is None: time = CONFIG.getint("Pomodoro", "work_time") today = dt.date.today().toordinal() task = self.task.get().lower().replace(' ', '_') db = sqlite3.connect(PATH_STATS) cursor = db.cursor() try: cursor.execute('SELECT * FROM {} ORDER BY id DESC LIMIT 1'.format( scrub(task))) key, date, work = cursor.fetchone() except sqlite3.OperationalError: cursor.execute('''CREATE TABLE {} (id INTEGER PRIMARY KEY, date INTEGER, work INTEGER)'''.format( scrub(task))) cursor.execute( 'INSERT INTO {}(date, work) VALUES (?, ?)'.format(scrub(task)), (today, time)) else: if today != date: cursor.execute( 'INSERT INTO {}(date, work) VALUES (?, ?)'.format( scrub(task)), (today, time)) else: # update day's value cursor.execute( 'UPDATE {} SET work=? WHERE id=?'.format(scrub(task)), (work + time, key)) finally: db.commit() db.close() def display_stats(self): """ affiche les statistiques """ if self._stats is None: self._stats = Stats(self) self._stats.bind('<Destroy>', self._on_close_stats) else: self._stats.lift() def _on_close_stats(self, event): self._stats = None def go(self): if self.on: self.stop() else: self.on = True self.choose_task.state(["disabled"]) self.b_go.configure(image=self.im_stop) self.after(1000, self.affiche) logging.info('Start work cycle for task ' + self.task.get()) def stop(self, confirmation=True): """ Arrête le décompte du temps et le réinitialise, demande une confirmation avant de le faire si confirmation=True """ tps = int( CONFIG.getint("Pomodoro", "work_time") - self.tps[0] - self.tps[1] / 60) self.on = False rep = True if confirmation: rep = askyesno( title=_("Confirmation"), message=_( "Are you sure you want to give up the current session?")) if rep: self.choose_task.state(["!disabled"]) self.b_go.configure(image=self.im_go) if self.activite.get() == _("Work"): self.stats(tps) self.pomodori.set(0) self.nb_cycles = 0 self.tps = [CONFIG.getint("Pomodoro", "work_time"), 0] self.temps.configure( text="{0:02}:{1:02}".format(self.tps[0], self.tps[1])) act = _("Work") self.activite.set(act) self.style.configure('timer.pomodoro.TLabel', background=self.background[act], foreground=self.foreground[act]) logging.info('Pomodoro session interrupted.') else: self.on = True self.affiche() return rep @staticmethod def ting(): """ joue le son marquant le changement de période """ if not CONFIG.getboolean("Pomodoro", "mute", fallback=False): Popen([ CONFIG.get("General", "soundplayer"), CONFIG.get("Pomodoro", "beep") ]) def affiche(self): if self.on: self.tps[1] -= 1 if self.tps[1] == 0: if self.tps[0] == 0: self.ting() if self.activite.get() == _("Work"): self.pomodori.set(self.pomodori.get() + 1) self.nb_cycles += 1 self.choose_task.state(["!disabled"]) logging.info( 'Pomodoro: completed work session for task ' + self.task.get()) if self.nb_cycles % 4 == 0: # pause longue self.stats() self.activite.set(_("Rest")) self.tps = [ CONFIG.getint("Pomodoro", "rest_time"), 0 ] else: # pause courte self.stats() self.activite.set(_("Break")) self.tps = [ CONFIG.getint("Pomodoro", "break_time"), 0 ] else: self.choose_task.state(["disabled"]) self.activite.set(_("Work")) self.tps = [CONFIG.getint("Pomodoro", "work_time"), 0] act = self.activite.get() self.style.configure('timer.pomodoro.TLabel', background=self.background[act], foreground=self.foreground[act]) elif self.tps[1] == -1: self.tps[0] -= 1 self.tps[1] = 59 self.temps.configure(text="{0:02}:{1:02}".format(*self.tps)) self.after(1000, self.affiche)
class PlaylistHandlerSet(Frame): def __init__(self, w: PlaylistControl, *args, **kwargs): self.EMPTY_MENUBTN = _("Select Playlist") #After defined _ by gettext self.playlist = w.playlist self.playlist_control = w super().__init__(w, *args, **kwargs) #___ self.menubtn = Menubutton(self, direction="above", width=13, text=config.general["playlist"]) self.menubtn.pack() self.menu = Menu(self.menubtn, bg=config.colors["BG"], activebackground=config.colors["TV_BG_HOVER"], activeforeground=config.colors["FG"], tearoff=False) self.menu.add_command(label=_("Import Playlist"), command=self._newPlaylist) self.menu.add_separator() for playlist in config.user_config["Playlists"]: #https://stackoverflow.com/questions/11723217/python-lambda-doesnt-remember-argument-in-for-loop func_get_set_playlist = lambda playlist=playlist: self.setPlaylist( playlist) self.menu.add_command(label=playlist, command=func_get_set_playlist) self.menubtn.configure(menu=self.menu) #__________________________________________________ def _newPlaylist(self): folder: str = filedialog.askdirectory( title=_("Select folder for new playlist")) if folder == "": return playlist = os.path.basename(folder) if playlist not in config.user_config["Playlists"]: config.user_config["Playlists"][playlist] = { "path": os.path.normcase(folder), "orderby": ["title", 1], "filter": "" } self.menu.add_command(label=playlist, command=lambda: self.setPlaylist(playlist)) self.setPlaylist(playlist) def setPlaylist(self, playlist: str): ''' There will be no playlist when starting the application if the playlist had been destroyed with "self.delPlaylist()" and closed without selecting one playlist ''' if playlist == "": self.menubtn["text"] = self.EMPTY_MENUBTN return playlist_path = config.user_config["Playlists"][playlist]["path"] if not os.path.exists(playlist_path): messagebox.showerror(_("Load failed"), _("The folder does not to exist")) self.delPlaylist(playlist) return config.general["playlist"] = playlist config.playlist = config.user_config["Playlists"][playlist] self.menubtn["text"] = playlist self.playlist.setPlaylist(playlist_path) self.playlist_control.sortPlaylistForced(config.playlist["orderby"][0], config.playlist["orderby"][1]) self.playlist_control.setSearch(config.playlist["filter"]) def delPlaylist(self, playlist: str, in_tv: bool = True): config.user_config["Playlists"].pop(playlist) self.menu.delete(playlist) if in_tv: config.general["playlist"] = "" self.menubtn["text"] = self.EMPTY_MENUBTN self.playlist.delPlaylist() def renamePlaylist(self, playlist_new: str, playlist_new_path: str): playlist_old = config.general["playlist"] config.general["playlist"] = playlist_new config.user_config["Playlists"][playlist_new] = config.user_config[ "Playlists"].pop(playlist_old) config.playlist = config.user_config["Playlists"][playlist_new] config.playlist["path"] = playlist_new_path self.menu.entryconfig(self.menu.index(playlist_old), label=playlist_new, command=lambda: self.setPlaylist(playlist_new)) self.menubtn["text"] = playlist_new #Change the path of each song in the playlist for song in self.playlist.getAllSongs(): song.path = os.path.join(playlist_new_path, song.name) + song.extension