Загрузка данных
import tkinter as tk
from tkinter import ttk
from database import Database
class ProductsView:
def __init__(self, parent, user):
self.parent = parent
self.user = user
self.db = Database()
# Главный контейнер страницы (встраивается в главное окно)
self.window = tk.Frame(parent, bg="white")
self.window.pack(fill=tk.BOTH, expand=True)
tk.Label(self.window, text="Каталог товаров", font=("Arial", 14, "bold"), bg="white").pack(pady=10)
# --- Создание скроллируемой области для ленты карточек ---
container = tk.Frame(self.window, bg="white")
container.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
self.canvas = tk.Canvas(container, bg="white", highlightthickness=0)
scrollbar = ttk.Scrollbar(container, orient=tk.VERTICAL, command=self.canvas.yview)
# Фрейм внутри холста (Canvas), в котором вертикально будут упаковываться карточки
self.scrollable_frame = tk.Frame(self.canvas, bg="white")
self.scrollable_frame.bind(
"<Configure>",
lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))
)
self.canvas_frame_id = self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
# Автоматически растягиваем карточки по всей доступной ширине окна
self.canvas.bind('<Configure>', lambda event: self.canvas.itemconfigure(self.canvas_frame_id, width=event.width))
self.canvas.configure(yscrollcommand=scrollbar.set)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# Поддержка прокрутки колесиком мыши
self.canvas.bind_all("<MouseWheel>", lambda event: self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units"))
# Загружаем карточки товаров
self.load_products_cards()
def load_products_cards(self):
# Очищаем старые карточки при обновлении страницы
for widget in self.scrollable_frame.winfo_children():
widget.destroy()
products = self.db.get_all_products()
for p in products:
# Безопасное приведение типов данных для проверок
try: discount = float(p.discount.replace('%', '').strip())
except: discount = 0.0
try: qty = float(p.quantity.strip())
except: qty = 0.0
# Подсветка карточек по цветам из Практики 5 (мягкие пастельные тона для читаемости текста)
if qty == 0:
bg_color = "#E3F2FD" # Светло-голубой (нет на складе)
elif discount > 10:
bg_color = "#E8F5E9" # Светло-зеленый (высокая скидка)
else:
bg_color = "white" # Обычный белый цвет карточки
# Создаем контейнер для ОДНОЙ карточки товара
card_frame = tk.Frame(self.scrollable_frame, bg=bg_color, bd=1, relief=tk.SOLID)
card_frame.pack(fill=tk.X, padx=10, pady=5, ipady=5)
# ----------------------------------------------------
# БЛОК 1 (ЛЕВЫЙ): Картинка товара
# ----------------------------------------------------
img_container = tk.Frame(card_frame, width=120, height=120, bg="white", bd=1, relief=tk.SOLID)
img_container.pack(side=tk.LEFT, padx=15, pady=10)
img_container.pack_propagate(False)
try:
# Пытаемся взять реальное имя фото товара, если пусто — берем picture.png
img_file = p.photo if p.photo else "picture.png"
img = tk.PhotoImage(file=img_file).subsample(2, 2) # Оптимизируем размер под список
img_lbl = tk.Label(img_container, image=img, bg="white")
img_lbl.image = img
img_lbl.pack(fill=tk.BOTH, expand=True)
except Exception:
# Если файл картинки физически отсутствует в папке
tk.Label(img_container, text="Нет фото", font=("Arial", 9, "italic"), bg="white", fg="gray").pack(fill=tk.BOTH, expand=True)
# ----------------------------------------------------
# БЛОК 2 (ЦЕНТРАЛЬНЫЙ): Текстовое описание товара
# ----------------------------------------------------
info_frame = tk.Frame(card_frame, bg=bg_color)
info_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=15, pady=10)
# Название товара
tk.Label(info_frame, text=p.name, font=("Arial", 12, "bold"), bg=bg_color).pack(anchor=tk.W)
# Описание (wraplength автоматически переносит длинный текст, чтобы он не улетал за экран)
tk.Label(info_frame, text=f"Описание: {p.description}", font=("Arial", 9.5), bg=bg_color, fg="#333333", wraplength=500, justify=tk.LEFT).pack(anchor=tk.W, pady=3)
# Производитель и Категория
tk.Label(info_frame, text=f"Производитель: {p.manufacturer} | Категория: {p.category}", font=("Arial", 9), bg=bg_color, fg="gray").pack(anchor=tk.W)
# Логика стоимости с зачеркиванием старой цены при наличии скидки (Практика 4 и 5)
price_frame = tk.Frame(info_frame, bg=bg_color)
price_frame.pack(anchor=tk.W, pady=5)
tk.Label(price_frame, text="Цена: ", font=("Arial", 10), bg=bg_color).pack(side=tk.LEFT)
if discount > 0:
try:
old_price = float(p.price)
new_price = old_price * (1 - discount / 100)
# Старая зачеркнутая цена (fg="red", overstrike)
tk.Label(price_frame, text=f"{old_price} руб.", fg="red", font=("Arial", 10, "overstrike"), bg=bg_color).pack(side=tk.LEFT, padx=(0, 7))
# Новая цена крупным шрифтом
tk.Label(price_frame, text=f"{new_price:.2f} руб.", fg="black", font=("Arial", 10, "bold"), bg=bg_color).pack(side=tk.LEFT)
except Exception:
tk.Label(price_frame, text=f"{p.price} руб.", font=("Arial", 10, "bold"), bg=bg_color).pack(side=tk.LEFT)
else:
tk.Label(price_frame, text=f"{p.price} руб.", font=("Arial", 10, "bold"), bg=bg_color).pack(side=tk.LEFT)
# Количество на складе
tk.Label(info_frame, text=f"Наличие на складе: {int(qty)} {p.unit}", font=("Arial", 9, "italic"), bg=bg_color).pack(anchor=tk.W)
# ----------------------------------------------------
# БЛОК 3 (ПРАВЫЙ): Размер скидки товара
# ----------------------------------------------------
discount_frame = tk.Frame(card_frame, bg=bg_color)
discount_frame.pack(side=tk.RIGHT, padx=25, pady=10)
tk.Label(discount_frame, text="Скидка", font=("Arial", 9), bg=bg_color, fg="gray").pack()
# Если скидка большая (>10%), выделяем её жирным темно-зеленым цветом
discount_color = "darkgreen" if discount > 10 else "black"
tk.Label(discount_frame, text=f"{p.discount}%", font=("Arial", 16, "bold"), fg=discount_color, bg=bg_color).pack(pady=2)