1. "Пользователь не найден" + заполнила таблицы из бд Dogs, Qoestions. 2. Меню паузы + оно больше не появляется в главном меню, при выборе собаки и уровня. 3. Окно победы + не открывается куча окон, собака не двигается, пока окно победы активно + добавлена кнопка "выйти в главное меню" 3. Окно регистрации + кнопка "посмотреть пароль" 4. таблица GameSessions + сохранение игрового процесса в таблицу GameSessions + одинаковые данные переносятся автоматически в таблицу GameSessions + собранные косточки и тп заполняют только таблицу GameSessions 5. Окно "Профиль пользователя" + нет ошибки открытия окна + корректное сохранение игрового процесса (уровни, косточки) 6. Окно "Магазин" + не кликабельная + кнопка "назад" - shop_ui.py 7. Окно "База знаний" + не кликабельная + кнопка "назад" - knowledge_ui.py
510 lines
No EOL
22 KiB
Python
510 lines
No EOL
22 KiB
Python
import tkinter as tk
|
||
from tkinter import messagebox, ttk
|
||
from PIL import Image, ImageTk
|
||
from sqlalchemy.exc import SQLAlchemyError
|
||
|
||
from config import SETTINGS_IMG
|
||
from database.db_session import get_session
|
||
from database.models import Dogs, Questions, Users
|
||
from src.admin_functions import admin_logging, statistics
|
||
from src.utils import clear_frame, feature_in_development_admin # Импортируем общую функцию для очистки фрейма
|
||
from database.db_events import check_user, get_all_users, get_all_questions, get_all_dogs
|
||
|
||
# Конфигурация цветов из config.py
|
||
BACKGROUND_COLOR = "#403d49"
|
||
TOP_BAR_COLOR = "#383441"
|
||
TEXT_COLOR = "#b2acc0"
|
||
BUTTON_COLOR = "#403d49"
|
||
MENU_COLOR = "#2f2b38"
|
||
MENU_OPACITY = 0.9 # Прозрачность меню
|
||
|
||
class AdminApp:
|
||
def __init__(self, root):
|
||
self.root = root
|
||
self.root.title("Админ-Панель")
|
||
self.root.geometry("1920x1080")
|
||
self.root.config(bg=BACKGROUND_COLOR)
|
||
|
||
# Верхняя панель
|
||
self.top_bar = tk.Frame(self.root, bg=TOP_BAR_COLOR, height=60)
|
||
self.top_bar.pack(side="top", fill="x")
|
||
|
||
# Кнопка настроек
|
||
settings_img = Image.open(SETTINGS_IMG)
|
||
settings_img = settings_img.resize((40, 40), Image.Resampling.LANCZOS)
|
||
settings_icon = ImageTk.PhotoImage(settings_img)
|
||
|
||
self.settings_button = tk.Button(
|
||
self.top_bar,
|
||
image=settings_icon,
|
||
bg=TOP_BAR_COLOR,
|
||
activebackground=TOP_BAR_COLOR,
|
||
bd=0
|
||
)
|
||
self.settings_button.image = settings_icon # Сохраняем ссылку на изображение
|
||
self.settings_button.pack(side="left", padx=10, pady=10)
|
||
|
||
# Кнопки навигации
|
||
self.create_nav_button("Логирование", lambda: admin_logging.show_logs(self.main_frame))
|
||
self.create_nav_button("Статистика", lambda: statistics.show_statistics(self.main_frame))
|
||
self.create_nav_button("Уведомления", lambda: self.show_notifications(self.main_frame))
|
||
self.create_nav_button("Безопасность", lambda: self.show_security(self.main_frame))
|
||
self.create_nav_button("Открыть сессию пользователя", self.open_user_session)
|
||
|
||
# Бургер-меню
|
||
self.menu_button = tk.Button(
|
||
self.top_bar,
|
||
text="☰ Меню",
|
||
bg=BUTTON_COLOR,
|
||
fg=TEXT_COLOR,
|
||
font=("Comic Sans MS", 14),
|
||
activebackground=BUTTON_COLOR,
|
||
activeforeground=TEXT_COLOR,
|
||
bd=0,
|
||
command=self.toggle_menu
|
||
)
|
||
self.menu_button.pack(side="right", padx=10, pady=10)
|
||
|
||
# Основное окно
|
||
self.main_frame = tk.Frame(self.root, bg=BACKGROUND_COLOR)
|
||
self.main_frame.pack(fill="both", expand=True)
|
||
|
||
# Бургер-меню (скрытое по умолчанию)
|
||
self.menu_frame = tk.Frame(self.root, bg=MENU_COLOR, width=300)
|
||
self.menu_frame.place(x=1620, y=60, width=300, height=1020)
|
||
self.menu_frame.lower()
|
||
self.menu_visible = False
|
||
|
||
def toggle_menu(self):
|
||
"""Показ или скрытие меню."""
|
||
if self.menu_visible:
|
||
self.menu_frame.lower()
|
||
self.menu_visible = False
|
||
else:
|
||
self.menu_frame.lift()
|
||
self.menu_visible = True
|
||
self.populate_menu()
|
||
|
||
def populate_menu(self):
|
||
# Очистка меню
|
||
for widget in self.menu_frame.winfo_children():
|
||
widget.destroy()
|
||
|
||
# Список разделов и их элементов
|
||
menu_sections = [
|
||
("Работа с базой данных", [
|
||
("Редактирование пользователей", self.manage_users),
|
||
("Управление вопросами", self.manage_questions),
|
||
("Управление собаками", self.manage_dogs),
|
||
("Просмотр таблиц", self.view_tables),
|
||
]),
|
||
("Управление игровым контентом", [
|
||
("Создание и настройка уровней", feature_in_development_admin),
|
||
("Настройка параметров собаки", feature_in_development_admin),
|
||
]),
|
||
("Управление интерфейсом пользователя", [
|
||
("Добавление подсказок в интерфейс", feature_in_development_admin),
|
||
]),
|
||
("Работа с базой знаний", [
|
||
("Добавление информации", feature_in_development_admin),
|
||
("Редактирование записей", feature_in_development_admin),
|
||
("Удаление записей", feature_in_development_admin),
|
||
("Просмотр базы знаний", feature_in_development_admin),
|
||
("Генерация вопросов", feature_in_development_admin),
|
||
]),
|
||
]
|
||
|
||
# Определяем максимальную ширину текста для настройки ширины меню и кнопок
|
||
max_text_length = max(
|
||
len(title) for title, items in menu_sections
|
||
) + max(
|
||
max(len(text) for text, _ in items) for _, items in menu_sections
|
||
)
|
||
menu_width = max(300, max_text_length * 10) # Устанавливаем минимальную ширину
|
||
|
||
# Обновляем ширину меню
|
||
self.menu_frame.config(width=menu_width)
|
||
|
||
# Высота одной кнопки и отступов
|
||
button_height = 40
|
||
button_spacing = 10
|
||
section_spacing = 15
|
||
|
||
total_height = 0
|
||
|
||
for title, items in menu_sections:
|
||
section_label = tk.Label(
|
||
self.menu_frame,
|
||
text=title,
|
||
bg=MENU_COLOR,
|
||
fg=TEXT_COLOR,
|
||
font=("Comic Sans MS", 14, "bold"),
|
||
)
|
||
section_label.pack(fill="x", padx=10, pady=5)
|
||
|
||
for text, command in items:
|
||
item_button = tk.Button(
|
||
self.menu_frame,
|
||
text=text,
|
||
bg=BUTTON_COLOR,
|
||
fg=TEXT_COLOR,
|
||
font=("Comic Sans MS", 12),
|
||
activebackground=BUTTON_COLOR,
|
||
activeforeground=TEXT_COLOR,
|
||
bd=0,
|
||
command=command # Используем lambda, чтобы передать команду без аргументов
|
||
)
|
||
item_button.pack(fill="x", padx=20, pady=5)
|
||
|
||
# Кнопка "Выйти" внизу меню
|
||
exit_button = tk.Button(
|
||
self.menu_frame,
|
||
text="Выйти",
|
||
bg=BUTTON_COLOR,
|
||
fg=TEXT_COLOR,
|
||
font=("Comic Sans MS", 12),
|
||
activebackground=BUTTON_COLOR,
|
||
activeforeground=TEXT_COLOR,
|
||
bd=0,
|
||
command=self.exit_app
|
||
)
|
||
exit_button.pack(side="bottom", padx=10, pady=20) # Размещение внизу
|
||
|
||
def create_nav_button(self, text, command):
|
||
"""Создание кнопки навигации."""
|
||
button = tk.Button(
|
||
self.top_bar,
|
||
text=text,
|
||
bg=BUTTON_COLOR,
|
||
fg=TEXT_COLOR,
|
||
font=("Comic Sans MS", 14),
|
||
activebackground=BUTTON_COLOR,
|
||
activeforeground=TEXT_COLOR,
|
||
bd=0,
|
||
padx=10,
|
||
pady=5,
|
||
command=command # Передаем функцию напрямую
|
||
)
|
||
button.pack(side="left", padx=10, pady=10)
|
||
|
||
def open_manage_dogs_window(self, frame):
|
||
"""Открыть окно для управления собаками."""
|
||
self.manage_dogs()
|
||
|
||
def manage_ui_tips(self, frame):
|
||
# Пример логики для управления подсказками
|
||
print("Управление подсказками интерфейса.")
|
||
# Код для управления подсказками (например, скрытие или показ подсказок)
|
||
tk.Label(frame, text="Здесь будут подсказки для интерфейса", bg=BACKGROUND_COLOR, fg=TEXT_COLOR, font=("Comic Sans MS", 16)).pack()
|
||
|
||
def show_notifications(self, frame):
|
||
"""Отображение экрана уведомлений"""
|
||
clear_frame(frame) # Очищаем текущий экран
|
||
tk.Label(
|
||
frame,
|
||
text="Модуль <Уведомления> в разработке.\nВ планах реализовать: создание оповещений для пользователей (обновления, новости), сообщения от БД (корректность работы)",
|
||
bg=BACKGROUND_COLOR,
|
||
fg=TEXT_COLOR,
|
||
font=("Comic Sans MS", 16)
|
||
).pack(expand=True)
|
||
|
||
def show_security(self, frame):
|
||
"""Отображение экрана безопасности"""
|
||
clear_frame(frame) # Очищаем текущий экран
|
||
tk.Label(
|
||
frame,
|
||
text="Модуль <Безопасность> в разработке.\nВ планах реализовать: управление доступом (создание других админов, смена пароля администратора).",
|
||
bg=BACKGROUND_COLOR,
|
||
fg=TEXT_COLOR,
|
||
font=("Comic Sans MS", 16)
|
||
).pack(expand=True)
|
||
|
||
def change_ui_settings(self, frame):
|
||
clear_frame(frame)
|
||
"""Метод для изменения цветовой схемы, фона и логотипа"""
|
||
print("Изменение UI настроек") # Пока просто тестовый вывод
|
||
|
||
def exit_app(self):
|
||
"""Закрыть приложение."""
|
||
self.root.quit()
|
||
|
||
# Метод для авторизации под пользователем
|
||
def open_user_session(self):
|
||
"""Открыть новую сессию пользователя."""
|
||
user_login_window = tk.Toplevel(self.root)
|
||
user_login_window.title("Авторизация пользователя")
|
||
user_login_window.geometry("400x300")
|
||
user_login_window.configure(bg=BACKGROUND_COLOR)
|
||
|
||
tk.Label(
|
||
user_login_window,
|
||
text="Введите логин пользователя:",
|
||
bg=BACKGROUND_COLOR,
|
||
fg=TEXT_COLOR,
|
||
font=("Comic Sans MS", 12)
|
||
).pack(pady=20)
|
||
|
||
user_login_entry = tk.Entry(user_login_window, font=("Comic Sans MS", 12))
|
||
user_login_entry.pack(pady=10)
|
||
|
||
def open_user_interface():
|
||
login = user_login_entry.get() # Получаем логин из поля ввода
|
||
user_id = check_user(login) # Передаем логин для проверки
|
||
|
||
if user_id:
|
||
user_login_window.destroy()
|
||
user_window = tk.Toplevel(self.root)
|
||
from src.ui.user_ui.main_menu import UserApp
|
||
UserApp(user_window, user_id=user_id)
|
||
else:
|
||
messagebox.showerror("Ошибка", "Пользователь не найден.")
|
||
|
||
tk.Button(
|
||
user_login_window,
|
||
text="Открыть сессию",
|
||
bg=BUTTON_COLOR,
|
||
fg=TEXT_COLOR,
|
||
font=("Comic Sans MS", 12),
|
||
command=open_user_interface
|
||
).pack(pady=20)
|
||
|
||
tk.Button(
|
||
user_login_window,
|
||
text="Отмена",
|
||
bg=BUTTON_COLOR,
|
||
fg=TEXT_COLOR,
|
||
font=("Comic Sans MS", 12),
|
||
command=user_login_window.destroy
|
||
).pack(pady=10)
|
||
|
||
def manage_users(self):
|
||
"""Управление пользователями."""
|
||
clear_frame(self.main_frame)
|
||
tk.Label(self.main_frame, text="Редактирование пользователей", font=("Comic Sans MS", 16)).pack()
|
||
|
||
users = get_all_users() # Получаем пользователей
|
||
if not users:
|
||
tk.Label(self.main_frame, text="Нет пользователей в базе данных.", bg=BACKGROUND_COLOR,
|
||
fg=TEXT_COLOR).pack()
|
||
return
|
||
|
||
table = ttk.Treeview(self.main_frame, columns=("ID", "Логин", "Имя"), show="headings")
|
||
table.heading("ID", text="ID")
|
||
table.heading("Логин", text="Логин")
|
||
table.heading("Имя", text="Имя")
|
||
table.pack(fill="both", expand=True)
|
||
|
||
for user in users:
|
||
table.insert("", "end", values=(user.user_id, user.auth.login, user.username))
|
||
|
||
def manage_questions(self):
|
||
"""Управление вопросами."""
|
||
clear_frame(self.main_frame)
|
||
tk.Label(self.main_frame, text="Управление вопросами", font=("Comic Sans MS", 16)).pack()
|
||
|
||
questions = get_all_questions() # Получаем вопросы
|
||
if not questions:
|
||
tk.Label(self.main_frame, text="Нет вопросов в базе данных.", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack()
|
||
return
|
||
|
||
table = ttk.Treeview(self.main_frame, columns=("ID", "Вопрос", "Ответ"), show="headings")
|
||
table.heading("ID", text="ID")
|
||
table.heading("Вопрос", text="Вопрос")
|
||
table.heading("Ответ", text="Ответ")
|
||
table.pack(fill="both", expand=True)
|
||
|
||
for question in questions:
|
||
table.insert("", "end", values=(
|
||
question.question_id, question.question_text, question.helpful_info)) # Заполняем таблицу данными
|
||
|
||
def manage_dogs(self):
|
||
"""Управление собаками."""
|
||
clear_frame(self.main_frame)
|
||
tk.Label(self.main_frame, text="Управление собаками", font=("Comic Sans MS", 16)).pack()
|
||
|
||
dogs = get_all_dogs() # Получаем список собак
|
||
if not dogs:
|
||
tk.Label(self.main_frame, text="Нет собак в базе данных.", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack()
|
||
return
|
||
|
||
table = ttk.Treeview(self.main_frame, columns=("ID", "Порода", "Характеристики"), show="headings")
|
||
table.heading("ID", text="ID")
|
||
table.heading("Порода", text="Порода")
|
||
table.heading("Характеристики", text="Характеристики")
|
||
table.pack(fill="both", expand=True)
|
||
|
||
for dog in dogs:
|
||
table.insert("", "end", values=(dog.dog_id, dog.breed, dog.characteristics)) # Заполняем таблицу данными
|
||
|
||
def view_tables(self):
|
||
"""Просмотр таблиц."""
|
||
clear_frame(self.main_frame) # Очищаем старое содержимое
|
||
self.manage_users() # Отображаем пользователей
|
||
self.manage_questions() # Отображаем вопросы
|
||
self.manage_dogs() # Отображаем собак
|
||
|
||
|
||
def open_add_user_window(self):
|
||
"""Открыть окно для добавления нового пользователя."""
|
||
add_user_window = tk.Toplevel(self.root)
|
||
add_user_window.title("Добавить нового пользователя")
|
||
add_user_window.geometry("400x300")
|
||
add_user_window.configure(bg=BACKGROUND_COLOR)
|
||
|
||
tk.Label(add_user_window, text="Логин", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack(pady=10)
|
||
login_entry = tk.Entry(add_user_window, font=("Comic Sans MS", 12))
|
||
login_entry.pack(pady=5)
|
||
|
||
tk.Label(add_user_window, text="Пароль", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack(pady=10)
|
||
password_entry = tk.Entry(add_user_window, font=("Comic Sans MS", 12), show="*")
|
||
password_entry.pack(pady=5)
|
||
|
||
tk.Label(add_user_window, text="Имя пользователя", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack(pady=10)
|
||
username_entry = tk.Entry(add_user_window, font=("Comic Sans MS", 12))
|
||
username_entry.pack(pady=5)
|
||
|
||
def save_user():
|
||
user_data = {
|
||
'login': login_entry.get(),
|
||
'password': password_entry.get(),
|
||
'username': username_entry.get()
|
||
}
|
||
add_user_to_db(user_data)
|
||
add_user_window.destroy()
|
||
|
||
def cancel_add():
|
||
add_user_window.destroy()
|
||
|
||
save_button = tk.Button(add_user_window, text="Сохранить", command=save_user, bg=BUTTON_COLOR, fg=TEXT_COLOR)
|
||
save_button.pack(pady=20)
|
||
|
||
cancel_button = tk.Button(add_user_window, text="Отменить", command=cancel_add, bg=BUTTON_COLOR, fg=TEXT_COLOR)
|
||
cancel_button.pack(pady=10)
|
||
|
||
def open_add_question_window(self):
|
||
"""Открыть окно для добавления нового вопроса."""
|
||
add_question_window = tk.Toplevel(self.root)
|
||
add_question_window.title("Добавить новый вопрос")
|
||
add_question_window.geometry("400x300")
|
||
add_question_window.configure(bg=BACKGROUND_COLOR)
|
||
|
||
tk.Label(add_question_window, text="ID собаки", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack(pady=10)
|
||
dog_id_entry = tk.Entry(add_question_window, font=("Comic Sans MS", 12))
|
||
dog_id_entry.pack(pady=5)
|
||
|
||
tk.Label(add_question_window, text="Вопрос", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack(pady=10)
|
||
question_text_entry = tk.Entry(add_question_window, font=("Comic Sans MS", 12))
|
||
question_text_entry.pack(pady=5)
|
||
|
||
tk.Label(add_question_window, text="Изображение URL", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack(pady=10)
|
||
image_url_entry = tk.Entry(add_question_window, font=("Comic Sans MS", 12))
|
||
image_url_entry.pack(pady=5)
|
||
|
||
tk.Label(add_question_window, text="Полезная информация", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack(pady=10)
|
||
helpful_info_entry = tk.Entry(add_question_window, font=("Comic Sans MS", 12))
|
||
helpful_info_entry.pack(pady=5)
|
||
|
||
def save_question():
|
||
question_data = {
|
||
'dog_id': int(dog_id_entry.get()),
|
||
'question_text': question_text_entry.get(),
|
||
'image_url': image_url_entry.get(),
|
||
'helpful_info': helpful_info_entry.get()
|
||
}
|
||
add_question_to_db(question_data)
|
||
add_question_window.destroy()
|
||
|
||
def cancel_add():
|
||
add_question_window.destroy()
|
||
|
||
save_button = tk.Button(add_question_window, text="Сохранить", command=save_question, bg=BUTTON_COLOR, fg=TEXT_COLOR)
|
||
save_button.pack(pady=20)
|
||
|
||
cancel_button = tk.Button(add_question_window, text="Отменить", command=cancel_add, bg=BUTTON_COLOR, fg=TEXT_COLOR)
|
||
cancel_button.pack(pady=10)
|
||
|
||
def open_add_dog_window(self):
|
||
"""Открыть окно для добавления новой собаки."""
|
||
add_dog_window = tk.Toplevel(self.root)
|
||
add_dog_window.title("Добавить новую собаку")
|
||
add_dog_window.geometry("400x300")
|
||
add_dog_window.configure(bg=BACKGROUND_COLOR)
|
||
|
||
tk.Label(add_dog_window, text="Порода", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack(pady=10)
|
||
breed_entry = tk.Entry(add_dog_window, font=("Comic Sans MS", 12))
|
||
breed_entry.pack(pady=5)
|
||
|
||
tk.Label(add_dog_window, text="Характеристики", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack(pady=10)
|
||
characteristics_entry = tk.Entry(add_dog_window, font=("Comic Sans MS", 12))
|
||
characteristics_entry.pack(pady=5)
|
||
|
||
tk.Label(add_dog_window, text="Поведение", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack(pady=10)
|
||
behavior_entry = tk.Entry(add_dog_window, font=("Comic Sans MS", 12))
|
||
behavior_entry.pack(pady=5)
|
||
|
||
tk.Label(add_dog_window, text="Уход", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack(pady=10)
|
||
care_info_entry = tk.Entry(add_dog_window, font=("Comic Sans MS", 12))
|
||
care_info_entry.pack(pady=5)
|
||
|
||
tk.Label(add_dog_window, text="Комментарии администратора", bg=BACKGROUND_COLOR, fg=TEXT_COLOR).pack(pady=10)
|
||
admin_comments_entry = tk.Entry(add_dog_window, font=("Comic Sans MS", 12))
|
||
admin_comments_entry.pack(pady=5)
|
||
|
||
def save_dog():
|
||
dog_data = {
|
||
'breed': breed_entry.get(),
|
||
'characteristics': characteristics_entry.get(),
|
||
'behavior': behavior_entry.get(),
|
||
'care_info': care_info_entry.get(),
|
||
'admin_comments': admin_comments_entry.get()
|
||
}
|
||
add_dog_to_db(dog_data)
|
||
add_dog_window.destroy()
|
||
|
||
def cancel_add():
|
||
add_dog_window.destroy()
|
||
|
||
save_button = tk.Button(add_dog_window, text="Сохранить", command=save_dog, bg=BUTTON_COLOR, fg=TEXT_COLOR)
|
||
save_button.pack(pady=20)
|
||
|
||
cancel_button = tk.Button(add_dog_window, text="Отменить", command=cancel_add, bg=BUTTON_COLOR, fg=TEXT_COLOR)
|
||
cancel_button.pack(pady=10)
|
||
|
||
def add_user_to_db(user_data):
|
||
session = get_session()
|
||
try:
|
||
new_user = Users(**user_data)
|
||
session.add(new_user)
|
||
session.commit()
|
||
print(f"Пользователь {user_data['username']} успешно добавлен.")
|
||
except SQLAlchemyError as e:
|
||
print(f"Ошибка при добавлении пользователя: {e}")
|
||
session.rollback()
|
||
finally:
|
||
session.close()
|
||
|
||
def add_question_to_db(question_data):
|
||
session = get_session()
|
||
try:
|
||
new_question = Questions(**question_data)
|
||
session.add(new_question)
|
||
session.commit()
|
||
print(f"Вопрос успешно добавлен: {question_data['question_text']}")
|
||
except SQLAlchemyError as e:
|
||
print(f"Ошибка при добавлении вопроса: {e}")
|
||
session.rollback()
|
||
finally:
|
||
session.close()
|
||
|
||
def add_dog_to_db(dog_data):
|
||
session = get_session()
|
||
try:
|
||
new_dog = Dogs(**dog_data)
|
||
session.add(new_dog)
|
||
session.commit()
|
||
print(f"Собака успешно добавлена: {dog_data['breed']}")
|
||
except SQLAlchemyError as e:
|
||
print(f"Ошибка при добавлении собаки: {e}")
|
||
session.rollback()
|
||
finally:
|
||
session.close() |