296 lines
No EOL
10 KiB
Python
296 lines
No EOL
10 KiB
Python
"""Диалоговые окна"""
|
||
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() |