Изменён пользовательский интерфейс main_menu.py, game_ui.py. Настроен вызов функций для запуска игрового процесса: после нажатия кнопки 'играть' открывается выбор собаки, а затем выбор уровня. Обновлена структура базы данных. Удалены не нужные файлы для проекта.

This commit is contained in:
Xatiko 2024-11-26 23:02:04 +03:00
parent 989a9056e7
commit c08cf51998
46 changed files with 804 additions and 111 deletions

Binary file not shown.

BIN
assets/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 KiB

BIN
assets/bone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

BIN
assets/dogs/Chihuahua.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

BIN
assets/dogs/Corgi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

BIN
assets/dogs/Husky.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
assets/dogs/Pomeranian.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

BIN
assets/dogs/Pug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

BIN
assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

103
config.py
View file

@ -6,7 +6,7 @@ ADMIN_BUTTON_TEXT_COLOR = "#ffffff"
ADMIN_FONT = ("Comic Sans MS", 25)
ADMIN_BIG_FONT = ("Comic Sans MS", 40)
# Интерфейс пользователя
# Интерфейс пользователя (АВТОРИЗАЦИЯ)
BACKGROUND_COLOR = "#f8e1e1"
PRIMARY_COLOR = "#ff6347"
BUTTON_COLOR = "#87ceeb"
@ -14,6 +14,26 @@ BUTTON_TEXT_COLOR = "white"
FONT = ("Comic Sans MS", 25)
BIG_FONT = ("Comic Sans MS", 40)
# ГЛАВНОЕ МЕНЮ
BACKGROUND_COLOR_USER = "#bcabe5" # Основной фон
TOP_PANEL_COLOR_USER = "#aa9bcd" # Цвет верхней панели
BUTTON_COLOR_PROFILE_USER = "#a2c792" # Цвет кнопок "Профиль", "Магазин", "База знаний"
BUTTON_COLOR_PLAY_USER = "#b4e1a1" # Цвет кнопки "Играть"
BUTTON_COLOR_EXIT_USER = "#a2c792" # Цвет кнопки "Выход"
BUTTON_TEXT_COLOR_USER = "white" # Цвет текста на кнопках
FONT_USER = ("Comic Sans MS", 20) # Шрифт для текста кнопок
BIG_FONT_USER = ("Comic Sans MS", 30) # Большой шрифт (например, для заголовков)
BUTTON_RADIUS_USER = 50 # Радиус круглой кнопки
EXIT_BUTTON_SIZE_USER = (80, 40) # Размер кнопки "Выход"
TOP_PANEL_COLOR = "#BBA0D0"
BUTTON_COLOR_PROFILE = "#8FC085"
BUTTON_COLOR_PLAY = "#8FC085"
BUTTON_COLOR_EXIT = "#8FC085"
# Размеры
PLAY_BUTTON_RADIUS = 100
EXIT_BUTTON_WIDTH = 100
EXIT_BUTTON_HEIGHT = 50
# Данные для авторизации администратора
ADMIN_LOGIN = "admin"
ADMIN_PASSWORD = "admin123"
@ -23,23 +43,78 @@ DATABASE_URL = "sqlite:///database/DogAcademy.db" # Обновлено на п
# Иконки
SETTINGS_IMG = "assets/settings.png"
LOGO = "F:/Projects/Dog_Academy/assets/logo.png"
BACKGROUND_GAME = "F:/Projects/Dog_Academy/assets/background.png"
BONE = "F:/Projects/Dog_Academy/assets/bone.png"
# Собаки
CHIHUAHUA = "F:/Projects/Dog_Academy/assets/dogs/Chihuahua.png"
CORGI = "F:/Projects/Dog_Academy/assets/dogs/Corgi.png"
RETRIEVER = "F:/Projects/Dog_Academy/assets/dogs/Golden_Retriever.png"
HUSKY = "F:/Projects/Dog_Academy/assets/dogs/Husky.png"
POMERANIAN = "F:/Projects/Dog_Academy/assets/dogs/Pomeranian.png"
PUG = "F:/Projects/Dog_Academy/assets/dogs/Pug.png"
YORKSHIRE = "F:/Projects/Dog_Academy/assets/dogs/Yorkshire_Terrier.png"
DOG_CHARACTERS = {
"Chihuahua": {
"image": CHIHUAHUA,
"speed": 8,
"endurance": 5,
"special_ability": "Fast Dodge", # Уклонение от препятствий
},
"Corgi": {
"image": CORGI,
"speed": 6,
"endurance": 7,
"special_ability": "Extra Jump", # Дополнительный прыжок
},
"Golden Retriever": {
"image": RETRIEVER,
"speed": 7,
"endurance": 8,
"special_ability": "Bonus Points", # Увеличенные очки за правильные ответы
},
"Husky": {
"image": HUSKY,
"speed": 9,
"endurance": 6,
"special_ability": "Speed Boost", # Ускорение
},
"Pomeranian": {
"image": POMERANIAN,
"speed": 7,
"endurance": 4,
"special_ability": "Charm", # Уменьшает штраф за ошибки
},
"Pug": {
"image": PUG,
"speed": 5,
"endurance": 9,
"special_ability": "Resilience", # Сохраняет здоровье при столкновениях
},
"Yorkshire Terrier": {
"image": YORKSHIRE,
"speed": 6,
"endurance": 5,
"special_ability": "Quick Recovery", # Быстрое восстановление характеристик
},
}
# Утилиты
NOTIFICATION_LEVEL = "info" # Возможные значения: "info", "warning", "error"
USE_DATABASE_LOGS = True
# ГЛАВНОЕ МЕНЮ
BACKGROUND_COLOR_USER = "#bcabe5" # Основной фон
TOP_PANEL_COLOR_USER = "#aa9bcd" # Цвет верхней панели
BUTTON_COLOR_PROFILE_USER = "#a2c792" # Цвет кнопок "Профиль", "Магазин", "База знаний"
BUTTON_COLOR_PLAY_USER = "#b4e1a1" # Цвет кнопки "Играть"
BUTTON_COLOR_EXIT_USER = "#a2c792" # Цвет кнопки "Выход"
# Игровые параметры
INITIAL_SCORE = 5 # Начальные очки игрока
POINTS_CORRECT_ANSWER = 2 # Очки за правильный ответ
POINTS_WRONG_ANSWER = -1 # Штраф за неправильный ответ
MAX_LEVELS = 100 # Максимальное количество уровней
INITIAL_DOG_STATUS = {"health": 100, "hunger": 0, "sleepiness": 0} # Стартовые характеристики собаки
# Текст и шрифты
BUTTON_TEXT_COLOR_USER = "white" # Цвет текста на кнопках
FONT_USER = ("Comic Sans MS", 20) # Шрифт для текста кнопок
BIG_FONT_USER = ("Comic Sans MS", 30) # Большой шрифт (например, для заголовков)
# Параметры карты
MIN_OBSTACLES = 3 # Минимум препятствий на уровне
MAX_OBSTACLES = 6 # Максимум препятствий на уровне
# Размеры кнопок
BUTTON_RADIUS_USER = 50 # Радиус круглой кнопки
EXIT_BUTTON_SIZE_USER = (80, 40) # Размер кнопки "Выход"
# Графика и анимация
COUNTDOWN_DURATION = 3 # Продолжительность обратного отсчёта в секундах

View file

@ -1,12 +1,18 @@
from sqlalchemy import func
from sqlalchemy.orm import joinedload
from database.db_session import get_session
from database.models import Auth, Notifications, Users
from database.models import Auth, Notifications, Users, GameSession, Dogs, Questions
from sqlalchemy.exc import SQLAlchemyError
def create_user(login, password):
def create_user(login, password, username):
"""Создание нового пользователя в базе данных."""
session = get_session()
try:
new_user = Auth(login=login, password=password)
new_user_auth = Auth(login=login, password=password)
session.add(new_user_auth)
session.commit()
new_user = Users(username=username, auth=new_user_auth)
session.add(new_user)
session.commit()
except SQLAlchemyError as e:
@ -20,9 +26,111 @@ def check_user(login, password):
session = get_session()
try:
user = session.query(Auth).filter_by(login=login, password=password).first()
return user is not None
if user:
return user.user_id
return None
except SQLAlchemyError as e:
print(f"Ошибка при проверке пользователя: {e}")
return False
return None
finally:
session.close()
def save_progress(user_id, level, score, duration, health, hunger, sleepiness):
"""Сохраняет прогресс пользователя в базу данных."""
session = get_session()
try:
session_data = GameSession(
user_id=user_id,
level=level,
score=score,
duration=duration,
health=health,
hunger=hunger,
sleepiness=sleepiness,
end_time=func.now()
)
session.add(session_data)
session.commit()
except SQLAlchemyError as e:
print(f"Ошибка при сохранении прогресса: {e}")
session.rollback()
finally:
session.close()
def get_user_progress(user_id):
"""Получение прогресса пользователя по его ID."""
session = get_session()
try:
progress = session.query(GameSession).filter_by(user_id=user_id).all()
return progress
except SQLAlchemyError as e:
print(f"Ошибка при получении прогресса: {e}")
return []
finally:
session.close()
def create_notification(user_id, message):
"""Создание уведомления для пользователя."""
session = get_session()
try:
notification = Notifications(user_id=user_id, message=message)
session.add(notification)
session.commit()
except SQLAlchemyError as e:
print(f"Ошибка при создании уведомления: {e}")
session.rollback()
finally:
session.close()
def get_notifications(user_id):
"""Получение уведомлений для пользователя."""
session = get_session()
try:
notifications = session.query(Notifications).filter_by(user_id=user_id).all()
return notifications
except SQLAlchemyError as e:
print(f"Ошибка при получении уведомлений: {e}")
return []
finally:
session.close()
def get_knowledge_base():
"""Получение базы знаний (статей о собаках)."""
session = get_session()
try:
dogs = session.query(Dogs).all()
return dogs # Список объектов Dogs
except SQLAlchemyError as e:
print(f"Ошибка при получении базы знаний: {e}")
return []
finally:
session.close()
def get_dogs():
"""Получение списка пород собак."""
session = get_session()
try:
dogs = session.query(Dogs).all()
return dogs # Список объектов Dogs
except SQLAlchemyError as e:
print(f"Ошибка при получении списка собак: {e}")
return []
finally:
session.close()
def update_user_dog(user_id, dog_id):
"""Обновление выбранной пользователем породы собаки."""
session = get_session()
try:
user = session.query(Users).filter_by(user_id=user_id).first()
if user:
user.dog_id = dog_id
session.commit()
print(f"Порода пользователя обновлена на {dog_id}")
else:
print("Пользователь не найден.")
except SQLAlchemyError as e:
print(f"Ошибка при обновлении породы собаки: {e}")
session.rollback()
finally:
session.close()

View file

@ -13,7 +13,6 @@ Session = sessionmaker(bind=engine)
# Переменная для хранения текущей сессии
current_session = None
def init_db(refresh=False):
"""
Инициализация базы данных: создание файла и таблиц.
@ -33,18 +32,14 @@ def init_db(refresh=False):
# Инициализация сессии при запуске
current_session = get_session()
def get_session():
"""Возвращает сессию для работы с базой данных."""
return Session()
def close_sessions():
"""Закрытие всех сессий перед выходом из программы."""
global current_session
if current_session is not None:
if current_session:
print("Закрытие сессии...")
current_session.close() # Закрываем текущую сессию базы данных
current_session = None
current_session.close()
else:
print("Нет активной сессии для закрытия.")

View file

@ -1,11 +1,10 @@
from sqlalchemy import Column, Integer, String, ForeignKey, Text, DateTime
from sqlalchemy import Column, Integer, String, ForeignKey, Text, DateTime, UniqueConstraint, Boolean
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Auth(Base):
__tablename__ = 'auth'
user_id = Column(Integer, primary_key=True)
@ -50,7 +49,7 @@ class Questions(Base):
__tablename__ = 'questions'
question_id = Column(Integer, primary_key=True)
dog_id = Column(Integer, ForeignKey('dogs.dog_id'))
question_text = Column(Text, nullable=False) # Исправлено поле
question_text = Column(Text, nullable=False)
image_url = Column(String)
helpful_info = Column(Text)
incorrect_attempts = Column(Integer, default=0)
@ -61,24 +60,31 @@ class Questions(Base):
class GameSession(Base):
__tablename__ = 'game_sessions'
__table_args__ = (
UniqueConstraint('user_id', 'level', name='uix_user_level'),
)
session_id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.user_id'))
level = Column(Integer, nullable=False)
score = Column(Integer, default=0)
duration = Column(Integer) # Время игры в секундах
start_time = Column(DateTime, default=func.now()) # Исправлено
start_time = Column(DateTime, default=func.now())
end_time = Column(DateTime, nullable=True)
health = Column(Integer, default=100) # Здоровье
hunger = Column(Integer, default=0) # Голод
sleepiness = Column(Integer, default=0) # Сонливость
# Связь с таблицей Users
user = relationship("Users", back_populates="game_sessions")
class Notifications(Base):
__tablename__ = 'notifications'
notification_id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.user_id'))
message = Column(Text, nullable=False)
timestamp = Column(DateTime, default=func.now())
is_read = Column(Integer, default=0) # 0 - не прочитано, 1 - прочитано
is_read = Column(Boolean, default=False) # Булевый тип для read
# Связь с таблицей Users
user = relationship("Users", back_populates="notifications")

Binary file not shown.

BIN
ishodniki/dogs.psd Normal file

Binary file not shown.

BIN
ishodniki/user_ui.psd Normal file

Binary file not shown.

View file

@ -1343,3 +1343,128 @@ WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-25 22:36:09 - [generated in 0.00045s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-25 22:36:09 - ROLLBACK
2024-11-26 12:26:39 - BEGIN (implicit)
2024-11-26 12:26:39 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 12:26:39 - [generated in 0.00021s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 12:26:39 - ROLLBACK
2024-11-26 12:33:48 - BEGIN (implicit)
2024-11-26 12:33:48 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 12:33:48 - [generated in 0.00022s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 12:33:48 - ROLLBACK
2024-11-26 12:38:38 - BEGIN (implicit)
2024-11-26 12:38:38 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 12:38:38 - [generated in 0.00029s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 12:38:38 - ROLLBACK
2024-11-26 17:00:02 - BEGIN (implicit)
2024-11-26 17:00:02 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 17:00:02 - [generated in 0.00024s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 17:00:02 - ROLLBACK
2024-11-26 17:01:01 - BEGIN (implicit)
2024-11-26 17:01:01 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 17:01:01 - [generated in 0.00021s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 17:01:01 - ROLLBACK
2024-11-26 17:07:02 - BEGIN (implicit)
2024-11-26 17:07:02 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 17:07:02 - [generated in 0.00023s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 17:07:02 - ROLLBACK
2024-11-26 17:08:03 - BEGIN (implicit)
2024-11-26 17:08:03 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 17:08:03 - [generated in 0.00025s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 17:08:03 - ROLLBACK
2024-11-26 17:27:44 - BEGIN (implicit)
2024-11-26 17:27:44 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 17:27:44 - [generated in 0.00021s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 17:27:44 - ROLLBACK
2024-11-26 19:46:13 - BEGIN (implicit)
2024-11-26 19:46:13 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 19:46:13 - [generated in 0.00029s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 19:46:13 - ROLLBACK
2024-11-26 21:23:20 - BEGIN (implicit)
2024-11-26 21:23:20 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 21:23:20 - [generated in 0.00025s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 21:23:20 - ROLLBACK
2024-11-26 21:31:58 - BEGIN (implicit)
2024-11-26 21:31:58 - SELECT count(*) AS count_1
FROM (SELECT users.user_id AS users_user_id, users.dog_id AS users_dog_id, users.username AS users_username, users.level AS users_level, users.achievement AS users_achievement
FROM users) AS anon_1
2024-11-26 21:31:58 - [generated in 0.00026s] ()
2024-11-26 21:31:58 - SELECT game_sessions.level AS game_sessions_level, count(game_sessions.session_id) AS count_1
FROM game_sessions GROUP BY game_sessions.level
2024-11-26 21:31:58 - [generated in 0.00022s] ()
2024-11-26 21:31:58 - SELECT questions.question_text AS questions_question_text, questions.incorrect_attempts AS questions_incorrect_attempts
FROM questions ORDER BY questions.incorrect_attempts DESC
2024-11-26 21:31:58 - [generated in 0.00019s] ()
2024-11-26 21:31:58 - SELECT avg(game_sessions.duration) AS avg_1
FROM game_sessions
2024-11-26 21:31:58 - [generated in 0.00014s] ()
2024-11-26 21:31:58 - ROLLBACK
2024-11-26 21:31:58 - BEGIN (implicit)
2024-11-26 21:31:58 - SELECT game_sessions.start_time AS game_sessions_start_time
FROM game_sessions
2024-11-26 21:31:58 - [generated in 0.00017s] ()
2024-11-26 21:31:58 - ROLLBACK
2024-11-26 21:34:57 - BEGIN (implicit)
2024-11-26 21:34:57 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 21:34:57 - [generated in 0.00025s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 21:34:57 - ROLLBACK
2024-11-26 21:47:02 - BEGIN (implicit)
2024-11-26 21:47:02 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 21:47:02 - [generated in 0.00021s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 21:47:02 - ROLLBACK
2024-11-26 21:49:25 - BEGIN (implicit)
2024-11-26 21:49:25 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 21:49:25 - [generated in 0.00023s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 21:49:25 - ROLLBACK
2024-11-26 21:54:33 - BEGIN (implicit)
2024-11-26 21:54:33 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 21:54:33 - [generated in 0.00025s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 21:54:33 - ROLLBACK
2024-11-26 22:41:31 - BEGIN (implicit)
2024-11-26 22:41:31 - SELECT auth.user_id AS auth_user_id, auth.login AS auth_login, auth.password AS auth_password
FROM auth
WHERE auth.login = ? AND auth.password = ?
LIMIT ? OFFSET ?
2024-11-26 22:41:31 - [generated in 0.00027s] ('lubluNikitu', 'meow123', 1, 0)
2024-11-26 22:41:31 - ROLLBACK

View file

@ -1,3 +1,7 @@
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from tkinter import Tk, messagebox
from src.ui.auth_ui import DogAcademyApp # Путь к приложению
from database.db_session import init_db, close_sessions # Функция для закрытия сессий

View file

@ -219,4 +219,4 @@ class DogAcademyApp:
def show_user_dashboard(self):
self.clear_frame()
"""Перейти к главному меню пользователя после авторизации."""
UserApp(self.root, self)
UserApp(self.root)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,149 @@
import tkinter as tk
from PIL import Image, ImageTk
from src.utils import clear_frame
from config import DOG_CHARACTERS, BACKGROUND_GAME, LOGO, COUNTDOWN_DURATION
from src.user_functions.map_generator import generate_map
from src.user_functions.game_functions import handle_obstacle
class GameUI:
def __init__(self, root, user_id):
self.root = root
self.user_id = user_id
self.selected_dog = None
self.current_level = 1
self.score = 0
self.root.configure(bg="lightblue")
self.create_logo()
self.show_dog_selection()
def create_logo(self):
"""Создание логотипа."""
logo_image = Image.open(LOGO)
logo_photo = ImageTk.PhotoImage(logo_image.resize((200, 100), Image.Resampling.LANCZOS))
logo_label = tk.Label(self.root, image=logo_photo, bg="lightblue")
logo_label.image = logo_photo
logo_label.pack(pady=10)
def show_dog_selection(self):
"""Выбор собаки пользователем."""
clear_frame(self.root)
tk.Label(
self.root, text="Выберите собаку", font=("Comic Sans MS", 24), bg="lightblue"
).pack(pady=20)
dog_frame = tk.Frame(self.root, bg="lightblue")
dog_frame.place(relx=0.5, rely=0.5, anchor=tk.CENTER) # Центрируем фрейм
for breed, details in DOG_CHARACTERS.items():
dog_image = Image.open(details["image"])
dog_photo = ImageTk.PhotoImage(dog_image.resize((150, 150), Image.Resampling.LANCZOS))
# Фрейм для кнопки и подписи
dog_item = tk.Frame(dog_frame, bg="lightblue")
dog_item.pack(side=tk.LEFT, padx=15, pady=15)
# Кнопка с изображением
button = tk.Button(
dog_item,
image=dog_photo,
command=lambda b=breed: self.confirm_dog_selection(b),
bg="lightblue",
borderwidth=0,
)
button.image = dog_photo # Сохраняем ссылку на изображение
button.pack()
# Подпись с породой собаки
tk.Label(
dog_item,
text=breed,
font=("Comic Sans MS", 16),
bg="lightblue"
).pack(pady=5)
def confirm_dog_selection(self, breed):
"""Подтверждение выбора собаки."""
self.selected_dog = breed
self.show_level_selection()
def show_level_selection(self):
"""Отображение выбора уровня."""
clear_frame(self.root)
tk.Label(
self.root, text="Выберите уровень", font=("Comic Sans MS", 20), bg="lightblue"
).pack(pady=10)
level_frame = tk.Frame(self.root, bg="lightblue")
level_frame.place(relx=0.5, rely=0.5, anchor=tk.CENTER) # Центрируем фрейм
for level in range(1, 6): # Доступно 5 уровней
tk.Button(
level_frame,
text=f"Уровень {level}",
command=lambda l=level: self.start_level(l),
font=("Comic Sans MS", 16),
bg="lightgreen",
width=12,
).pack(pady=10)
def start_level(self, level):
"""Начало выбранного уровня."""
self.current_level = level
self.countdown()
def countdown(self):
"""Обратный отсчёт перед началом уровня."""
clear_frame(self.root)
countdown_label = tk.Label(
self.root, text="", font=("Comic Sans MS", 30), bg="lightblue"
)
countdown_label.pack(expand=True)
for i in range(COUNTDOWN_DURATION, 0, -1):
countdown_label.config(text=f"{i}...")
self.root.update()
self.root.after(1000)
self.start_game()
def start_game(self):
"""Запуск игрового процесса."""
clear_frame(self.root)
# Генерация карты
map_data = generate_map(self.current_level)
for obstacle in map_data:
result = handle_obstacle(obstacle, self.score, self.root)
self.score = result["new_score"]
if self.score >= 10: # Условие победы
self.show_victory_screen()
def show_victory_screen(self):
"""Экран победы."""
clear_frame(self.root)
tk.Label(
self.root, text="Ура, победа!", font=("Comic Sans MS", 30), bg="lightblue"
).pack(pady=10)
dog_image = Image.open(DOG_CHARACTERS[self.selected_dog]["image"])
dog_photo = ImageTk.PhotoImage(dog_image.resize((150, 150), Image.Resampling.LANCZOS))
tk.Label(self.root, image=dog_photo, bg="lightblue").pack(pady=10)
tk.Label(
self.root,
text=f"Порода: {self.selected_dog}\nСобрано косточек: {self.score}",
font=("Comic Sans MS", 20),
bg="lightblue",
).pack(pady=10)
tk.Button(
self.root,
text="Вернуться в главное меню",
command=lambda: self.__init__(self.root, self.user_id),
font=("Comic Sans MS", 16),
bg="lightgreen",
).pack(pady=10)

View file

@ -0,0 +1,34 @@
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

@ -1,78 +1,167 @@
import tkinter as tk
from config import (
BACKGROUND_COLOR_USER,
TOP_PANEL_COLOR_USER,
BUTTON_COLOR_PROFILE_USER,
BUTTON_COLOR_PLAY_USER,
BUTTON_COLOR_EXIT_USER,
BUTTON_TEXT_COLOR_USER,
FONT_USER,
BIG_FONT_USER,
BUTTON_RADIUS_USER,
EXIT_BUTTON_SIZE_USER,
)
from tkinter import Canvas
from PIL import Image, ImageTk
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
# Пути к изображениям собак
DOG_IMAGES = [
"F:/Projects/Dog_Academy/assets/dogs/Chihuahua.png",
"F:/Projects/Dog_Academy/assets/dogs/Corgi.png",
"F:/Projects/Dog_Academy/assets/dogs/Golden_Retriever.png",
"F:/Projects/Dog_Academy/assets/dogs/Husky.png",
"F:/Projects/Dog_Academy/assets/dogs/Pomeranian.png",
"F:/Projects/Dog_Academy/assets/dogs/Pug.png",
"F:/Projects/Dog_Academy/assets/dogs/Yorkshire_Terrier.png"
]
# Настройки
BACKGROUND_COLOR = "#E5E5E5" # Цвет фона
BUTTON_COLOR_PLAY = "#4CAF50" # Цвет кнопки играть
BUTTON_TEXT_COLOR = "white" # Цвет текста на кнопке
FONT = ("Arial", 12)
BIG_FONT = ("Arial", 24)
PLAY_BUTTON_RADIUS = 100 # Радиус кнопки "Играть"
class UserApp:
def __init__(self, root, auth_ui):
def __init__(self, root, user_id=None):
self.root = root
self.auth_ui = auth_ui
self.root.configure(bg=BACKGROUND_COLOR_USER)
self.root.geometry("1920x1080") # Разрешение окна
self.user_id = user_id
self.root.configure(bg=BACKGROUND_COLOR)
self.root.geometry("1920x1080")
self.root.title("Собачья академия")
print("Главное меню активно") # Лог при открытии меню
self.show_user_dashboard()
def show_user_dashboard(self):
"""Показать интерфейс пользователя."""
center_x, center_y = 960, 540 # Центр экрана
radius = 300 # Радиус круга для размещения собак
num_dogs = len(DOG_IMAGES)
# Верхняя панель
top_panel = tk.Frame(self.root, bg=TOP_PANEL_COLOR_USER, height=100)
top_panel = tk.Frame(self.root, bg="#333333", height=100)
top_panel.pack(fill=tk.X, side=tk.TOP)
# Кнопки на верхней панели
for text, command in [("Профиль", self.show_profile), ("Магазин", self.show_shop), ("База знаний", self.show_knowledge)]:
button = tk.Button(
top_panel,
text=text,
bg=BUTTON_COLOR_PROFILE_USER,
fg=BUTTON_TEXT_COLOR_USER,
font=FONT_USER,
relief=tk.FLAT,
padx=20,
pady=10,
command=command,
)
button.pack(side=tk.LEFT, padx=20)
# Кнопка "Играть" в центре
play_button = tk.Button(
self.root,
text="Играть",
bg=BUTTON_COLOR_PLAY_USER,
fg=BUTTON_TEXT_COLOR_USER,
font=BIG_FONT_USER,
profile_button = tk.Button(
top_panel,
text="Профиль",
bg="#555555",
fg="white",
font=FONT,
relief=tk.FLAT,
height=2,
width=10,
command=self.play_game,
padx=20,
pady=10,
command=self.show_profile
)
play_button.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
profile_button.pack(side=tk.LEFT, padx=20)
# Кнопка "Выход" в правом нижнем углу
exit_button = tk.Button(
self.root,
text="Выход",
bg=BUTTON_COLOR_EXIT_USER,
fg=BUTTON_TEXT_COLOR_USER,
font=FONT_USER,
width=EXIT_BUTTON_SIZE_USER[0] // 10,
height=EXIT_BUTTON_SIZE_USER[1] // 10,
command=self.exit_app,
shop_button = tk.Button(
top_panel,
text="Магазин",
bg="#555555",
fg="white",
font=FONT,
relief=tk.FLAT,
padx=20,
pady=10,
command=self.show_shop
)
exit_button.place(relx=1.0, rely=1.0, x=-20, y=-20, anchor=tk.SE)
shop_button.pack(side=tk.LEFT, padx=20)
knowledge_button = tk.Button(
top_panel,
text="База знаний",
bg="#555555",
fg="white",
font=FONT,
relief=tk.FLAT,
padx=20,
pady=10,
command=self.show_knowledge
)
knowledge_button.pack(side=tk.LEFT, padx=20)
# Размещение собак по кругу
self.place_dog_images(center_x, center_y, radius, num_dogs)
# Кнопка "Играть" (увеличенная)
play_button_canvas = tk.Canvas(
self.root,
width=PLAY_BUTTON_RADIUS * 2,
height=PLAY_BUTTON_RADIUS * 2,
bg=BACKGROUND_COLOR,
highlightthickness=0,
)
play_button_canvas.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
play_button_canvas.create_oval(
0, 0, PLAY_BUTTON_RADIUS * 2, PLAY_BUTTON_RADIUS * 2,
fill=BUTTON_COLOR_PLAY,
outline=BUTTON_COLOR_PLAY,
)
play_button_canvas.create_text(
PLAY_BUTTON_RADIUS,
PLAY_BUTTON_RADIUS,
text="Играть",
fill=BUTTON_TEXT_COLOR,
font=BIG_FONT,
)
play_button_canvas.tag_bind("all", "<Button-1>", lambda e: self.play_game())
def place_dog_images(self, center_x, center_y, radius, num_dogs):
"""Размещает изображения собак по кругу."""
angle_step = 2 * math.pi / num_dogs # Шаг угла для размещения собак
for i in range(num_dogs):
angle = i * angle_step
x = center_x + radius * math.cos(angle)
y = center_y + radius * math.sin(angle)
# Загрузка изображения собаки
image_path = DOG_IMAGES[i]
try:
dog_image = Image.open(image_path)
dog_image = dog_image.resize((100, 100), Image.Resampling.LANCZOS)
dog_photo = ImageTk.PhotoImage(dog_image)
# Создание метки с изображением
dog_label = tk.Label(self.root, image=dog_photo, bg=BACKGROUND_COLOR)
dog_label.image = dog_photo # Сохраняем ссылку на изображение
dog_label.place(x=x - 50, y=y - 50) # Центрируем метку относительно позиции
except Exception as e:
print(f"Ошибка загрузки изображения {image_path}: {e}")
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)
def clear_frame(self):
"""Очистить текущий экран."""
for widget in self.root.winfo_children():
widget.destroy()
def play_game(self):
"""Заглушка для игры."""
"""Переход к игровому интерфейсу."""
clear_frame(self.root) # Очищаем главное меню
GameUI(self.root, self.user_id) # Открываем игровой интерфейс
print("Запуск игры...")
def exit_app(self):
@ -80,14 +169,9 @@ class UserApp:
print("Приложение закрыто")
self.root.quit()
def show_profile(self):
"""Заглушка для профиля."""
print("Переход в профиль...")
# Запуск главного окна
if __name__ == "__main__":
root = tk.Tk()
app = UserApp(root, user_id=123) # Передаем user_id (это может быть получено после авторизации)
root.mainloop()
def show_shop(self):
"""Заглушка для магазина."""
print("Переход в магазин...")
def show_knowledge(self):
"""Заглушка для базы знаний."""
print("Переход в базу знаний...")

View file

@ -0,0 +1,23 @@
import tkinter as tk
from src.utils import clear_frame
from database.db_events import get_user_progress
def profile_ui(root, user_id):
"""Интерфейс профиля пользователя."""
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)
# Статистика пользователя
progress = get_user_progress(user_id)
levels_completed = len(progress)
bones_collected = sum([session.score for session in progress])
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)

View file

@ -0,0 +1,24 @@
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

@ -0,0 +1,57 @@
import time
import tkinter as tk
from src.utils import clear_frame
from database.db_events import save_progress
def start_game(root, user_id, dog_id):
"""Игровой процесс."""
clear_frame(root)
# Обратный отсчет
for i in range(3, 0, -1):
clear_frame(root)
tk.Label(root, text=f"{i}...", font=("Comic Sans MS", 30)).pack(expand=True)
root.update()
time.sleep(1)
# Начало уровня
# Здесь подключается логика работы с картой и вопросами
pass
def handle_obstacle(obstacle, current_score, root):
"""
Обработка препятствия (вопроса) с использованием окна.
Возвращает новый счёт игрока.
"""
result = {"new_score": current_score}
def submit_answer():
user_answer = answer_var.get().strip().lower()
if user_answer == "правильно": # Условие для правильного ответа
result["new_score"] += 1
else:
result["new_score"] -= 1
question_window.destroy() # Закрываем окно вопроса
# Создаём новое окно для вопроса
question_window = tk.Toplevel(root)
question_window.title("Вопрос")
question_window.geometry("400x200")
# Отображение текста вопроса
tk.Label(question_window, text=f"Вопрос сложности {obstacle['difficulty']}:", font=("Arial", 14)).pack(pady=10)
# Поле ввода ответа
answer_var = tk.StringVar()
tk.Entry(question_window, textvariable=answer_var, font=("Arial", 12)).pack(pady=10)
# Кнопка подтверждения ответа
tk.Button(question_window, text="Ответить", command=submit_answer).pack(pady=10)
# Ожидаем закрытия окна
question_window.grab_set() # Блокируем основное окно
root.wait_window(question_window) # Ждём завершения окна вопроса
return result

View file

@ -0,0 +1,13 @@
import random
def generate_map(level):
"""Генерация карты уровня."""
num_obstacles = random.randint(3, 6)
map_data = []
for i in range(num_obstacles):
map_data.append({
"type": "question",
"difficulty": level,
"position": random.randint(1, 100)
})
return map_data

View file

@ -1,4 +1,6 @@
import tkinter as tk
from tkinter import messagebox
def clear_frame(frame):
"""Удаление всех виджетов из фрейма."""
@ -16,16 +18,10 @@ def feature_in_development(frame):
font=("Comic Sans MS", 16)
).pack(expand=True)
def create_tooltip(widget, text):
"""Создание подсказки для виджета."""
tooltip = tk.Toplevel()
tooltip.wm_overrideredirect(True) # Отключаем рамки окна
tooltip.wm_geometry(f"+{widget.winfo_rootx() + 20}+{widget.winfo_rooty() + 20}")
label = tk.Label(tooltip, text=text, bg="#333", fg="#fff", font=("Comic Sans MS", 10), padx=5, pady=5)
label.pack()
def hide_tooltip(event):
tooltip.destroy()
widget.bind("<Enter>", lambda event: tooltip.deiconify())
widget.bind("<Leave>", hide_tooltip)
def show_message(message):
"""Показать сообщение пользователю"""
message_window = tk.Toplevel()
message_label = tk.Label(message_window, text=message, font=("Comic Sans MS", 16))
message_label.pack(pady=20)
ok_button = tk.Button(message_window, text="OK", command=message_window.destroy)
ok_button.pack(pady=10)