import tkinter as tk from tktooltip import ToolTip from tkinter import ttk from tkinter.messagebox import askyesno from tkinter.filedialog import askopenfilename, asksaveasfilename from icecream import ic from functools import partial from threading import Thread import time import model import json import matplotlib.pyplot as plt queue = model.Queue() btns = {} root = tk.Tk() root.title("Прям очень Таскер") root.geometry("250x350") root.option_add("*tearOff", tk.FALSE) root.iconbitmap(default=model.resource_path("icon.ico")) def save_session(): path = asksaveasfilename(filetypes=[("Json", '*.json')], defaultextension=".json") ic(path) data = { "tasks": [task.__dict__ for id, task in queue.task_slots.items()], "break": queue.task_brake.__dict__ } with open(path, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=4) def build_plt(): labels = [task.name for id, task in queue.task_slots.items()] + [queue.task_brake.name] times = [int(task.time/60) for id, task in queue.task_slots.items()] + [int(queue.task_brake.time/60)] ic(labels) ic(times) plt.figure(figsize=(len(labels), 2)) plt.bar(labels, times) plt.title("Статистика выполнения") plt.show() # plt.savefig("chart.png") def load_session(): path = askopenfilename(filetypes=[("Json", '*.json')]) ic(path) with open(path, "r", encoding="utf-8") as f: data = json.load(f) queue.task_slots = {str(task["id"]): model.Task(**task) for task in data["tasks"]} queue.task_brake = model.Task(**data["break"]) if data["break"] else None refrash() def refrash(): for id, task in queue.task_slots.items(): btns[str(id)]["text"] = f"{id} {task.name} ({task.count})" if hasattr(btns[str(id)], 'toolTip'): btns[str(id)].toolTip.on_leave() btns[str(id)].toolTip.destroy() btns[str(id)].toolTip = ToolTip(btns[str(id)], msg=task.description) def start_task_unic(num, event): if not queue.run_task: queue.run_task = True queue.start_unic_task(str(num)) btnLabel.bind("", pause_task) btnLabel.bind("", stop_task) if hasattr(btnLabel, 'toolTip'): btnLabel.toolTip.on_leave() btnLabel.toolTip.destroy() btnLabel.toolTip = ToolTip(btnLabel, msg=queue.current_task.description) Thread(target=do_task).start() def start_task(event): if not queue.run_task: queue.run_task = True queue.start_task() btnLabel.bind("", pause_task) btnLabel.bind("", stop_task) if hasattr(btnLabel, 'toolTip'): btnLabel.toolTip.on_leave() btnLabel.toolTip.destroy() btnLabel.toolTip = ToolTip(btnLabel, msg=queue.current_task.description) Thread(target=do_task).start() def restore_task(event): queue.run_task = True queue.current_task_time_max = queue.current_task_time_current + time.time() Thread(target=do_task).start() btnLabel.bind("", pause_task) def pause_task(event): myText = f"{btnLabel.cget('text')}\nПауза" queue.run_task = False time.sleep(0.2) ic(myText) btnLabel["text"] = myText btnLabel.bind("", restore_task) def stop_task(event): queue.run_task = False btnLabel["text"] = "Начать" btnLabel.bind("", start_task) if hasattr(btnLabel, 'toolTip'): btnLabel.toolTip.on_leave() btnLabel.toolTip.destroy() btnLabel.toolTip = ToolTip(btnLabel, msg="Начать задачу") def do_task(): ic(queue.current_task.name) while queue.current_task_time_max > time.time() and queue.run_task: queue.current_task_time_current = queue.current_task_time_max - time.time() m, s = divmod(queue.current_task_time_current, 60) btnLabel["text"] = f"{queue.current_task.name}\n{int(m)} минут {int(s)} секкунд" time.sleep(1) if queue.run_task: queue.current_task.count += 1 queue.current_task.time += queue.current_task_time if queue.current_task.id > 0: ic(queue.task_slots) if queue.task_slots[str(queue.current_task.id)].finished: btns[str(queue.current_task.id)]["text"] = f"{queue.current_task.id} {queue.current_task.name} ({queue.current_task.count})\nЗавершено" else: btns[str(queue.current_task.id)]["text"] = f"{queue.current_task.id} {queue.current_task.name} ({queue.current_task.count})" queue.sound() stop_task(0) def submit(window, btn, num, name, description, event=0): name = name.get() description = description.get() ic(name, description) btn["text"] = f"{num} {name} (0)" if hasattr(btn, 'toolTip'): btn.toolTip.on_leave() btn.toolTip.destroy() btn.toolTip = ToolTip(btn, msg=description) queue.create_task(name, description, num) window.destroy() def finish_unfinished_task(btn, num, event): if str(num) in queue.task_slots.keys(): if queue.task_slots[str(num)].finished: result = askyesno(title="Подтвержение операции", message="Отменить завершение задачи?") else: result = askyesno(title="Подтвержение операции", message="Завершить задачу?") if result: queue.task_slots[str(num)].finished = False if queue.task_slots[str(num)].finished else True if queue.task_slots[str(num)].finished: btns[str(num)]["text"] = f"{queue.task_slots[str(num)].id} {queue.task_slots[str(num)].name} ({queue.task_slots[str(num)].count})\nЗавершено" else: btns[str(num)]["text"] = f"{queue.task_slots[str(num)].id} {queue.task_slots[str(num)].name} ({queue.task_slots[str(num)].count})" def click(btn, num, event): if (str(num) in queue.task_slots.keys()): start_task_unic(num, 1) else: ic(event, btn, num) window = tk.Toplevel() window.title("Новая задача") window.geometry("250x200") label = ttk.Label(window, text=f"Имя задачи:") input_name = ttk.Entry(window) label_description = ttk.Label(window, text=f"Описание задачи (если необходимо):") input_description = ttk.Entry(window) window.bind('', partial(submit, window, btn, num, input_name, input_description)) button_sambit = ttk.Button(window, text="Создать задачу", command = partial(submit, window, btn, num, input_name, input_description)) label.pack(fill="both") input_name.pack(expand=True, fill="both") label_description.pack(fill="both") input_description.pack(expand=True, fill="both") input_name.focus() button_sambit.pack(expand=True, fill="both") window.grab_set() window.attributes("-topmost",True) def clear_task(btn, num, event): result = askyesno(title="Подтвержение операции", message="Хотите удалить задачу?") if result: btn["text"] = f"{num} ____________ ()" queue.delete_task(num) for c in range(2): root.columnconfigure(index=c, weight=1) for r in range(4): root.rowconfigure(index=r, weight=1) for i in range(0,6): btn = ttk.Button(text=f"{i+1} ____________ ()") btn.bind("", partial(click, btn, i+1)) btn.bind("", partial(clear_task, btn, i+1)) btn.bind("", partial(finish_unfinished_task, btn, i+1)) btn.grid(column=(i%2), row=(i//2), sticky="nsew") btns[str(i+1)] = btn ic(btns) btnLabel = ttk.Button(text=f"Начать") btnLabel.bind("", start_task) btnLabel.bind("", stop_task) btnLabel.grid(column=0, row=3, columnspan=2, sticky="nsew") main_menu = tk.Menu() file_menu = tk.Menu() file_menu.add_command(label="Сохранить сессию", command=save_session) file_menu.add_command(label="Загрузить сессию", command=load_session) # file_menu.add_separator() # file_menu.add_command(label="Выход") main_menu.add_cascade(label="Файл", menu=file_menu) main_menu.add_cascade(label="График сессии", command=build_plt) main_menu.add_cascade(label="Закрепить", command= lambda: root.attributes("-topmost",True)) root.config(menu=main_menu) root.update_idletasks() root.mainloop()