Загрузка данных


import tkinter as tk
from tkinter import messagebox, simpledialog
import json
import os
from PIL import Image, ImageTk  # pip install pillow

# ------------------ Функции работы с файлом заметок ------------------
NOTES_FILE = "notes.json"

def load_notes():
    """Загружает заметки из JSON-файла"""
    if not os.path.exists(NOTES_FILE):
        return []
    try:
        with open(NOTES_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    except:
        return []

def save_notes(notes):
    """Сохраняет заметки в JSON-файл"""
    with open(NOTES_FILE, "w", encoding="utf-8") as f:
        json.dump(notes, f, ensure_ascii=False, indent=2)

# ------------------ Главный класс приложения ------------------
class NotebookApp:
    def __init__(self, root):
        self.root = root
        self.root.title("~VER$Y - Notebook~")
        self.root.geometry("950x600")

        # --- Загрузка фонового изображения с помощью Pillow (поддержка PNG/JPG) ---
        # Создаём Canvas, на котором будет фон и все виджеты
        self.canvas = tk.Canvas(root, highlightthickness=0)
        self.canvas.pack(fill="both", expand=True)

        self.bg_image = None  # будет хранить текущий PhotoImage
        self.bg_image_pil = None  # оригинальное PIL-изображение для перерисовки

        try:
            # Открываем изображение через Pillow
            self.bg_image_pil = Image.open("background.png")  # можно поменять на .jpg
            # Первоначальный показ
            self.update_background()
            # Привязываем функцию изменения размера окна
            self.root.bind("<Configure>", self.on_resize)
        except Exception as e:
            print(f"Фоновое изображение не загружено: {e}")
            # Если нет фона, просто заливаем цветом
            self.canvas.configure(bg="#f0f0f0")
            # Но продолжаем работу – будем размещать виджеты на Canvas

        # Иконка окна (если файл существует)
        try:
            root.iconbitmap(default="MIKU.ico")
        except:
            pass

        # Данные
        self.notes = load_notes()
        self.current_note_index = None

        # Создаём интерфейс
        self.create_widgets()
        self.update_notes_list()

    def update_background(self):
        """Перерисовывает фон под текущий размер окна"""
        if self.bg_image_pil is None:
            return
        # Получаем текущий размер окна
        width = self.root.winfo_width()
        height = self.root.winfo_height()
        if width <= 1 or height <= 1:
            width, height = 950, 600  # начальные значения
        # Масштабируем изображение до размера окна
        resized = self.bg_image_pil.resize((width, height), Image.Resampling.LANCZOS)
        self.bg_image = ImageTk.PhotoImage(resized)
        # Удаляем старый фон (если есть)
        self.canvas.delete("bg_image")
        # Рисуем новый фон с тегом "bg_image", чтобы потом легко удалить
        self.canvas.create_image(0, 0, image=self.bg_image, anchor="nw", tags="bg_image")
        # Опускаем фон на самый нижний уровень
        self.canvas.tag_lower("bg_image")

    def on_resize(self, event):
        """Срабатывает при изменении размера окна"""
        # Избегаем лишних вызовов: обновляем фон только если это корневое окно
        if event.widget == self.root:
            self.update_background()

    # ------------------ Создание виджетов ------------------
    def create_widgets(self):
        # Левый фрейм для списка заметок (будет размещён на Canvas)
        self.left_frame = tk.Frame(self.canvas, bg="#ffffff", bd=2, relief=tk.RAISED)
        self.canvas.create_window(10, 10, window=self.left_frame, anchor="nw", width=250, height=580)

        tk.Label(self.left_frame, text="Мои заметки", font=("Arial", 14, "bold"), bg="#ffffff").pack(pady=5)
        self.notes_listbox = tk.Listbox(self.left_frame, width=30, height=25, font=("Arial", 10))
        self.notes_listbox.pack(pady=5, padx=5, fill=tk.BOTH, expand=True)
        self.notes_listbox.bind("<<ListboxSelect>>", self.on_note_select)

        btn_frame = tk.Frame(self.left_frame, bg="#ffffff")
        btn_frame.pack(pady=5)
        tk.Button(btn_frame, text="Новая", width=8, command=self.new_note).pack(side=tk.LEFT, padx=5)
        tk.Button(btn_frame, text="Удалить", width=8, command=self.delete_note).pack(side=tk.LEFT, padx=5)

        # Правый фрейм для редактирования заметки
        self.right_frame = tk.Frame(self.canvas, bg="#ffffff", bd=2, relief=tk.RAISED)
        self.canvas.create_window(280, 10, window=self.right_frame, anchor="nw", width=650, height=580)

        tk.Label(self.right_frame, text="Заголовок:", font=("Arial", 10), bg="#ffffff").pack(anchor="w", padx=10, pady=(10, 0))
        self.title_entry = tk.Entry(self.right_frame, font=("Arial", 12), width=60)
        self.title_entry.pack(padx=10, pady=5, fill=tk.X)

        tk.Label(self.right_frame, text="Текст заметки:", font=("Arial", 10), bg="#ffffff").pack(anchor="w", padx=10, pady=(10, 0))
        self.text_text = tk.Text(self.right_frame, font=("Arial", 11), wrap=tk.WORD, height=20)
        self.text_text.pack(padx=10, pady=5, fill=tk.BOTH, expand=True)

        btn_save = tk.Button(self.right_frame, text="Сохранить заметку", command=self.save_current_note, bg="#4CAF50", fg="white", font=("Arial", 10, "bold"))
        btn_save.pack(pady=10)

        # Настройка прокрутки для правого фрейма (опционально, если текст большой)
        scrollbar = tk.Scrollbar(self.right_frame, command=self.text_text.yview)
        self.text_text.config(yscrollcommand=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y, padx=(0, 10), pady=5)

        # Делаем фреймы "прозрачными" в смысле отсутствия фона поверх Canvas? 
        # Цвет фона фреймов мы задали явно (белый), но чтобы не было конфликта, 
        # можно сделать их полупрозрачными? Tkinter не поддерживает прозрачность виджетов.
        # Поэтому просто оставляем белый фон – заметки читаются хорошо.

    # ------------------ Логика работы с заметками ------------------
    def update_notes_list(self):
        """Обновляет список заметок в Listbox"""
        self.notes_listbox.delete(0, tk.END)
        for note in self.notes:
            title = note.get("title", "Без названия")
            self.notes_listbox.insert(tk.END, title)

    def on_note_select(self, event):
        """Выбор заметки из списка"""
        selection = self.notes_listbox.curselection()
        if not selection:
            return
        index = selection[0]
        self.current_note_index = index
        note = self.notes[index]
        # Заполняем поля
        self.title_entry.delete(0, tk.END)
        self.title_entry.insert(0, note.get("title", ""))
        self.text_text.delete(1.0, tk.END)
        self.text_text.insert(1.0, note.get("content", ""))

    def new_note(self):
        """Создаёт новую заметку"""
        # Очищаем поля ввода
        self.title_entry.delete(0, tk.END)
        self.text_text.delete(1.0, tk.END)
        self.current_note_index = None

    def save_current_note(self):
        """Сохраняет текущую заметку (новую или редактируемую)"""
        title = self.title_entry.get().strip()
        content = self.text_text.get(1.0, tk.END).strip()
        if not title and not content:
            messagebox.showwarning("Пустая заметка", "Невозможно сохранить пустую заметку.")
            return
        if not title:
            title = "Без названия"

        if self.current_note_index is not None:
            # Редактируем существующую
            self.notes[self.current_note_index] = {"title": title, "content": content}
        else:
            # Новая заметка
            self.notes.append({"title": title, "content": content})
        save_notes(self.notes)
        self.update_notes_list()
        # Находим сохранённую заметку в списке и выделяем её
        for i, note in enumerate(self.notes):
            if note["title"] == title and note["content"] == content:
                self.notes_listbox.selection_clear(0, tk.END)
                self.notes_listbox.selection_set(i)
                self.notes_listbox.see(i)
                self.current_note_index = i
                break
        messagebox.showinfo("Сохранено", f"Заметка \"{title}\" сохранена.")

    def delete_note(self):
        """Удаляет выбранную заметку"""
        if self.current_note_index is None:
            messagebox.showwarning("Нет выбора", "Выберите заметку для удаления.")
            return
        title = self.notes[self.current_note_index].get("title", "Без названия")
        if messagebox.askyesno("Удаление", f"Удалить заметку \"{title}\"?"):
            del self.notes[self.current_note_index]
            save_notes(self.notes)
            self.current_note_index = None
            self.title_entry.delete(0, tk.END)
            self.text_text.delete(1.0, tk.END)
            self.update_notes_list()


# ------------------ Запуск ------------------
if __name__ == "__main__":
    root = tk.Tk()
    app = NotebookApp(root)
    root.mainloop()