"""Главное окно приложения""" import tkinter as tk from tkinter import ttk, filedialog, messagebox import json import zipfile import os from datetime import datetime from app.config import Config from app.database import Database from app.auth import Auth from app.ui.sidebar import Sidebar from app.ui.note_editor import NoteEditor from app.ui.dialogs import PasswordDialog class MainWindow(tk.Tk): """Главное окно приложения""" def __init__(self): super().__init__() self.title(Config.APP_NAME) self.geometry(f"{Config.WINDOW_WIDTH}x{Config.WINDOW_HEIGHT}") self.minsize(Config.MIN_WINDOW_WIDTH, Config.MIN_WINDOW_HEIGHT) self.configure(bg=Config.COLORS['bg']) # Инициализация компонентов self.db = Database() self.auth = Auth(self.db) # Проверка пароля if not self.check_auth(): self.destroy() return # Создание интерфейса self.create_menu() self.create_widgets() # Загрузка заметок для текущей даты self.sidebar.refresh_notes() # Привязка закрытия окна self.protocol("WM_DELETE_WINDOW", self.on_closing) def check_auth(self): """Проверка аутентификации""" if self.auth.is_password_set(): if self.auth.ask_password_on_startup(): dialog = PasswordDialog(self, self.auth, mode='check') self.wait_window(dialog) return dialog.result else: # Предлагаем создать пароль dialog = PasswordDialog(self, self.auth, mode='set') self.wait_window(dialog) return dialog.result return True def create_menu(self): """Создание меню""" menubar = tk.Menu(self) self.config(menu=menubar) # Меню Файл file_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="Файл", menu=file_menu) file_menu.add_command(label="Экспорт в ZIP", command=self.export_to_zip, accelerator="Ctrl+E") file_menu.add_command(label="Импорт из ZIP", command=self.import_from_zip, accelerator="Ctrl+I") file_menu.add_separator() file_menu.add_command(label="Выход", command=self.on_closing, accelerator="Ctrl+Q") # Меню Настройки settings_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="Настройки", menu=settings_menu) settings_menu.add_command(label="Изменить мастер-пароль", command=self.change_password) # Горячие клавиши self.bind('', lambda e: self.export_to_zip()) self.bind('', lambda e: self.import_from_zip()) self.bind('', lambda e: self.on_closing()) def create_widgets(self): """Создание виджетов интерфейса""" # Основной контейнер main_container = tk.Frame(self, bg=Config.COLORS['bg']) main_container.pack(fill='both', expand=True) # Боковая панель self.sidebar = Sidebar( main_container, self.db, on_note_select=self.on_note_selected, on_note_delete=self.on_note_deleted ) # Разделитель separator = tk.Frame(main_container, width=2, bg=Config.COLORS['separator']) separator.pack(side='left', fill='y') # Редактор заметок self.editor = NoteEditor( main_container, self.db, on_note_saved=self.on_note_saved, on_note_deleted=self.on_note_deleted, on_new_note=self.on_new_note_created ) self.editor.pack(side='left', fill='both', expand=True) def on_note_selected(self, note): """Обработчик выбора заметки""" self.editor.show_note(note) def on_note_saved(self, note): """Обработчик сохранения заметки""" self.sidebar.refresh_notes() def on_note_deleted(self): """Обработчик удаления заметки""" self.sidebar.refresh_notes() def on_new_note_created(self): """Обработчик создания новой заметки""" pass def export_to_zip(self): """Экспорт данных в ZIP архив""" filename = filedialog.asksaveasfilename( defaultextension=".zip", filetypes=[("ZIP files", "*.zip"), ("All files", "*.*")], initialfile=f"captains_log_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip" ) if filename: try: # Экспортируем данные data = self.db.export_data() # Создаем временный JSON файл temp_json = os.path.join(os.path.dirname(filename), 'temp_export.json') with open(temp_json, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) # Создаем ZIP архив with zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED) as zipf: zipf.write(temp_json, 'captains_log_backup.json') # Удаляем временный файл os.remove(temp_json) messagebox.showinfo("Успех", f"Данные экспортированы в:\n{filename}") except Exception as e: messagebox.showerror("Ошибка", f"Не удалось экспортировать данные:\n{str(e)}") def import_from_zip(self): """Импорт данных из ZIP архива""" filename = filedialog.askopenfilename( filetypes=[("ZIP files", "*.zip"), ("All files", "*.*")] ) if filename: # Подтверждение if not messagebox.askyesno( "Подтверждение", "Импорт данных заменит все существующие заметки.\nПродолжить?" ): return try: # Извлекаем из ZIP with zipfile.ZipFile(filename, 'r') as zipf: temp_json = os.path.join(os.path.dirname(filename), 'temp_import.json') zipf.extract('captains_log_backup.json', os.path.dirname(filename)) # Читаем JSON with open(temp_json, 'r', encoding='utf-8') as f: data = json.load(f) # Импортируем данные self.db.import_data(data) # Удаляем временный файл os.remove(temp_json) # Обновляем интерфейс self.sidebar.refresh_notes() self.editor.show_empty_state() messagebox.showinfo("Успех", "Данные успешно импортированы") except Exception as e: messagebox.showerror("Ошибка", f"Не удалось импортировать данные:\n{str(e)}") def change_password(self): """Изменение мастер-пароля""" if self.auth.is_password_set(): dialog = PasswordDialog(self, self.auth, mode='check') self.wait_window(dialog) if dialog.result: dialog = PasswordDialog(self, self.auth, mode='set') self.wait_window(dialog) else: dialog = PasswordDialog(self, self.auth, mode='set') self.wait_window(dialog) def on_closing(self): """Обработчик закрытия окна""" self.destroy()