Captain_s-Log/app/ui/dialogs.py
Vic Sergeev 9b0a20aa91 working
2026-07-04 16:14:16 +03:00

296 lines
No EOL
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Диалоговые окна"""
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()