2026-07-04 16:04:48 +03:00
|
|
|
|
"""Диалоговые окна"""
|
|
|
|
|
|
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
|
2026-07-04 16:14:16 +03:00
|
|
|
|
self.mode = mode # 'set', 'check', 'reset'
|
2026-07-04 16:04:48 +03:00
|
|
|
|
self.result = False
|
|
|
|
|
|
|
2026-07-04 16:14:16 +03:00
|
|
|
|
self.title("Мастер-пароль" if mode == 'set' else
|
|
|
|
|
|
"Введите пароль" if mode == 'check' else
|
|
|
|
|
|
"Сброс пароля")
|
|
|
|
|
|
self.geometry("400x300" if mode == 'reset' else "400x250")
|
2026-07-04 16:04:48 +03:00
|
|
|
|
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для защиты ваших заметок:"
|
2026-07-04 16:14:16 +03:00
|
|
|
|
elif self.mode == 'check':
|
2026-07-04 16:04:48 +03:00
|
|
|
|
text = "Введите мастер-пароль:"
|
2026-07-04 16:14:16 +03:00
|
|
|
|
else: # reset
|
|
|
|
|
|
text = "ВНИМАНИЕ!\nЭто сбросит текущий пароль.\nВсе заметки останутся.\n\nДля сброса введите 'RESET':"
|
2026-07-04 16:04:48 +03:00
|
|
|
|
|
|
|
|
|
|
tk.Label(
|
|
|
|
|
|
main_frame,
|
|
|
|
|
|
text=text,
|
|
|
|
|
|
bg=Config.COLORS['bg'],
|
|
|
|
|
|
fg=Config.COLORS['fg'],
|
|
|
|
|
|
font=('Arial', 12),
|
|
|
|
|
|
wraplength=350
|
|
|
|
|
|
).pack(pady=(0, 15))
|
|
|
|
|
|
|
2026-07-04 16:14:16 +03:00
|
|
|
|
if self.mode == 'reset':
|
|
|
|
|
|
# Поле для сброса
|
|
|
|
|
|
self.reset_var = tk.StringVar()
|
|
|
|
|
|
self.reset_entry = ttk.Entry(
|
2026-07-04 16:04:48 +03:00
|
|
|
|
main_frame,
|
2026-07-04 16:14:16 +03:00
|
|
|
|
textvariable=self.reset_var,
|
2026-07-04 16:04:48 +03:00
|
|
|
|
font=('Arial', 12)
|
|
|
|
|
|
)
|
2026-07-04 16:14:16 +03:00
|
|
|
|
self.reset_entry.pack(fill='x', pady=(0, 15))
|
|
|
|
|
|
self.reset_entry.focus()
|
2026-07-04 16:04:48 +03:00
|
|
|
|
|
|
|
|
|
|
tk.Button(
|
2026-07-04 16:14:16 +03:00
|
|
|
|
main_frame,
|
|
|
|
|
|
text="Сбросить пароль",
|
|
|
|
|
|
command=self.reset_password,
|
|
|
|
|
|
bg=Config.COLORS['danger'],
|
2026-07-04 16:04:48 +03:00
|
|
|
|
fg=Config.COLORS['fg'],
|
|
|
|
|
|
font=('Arial', 11, 'bold'),
|
|
|
|
|
|
relief='flat',
|
|
|
|
|
|
cursor='hand2'
|
|
|
|
|
|
).pack(fill='x', pady=(0, 5))
|
|
|
|
|
|
|
|
|
|
|
|
tk.Button(
|
2026-07-04 16:14:16 +03:00
|
|
|
|
main_frame,
|
2026-07-04 16:04:48 +03:00
|
|
|
|
text="Отмена",
|
|
|
|
|
|
command=self.destroy,
|
|
|
|
|
|
bg=Config.COLORS['button_bg'],
|
|
|
|
|
|
fg=Config.COLORS['fg'],
|
|
|
|
|
|
relief='flat',
|
|
|
|
|
|
cursor='hand2'
|
|
|
|
|
|
).pack(fill='x')
|
2026-07-04 16:14:16 +03:00
|
|
|
|
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')
|
2026-07-04 16:04:48 +03:00
|
|
|
|
|
|
|
|
|
|
def set_password(self):
|
|
|
|
|
|
password = self.password_var.get()
|
|
|
|
|
|
confirm = self.confirm_var.get()
|
|
|
|
|
|
|
|
|
|
|
|
if not password:
|
|
|
|
|
|
messagebox.showwarning("Ошибка", "Введите пароль")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2026-07-04 16:14:16 +03:00
|
|
|
|
if len(password) < 4:
|
|
|
|
|
|
messagebox.showwarning("Ошибка", "Пароль должен быть минимум 4 символа")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2026-07-04 16:04:48 +03:00
|
|
|
|
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()
|
|
|
|
|
|
|
2026-07-04 16:14:16 +03:00
|
|
|
|
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()
|
|
|
|
|
|
|
2026-07-04 16:04:48 +03:00
|
|
|
|
|
|
|
|
|
|
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()
|