Загрузка данных
```python
import tkinter as tk
from tkinter import ttk, messagebox
import tkinter.font as tkfont
# Цвета по заданию
COLOR_BIG_DISCOUNT = "#FFDEAD"
COLOR_OUT_OF_STOCK = "#ADD8E6"
COLOR_NORMAL = "#FFFFFF"
# Тестовые пользователи
USERS = [
{
"login": "guest",
"password": "123",
"full_name": "Гость",
"role": "Гость"
},
{
"login": "client",
"password": "123",
"full_name": "Иванов Иван Иванович",
"role": "Авторизованный клиент"
},
{
"login": "manager",
"password": "123",
"full_name": "Петров Петр Петрович",
"role": "Менеджер"
},
{
"login": "admin",
"password": "123",
"full_name": "Администратор",
"role": "Администратор"
}
]
# Демонстрационные товары
PRODUCTS = [
{
"category": "Бытовая техника",
"name": "Холодильник",
"description": "Двухкамерный холодильник",
"manufacturer": "Samsung",
"supplier": "ООО Техно",
"price": 45000,
"discount": 20,
"unit": "шт.",
"quantity": 5,
"image": None
},
{
"category": "Компьютеры",
"name": "Клавиатура",
"description": "Механическая игровая клавиатура",
"manufacturer": "Logitech",
"supplier": "ООО Компьютер",
"price": 5000,
"discount": 10,
"unit": "шт.",
"quantity": 12,
"image": None
},
{
"category": "Телефоны",
"name": "Смартфон",
"description": "Смартфон с большим экраном",
"manufacturer": "Xiaomi",
"supplier": "ООО Мобильный",
"price": 25000,
"discount": 25,
"unit": "шт.",
"quantity": 0,
"image": None
},
{
"category": "Аксессуары",
"name": "Наушники",
"description": "Беспроводные наушники",
"manufacturer": "Sony",
"supplier": "ООО Аудио",
"price": 8000,
"discount": 0,
"unit": "шт.",
"quantity": 7,
"image": None
}
]
def get_background(product):
if product["quantity"] == 0:
return COLOR_OUT_OF_STOCK
if product["discount"] > 17:
return COLOR_BIG_DISCOUNT
return COLOR_NORMAL
class LoginWindow(tk.Tk):
def __init__(self):
super().__init__()
self.title("Авторизация")
self.geometry("380x360")
self.resizable(False, False)
self.user = None
self.configure(bg="#F4F4F4")
frame = tk.Frame(
self,
bg="white",
padx=30,
pady=25,
highlightbackground="#CCCCCC",
highlightthickness=1
)
frame.pack(
padx=25,
pady=25,
fill="both",
expand=True
)
tk.Label(
frame,
text="Вход в систему",
font=("Arial", 18, "bold"),
bg="white"
).pack(pady=(0, 20))
tk.Label(
frame,
text="Логин",
font=("Arial", 10),
bg="white"
).pack(anchor="w")
self.login_entry = tk.Entry(
frame,
font=("Arial", 11),
relief="solid",
bd=1
)
self.login_entry.pack(fill="x", pady=(5, 12))
tk.Label(
frame,
text="Пароль",
font=("Arial", 10),
bg="white"
).pack(anchor="w")
self.password_entry = tk.Entry(
frame,
font=("Arial", 11),
show="*",
relief="solid",
bd=1
)
self.password_entry.pack(fill="x", pady=(5, 18))
tk.Button(
frame,
text="Войти",
font=("Arial", 10, "bold"),
bg="#DCDCDC",
command=self.login
).pack(fill="x", ipady=4)
tk.Button(
frame,
text="Войти как гость",
font=("Arial", 10),
bg="#EEEEEE",
command=self.login_as_guest
).pack(fill="x", pady=(8, 0), ipady=3)
self.bind("<Return>", lambda event: self.login())
self.login_entry.focus_set()
def login(self):
login = self.login_entry.get().strip()
password = self.password_entry.get()
for user in USERS:
if (
user["login"] == login
and user["password"] == password
):
self.user = user.copy()
self.destroy()
return
messagebox.showerror(
"Ошибка входа",
"Неверный логин или пароль",
parent=self
)
def login_as_guest(self):
self.user = {
"login": "",
"password": "",
"full_name": "Гость",
"role": "Гость"
}
self.destroy()
class ProductsWindow(tk.Tk):
def __init__(self, user):
super().__init__()
self.user = user
self.logged_out = False
self.product_images = []
self.title("Список товаров")
self.geometry("1050x650")
self.minsize(750, 450)
self.configure(bg="#F4F4F4")
self.create_header()
self.create_scroll_area()
self.show_products()
def create_header(self):
header = tk.Frame(
self,
bg="white",
padx=15,
pady=12
)
header.pack(fill="x")
tk.Label(
header,
text="Список товаров",
font=("Arial", 19, "bold"),
bg="white"
).pack(side="left")
tk.Label(
header,
text=f'{self.user["full_name"]} | '
f'{self.user["role"]}',
font=("Arial", 10),
bg="white"
).pack(side="right", padx=(10, 0))
tk.Button(
header,
text="Выход",
command=self.logout
).pack(side="right", padx=10)
def create_scroll_area(self):
container = tk.Frame(self, bg="#F4F4F4")
container.pack(fill="both", expand=True)
self.canvas = tk.Canvas(
container,
bg="#F4F4F4",
highlightthickness=0
)
scrollbar = ttk.Scrollbar(
container,
orient="vertical",
command=self.canvas.yview
)
self.list_frame = tk.Frame(
self.canvas,
bg="#F4F4F4"
)
self.window_id = self.canvas.create_window(
(0, 0),
window=self.list_frame,
anchor="nw"
)
self.list_frame.bind(
"<Configure>",
lambda event: self.canvas.configure(
scrollregion=self.canvas.bbox("all")
)
)
self.canvas.bind(
"<Configure>",
lambda event: self.canvas.itemconfigure(
self.window_id,
width=event.width
)
)
self.canvas.configure(
yscrollcommand=scrollbar.set
)
self.canvas.pack(
side="left",
fill="both",
expand=True
)
scrollbar.pack(
side="right",
fill="y"
)
self.canvas.bind_all(
"<MouseWheel>",
self.on_mousewheel
)
def on_mousewheel(self, event):
if self.winfo_exists():
self.canvas.yview_scroll(
int(-event.delta / 120),
"units"
)
def show_products(self):
for product in PRODUCTS:
self.create_product_card(product)
def create_product_card(self, product):
background = get_background(product)
# Внешняя строка товара
card = tk.Frame(
self.list_frame,
bg=background,
highlightbackground="#333333",
highlightthickness=1
)
card.pack(
fill="x",
padx=12,
pady=5
)
card.grid_columnconfigure(1, weight=1)
# Левая часть: изображение
photo_frame = tk.Frame(
card,
bg=background,
width=180,
height=155
)
photo_frame.grid(
row=0,
column=0,
sticky="nsew",
padx=5,
pady=5
)
photo_frame.grid_propagate(False)
image_path = product.get("image")
if image_path:
try:
photo = tk.PhotoImage(file=image_path)
self.product_images.append(photo)
image_label = tk.Label(
photo_frame,
image=photo,
bg=background
)
image_label.place(
relx=0.5,
rely=0.5,
anchor="center"
)
except (tk.TclError, OSError):
self.show_image_placeholder(photo_frame)
else:
self.show_image_placeholder(photo_frame)
# Центральная часть: описание товара
info = tk.Frame(
card,
bg=background,
padx=10,
pady=7
)
info.grid(
row=0,
column=1,
sticky="nsew"
)
tk.Label(
info,
text=f'{product["category"]} | '
f'{product["name"]}',
font=("Arial", 12, "bold"),
bg=background,
anchor="w",
justify="left",
wraplength=550
).pack(fill="x", pady=(0, 5))
details = [
f'Описание товара: {product["description"]}',
f'Производитель: {product["manufacturer"]}',
f'Поставщик: {product["supplier"]}',
f'Единица измерения: {product["unit"]}',
f'Количество на складе: {product["quantity"]}'
]
for text in details:
tk.Label(
info,
text=text,
font=("Arial", 10),
bg=background,
anchor="w",
justify="left",
wraplength=550
).pack(fill="x")
# Цена с учётом скидки
price_frame = tk.Frame(
info,
bg=background
)
price_frame.pack(
fill="x",
anchor="w",
pady=(5, 0)
)
old_price = product["price"]
discount = product["discount"]
new_price = old_price * (100 - discount) / 100
if discount > 0:
old_font = tkfont.Font(
family="Arial",
size=10,
overstrike=True
)
tk.Label(
price_frame,
text=f'{old_price:.2f} руб.',
font=old_font,
fg="#777777",
bg=background
).pack(side="left", padx=(0, 10))
tk.Label(
price_frame,
text=f'{new_price:.2f} руб.',
font=("Arial", 10, "bold"),
fg="black",
bg=background
).pack(side="left")
else:
tk.Label(
price_frame,
text=f'Цена: {old_price:.2f} руб.',
font=("Arial", 10),
bg=background
).pack(side="left")
# Правая часть: скидка
discount_frame = tk.Frame(
card,
bg=background,
width=110
)
discount_frame.grid(
row=0,
column=2,
sticky="ns"
)
discount_frame.grid_propagate(False)
tk.Label(
discount_frame,
text="Действующая\nскидка",
font=("Arial", 9),
bg=background,
justify="center"
).pack(
expand=True,
pady=(15, 3)
)
tk.Label(
discount_frame,
text=f'{discount}%',
font=("Arial", 17, "bold"),
bg=background
).pack(
expand=True,
pady=(0, 15)
)
def show_image_placeholder(self, parent):
placeholder = tk.Frame(
parent,
bg="white",
highlightbackground="#AAAAAA",
highlightthickness=1
)
placeholder.place(
relx=0.5,
rely=0.5,
anchor="center",
width=85,
height=55
)
tk.Label(
placeholder,
text="Фото",
font=("Arial", 10),
bg="white"
).pack(expand=True)
def logout(self):
self.logged_out = True
self.destroy()
def main():
while True:
login_window = LoginWindow()
login_window.mainloop()
if login_window.user is None:
break
products_window = ProductsWindow(
login_window.user
)
products_window.mainloop()
if not products_window.logged_out:
break
if __name__ == "__main__":
main()
```