diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 8dd4581..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,35 +0,0 @@
-# Python
-__pycache__/
-*.py[cod]
-*$py.class
-*.so
-*.egg-info/
-dist/
-build/
-*.egg
-
-# Виртуальное окружение
-.venv/
-venv/
-ENV/
-env/
-
-# База данных и данные приложения
-captains_log.db
-*.db
-
-# IDE
-.idea/
-.vscode/
-*.swp
-*.swo
-*~
-
-# Nuitka
-*.build/
-*.dist/
-*.onefile-build/
-
-# OS
-.DS_Store
-Thumbs.db
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index ab1f416..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Ignored default folder with query files
-/queries/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
-# Editor-based HTTP Client requests
-/httpRequests/
diff --git a/.idea/CapLog.iml b/.idea/CapLog.iml
deleted file mode 100644
index f2cca7e..0000000
--- a/.idea/CapLog.iml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100644
index 105ce2d..0000000
--- a/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 25d64c7..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 2089c61..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1dd..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/__init__.py b/app/__init__.py
deleted file mode 100644
index b71b4f8..0000000
--- a/app/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-"""
-Captain's Log - Приложение для быстрых заметок
-"""
\ No newline at end of file
diff --git a/app/auth.py b/app/auth.py
deleted file mode 100644
index 25adb28..0000000
--- a/app/auth.py
+++ /dev/null
@@ -1,55 +0,0 @@
-"""Модуль аутентификации"""
-import hashlib
-import os
-from app.database import Database
-
-class Auth:
- def __init__(self, db: Database):
- self.db = db
-
- def set_master_password(self, password):
- """Установить мастер-пароль"""
- password_hash = self._hash_password(password)
- self.db.set_setting('master_password_hash', password_hash)
- self.db.set_setting('password_set', '1')
-
- def check_password(self, password):
- """Проверить мастер-пароль"""
- stored_hash = self.db.get_setting('master_password_hash')
- if not stored_hash:
- return True # Пароль не установлен
-
- return self._hash_password(password) == stored_hash
-
- def is_password_set(self):
- """Проверяет, установлен ли пароль"""
- return self.db.get_setting('password_set', '0') == '1'
-
- def ask_password_on_startup(self):
- """Спрашивать ли пароль при входе"""
- return self.db.get_setting('ask_password_on_startup', '1') == '1'
-
- def set_ask_password_on_startup(self, value):
- """Установить, спрашивать ли пароль при входе"""
- self.db.set_setting('ask_password_on_startup', '1' if value else '0')
-
- def reset_password(self):
- """Сбросить мастер-пароль"""
- self.db.set_setting('password_set', '0')
- self.db.set_setting('master_password_hash', '')
- self.db.set_setting('ask_password_on_startup', '1')
-
- def change_password(self, old_password, new_password):
- """Изменить мастер-пароль"""
- if self.is_password_set():
- if not self.check_password(old_password):
- return False, "Неверный текущий пароль"
-
- self.set_master_password(new_password)
- return True, "Пароль успешно изменен"
-
- def _hash_password(self, password):
- """Хешировать пароль с солью"""
- # В будущем здесь будет более безопасное хеширование
- # Пока используем SHA-256 для простоты
- return hashlib.sha256(password.encode()).hexdigest()
\ No newline at end of file
diff --git a/app/config.py b/app/config.py
deleted file mode 100644
index f0f6899..0000000
--- a/app/config.py
+++ /dev/null
@@ -1,51 +0,0 @@
-"""Конфигурация приложения"""
-import os
-import sys
-
-
-class Config:
- APP_NAME = "Captain's Log"
- APP_VERSION = "0.1.0"
-
- # Путь к директории приложения
- if getattr(sys, 'frozen', False):
- APP_DIR = os.path.dirname(sys.executable)
- else:
- APP_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-
- # База данных
- DB_NAME = "captains_log.db"
- DB_PATH = os.path.join(APP_DIR, DB_NAME)
-
- # Настройки UI
- WINDOW_WIDTH = 900
- WINDOW_HEIGHT = 650
- SIDEBAR_WIDTH = 350
- MIN_WINDOW_WIDTH = 800
- MIN_WINDOW_HEIGHT = 600
-
- # Настройки цветов
- COLORS = {
- 'bg': '#2b2b2b',
- 'fg': '#ffffff',
- 'sidebar_bg': '#333333',
- 'editor_bg': '#3c3c3c',
- 'button_bg': '#4a4a4a',
- 'button_fg': '#ffffff',
- 'button_hover': '#5a5a5a',
- 'accent': '#007acc',
- 'accent_hover': '#005f9e',
- 'danger': '#d9534f',
- 'danger_hover': '#c9302c',
- 'success': '#5cb85c',
- 'today_bg': '#007acc',
- 'text_bg': '#252525',
- 'text_fg': '#ffffff',
- 'calendar_bg': '#333333',
- 'calendar_fg': '#ffffff',
- 'calendar_header': '#007acc',
- 'list_bg': '#2d2d2d',
- 'list_fg': '#ffffff',
- 'list_selected': '#404040',
- 'separator': '#555555'
- }
\ No newline at end of file
diff --git a/app/database.py b/app/database.py
deleted file mode 100644
index c98bc33..0000000
--- a/app/database.py
+++ /dev/null
@@ -1,245 +0,0 @@
-"""Модуль для работы с базой данных SQLite"""
-import sqlite3
-import os
-from datetime import datetime
-from app.config import Config
-
-
-class Database:
- def __init__(self):
- self.db_path = Config.DB_PATH
- self.init_db()
-
- def get_connection(self):
- """Получить соединение с БД"""
- conn = sqlite3.connect(self.db_path)
- conn.row_factory = sqlite3.Row
- return conn
-
- def init_db(self):
- """Инициализация базы данных"""
- with self.get_connection() as conn:
- cursor = conn.cursor()
-
- # Таблица настроек
- cursor.execute('''
- CREATE TABLE IF NOT EXISTS settings
- (
- key
- TEXT
- PRIMARY
- KEY,
- value
- TEXT
- NOT
- NULL
- )
- ''')
-
- # Таблица заметок
- cursor.execute('''
- CREATE TABLE IF NOT EXISTS notes
- (
- id
- INTEGER
- PRIMARY
- KEY
- AUTOINCREMENT,
- title
- TEXT,
- content
- TEXT,
- created_at
- TIMESTAMP
- DEFAULT
- CURRENT_TIMESTAMP,
- updated_at
- TIMESTAMP
- DEFAULT
- CURRENT_TIMESTAMP
- )
- ''')
-
- conn.commit()
-
- def get_setting(self, key, default=None):
- """Получить значение настройки"""
- with self.get_connection() as conn:
- cursor = conn.cursor()
- cursor.execute('SELECT value FROM settings WHERE key = ?', (key,))
- row = cursor.fetchone()
- return row['value'] if row else default
-
- def set_setting(self, key, value):
- """Установить значение настройки"""
- with self.get_connection() as conn:
- cursor = conn.cursor()
- cursor.execute('''
- INSERT OR REPLACE INTO settings (key, value)
- VALUES (?, ?)
- ''', (key, value))
- conn.commit()
-
- def get_notes_by_date(self, year, month, day=None):
- """Получить заметки по дате"""
- with self.get_connection() as conn:
- cursor = conn.cursor()
-
- if day:
- cursor.execute('''
- SELECT *
- FROM notes
- WHERE strftime('%Y', created_at) = ?
- AND strftime('%m', created_at) = ?
- AND strftime('%d', created_at) = ?
- ORDER BY created_at DESC
- ''', (str(year), str(month).zfill(2), str(day).zfill(2)))
- else:
- cursor.execute('''
- SELECT *
- FROM notes
- WHERE strftime('%Y', created_at) = ?
- AND strftime('%m', created_at) = ?
- ORDER BY created_at DESC
- ''', (str(year), str(month).zfill(2)))
-
- return [dict(row) for row in cursor.fetchall()]
-
- def get_notes_with_content_by_date(self, date_str):
- """Получить заметки с контентом по дате (YYYY-MM-DD)"""
- with self.get_connection() as conn:
- cursor = conn.cursor()
- cursor.execute('''
- SELECT *
- FROM notes
- WHERE date (created_at) = ?
- ORDER BY created_at DESC
- ''', (date_str,))
- return [dict(row) for row in cursor.fetchall()]
-
- def get_note_by_id(self, note_id):
- """Получить заметку по ID"""
- with self.get_connection() as conn:
- cursor = conn.cursor()
- cursor.execute('SELECT * FROM notes WHERE id = ?', (note_id,))
- row = cursor.fetchone()
- return dict(row) if row else None
-
- def create_note(self, title=None, content='', created_at=None):
- """Создать новую заметку"""
- with self.get_connection() as conn:
- cursor = conn.cursor()
-
- if created_at:
- cursor.execute('''
- INSERT INTO notes (title, content, created_at, updated_at)
- VALUES (?, ?, ?, ?)
- ''', (title, content, created_at, created_at))
- else:
- cursor.execute('''
- INSERT INTO notes (title, content)
- VALUES (?, ?)
- ''', (title, content))
-
- conn.commit()
- return cursor.lastrowid
-
- def update_note(self, note_id, title=None, content=None):
- """Обновить заметку"""
- with self.get_connection() as conn:
- cursor = conn.cursor()
-
- updates = []
- params = []
-
- if title is not None:
- updates.append('title = ?')
- params.append(title)
-
- if content is not None:
- updates.append('content = ?')
- params.append(content)
-
- if updates:
- updates.append('updated_at = CURRENT_TIMESTAMP')
- params.append(note_id)
-
- cursor.execute(f'''
- UPDATE notes
- SET {', '.join(updates)}
- WHERE id = ?
- ''', params)
- conn.commit()
-
- def delete_note(self, note_id):
- """Удалить заметку"""
- with self.get_connection() as conn:
- cursor = conn.cursor()
- cursor.execute('DELETE FROM notes WHERE id = ?', (note_id,))
- conn.commit()
-
- def get_all_notes(self):
- """Получить все заметки"""
- with self.get_connection() as conn:
- cursor = conn.cursor()
- cursor.execute('SELECT * FROM notes ORDER BY created_at DESC')
- return [dict(row) for row in cursor.fetchall()]
-
- def export_data(self):
- """Экспортировать все данные"""
- return {
- 'notes': self.get_all_notes(),
- 'settings': self.get_all_settings()
- }
-
- def get_all_settings(self):
- """Получить все настройки"""
- with self.get_connection() as conn:
- cursor = conn.cursor()
- cursor.execute('SELECT * FROM settings')
- return {row['key']: row['value'] for row in cursor.fetchall()}
-
- def import_data(self, data):
- """Импортировать данные"""
- with self.get_connection() as conn:
- cursor = conn.cursor()
-
- # Очищаем существующие данные
- cursor.execute('DELETE FROM notes')
- cursor.execute('DELETE FROM settings')
-
- # Импортируем заметки
- for note in data.get('notes', []):
- cursor.execute('''
- INSERT INTO notes (id, title, content, created_at, updated_at)
- VALUES (?, ?, ?, ?, ?)
- ''', (
- note['id'],
- note.get('title'),
- note.get('content', ''),
- note.get('created_at'),
- note.get('updated_at')
- ))
-
- # Импортируем настройки
- for key, value in data.get('settings', {}).items():
- cursor.execute('''
- INSERT INTO settings (key, value)
- VALUES (?, ?)
- ''', (key, value))
-
- conn.commit()
-
- def get_dates_with_notes(self, year, month):
- """Получить дни месяца, в которых есть заметки"""
- with self.get_connection() as conn:
- cursor = conn.cursor()
- cursor.execute('''
- SELECT DISTINCT strftime('%d', created_at) as day
- FROM notes
- WHERE strftime('%Y'
- , created_at) = ?
- AND strftime('%m'
- , created_at) = ?
- ''', (str(year), str(month).zfill(2)))
- return [int(row['day']) for row in cursor.fetchall()]
\ No newline at end of file
diff --git a/app/models.py b/app/models.py
deleted file mode 100644
index e69de29..0000000
diff --git a/app/ui/__init__.py b/app/ui/__init__.py
deleted file mode 100644
index 7cff528..0000000
--- a/app/ui/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""UI модули"""
\ No newline at end of file
diff --git a/app/ui/calendar_widget.py b/app/ui/calendar_widget.py
deleted file mode 100644
index 2eb5e2c..0000000
--- a/app/ui/calendar_widget.py
+++ /dev/null
@@ -1,189 +0,0 @@
-"""Виджет календаря"""
-import tkinter as tk
-from tkinter import ttk
-import calendar
-from datetime import datetime
-from app.config import Config
-
-
-class CalendarWidget(tk.Frame):
- """Кастомный виджет календаря"""
-
- def __init__(self, parent, db, on_date_select=None):
- super().__init__(parent, bg=Config.COLORS['calendar_bg'])
- self.db = db
- self.on_date_select = on_date_select
-
- self.current_date = datetime.now()
- self.selected_date = datetime.now()
-
- self.create_widgets()
- self.update_calendar()
-
- def create_widgets(self):
- # Заголовок с месяцем и годом
- self.header_frame = tk.Frame(self, bg=Config.COLORS['calendar_bg'])
- self.header_frame.pack(fill='x', pady=(10, 5))
-
- self.prev_btn = tk.Button(
- self.header_frame,
- text="◀",
- command=self.prev_month,
- bg=Config.COLORS['calendar_bg'],
- fg=Config.COLORS['fg'],
- relief='flat',
- font=('Arial', 12),
- cursor='hand2'
- )
- self.prev_btn.pack(side='left', padx=(20, 0))
-
- self.month_label = tk.Label(
- self.header_frame,
- text="",
- bg=Config.COLORS['calendar_bg'],
- fg=Config.COLORS['calendar_header'],
- font=('Arial', 14, 'bold')
- )
- self.month_label.pack(side='left', expand=True)
-
- self.next_btn = tk.Button(
- self.header_frame,
- text="▶",
- command=self.next_month,
- bg=Config.COLORS['calendar_bg'],
- fg=Config.COLORS['fg'],
- relief='flat',
- font=('Arial', 12),
- cursor='hand2'
- )
- self.next_btn.pack(side='right', padx=(0, 20))
-
- # Дни недели
- days_frame = tk.Frame(self, bg=Config.COLORS['calendar_bg'])
- days_frame.pack(fill='x', padx=5, pady=5)
-
- days = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']
- for i, day in enumerate(days):
- days_frame.grid_columnconfigure(i, weight=1)
- tk.Label(
- days_frame,
- text=day,
- bg=Config.COLORS['calendar_bg'],
- fg=Config.COLORS['fg'],
- font=('Arial', 9, 'bold')
- ).grid(row=0, column=i)
-
- # Сетка календаря
- self.calendar_frame = tk.Frame(self, bg=Config.COLORS['calendar_bg'])
- self.calendar_frame.pack(fill='both', expand=True, padx=5, pady=5)
-
- self.day_buttons = []
- for row in range(6):
- self.calendar_frame.grid_rowconfigure(row, weight=1)
- for col in range(7):
- self.calendar_frame.grid_columnconfigure(col, weight=1)
- btn = tk.Label(
- self.calendar_frame,
- text="",
- bg=Config.COLORS['calendar_bg'],
- fg=Config.COLORS['fg'],
- font=('Arial', 10),
- cursor='hand2'
- )
- btn.grid(row=row, column=col, sticky='nsew', padx=1, pady=1)
- btn.bind('', lambda e, r=row, c=col: self.on_day_click(r, c))
- self.day_buttons.append(btn)
-
- def update_calendar(self):
- year = self.current_date.year
- month = self.current_date.month
-
- # Обновляем заголовок
- months = ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь',
- 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь']
- self.month_label.config(text=f"{months[month - 1]} {year}")
-
- # Получаем дни с заметками
- days_with_notes = self.db.get_dates_with_notes(year, month)
-
- # Создаем календарь
- cal = calendar.monthcalendar(year, month)
-
- for idx, btn in enumerate(self.day_buttons):
- row = idx // 7
- col = idx % 7
-
- if row < len(cal) and col < len(cal[row]):
- day = cal[row][col]
- if day != 0:
- btn.config(text=str(day))
-
- # Определяем цвет фона
- today = datetime.now()
- is_today = (day == today.day and
- month == today.month and
- year == today.year)
-
- is_selected = (day == self.selected_date.day and
- month == self.selected_date.month and
- year == self.selected_date.year)
-
- has_notes = day in days_with_notes
-
- if is_today and is_selected:
- btn.config(
- bg=Config.COLORS['today_bg'],
- fg=Config.COLORS['fg']
- )
- elif is_today:
- btn.config(
- bg=Config.COLORS['today_bg'],
- fg=Config.COLORS['fg']
- )
- elif is_selected:
- btn.config(
- bg=Config.COLORS['accent'],
- fg=Config.COLORS['fg']
- )
- elif has_notes:
- btn.config(
- bg='#5a7a3a',
- fg=Config.COLORS['fg']
- )
- else:
- btn.config(
- bg=Config.COLORS['calendar_bg'],
- fg=Config.COLORS['fg']
- )
- else:
- btn.config(text="", bg=Config.COLORS['calendar_bg'])
- else:
- btn.config(text="", bg=Config.COLORS['calendar_bg'])
-
- def prev_month(self):
- if self.current_date.month == 1:
- self.current_date = self.current_date.replace(year=self.current_date.year - 1, month=12)
- else:
- self.current_date = self.current_date.replace(month=self.current_date.month - 1)
- self.update_calendar()
-
- def next_month(self):
- if self.current_date.month == 12:
- self.current_date = self.current_date.replace(year=self.current_date.year + 1, month=1)
- else:
- self.current_date = self.current_date.replace(month=self.current_date.month + 1)
- self.update_calendar()
-
- def on_day_click(self, row, col):
- year = self.current_date.year
- month = self.current_date.month
- cal = calendar.monthcalendar(year, month)
-
- if row < len(cal) and col < len(cal[row]):
- day = cal[row][col]
- if day != 0:
- self.selected_date = datetime(year, month, day)
- self.update_calendar()
-
- if self.on_date_select:
- self.on_date_select(self.selected_date)
\ No newline at end of file
diff --git a/app/ui/dialogs.py b/app/ui/dialogs.py
deleted file mode 100644
index 50807ce..0000000
--- a/app/ui/dialogs.py
+++ /dev/null
@@ -1,296 +0,0 @@
-"""Диалоговые окна"""
-import tkinter as tk
-from tkinter import ttk, messagebox
-from app.config import Config
-
-class PasswordDialog(tk.Toplevel):
- """Диалог установки/ввода мастер-пароля"""
-
- def __init__(self, parent, auth, mode='set'):
- super().__init__(parent)
- self.auth = auth
- self.mode = mode # 'set', 'check', 'reset'
- self.result = False
-
- self.title("Мастер-пароль" if mode == 'set' else
- "Введите пароль" if mode == 'check' else
- "Сброс пароля")
- self.geometry("400x300" if mode == 'reset' else "400x250")
- self.resizable(False, False)
- self.configure(bg=Config.COLORS['bg'])
-
- self.transient(parent)
- self.grab_set()
-
- self.create_widgets()
-
- # Центрируем окно
- self.update_idletasks()
- x = parent.winfo_rootx() + (parent.winfo_width() - 400) // 2
- y = parent.winfo_rooty() + (parent.winfo_height() - 250) // 2
- self.geometry(f"+{x}+{y}")
-
- def create_widgets(self):
- main_frame = tk.Frame(self, bg=Config.COLORS['bg'])
- main_frame.pack(expand=True, fill='both', padx=20, pady=20)
-
- if self.mode == 'set':
- text = "Придумайте мастер-пароль\nдля защиты ваших заметок:"
- elif self.mode == 'check':
- text = "Введите мастер-пароль:"
- else: # reset
- text = "ВНИМАНИЕ!\nЭто сбросит текущий пароль.\nВсе заметки останутся.\n\nДля сброса введите 'RESET':"
-
- tk.Label(
- main_frame,
- text=text,
- bg=Config.COLORS['bg'],
- fg=Config.COLORS['fg'],
- font=('Arial', 12),
- wraplength=350
- ).pack(pady=(0, 15))
-
- if self.mode == 'reset':
- # Поле для сброса
- self.reset_var = tk.StringVar()
- self.reset_entry = ttk.Entry(
- main_frame,
- textvariable=self.reset_var,
- font=('Arial', 12)
- )
- self.reset_entry.pack(fill='x', pady=(0, 15))
- self.reset_entry.focus()
-
- tk.Button(
- main_frame,
- text="Сбросить пароль",
- command=self.reset_password,
- bg=Config.COLORS['danger'],
- fg=Config.COLORS['fg'],
- font=('Arial', 11, 'bold'),
- relief='flat',
- cursor='hand2'
- ).pack(fill='x', pady=(0, 5))
-
- tk.Button(
- main_frame,
- text="Отмена",
- command=self.destroy,
- bg=Config.COLORS['button_bg'],
- fg=Config.COLORS['fg'],
- relief='flat',
- cursor='hand2'
- ).pack(fill='x')
- else:
- # Поле ввода пароля
- self.password_var = tk.StringVar()
- self.password_entry = ttk.Entry(
- main_frame,
- textvariable=self.password_var,
- show='*',
- font=('Arial', 12)
- )
- self.password_entry.pack(fill='x', pady=(0, 10))
- self.password_entry.focus()
-
- if self.mode == 'set':
- # Подтверждение пароля
- tk.Label(
- main_frame,
- text="Подтвердите пароль:",
- bg=Config.COLORS['bg'],
- fg=Config.COLORS['fg'],
- font=('Arial', 11)
- ).pack()
-
- self.confirm_var = tk.StringVar()
- self.confirm_entry = ttk.Entry(
- main_frame,
- textvariable=self.confirm_var,
- show='*',
- font=('Arial', 12)
- )
- self.confirm_entry.pack(fill='x', pady=(0, 15))
-
- # Чекбокс "спрашивать при входе"
- self.ask_var = tk.BooleanVar(value=True)
- tk.Checkbutton(
- main_frame,
- text="Спрашивать при входе",
- variable=self.ask_var,
- bg=Config.COLORS['bg'],
- fg=Config.COLORS['fg'],
- selectcolor=Config.COLORS['bg'],
- activebackground=Config.COLORS['bg'],
- activeforeground=Config.COLORS['fg']
- ).pack(pady=(0, 15))
-
- # Кнопки
- button_frame = tk.Frame(main_frame, bg=Config.COLORS['bg'])
- button_frame.pack(fill='x')
-
- if self.mode == 'set':
- tk.Button(
- button_frame,
- text="Установить пароль",
- command=self.set_password,
- bg=Config.COLORS['accent'],
- fg=Config.COLORS['fg'],
- font=('Arial', 11, 'bold'),
- relief='flat',
- cursor='hand2'
- ).pack(fill='x', pady=(0, 5))
- else:
- tk.Button(
- button_frame,
- text="Войти",
- command=self.check_password,
- bg=Config.COLORS['accent'],
- fg=Config.COLORS['fg'],
- font=('Arial', 11, 'bold'),
- relief='flat',
- cursor='hand2'
- ).pack(fill='x', pady=(0, 5))
-
- # Кнопка сброса пароля
- tk.Button(
- button_frame,
- text="Забыли пароль?",
- command=self.show_reset_warning,
- bg=Config.COLORS['button_bg'],
- fg=Config.COLORS['fg'],
- relief='flat',
- cursor='hand2',
- font=('Arial', 9)
- ).pack(fill='x', pady=(0, 5))
-
- if self.mode == 'check':
- tk.Button(
- button_frame,
- text="Отмена",
- command=self.destroy,
- bg=Config.COLORS['button_bg'],
- fg=Config.COLORS['fg'],
- relief='flat',
- cursor='hand2'
- ).pack(fill='x')
-
- def set_password(self):
- password = self.password_var.get()
- confirm = self.confirm_var.get()
-
- if not password:
- messagebox.showwarning("Ошибка", "Введите пароль")
- return
-
- if len(password) < 4:
- messagebox.showwarning("Ошибка", "Пароль должен быть минимум 4 символа")
- return
-
- if password != confirm:
- messagebox.showwarning("Ошибка", "Пароли не совпадают")
- return
-
- self.auth.set_master_password(password)
- self.auth.set_ask_password_on_startup(self.ask_var.get())
- self.result = True
- self.destroy()
-
- def check_password(self):
- password = self.password_var.get()
-
- if self.auth.check_password(password):
- self.result = True
- self.destroy()
- else:
- messagebox.showerror("Ошибка", "Неверный пароль")
- self.password_var.set('')
- self.password_entry.focus()
-
- def reset_password(self):
- if self.reset_var.get().upper() == 'RESET':
- self.auth.reset_password()
- messagebox.showinfo("Успех", "Пароль сброшен.\nПри следующем запуске создайте новый пароль.")
- self.result = True
- self.destroy()
- else:
- messagebox.showerror("Ошибка", "Введите RESET для подтверждения сброса")
- self.reset_var.set('')
- self.reset_entry.focus()
-
- def show_reset_warning(self):
- """Показать предупреждение о сбросе пароля"""
- if messagebox.askyesno(
- "Сброс пароля",
- "Вы уверены, что хотите сбросить пароль?\n\n"
- "Это действие удалит только пароль, все заметки останутся.\n"
- "После сброса вы сможете установить новый пароль."
- ):
- self.destroy()
- reset_dialog = PasswordDialog(self.master, self.auth, mode='reset')
- self.master.wait_window(reset_dialog)
- if reset_dialog.result:
- self.result = True
- self.destroy()
-
-
-class ConfirmDialog(tk.Toplevel):
- """Диалог подтверждения действия"""
-
- def __init__(self, parent, title, message):
- super().__init__(parent)
- self.result = False
-
- self.title(title)
- self.geometry("350x150")
- self.resizable(False, False)
- self.configure(bg=Config.COLORS['bg'])
-
- self.transient(parent)
- self.grab_set()
-
- # Центрируем окно
- self.update_idletasks()
- x = parent.winfo_rootx() + (parent.winfo_width() - 350) // 2
- y = parent.winfo_rooty() + (parent.winfo_height() - 150) // 2
- self.geometry(f"+{x}+{y}")
-
- # Сообщение
- tk.Label(
- self,
- text=message,
- bg=Config.COLORS['bg'],
- fg=Config.COLORS['fg'],
- font=('Arial', 11),
- wraplength=320
- ).pack(expand=True, pady=(20, 10))
-
- # Кнопки
- btn_frame = tk.Frame(self, bg=Config.COLORS['bg'])
- btn_frame.pack(fill='x', padx=20, pady=(0, 20))
-
- tk.Button(
- btn_frame,
- text="Да",
- command=self.confirm,
- bg=Config.COLORS['danger'],
- fg=Config.COLORS['fg'],
- relief='flat',
- cursor='hand2',
- width=10
- ).pack(side='left', padx=(0, 10))
-
- tk.Button(
- btn_frame,
- text="Нет",
- command=self.destroy,
- bg=Config.COLORS['button_bg'],
- fg=Config.COLORS['fg'],
- relief='flat',
- cursor='hand2',
- width=10
- ).pack(side='left')
-
- def confirm(self):
- self.result = True
- self.destroy()
\ No newline at end of file
diff --git a/app/ui/main_window.py b/app/ui/main_window.py
deleted file mode 100644
index 4c5fb5f..0000000
--- a/app/ui/main_window.py
+++ /dev/null
@@ -1,212 +0,0 @@
-"""Главное окно приложения"""
-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()
\ No newline at end of file
diff --git a/app/ui/note_editor.py b/app/ui/note_editor.py
deleted file mode 100644
index c4ba58b..0000000
--- a/app/ui/note_editor.py
+++ /dev/null
@@ -1,297 +0,0 @@
-"""Редактор заметок"""
-import tkinter as tk
-from tkinter import ttk, messagebox
-from datetime import datetime
-from app.config import Config
-from app.ui.dialogs import ConfirmDialog
-
-class NoteEditor(tk.Frame):
- """Правая панель с редактором заметок"""
-
- def __init__(self, parent, db, on_note_saved=None, on_note_deleted=None, on_new_note=None):
- super().__init__(parent, bg=Config.COLORS['editor_bg'])
- self.db = db
- self.on_note_saved = on_note_saved
- self.on_note_deleted = on_note_deleted
- self.on_new_note = on_new_note
-
- self.current_note = None
- self.is_new_note = False
- self.is_editing = False
-
- self.create_widgets()
- self.show_empty_state()
-
- def create_widgets(self):
- # Панель инструментов
- self.toolbar = tk.Frame(self, bg=Config.COLORS['sidebar_bg'], height=40)
- self.toolbar.pack(fill='x')
- self.toolbar.pack_propagate(False)
-
- # Дата и время (левый край)
- self.date_label = tk.Label(
- self.toolbar,
- text="",
- bg=Config.COLORS['sidebar_bg'],
- fg=Config.COLORS['fg'],
- font=('Arial', 11)
- )
- self.date_label.pack(side='left', padx=10)
-
- # Кнопки (правый край)
- buttons_frame = tk.Frame(self.toolbar, bg=Config.COLORS['sidebar_bg'])
- buttons_frame.pack(side='right', padx=5)
-
- # Кнопка сохранения
- self.save_btn = tk.Button(
- buttons_frame,
- text="💾",
- command=self.save_note,
- bg=Config.COLORS['button_bg'],
- fg=Config.COLORS['fg'],
- relief='flat',
- font=('Arial', 14),
- cursor='hand2',
- width=2
- )
- self.save_btn.pack(side='left', padx=2)
-
- # Кнопка удаления
- self.delete_btn = tk.Button(
- buttons_frame,
- text="🗑",
- command=self.delete_current_note,
- bg=Config.COLORS['button_bg'],
- fg=Config.COLORS['fg'],
- relief='flat',
- font=('Arial', 14),
- cursor='hand2',
- width=2
- )
- self.delete_btn.pack(side='left', padx=2)
-
- # Кнопка создания новой заметки
- self.new_btn = tk.Button(
- buttons_frame,
- text="+",
- command=self.create_new_note,
- bg=Config.COLORS['accent'],
- fg=Config.COLORS['fg'],
- relief='flat',
- font=('Arial', 14, 'bold'),
- cursor='hand2',
- width=2
- )
- self.new_btn.pack(side='left', padx=2)
-
- # Скрываем кнопки сохранения и удаления по умолчанию
- self.hide_editor_buttons()
-
- # Область контента
- self.content_frame = tk.Frame(self, bg=Config.COLORS['editor_bg'])
- self.content_frame.pack(fill='both', expand=True, padx=10, pady=10)
-
- # Текстовое поле для редактирования
- self.text_widget = tk.Text(
- self.content_frame,
- bg=Config.COLORS['text_bg'],
- fg=Config.COLORS['text_fg'],
- font=('Arial', 12),
- wrap='word',
- relief='flat',
- padx=10,
- pady=10,
- insertbackground=Config.COLORS['fg']
- )
-
- # Scrollbar для текстового поля
- self.text_scrollbar = ttk.Scrollbar(
- self.content_frame,
- orient='vertical',
- command=self.text_widget.yview
- )
- self.text_widget.configure(yscrollcommand=self.text_scrollbar.set)
-
- # Метка для пустого состояния
- self.empty_label = tk.Label(
- self.content_frame,
- text="Выберите заметку из списка\nили создайте новую",
- bg=Config.COLORS['editor_bg'],
- fg=Config.COLORS['fg'],
- font=('Arial', 14),
- justify='center'
- )
- self.empty_label.pack(expand=True)
-
- # Привязываем события изменения текста
- self.text_widget.bind('<>', self.on_text_modified)
-
- def show_editor_buttons(self):
- """Показать кнопки редактора"""
- self.save_btn.pack(side='left', padx=2)
- self.delete_btn.pack(side='left', padx=2)
-
- def hide_editor_buttons(self):
- """Скрыть кнопки редактора"""
- self.save_btn.pack_forget()
- self.delete_btn.pack_forget()
-
- def show_empty_state(self):
- """Показать пустое состояние"""
- self.text_widget.pack_forget()
- self.text_scrollbar.pack_forget()
- self.empty_label.pack(expand=True)
- self.date_label.config(text="")
- self.hide_editor_buttons()
- self.current_note = None
- self.is_new_note = False
- self.is_editing = False
-
- def show_note(self, note):
- """Показать существующую заметку"""
- self.current_note = note
- self.is_new_note = False
- self.is_editing = False
-
- # Скрываем пустую метку
- self.empty_label.pack_forget()
-
- # Показываем текстовое поле
- self.text_widget.pack(side='left', fill='both', expand=True)
- self.text_scrollbar.pack(side='right', fill='y')
-
- # Устанавливаем дату
- created_at = datetime.strptime(
- note['created_at'], '%Y-%m-%d %H:%M:%S'
- )
- self.date_label.config(
- text=created_at.strftime('%d.%m.%Y %H:%M')
- )
-
- # Очищаем и вставляем текст
- self.text_widget.delete('1.0', 'end')
- self.text_widget.insert('1.0', note.get('content', ''))
- self.text_widget.edit_modified(False)
-
- # Показываем кнопки
- self.show_editor_buttons()
-
- # Делаем поле только для чтения
- self.text_widget.configure(state='disabled')
-
- # Привязываем клик для редактирования
- self.text_widget.bind('', self.enable_editing)
-
- def enable_editing(self, event=None):
- """Включить режим редактирования"""
- if not self.is_editing and self.current_note:
- self.is_editing = True
- self.text_widget.configure(state='normal')
- self.text_widget.unbind('')
-
- def create_new_note(self):
- """Создать новую заметку"""
- self.current_note = None
- self.is_new_note = True
- self.is_editing = True
-
- # Скрываем пустую метку
- self.empty_label.pack_forget()
-
- # Показываем текстовое поле
- self.text_widget.pack(side='left', fill='both', expand=True)
- self.text_scrollbar.pack(side='right', fill='y')
-
- # Устанавливаем текущую дату
- now = datetime.now()
- self.date_label.config(text=now.strftime('%d.%m.%Y %H:%M'))
-
- # Очищаем текстовое поле
- self.text_widget.delete('1.0', 'end')
- self.text_widget.configure(state='normal')
- self.text_widget.edit_modified(False)
-
- # Показываем кнопки
- self.show_editor_buttons()
-
- # Убираем привязку клика
- self.text_widget.unbind('')
-
- # Ставим фокус
- self.text_widget.focus_set()
-
- if self.on_new_note:
- self.on_new_note()
-
- def save_note(self):
- """Сохранить заметку"""
- content = self.text_widget.get('1.0', 'end-1c')
-
- if not content.strip() and self.is_new_note:
- messagebox.showwarning(
- "Предупреждение",
- "Нельзя сохранить пустую заметку"
- )
- return
-
- if self.is_new_note:
- # Создаем новую заметку
- now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- note_id = self.db.create_note(
- title=None,
- content=content,
- created_at=now
- )
- self.current_note = self.db.get_note_by_id(note_id)
- self.is_new_note = False
- self.is_editing = False
-
- # Делаем поле только для чтения
- self.text_widget.configure(state='disabled')
- self.text_widget.bind('', self.enable_editing)
-
- messagebox.showinfo("Успех", "Заметка сохранена")
- else:
- # Обновляем существующую заметку
- self.db.update_note(
- self.current_note['id'],
- content=content
- )
- self.current_note = self.db.get_note_by_id(self.current_note['id'])
- self.is_editing = False
-
- # Делаем поле только для чтения
- self.text_widget.configure(state='disabled')
- self.text_widget.bind('', self.enable_editing)
-
- messagebox.showinfo("Успех", "Заметка обновлена")
-
- self.text_widget.edit_modified(False)
-
- if self.on_note_saved:
- self.on_note_saved(self.current_note)
-
- def delete_current_note(self):
- """Удалить текущую заметку"""
- if not self.current_note:
- return
-
- dialog = ConfirmDialog(
- self.winfo_toplevel(),
- "Удаление заметки",
- "Вы уверены, что хотите удалить эту заметку?"
- )
- self.wait_window(dialog)
-
- if dialog.result:
- self.db.delete_note(self.current_note['id'])
- self.show_empty_state()
-
- if self.on_note_deleted:
- self.on_note_deleted()
-
- def on_text_modified(self, event=None):
- """Обработчик изменения текста"""
- if self.text_widget.edit_modified() and self.current_note and not self.is_editing:
- # Текст был изменен программно, игнорируем
- self.text_widget.edit_modified(False)
\ No newline at end of file
diff --git a/app/ui/notes_list.py b/app/ui/notes_list.py
deleted file mode 100644
index ba72a82..0000000
--- a/app/ui/notes_list.py
+++ /dev/null
@@ -1,230 +0,0 @@
-"""Список заметок"""
-import tkinter as tk
-from tkinter import ttk
-from datetime import datetime
-from app.config import Config
-from app.ui.dialogs import ConfirmDialog
-
-
-class NotesList(tk.Frame):
- """Виджет списка заметок"""
-
- def __init__(self, parent, db, on_note_select=None, on_note_delete=None):
- super().__init__(parent, bg=Config.COLORS['list_bg'])
- self.db = db
- self.on_note_select = on_note_select
- self.on_note_delete = on_note_delete
-
- self.notes = []
- self.selected_note_id = None
-
- self.create_widgets()
-
- def create_widgets(self):
- # Заголовок
- self.header_label = tk.Label(
- self,
- text="Заметки",
- bg=Config.COLORS['list_bg'],
- fg=Config.COLORS['fg'],
- font=('Arial', 12, 'bold')
- )
- self.header_label.pack(fill='x', padx=10, pady=(10, 5))
-
- # Разделитель
- separator = tk.Frame(self, height=1, bg=Config.COLORS['separator'])
- separator.pack(fill='x', padx=5)
-
- # Canvas и Scrollbar для списка
- self.canvas = tk.Canvas(
- self,
- bg=Config.COLORS['list_bg'],
- highlightthickness=0
- )
- self.scrollbar = ttk.Scrollbar(
- self,
- orient="vertical",
- command=self.canvas.yview
- )
-
- self.notes_frame = tk.Frame(self.canvas, bg=Config.COLORS['list_bg'])
-
- self.notes_frame.bind("",
- lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))
-
- self.canvas_window = self.canvas.create_window(
- (0, 0), window=self.notes_frame, anchor="nw"
- )
-
- self.canvas.configure(yscrollcommand=self.scrollbar.set)
-
- self.canvas.pack(side="left", fill="both", expand=True)
- self.scrollbar.pack(side="right", fill="y")
-
- # Bind для изменения размера
- self.canvas.bind('', self.on_canvas_configure)
-
- # Bind для колесика мыши
- self.canvas.bind_all("", self.on_mousewheel)
-
- def on_canvas_configure(self, event):
- self.canvas.itemconfig(self.canvas_window, width=event.width)
-
- def on_mousewheel(self, event):
- self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
-
- def load_notes(self, date):
- """Загрузить заметки для выбранной даты"""
- # Очищаем текущий список
- for widget in self.notes_frame.winfo_children():
- widget.destroy()
-
- date_str = date.strftime('%Y-%m-%d')
- self.notes = self.db.get_notes_with_content_by_date(date_str)
-
- if not self.notes:
- empty_label = tk.Label(
- self.notes_frame,
- text="Нет заметок\nна этот день",
- bg=Config.COLORS['list_bg'],
- fg=Config.COLORS['fg'],
- font=('Arial', 10),
- justify='center'
- )
- empty_label.pack(expand=True, pady=20)
- return
-
- for note in self.notes:
- self.create_note_item(note)
-
- def create_note_item(self, note):
- """Создать элемент списка для заметки"""
- item_frame = tk.Frame(
- self.notes_frame,
- bg=Config.COLORS['list_bg'],
- cursor='hand2'
- )
- item_frame.pack(fill='x', padx=5, pady=2)
-
- # Рамка для элемента
- content_frame = tk.Frame(
- item_frame,
- bg=Config.COLORS['list_bg'],
- highlightbackground=Config.COLORS['separator'],
- highlightthickness=1
- )
- content_frame.pack(fill='x', padx=5, pady=2)
-
- # Левая часть с датой
- date_frame = tk.Frame(content_frame, bg=Config.COLORS['list_bg'])
- date_frame.pack(side='left', padx=(10, 5), pady=5)
-
- # Получаем дату заметки
- created_at = datetime.strptime(
- note['created_at'], '%Y-%m-%d %H:%M:%S'
- )
-
- day_month = created_at.strftime('%d.%m')
- year = created_at.strftime('%Y')
-
- tk.Label(
- date_frame,
- text=day_month,
- bg=Config.COLORS['list_bg'],
- fg=Config.COLORS['fg'],
- font=('Arial', 11, 'bold')
- ).pack(anchor='w')
-
- tk.Label(
- date_frame,
- text=year,
- bg=Config.COLORS['list_bg'],
- fg=Config.COLORS['fg'],
- font=('Arial', 9)
- ).pack(anchor='w')
-
- # Средняя часть с заголовком
- title_frame = tk.Frame(content_frame, bg=Config.COLORS['list_bg'])
- title_frame.pack(side='left', fill='x', expand=True, padx=10, pady=5)
-
- title = note.get('title', '')
- if not title:
- # Берем первые 22 символа текста
- content = note.get('content', '')
- title = content[:22] + ('...' if len(content) > 22 else '')
- if not title:
- title = "Пустая заметка"
-
- title_label = tk.Label(
- title_frame,
- text=title,
- bg=Config.COLORS['list_bg'],
- fg=Config.COLORS['fg'],
- font=('Arial', 10),
- anchor='w',
- justify='left'
- )
- title_label.pack(anchor='w')
-
- # Кнопка удаления
- delete_btn = tk.Button(
- content_frame,
- text="🗑",
- command=lambda n=note: self.delete_note(n),
- bg=Config.COLORS['list_bg'],
- fg=Config.COLORS['danger'],
- relief='flat',
- font=('Arial', 14),
- cursor='hand2',
- width=2
- )
- delete_btn.pack(side='right', padx=(0, 10), pady=5)
-
- # Привязываем события клика для выбора заметки
- for widget in [item_frame, content_frame, date_frame, title_frame, title_label]:
- widget.bind('', lambda e, n=note: self.select_note(n))
-
- # Подсветка при наведении
- for widget in [content_frame, date_frame, title_frame]:
- widget.bind('', lambda e, f=content_frame:
- f.configure(bg=Config.COLORS['list_selected']))
- widget.bind('', lambda e, f=content_frame:
- f.configure(bg=Config.COLORS['list_bg']))
-
- def select_note(self, note):
- """Выбрать заметку"""
- self.selected_note_id = note['id']
-
- # Обновляем визуальное выделение
- for widget in self.notes_frame.winfo_children():
- if isinstance(widget, tk.Frame):
- for child in widget.winfo_children():
- if isinstance(child, tk.Frame):
- child.configure(bg=Config.COLORS['list_bg'])
-
- if self.on_note_select:
- self.on_note_select(note)
-
- def delete_note(self, note):
- """Удалить заметку с подтверждением"""
- dialog = ConfirmDialog(
- self.winfo_toplevel(),
- "Удаление заметки",
- "Вы уверены, что хотите удалить эту заметку?"
- )
- self.wait_window(dialog)
-
- if dialog.result:
- self.db.delete_note(note['id'])
-
- if self.on_note_delete:
- self.on_note_delete()
-
- # Перезагружаем список
- if hasattr(self, 'current_date'):
- self.load_notes(self.current_date)
-
- def refresh(self, date):
- """Обновить список заметок"""
- self.current_date = date
- self.load_notes(date)
\ No newline at end of file
diff --git a/app/ui/sidebar.py b/app/ui/sidebar.py
deleted file mode 100644
index 6e2814b..0000000
--- a/app/ui/sidebar.py
+++ /dev/null
@@ -1,59 +0,0 @@
-"""Боковая панель"""
-import tkinter as tk
-from app.config import Config
-from app.ui.calendar_widget import CalendarWidget
-from app.ui.notes_list import NotesList
-
-
-class Sidebar(tk.Frame):
- """Левая боковая панель"""
-
- def __init__(self, parent, db, on_date_select=None, on_note_select=None, on_note_delete=None):
- super().__init__(parent, bg=Config.COLORS['sidebar_bg'], width=Config.SIDEBAR_WIDTH)
- self.db = db
- self.on_note_select = on_note_select
-
- self.pack_propagate(False)
- self.pack(side='left', fill='y')
-
- self.create_widgets(on_date_select, on_note_delete)
-
- def create_widgets(self, on_date_select, on_note_delete):
- # Календарь
- self.calendar = CalendarWidget(
- self,
- self.db,
- on_date_select=self.on_date_selected
- )
- self.calendar.pack(fill='x')
-
- # Разделитель
- separator = tk.Frame(self, height=2, bg=Config.COLORS['separator'])
- separator.pack(fill='x', padx=10, pady=(5, 0))
-
- # Список заметок
- self.notes_list = NotesList(
- self,
- self.db,
- on_note_select=self.on_note_selected,
- on_note_delete=on_note_delete
- )
- self.notes_list.pack(fill='both', expand=True)
-
- def on_date_selected(self, date):
- """Обработчик выбора даты в календаре"""
- self.notes_list.refresh(date)
-
- def on_note_selected(self, note):
- """Обработчик выбора заметки"""
- if self.on_note_select:
- self.on_note_select(note)
-
- def refresh_notes(self):
- """Обновить список заметок для текущей даты"""
- if self.calendar:
- self.notes_list.refresh(self.calendar.selected_date)
-
- def get_selected_date(self):
- """Получить выбранную дату"""
- return self.calendar.selected_date if self.calendar else None
\ No newline at end of file
diff --git a/icon.svg b/icon.svg
deleted file mode 100644
index 46fa0de..0000000
--- a/icon.svg
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/main.py b/main.py
deleted file mode 100644
index 361baef..0000000
--- a/main.py
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/env python3
-"""
-Captain's Log - Приложение для быстрых заметок
-"""
-
-import sys
-import os
-
-# Добавляем текущую директорию в путь для импорта
-sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
-
-from app.ui.main_window import MainWindow
-
-
-def create_gitignore():
- """Создать .gitignore если его нет"""
- gitignore_path = os.path.join(os.path.dirname(__file__), '.gitignore')
-
- if not os.path.exists(gitignore_path):
- gitignore_content = """# Python
-__pycache__/
-*.py[cod]
-*$py.class
-*.so
-*.egg-info/
-dist/
-build/
-*.egg
-
-# Виртуальное окружение
-.venv/
-venv/
-ENV/
-env/
-
-# База данных и данные приложения
-captains_log.db
-*.db
-
-# IDE
-.idea/
-.vscode/
-*.swp
-*.swo
-*~
-
-# Nuitka
-*.build/
-*.dist/
-*.onefile-build/
-
-# OS
-.DS_Store
-Thumbs.db
-"""
- try:
- with open(gitignore_path, 'w') as f:
- f.write(gitignore_content)
- except:
- pass # Игнорируем ошибки при создании .gitignore
-
-
-def main():
- """Точка входа в приложение"""
- # Создаем .gitignore при первом запуске
- create_gitignore()
-
- app = MainWindow()
- app.mainloop()
-
-
-if __name__ == "__main__":
- main()
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index b819adf..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-tkcalendar==1.6.1
-nuitka>=2.0.0
\ No newline at end of file