import tkinter as tk
from tkinter import messagebox
import yaml
import os
from datetime import datetime
from collections import defaultdict
class RoutineDiaryApp:
def __init__(self, root):
self.root = root
self.root.title("Routine Diary")
self.root.geometry("600x500")
self.tasks_file = "tasks.txt"
self.diary_file = "diary.yaml"
# Данные
self.all_tasks = [] # Все задачи из файла
self.today = datetime.now().strftime("%Y-%m-%d")
self.today_clicks = defaultdict(int) # Счетчики кликов на сегодня
self.clicked_tasks = set() # Задачи, нажатые сегодня
# Загрузка данных
self.load_tasks()
self.load_diary()
# GUI элементы
self.create_widgets()
self.update_buttons()
def load_tasks(self):
"""Загрузка списка задач из файла"""
try:
if os.path.exists(self.tasks_file):
with open(self.tasks_file, 'r', encoding='utf-8') as f:
self.all_tasks = sorted([line.strip() for line in f if line.strip()])
else:
# Создаем пример файла, если его нет
with open(self.tasks_file, 'w', encoding='utf-8') as f:
f.write("eat shizaprotective tablet\ntroll on dvach\nwipe ass after taking shit\n")
self.all_tasks = ["eat shizaprotective tablet", "troll on dvach", "wipe ass after taking shit"]
except Exception as e:
messagebox.showerror("Error", f"Failed to load tasks: {e}")
self.all_tasks = []
def load_diary(self):
"""Загрузка истории из YAML файла"""
try:
if os.path.exists(self.diary_file):
with open(self.diary_file, 'r', encoding='utf-8') as f:
diary_data = yaml.safe_load(f) or []
# Ищем записи за сегодня
for day_entry in diary_data:
if isinstance(day_entry, dict) and self.today in day_entry:
today_data = day_entry[self.today]
for task, timestamps in today_data.items():
if isinstance(timestamps, list):
self.today_clicks[task] = len(timestamps)
if timestamps:
self.clicked_tasks.add(task)
break
except Exception as e:
print(f"Warning: Could not load diary: {e}")
# Создаем пустой файл
self.save_to_diary([])
def save_to_diary(self, data):
"""Сохранение данных в YAML файл"""
try:
with open(self.diary_file, 'w', encoding='utf-8') as f:
yaml.dump(data, f, allow_unicode=True, default_flow_style=False)
except Exception as e:
messagebox.showerror("Error", f"Failed to save diary: {e}")
def create_widgets(self):
"""Создание элементов интерфейса"""
# Фрейм для кнопок
self.button_frame = tk.Frame(self.root)
self.button_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Scrollbar для кнопок
scrollbar = tk.Scrollbar(self.button_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# Canvas для скроллирования кнопок
self.canvas = tk.Canvas(self.button_frame, yscrollcommand=scrollbar.set)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.config(command=self.canvas.yview)
# Фрейм для кнопок внутри canvas
self.inner_frame = tk.Frame(self.canvas)
self.canvas_window = self.canvas.create_window((0, 0), window=self.inner_frame, anchor=tk.NW)
# Привязка событий для скроллинга
self.inner_frame.bind("<Configure>", self.on_frame_configure)
self.canvas.bind("<Configure>", self.on_canvas_configure)
# Метка с текущей датой
self.date_label = tk.Label(self.root, text=f"Today: {self.today}", font=("Arial", 12, "bold"))
self.date_label.pack(pady=5)
def on_frame_configure(self, event):
"""Обновление scrollregion при изменении размера фрейма"""
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def on_canvas_configure(self, event):
"""Обновление ширины внутреннего фрейма при изменении размера canvas"""
self.canvas.itemconfig(self.canvas_window, width=event.width)
def update_buttons(self):
"""Обновление отображения кнопок"""
# Очистка старых кнопок
for widget in self.inner_frame.winfo_children():
widget.destroy()
# Разделение задач на нажатые и ненажатые
unclicked = sorted([t for t in self.all_tasks if t not in self.clicked_tasks])
clicked = sorted([t for t in self.all_tasks if t in self.clicked_tasks])
# Создание кнопок для ненажатых задач
for task in unclicked:
btn = tk.Button(self.inner_frame, text=task,
command=lambda t=task: self.on_task_click(t),
bg="lightgray", height=2, width=40)
btn.pack(pady=2, padx=5, fill=tk.X)
# Создание кнопок для нажатых задач (с количеством кликов)
for task in clicked:
count = self.today_clicks[task]
btn_text = f"{task} ({count})"
btn = tk.Button(self.inner_frame, text=btn_text,
command=lambda t=task: self.on_task_click(t),
bg="lightgreen", height=2, width=40)
btn.pack(pady=2, padx=5, fill=tk.X)
def on_task_click(self, task):
"""Обработка клика по кнопке задачи"""
try:
current_time = datetime.now()
timestamp = current_time.strftime("%Y-%m-%d %H:%M")
# Обновление счетчиков
self.today_clicks[task] += 1
self.clicked_tasks.add(task)
# Загрузка существующего дневника
diary_data = []
if os.path.exists(self.diary_file):
with open(self.diary_file, 'r', encoding='utf-8') as f:
diary_data = yaml.safe_load(f) or []
# Поиск или создание записи за сегодня
today_entry = None
for entry in diary_data:
if isinstance(entry, dict) and self.today in entry:
today_entry = entry
break
if not today_entry:
today_entry = {self.today: {}}
diary_data.append(today_entry)
# Добавление timestamp
if task not in today_entry[self.today]:
today_entry[self.today][task] = []
today_entry[self.today][task].append(timestamp)
# Сохранение
self.save_to_diary(diary_data)
# Обновление интерфейса
self.update_buttons()
except Exception as e:
messagebox.showerror("Error", f"Failed to log task: {e}")
def main():
root = tk.Tk()
app = RoutineDiaryApp(root)
root.mainloop()
if __name__ == "__main__":
main()