import sys
import mysql.connector
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QVBoxLayout,
QHBoxLayout,
QLabel,
QLineEdit,
QComboBox,
QPushButton,
QTableWidget,
QTableWidgetItem,
QSpinBox,
QCheckBox,
QRadioButton,
QScrollArea,
QGridLayout,
QFrame,
QMessageBox,
QStackedWidget,
QDialog,
QGroupBox,
QButtonGroup,
)
from PySide6.QtCore import Qt, Signal
DB_CONFIG = {
"host": "localhost",
"user": "root",
"password": "Lbhtrnjh_4231",
"database": "cafeteria_db",
"port": 3305,
}
STYLE = """
QMainWindow, QWidget, QDialog { background: #FFF; color: #333; }
QLabel { color: #333; }
QPushButton { background: #FF5722; color: white; border: none; border-radius: 5px; padding: 10px; font-weight: bold; }
QPushButton:hover { background: #E64A19; }
QLineEdit, QComboBox, QSpinBox { border: 1px solid #E0E0E0; border-radius: 5px; padding: 8px; background: #FFF; color: #333; }
QComboBox QAbstractItemView { background: #FFF; color: #333; }
QTableWidget { background: #FFF; color: #333; gridline-color: #E0E0E0; }
QHeaderView::section { background: #F5F5F5; color: #333; padding: 8px; border: 1px solid #E0E0E0; }
QCheckBox, QRadioButton { color: #333; }
QScrollArea { background: #FAFAFA; }
QGroupBox { color: #333; }
"""
class DB:
def __init__(self):
try:
self.conn = mysql.connector.connect(**DB_CONFIG)
except:
self.conn = None
def query(self, sql, params=()):
if not self.conn:
return []
cur = self.conn.cursor(dictionary=True)
cur.execute(sql, params)
result = cur.fetchall()
cur.close()
return result
def execute(self, sql, params=()):
if not self.conn:
return None
cur = self.conn.cursor()
cur.execute(sql, params)
self.conn.commit()
lid = cur.lastrowid
cur.close()
return lid
class ProductCard(QFrame):
add_clicked = Signal(dict)
card_clicked = Signal(dict)
def __init__(self, product):
super().__init__()
self.product = product
self.setFixedSize(250, 280)
self.setStyleSheet(
"QFrame { background: #FFF; border: 1px solid #E0E0E0; border-radius: 8px; }"
)
self.setCursor(Qt.PointingHandCursor)
layout = QVBoxLayout(self)
img = QLabel("Изображение")
img.setAlignment(Qt.AlignCenter)
img.setFixedHeight(100)
img.setStyleSheet("background: #F5F5F5; border: 1px dashed #CCC; color: #999;")
layout.addWidget(img)
layout.addWidget(QLabel(f"{product['product_name']}"))
desc = (
product.get("product_description", "")[:40] + "..."
if len(product.get("product_description", "")) > 40
else product.get("product_description", "")
)
layout.addWidget(QLabel(f"{desc}"))
layout.addStretch()
bottom = QHBoxLayout()
bottom.addWidget(
QLabel(f"{product['product_price']:.2f} руб.")
)
bottom.addWidget(
QLabel(
f"{product.get('product_weight', '')}"
)
)
bottom.addStretch()
btn = QPushButton("Добавить")
btn.setFixedSize(90, 30)
btn.setStyleSheet(
"background: #FF5722; color: white; border: none; border-radius: 5px; font-weight: bold;"
)
btn.clicked.connect(lambda: self.add_clicked.emit(self.product))
bottom.addWidget(btn)
layout.addLayout(bottom)
def mousePressEvent(self, e):
if e.button() == Qt.LeftButton:
self.card_clicked.emit(self.product)
class CatalogPage(QWidget):
product_added = Signal(dict)
go_to_cart = Signal()
product_clicked = Signal(dict)
def __init__(self, db):
super().__init__()
self.db = db
self.sort_order = "ASC"
layout = QVBoxLayout(self)
header = QHBoxLayout()
header.addWidget(QLabel("
Каталог продуктов
"))
header.addStretch()
cart_btn = QPushButton("🛒 Корзина")
cart_btn.clicked.connect(self.go_to_cart.emit)
header.addWidget(cart_btn)
layout.addLayout(header)
filters = QHBoxLayout()
self.search = QLineEdit()
self.search.setPlaceholderText("🔍 Поиск...")
self.search.textChanged.connect(self.load)
filters.addWidget(self.search)
self.category = QComboBox()
self.category.addItem("Любая категория", None)
for c in db.query("SELECT * FROM categories"):
self.category.addItem(c["category_name"], c["category_id"])
self.category.currentIndexChanged.connect(self.load)
filters.addWidget(self.category)
asc_btn = QPushButton("Цена ↑")
asc_btn.clicked.connect(lambda: self.set_sort("ASC"))
filters.addWidget(asc_btn)
desc_btn = QPushButton("Цена ↓")
desc_btn.clicked.connect(lambda: self.set_sort("DESC"))
filters.addWidget(desc_btn)
layout.addLayout(filters)
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setStyleSheet("border: none; background: #FAFAFA;")
self.grid_widget = QWidget()
self.grid = QGridLayout(self.grid_widget)
self.grid.setAlignment(Qt.AlignTop | Qt.AlignLeft)
scroll.setWidget(self.grid_widget)
layout.addWidget(scroll)
self.load()
def set_sort(self, order):
self.sort_order = order
self.load()
def load(self):
while self.grid.count():
w = self.grid.takeAt(0).widget()
if w:
w.deleteLater()
sql = "SELECT p.*, c.category_name FROM products p LEFT JOIN categories c ON p.category_id=c.category_id WHERE p.is_available=TRUE"
params = []
if self.search.text():
sql += " AND (p.product_name LIKE %s OR p.product_description LIKE %s)"
params += [f"%{self.search.text()}%"] * 2
if self.category.currentData():
sql += " AND p.category_id=%s"
params.append(self.category.currentData())
sql += f" ORDER BY p.product_price {self.sort_order}"
products = self.db.query(sql, params)
for i, p in enumerate(products):
card = ProductCard(p)
card.add_clicked.connect(self.product_added.emit)
card.card_clicked.connect(self.product_clicked.emit)
self.grid.addWidget(card, i // 4, i % 4)
class CartPage(QWidget):
go_back = Signal()
order_done = Signal()
def __init__(self, db):
super().__init__()
self.db = db
self.items = []
main = QHBoxLayout(self)
left = QVBoxLayout()
back = QPushButton("← Назад")
back.clicked.connect(self.go_back.emit)
left.addWidget(back)
left.addWidget(QLabel("Корзина заказа
"))
self.table = QTableWidget()
self.table.setColumnCount(5)
self.table.setHorizontalHeaderLabels(
["Наименование", "Цена", "Кол-во", "Стоимость", ""]
)
left.addWidget(self.table)
main.addLayout(left, 2)
right = QVBoxLayout()
right.addWidget(QLabel("Заказ клиента
"))
self.client = QComboBox()
for c in db.query("SELECT * FROM clients"):
self.client.addItem(c["client_name"], c["client_id"])
right.addWidget(self.client)
self.no_client = QCheckBox("Без клиента")
self.no_client.stateChanged.connect(lambda s: self.client.setEnabled(not s))
right.addWidget(self.no_client)
self.total_lbl = QLabel("Итого: 0.00 руб.")
right.addWidget(self.total_lbl)
right.addWidget(QLabel("Тип оплаты:"))
self.payment = QComboBox()
self.payment.addItems(["Наличные", "Карта", "Онлайн"])
right.addWidget(self.payment)
self.r_here = QRadioButton("В заведении")
self.r_here.setChecked(True)
self.r_out = QRadioButton("На вынос")
self.r_cour = QRadioButton("Курьером")
right.addWidget(self.r_here)
right.addWidget(self.r_out)
right.addWidget(self.r_cour)
right.addStretch()
submit = QPushButton("Оформить заказ")
submit.setMinimumHeight(50)
submit.clicked.connect(self.submit)
right.addWidget(submit)
main.addLayout(right, 1)
def add(self, product):
for item in self.items:
if item["product"]["product_id"] == product["product_id"]:
item["qty"] += 1
self.refresh()
return
self.items.append({"product": product, "qty": 1})
self.refresh()
def refresh(self):
self.table.setRowCount(len(self.items))
total = 0
for i, item in enumerate(self.items):
p, q = item["product"], item["qty"]
price = float(p["product_price"])
sub = price * q
total += sub
self.table.setItem(i, 0, QTableWidgetItem(p["product_name"]))
self.table.setItem(i, 1, QTableWidgetItem(f"{price:.2f}"))
spin = QSpinBox()
spin.setRange(1, 99)
spin.setValue(q)
spin.valueChanged.connect(lambda v, row=i: self.set_qty(row, v))
self.table.setCellWidget(i, 2, spin)
self.table.setItem(i, 3, QTableWidgetItem(f"{sub:.2f}"))
del_btn = QPushButton("Исключить")
del_btn.clicked.connect(lambda _, row=i: self.remove(row))
self.table.setCellWidget(i, 4, del_btn)
self.total_lbl.setText(f"Итого: {total:.2f} руб.")
def set_qty(self, row, val):
if row < len(self.items):
self.items[row]["qty"] = val
self.refresh()
def remove(self, row):
if row < len(self.items):
self.items.pop(row)
self.refresh()
def submit(self):
if not self.items:
QMessageBox.warning(self, "Ошибка", "Корзина пуста!")
return
if (
QMessageBox.question(self, "Подтверждение", "Оформить заказ?")
!= QMessageBox.Yes
):
return
client_id = None if self.no_client.isChecked() else self.client.currentData()
delivery = (
"В заведении"
if self.r_here.isChecked()
else ("На вынос" if self.r_out.isChecked() else "Курьером")
)
total = sum(float(i["product"]["product_price"]) * i["qty"] for i in self.items)
order_id = self.db.execute(
"INSERT INTO orders (client_id, payment_type, delivery_type, total_amount) VALUES (%s,%s,%s,%s)",
(client_id, self.payment.currentText(), delivery, total),
)
for item in self.items:
self.db.execute(
"INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (%s,%s,%s,%s)",
(
order_id,
item["product"]["product_id"],
item["qty"],
float(item["product"]["product_price"]),
),
)
QMessageBox.information(self, "Успех", f"Заказ #{order_id} оформлен!")
self.items = []
self.refresh()
self.order_done.emit()
class ProductDialog(QDialog):
_instance = None
@classmethod
def show_product(cls, product, parent):
if cls._instance:
cls._instance.close()
cls._instance = cls(product, parent)
cls._instance.show()
def __init__(self, product, parent):
super().__init__(parent)
self.setWindowTitle("Информация о продукте")
self.setFixedSize(400, 350)
layout = QVBoxLayout(self)
layout.addWidget(QLabel(f"{product['product_name']}
"))
layout.addWidget(QLabel(f"Категория: {product.get('category_name', '-')}"))
layout.addWidget(QLabel(f"Описание: {product.get('product_description', '-')}"))
layout.addWidget(
QLabel(
f"Цена: {product['product_price']:.2f} руб."
)
)
layout.addWidget(QLabel(f"Вес: {product.get('product_weight', '-')}"))
layout.addStretch()
close_btn = QPushButton("Закрыть")
close_btn.clicked.connect(self.close)
layout.addWidget(close_btn)
def closeEvent(self, e):
ProductDialog._instance = None
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Кафетерий - Оператор-кассир")
self.setGeometry(100, 100, 1200, 700)
self.setStyleSheet(STYLE)
self.db = DB()
self.stack = QStackedWidget()
self.setCentralWidget(self.stack)
self.catalog = CatalogPage(self.db)
self.catalog.product_added.connect(self.on_add)
self.catalog.go_to_cart.connect(lambda: self.stack.setCurrentIndex(1))
self.catalog.product_clicked.connect(
lambda p: ProductDialog.show_product(p, self)
)
self.stack.addWidget(self.catalog)
self.cart = CartPage(self.db)
self.cart.go_back.connect(lambda: self.stack.setCurrentIndex(0))
self.cart.order_done.connect(lambda: self.stack.setCurrentIndex(0))
self.stack.addWidget(self.cart)
def on_add(self, product):
self.cart.add(product)
QMessageBox.information(
self, "Добавлено", f"«{product['product_name']}» добавлен!"
)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())