Исправления:

1. "Пользователь не найден"
+ заполнила таблицы из бд Dogs, Qoestions.
2. Меню паузы
+ оно больше не появляется в главном меню, при выборе собаки и уровня.
3. Окно победы
+ не открывается куча окон, собака не двигается, пока окно победы активно
+ добавлена кнопка "выйти в главное меню"
3. Окно регистрации
+ кнопка "посмотреть пароль"
4. таблица GameSessions
+ сохранение игрового процесса в таблицу GameSessions
+ одинаковые данные переносятся автоматически в таблицу GameSessions
+ собранные косточки и тп заполняют только таблицу GameSessions
5. Окно "Профиль пользователя"
+ нет ошибки открытия окна
+ корректное сохранение игрового процесса (уровни, косточки)
6. Окно "Магазин"
+ не кликабельная
+ кнопка "назад"
- shop_ui.py
7. Окно "База знаний"
+ не кликабельная
+ кнопка "назад"
- knowledge_ui.py
This commit is contained in:
Xatiko 2024-11-29 19:54:28 +03:00
parent 815f432536
commit 9f5d36d1a1
20 changed files with 3093 additions and 157 deletions

View file

@ -29,4 +29,4 @@ if __name__ == "__main__":
root = Tk() # Создание корневого окна
root.overrideredirect(True) # Убираем рамки окна
root.geometry("1920x1080") # Устанавливаем размер окна
main()
main()

View file

@ -0,0 +1,39 @@
from database.db_session import get_session, init_db
from database.info.Dogs_table import populate_dogs
from database.info.Questions_table import populate_questions
from database.models import Dogs, Questions
def test_data_integrity():
session = get_session()
try:
# Проверяем, есть ли собаки
dogs = session.query(Dogs).all()
assert len(dogs) > 0, "Таблица Dogs пуста!"
# Проверяем, есть ли вопросы
questions = session.query(Questions).all()
assert len(questions) > 0, "Таблица Questions пуста!"
# Проверяем связь вопросов с породами
for question in questions:
assert question.dog_id is not None, f"У вопроса {question.question_id} отсутствует dog_id"
dog = session.query(Dogs).filter_by(dog_id=question.dog_id).first()
assert dog is not None, f"Ссылка на несуществующую собаку в вопросе {question.question_id}"
print("Все тесты успешно пройдены.")
except AssertionError as e:
print(f"Ошибка теста: {e}")
finally:
session.close()
if __name__ == "__main__":
# Инициализируем базу данных (пересоздаём таблицы для чистого теста)
init_db(refresh=True)
# Заполняем таблицы
populate_dogs()
populate_questions()
# Запускаем тесты
test_data_integrity()

View file

@ -7,7 +7,7 @@ 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 # Импортируем общую функцию для очистки фрейма
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
@ -99,18 +99,18 @@ class AdminApp:
("Просмотр таблиц", self.view_tables),
]),
("Управление игровым контентом", [
("Создание и настройка уровней", feature_in_development),
("Настройка параметров собаки", feature_in_development),
("Создание и настройка уровней", feature_in_development_admin),
("Настройка параметров собаки", feature_in_development_admin),
]),
("Управление интерфейсом пользователя", [
("Добавление подсказок в интерфейс", feature_in_development),
("Добавление подсказок в интерфейс", feature_in_development_admin),
]),
("Работа с базой знаний", [
("Добавление информации", feature_in_development),
("Редактирование записей", feature_in_development),
("Удаление записей", feature_in_development),
("Просмотр базы знаний", feature_in_development),
("Генерация вопросов", feature_in_development),
("Добавление информации", feature_in_development_admin),
("Редактирование записей", feature_in_development_admin),
("Удаление записей", feature_in_development_admin),
("Просмотр базы знаний", feature_in_development_admin),
("Генерация вопросов", feature_in_development_admin),
]),
]

View file

@ -172,6 +172,17 @@ class DogAcademyApp:
self.reg_password_entry = tk.Entry(self.current_frame, show="*", font=FONT)
self.reg_password_entry.pack(pady=10)
# Кнопка "Показать пароль"
show_password_button = tk.Button(
self.current_frame,
text="Показать пароль",
bg=BUTTON_COLOR,
fg=BUTTON_TEXT_COLOR,
font=FONT,
command=self.toggle_registration_password,
)
show_password_button.pack(pady=10)
# Никнейм
username_label = tk.Label(self.current_frame, text="Никнейм:", bg=BACKGROUND_COLOR, fg=PRIMARY_COLOR, font=FONT)
username_label.pack()

View file

@ -4,6 +4,7 @@ import random
import logging
from database.db_events import get_user_by_id
from database.info.GameSessions_table import save_game_session
from src.user_functions.game_logs import setup_logging
from config import DOG_CHARACTERS, DONE, BONE, BACKGROUND_GAME
from src.utils import clear_frame
@ -41,6 +42,11 @@ class GameUI:
self.root.geometry("1920x1080")
self.root.configure(bg="#E5E5E5")
# Флаги
self.is_pause_menu_open = False
self.is_victory_screen_open = False
self.is_game_active = False # Для контроля состояния игры
# Привязка клавиш
self.root.bind("<KeyPress-w>", self.move_up)
self.root.bind("<KeyPress-s>", self.move_down)
@ -248,65 +254,52 @@ class GameUI:
self.update_map()
def on_escape(self, event):
"""Обработчик для нажатия клавиши ESC."""
self.show_pause_menu()
def show_pause_menu(self):
"""Создание окна паузы."""
pause_window = tk.Toplevel(self.root)
pause_window.title("Пауза")
pause_window.geometry("400x200")
pause_window.configure(bg="#E5E5E5")
pause_window.grab_set() # Блокируем взаимодействие с основным окном
# Кнопка "Сохранить и выйти"
save_exit_button = tk.Button(
pause_window,
text="Сохранить и выйти",
font=("Comic Sans MS", 16),
bg="#FF6347",
command=self.save_and_exit
)
save_exit_button.pack(pady=20)
# Кнопка "Продолжить"
continue_button = tk.Button(
pause_window,
text="Продолжить",
font=("Comic Sans MS", 16),
bg="#4CAF50",
command=pause_window.destroy
)
continue_button.pack(pady=20)
"""Обработчик для клавиши ESC."""
if self.map_canvas: # Проверяем, что игрок находится на карте
if self.is_pause_menu_open:
self.resume_game() # Закрываем окно паузы и продолжаем игру
else:
self.show_pause_menu() # Открываем окно паузы
def show_pause_menu(self):
"""Создание окна паузы."""
pause_window = tk.Toplevel(self.root)
pause_window.title("Пауза")
pause_window.geometry("400x200")
pause_window.configure(bg="#E5E5E5")
pause_window.grab_set() # Блокируем взаимодействие с основным окном
if self.is_pause_menu_open:
return # Если окно паузы уже открыто, ничего не делаем
self.is_pause_menu_open = True # Устанавливаем флаг
self.pause_window = tk.Toplevel(self.root)
self.pause_window.title("Пауза")
self.pause_window.geometry("400x200")
self.pause_window.configure(bg="#E5E5E5")
self.pause_window.grab_set() # Блокируем взаимодействие с основным окном
# Кнопка "Сохранить и выйти"
save_exit_button = tk.Button(
pause_window,
self.pause_window,
text="Сохранить и выйти",
font=("Comic Sans MS", 16),
bg="#FF6347",
command=self.save_and_exit # Этот метод теперь определён
command=self.save_and_exit
)
save_exit_button.pack(pady=20)
# Кнопка "Продолжить"
continue_button = tk.Button(
pause_window,
self.pause_window,
text="Продолжить",
font=("Comic Sans MS", 16),
bg="#4CAF50",
command=pause_window.destroy
command=self.resume_game
)
continue_button.pack(pady=20)
def resume_game(self):
"""Закрытие окна паузы и продолжение игры."""
if self.is_pause_menu_open:
self.pause_window.destroy() # Закрываем окно паузы
self.is_pause_menu_open = False # Сбрасываем флаг
def save_and_exit(self):
"""Сохранение данных и выход в главное меню."""
logging.info("Сохранение прогресса: уровень %d, собрано косточек %d.", self.current_level, self.total_bones)
@ -317,6 +310,9 @@ class GameUI:
def update_map(self):
"""Обновление карты."""
if self.is_victory_screen_open: # Отключаем обновления, если окно победы открыто
return
self.map_canvas.delete("all")
self.draw_grid()
@ -343,16 +339,22 @@ class GameUI:
# Условие перехода на следующий уровень
target_bones = 10 * (2 ** (self.current_level - 1)) # Геометрическая прогрессия
if self.total_bones >= target_bones:
if self.total_bones >= target_bones and not self.is_victory_screen_open:
self.show_victory_screen()
def show_victory_screen(self):
"""Экран победы."""
if self.is_victory_screen_open: # Проверяем, чтобы не было второго окна
return
self.is_victory_screen_open = True # Устанавливаем флаг
self.is_game_active = False # Останавливаем движение
victory_window = tk.Toplevel(self.root)
victory_window.title("Ура, победа!")
victory_window.geometry("800x600")
victory_window.configure(bg="#E5E5E5")
victory_window.grab_set()
victory_window.grab_set() # Блокируем взаимодействие с основным окном
# Изображение собаки
dog_image = Image.open(DOG_CHARACTERS[self.selected_dog]["image"]).resize((200, 200), Image.Resampling.LANCZOS)
@ -384,15 +386,32 @@ class GameUI:
# Кнопка перехода на следующий уровень
next_level_button = tk.Button(
victory_window, text="Следующий уровень", font=("Comic Sans MS", 16), bg="#4CAF50",
victory_window,
text="Следующий уровень",
font=("Comic Sans MS", 16),
bg="#4CAF50",
command=lambda: [victory_window.destroy(), self.start_next_level()]
)
next_level_button.place(relx=0.5, rely=0.75, anchor=tk.CENTER)
next_level_button.place(relx=0.5, rely=0.65, anchor=tk.CENTER)
# Кнопка выхода в главное меню
exit_button = tk.Button(
victory_window,
text="Выйти в главное меню",
font=("Comic Sans MS", 16),
bg="#FF6347",
command=lambda: [victory_window.destroy(), self.return_to_main_menu()]
)
exit_button.place(relx=0.5, rely=0.8, anchor=tk.CENTER)
def return_to_main_menu(self):
"""Возврат в главное меню."""
self.is_victory_screen_open = False # Сбрасываем флаг окна победы
self.return_to_main_menu_callback() # Вызываем колбэк для возврата
def start_next_level(self):
"""Переход на следующий уровень и сохранение прогресса."""
# Сохранение прогресса
self.save_progress()
"""Переход на следующий уровень."""
self.save_progress() # Сохраняем прогресс перед переходом на следующий уровень
# Переход на следующий уровень
self.current_level += 1
@ -400,19 +419,14 @@ class GameUI:
self.start_level(self.current_level)
def save_progress(self):
"""Сохранение прогресса в базе данных."""
from database.db_events import save_progress, update_user_dog
from database.db_events import get_user_by_id
"""Сохранение игрового процесса в таблицу GameSessions."""
from datetime import datetime
# Получаем информацию о пользователе
user = get_user_by_id(self.user_id)
if not user:
logging.error(f"Пользователь с ID {self.user_id} не найден в базе данных.")
return # Прерываем выполнение, если пользователь не найден
# Получаем время начала и окончания уровня
duration = self.steps_taken # Время можно рассчитать по шагам или в реальном времени
score = self.total_bones # Количество собранных косточек
# Сохраняем прогресс текущего уровня
save_progress(self.user_id, self.current_level, self.total_bones, 0, 100, 0, 0)
# Сохранение прогресса в базу данных
save_game_session(self.user_id, self.current_level, score, duration, 100, 0, 0)
# Получаем уровень и собаку
dog_id = user.dog_id # Получаем id собаки пользователя
update_user_dog(self.user_id, dog_id) # Сохраняем собаку в профиль
# Также можно сохранять дополнительные параметры, если необходимо (например, здоровье, голод, усталость)

View file

@ -1,34 +0,0 @@
import tkinter as tk
from src.utils import clear_frame
from database.db_events import get_knowledge_base
def knowledge_ui(root):
"""Интерфейс базы знаний."""
clear_frame(root)
frame = tk.Frame(root, bg="#f8e1e1")
frame.pack(fill=tk.BOTH, expand=True)
tk.Label(frame, text="База знаний", font=("Comic Sans MS", 30), bg="#f8e1e1").pack(pady=20)
articles = get_knowledge_base()
for article in articles:
article_frame = tk.Frame(frame, bg="#f8f8f8", bd=2, relief=tk.RIDGE)
article_frame.pack(pady=5, padx=10, fill=tk.X)
tk.Label(article_frame, text=article.breed, font=("Comic Sans MS", 20), bg="#f8f8f8").pack(side=tk.LEFT,
padx=10)
tk.Button(article_frame, text="Читать", command=lambda a=article: show_article(a)).pack(side=tk.RIGHT, padx=10)
tk.Button(frame, text="Назад", command=lambda: clear_frame(root), font=("Comic Sans MS", 20)).pack(pady=20)
def show_article(article):
"""Показать содержимое статьи."""
top = tk.Toplevel()
top.title(article.breed)
text = tk.Text(top, wrap=tk.WORD)
text.pack(fill=tk.BOTH, expand=True)
text.insert(tk.END, article.content)
text.config(state=tk.DISABLED)

View file

@ -8,8 +8,6 @@ import math
from config import EXIT_BUTTON_WIDTH, EXIT_BUTTON_HEIGHT, BUTTON_COLOR_EXIT
from src.ui.user_ui.game_ui import GameUI
from src.ui.user_ui.profile_ui import profile_ui
from src.ui.user_ui.shop_ui import shop_ui
from src.ui.user_ui.knowledge_ui import knowledge_ui
from src.utils import clear_frame
# Пути к изображениям собак
@ -74,7 +72,7 @@ class UserApp:
relief=tk.FLAT,
padx=20,
pady=10,
command=self.show_shop
state=tk.DISABLED # Делаем кнопку некликабельной
)
shop_button.pack(side=tk.LEFT, padx=20)
@ -87,7 +85,7 @@ class UserApp:
relief=tk.FLAT,
padx=20,
pady=10,
command=self.show_knowledge
state=tk.DISABLED # Делаем кнопку некликабельной
)
knowledge_button.pack(side=tk.LEFT, padx=20)
@ -154,17 +152,7 @@ class UserApp:
def show_profile(self):
"""Показать экран профиля пользователя."""
self.clear_frame()
profile_ui(self.root, self.user_id) # Передаем user_id в profile_ui
def show_shop(self):
"""Показать экран магазина."""
self.clear_frame()
shop_ui(self.root)
def show_knowledge(self):
"""Показать базу знаний."""
self.clear_frame()
knowledge_ui(self.root)
profile_ui(self.root, self.user_id, self) # Передаем сам объект self для доступа к show_user_dashboard
def clear_frame(self):
"""Очистить текущий экран."""
@ -184,4 +172,4 @@ class UserApp:
def exit_app(self):
"""Подтверждение выхода из приложения."""
if messagebox.askyesno("Выход", "Вы уверены, что хотите выйти?"):
self.root.quit()
self.root.quit()

View file

@ -3,7 +3,7 @@ from src.utils import clear_frame
from database.db_events import get_user_progress
def profile_ui(root, user_id):
def profile_ui(root, user_id, user_app):
"""Интерфейс профиля пользователя."""
clear_frame(root)
@ -12,7 +12,7 @@ def profile_ui(root, user_id):
tk.Label(frame, text="Профиль", font=("Comic Sans MS", 30), bg="#f8e1e1").pack(pady=20)
# Статистика пользователя
# Получение прогресса пользователя из базы данных
progress = get_user_progress(user_id)
levels_completed = len(progress) # Считаем количество уровней
bones_collected = sum([session.score for session in progress]) # Суммируем все собранные косточки
@ -20,4 +20,12 @@ def profile_ui(root, user_id):
stats_text = f"Пройдено уровней: {levels_completed}\nСобрано косточек: {bones_collected}"
tk.Label(frame, text=stats_text, font=("Comic Sans MS", 20), bg="#f8e1e1").pack(pady=10)
tk.Button(frame, text="Назад", command=lambda: clear_frame(root), font=("Comic Sans MS", 20)).pack(pady=20)
# Кнопка "Назад"
back_button = tk.Button(
frame,
text="Назад",
command=lambda: [clear_frame(root), user_app.show_user_dashboard()], # Очистить экран и вернуться на главное меню
font=("Comic Sans MS", 20)
)
back_button.pack(pady=20)

View file

@ -1,24 +0,0 @@
import tkinter as tk
from src.utils import clear_frame
from database.db_events import get_dogs, update_user_dog
def shop_ui(root, user_id):
"""Интерфейс магазина."""
clear_frame(root)
frame = tk.Frame(root, bg="#e5e5e5")
frame.pack(fill=tk.BOTH, expand=True)
tk.Label(frame, text="Магазин", font=("Comic Sans MS", 30), bg="#e5e5e5").pack(pady=20)
dogs = get_dogs() # Получить список собак из базы данных
for dog in dogs:
dog_frame = tk.Frame(frame, bg="#f8f8f8", bd=2, relief=tk.RIDGE)
dog_frame.pack(pady=5, padx=10, fill=tk.X)
tk.Label(dog_frame, text=dog.breed, font=("Comic Sans MS", 20), bg="#f8f8f8").pack(side=tk.LEFT, padx=10)
tk.Button(dog_frame, text="Купить", command=lambda d=dog: update_user_dog(user_id, d.dog_id)).pack(
side=tk.RIGHT, padx=10)
tk.Button(frame, text="Назад", command=lambda: clear_frame(root), font=("Comic Sans MS", 20)).pack(pady=20)

View file

@ -2,11 +2,12 @@ import tkinter as tk
def clear_frame(frame):
"""Удаление всех виджетов из фрейма."""
"""Очистить все виджеты в frame"""
for widget in frame.winfo_children():
widget.destroy()
def feature_in_development(frame):
def feature_in_development_admin(frame):
"""Сообщение о том, что функционал недоступен."""
clear_frame(frame) # Очистка фрейма перед выводом сообщения
tk.Label(
@ -17,6 +18,7 @@ def feature_in_development(frame):
font=("Comic Sans MS", 16)
).pack(expand=True)
def show_message(message):
"""Показать сообщение пользователю"""
message_window = tk.Toplevel()