commit 95c854f2150e2f34bf2c27fae6f744ad79483957 Author: Daniel Haus Date: Fri Jan 23 09:41:20 2026 +0300 Initial diff --git a/__pycache__/base.cpython-314.pyc b/__pycache__/base.cpython-314.pyc new file mode 100644 index 0000000..7327b91 Binary files /dev/null and b/__pycache__/base.cpython-314.pyc differ diff --git a/__pycache__/composer.cpython-314.pyc b/__pycache__/composer.cpython-314.pyc new file mode 100644 index 0000000..0b41039 Binary files /dev/null and b/__pycache__/composer.cpython-314.pyc differ diff --git a/__pycache__/data.cpython-314.pyc b/__pycache__/data.cpython-314.pyc new file mode 100644 index 0000000..708034f Binary files /dev/null and b/__pycache__/data.cpython-314.pyc differ diff --git a/__pycache__/windows.cpython-314.pyc b/__pycache__/windows.cpython-314.pyc new file mode 100644 index 0000000..856e384 Binary files /dev/null and b/__pycache__/windows.cpython-314.pyc differ diff --git a/base.py b/base.py new file mode 100644 index 0000000..31d957e --- /dev/null +++ b/base.py @@ -0,0 +1,15 @@ +from PyQt6.QtWidgets import QWidget + +class BaseWindow(QWidget): + def __init__(self, window_title: str, username: str, composer: object): + super().__init__() + + self._composer = composer + + title: str = "%s - %s" % (window_title, username) + + self.setWindowTitle(title) + self._setup_ui() + + def _setup_ui(self): + pass diff --git a/composer.py b/composer.py new file mode 100644 index 0000000..8fd81dc --- /dev/null +++ b/composer.py @@ -0,0 +1,39 @@ +import sys +from base import BaseWindow +from windows import CustomerWindow, CashierWindow, LoginWindow +from PyQt6.QtWidgets import QApplication +from PyQt6.QtCore import pyqtSignal, pyqtSlot, QObject + + +class Composer(QObject): + render_request = pyqtSignal(str) + + def __init__(self, + app: QApplication + ): + super().__init__() + + self._app = app + self._entry = LoginWindow("Окно Авторизации", "", self) + self._current = None + + self.render_request.connect(self._render) + + def run(self): + self._current = self._entry + self._current.show() + sys.exit(self._app.exec()) + + @pyqtSlot(str) + def _render(self, user: str): + if self._current: + self._current.close() + + match user: + case "Покупатель": + window = CustomerWindow("Покупатель", user, self) + case "Кассир": + window = CashierWindow("Кассир", user, self) + + self._current = window + self._current.show() diff --git a/data.py b/data.py new file mode 100644 index 0000000..a54aa0a --- /dev/null +++ b/data.py @@ -0,0 +1,18 @@ +USERS = { + 'petlover': {'password': '1111', 'role': 'Покупатель'}, + 'cash1': {'password': '2222', 'role': 'Кассир'}, +} + +PRODUCTS = [ + {'name': 'Корм кошек', 'animal': 'Кот', 'price': 500}, + {'name': 'Игрушка', 'animal': 'Собака', 'price': 300}, +] + +ORDERS = [ + {'id': '1', 'customer': 'Анна Петрова', + 'sum': '2500 р.', 'status': 'Новый'}, + {'id': '2', 'customer': 'Иван Иванов', + 'sum': '1800 р.', 'status': 'Оплачен'}, +] + +STATUS_OPTIONS = ['Новый', 'Оплачен', 'Отправлен', 'Отменён'] diff --git a/main.py b/main.py new file mode 100644 index 0000000..00341ab --- /dev/null +++ b/main.py @@ -0,0 +1,12 @@ +import sys +from composer import Composer +from PyQt6.QtWidgets import QApplication + +def main(): + app = QApplication(sys.argv) + + composer = Composer(app) + composer.run() + +if __name__ == "__main__": + main() diff --git a/windows.py b/windows.py new file mode 100644 index 0000000..bb4f120 --- /dev/null +++ b/windows.py @@ -0,0 +1,203 @@ +from PyQt6.QtWidgets import ( + QLabel, QLineEdit, QComboBox, QPushButton, + QMessageBox, QHBoxLayout, QVBoxLayout, QFormLayout, QTableWidget, + QTableWidgetItem, QSpinBox, QGroupBox, QFrame +) +from PyQt6.QtCore import Qt + +from base import BaseWindow +from data import USERS, PRODUCTS, ORDERS, STATUS_OPTIONS + +class LoginWindow(BaseWindow): + def _setup_ui(self): + layout = QVBoxLayout() + + form = QFormLayout() + self.login_edit = QLineEdit() + self.pass_edit = QLineEdit() + self.pass_edit.setEchoMode(QLineEdit.EchoMode.Password) + self.role_combo = QComboBox() + self.role_combo.addItems(['Покупатель', 'Кассир']) + + form.addRow('Логин:', self.login_edit) + form.addRow('Пароль:', self.pass_edit) + form.addRow('Роль:', self.role_combo) + + self.login_btn = QPushButton('Войти в систему') + self.login_btn.clicked.connect(self.try_login) + + layout.addLayout(form) + layout.addWidget(self.login_btn) + + self.setLayout(layout) + + def try_login(self): + username = self.login_edit.text().strip() + password = self.pass_edit.text() + role = self.role_combo.currentText() + + if username not in USERS: + QMessageBox.critical(self, 'Ошибка', 'Пользователь не найден') + return + user = USERS[username] + if password != user['password']: + QMessageBox.critical(self, 'Ошибка', 'Неправильный пароль') + return + if role != user['role']: + QMessageBox.critical(self, 'Ошибка', + 'Выбранная роль не совпадает с ролью пользователя') + return + + if role == 'Покупатель': + self._composer.render_request.emit("Покупатель") + else: + self._composer.render_request.emit("Кассир") + +class CustomerWindow(BaseWindow): + def _setup_ui(self): + main_layout = QVBoxLayout() + + top_layout = QHBoxLayout() + + left_box = QVBoxLayout() + self.table = QTableWidget(len(PRODUCTS), 3) + self.table.setHorizontalHeaderLabels([ + 'Товар', 'Тип животного', 'Цена' + ]) + self.table.setSelectionBehavior( + self.table.SelectionBehavior.SelectRows + ) + self.table.setEditTriggers(self.table.EditTrigger.NoEditTriggers) + for row, p in enumerate(PRODUCTS): + self.table.setItem(row, 0, QTableWidgetItem(p['name'])) + self.table.setItem(row, 1, QTableWidgetItem(p['animal'])) + self.table.setItem(row, 2, QTableWidgetItem(f"{p['price']} р.")) + left_box.addWidget(self.table) + + self.select_btn = QPushButton('Выбрать товар') + self.select_btn.clicked.connect(self.select_product) + left_box.addWidget(self.select_btn) + + top_layout.addLayout(left_box, 2) + + # Правая колонка: расчёт стоимости + right_group = QGroupBox('Расчёт стоимости') + right_layout = QFormLayout() + + self.price_edit = QLineEdit() + self.price_edit.setReadOnly(True) + self.quantity_spin = QSpinBox() + self.quantity_spin.setRange(1, 1000) + self.quantity_spin.setValue(1) + + self.calc_btn = QPushButton('Рассчитать стоимость') + self.calc_btn.setStyleSheet('background-color: #4CAF50; color: white;') + self.calc_btn.clicked.connect(self.calculate_sum) + + self.sum_label = QLabel('0 р.') + self.sum_label.setFrameShape(QFrame.Shape.Box) + self.sum_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + + self.add_btn = QPushButton('Добавить в заказ') + self.add_btn.setStyleSheet('background-color: #4CAF50; color: white;') + self.add_btn.clicked.connect(self.add_to_order) + + right_layout.addRow('Цена за единицу:', self.price_edit) + right_layout.addRow('Количество:', self.quantity_spin) + right_layout.addRow(self.calc_btn) + right_layout.addRow('Сумма:', self.sum_label) + right_layout.addRow(self.add_btn) + + right_group.setLayout(right_layout) + top_layout.addWidget(right_group, 1) + + main_layout.addLayout(top_layout) + + # Внизу: закрыть + self.close_btn = QPushButton('Закрыть') + self.close_btn.clicked.connect(self.close) + main_layout.addWidget(self.close_btn, + alignment=Qt.AlignmentFlag.AlignRight) + + self.setLayout(main_layout) + + # Внутренний список заказа (простой) + self.current_order = [] + + def select_product(self): + selected = self.table.currentRow() + if selected < 0: + QMessageBox.warning(self, 'Ошибка', 'Не выбран товар') + return + price_item = self.table.item(selected, 2).text() + # Из строки "500 р." выделим число + price_number = ''.join(ch for ch in price_item if ch.isdigit()) + self.price_edit.setText(price_number) + + def calculate_sum(self): + price_text = self.price_edit.text().strip() + if not price_text.isdigit(): + QMessageBox.warning(self, 'Ошибка', 'Сначала выберите товар') + return + price = int(price_text) + qty = self.quantity_spin.value() + total = price * qty + self.sum_label.setText(f"{total} р.") + + def add_to_order(self): + sum_text = self.sum_label.text() + if sum_text.startswith('0') or sum_text == '0 р.': + QMessageBox.warning(self, 'Ошибка', + 'Нужно рассчитать сумму перед добавлением') + return + + # Для простоты — добавляем запись в список и показываем сообщение + self.current_order.append({'sum': sum_text}) + QMessageBox.information(self, 'Добавлено', + f'Добавлено в заказ: {sum_text}') + +class CashierWindow(BaseWindow): + def _setup_ui(self): + main_layout = QVBoxLayout() + + self.orders_table = QTableWidget(len(ORDERS), 4) + self.orders_table.setHorizontalHeaderLabels([ + '№ заказа', 'Покупатель', 'Сумма', 'Статус' + ]) + self.orders_table.setSelectionBehavior( + self.orders_table.SelectionBehavior.SelectRows + ) + self.orders_table.setEditTriggers( + self.orders_table.EditTrigger.NoEditTriggers + ) + for row, o in enumerate(ORDERS): + self.orders_table.setItem(row, 0, QTableWidgetItem(o['id'])) + self.orders_table.setItem(row, 1, QTableWidgetItem(o['customer'])) + self.orders_table.setItem(row, 2, QTableWidgetItem(o['sum'])) + self.orders_table.setItem(row, 3, QTableWidgetItem(o['status'])) + + main_layout.addWidget(self.orders_table) + + ctrl_layout = QHBoxLayout() + self.status_combo = QComboBox() + self.status_combo.addItems(STATUS_OPTIONS) + self.change_btn = QPushButton('Изменить статус') + self.change_btn.clicked.connect(self.change_status) + ctrl_layout.addWidget(QLabel('Новый статус:')) + ctrl_layout.addWidget(self.status_combo) + ctrl_layout.addWidget(self.change_btn) + + main_layout.addLayout(ctrl_layout) + + self.setLayout(main_layout) + + self.setFixedSize(450, 200) + + def change_status(self): + selected = self.orders_table.currentRow() + if selected < 0: + QMessageBox.critical(self, 'Ошибка', 'Не выбрана строка') + return + new_status = self.status_combo.currentText() + self.orders_table.setItem(selected, 3, QTableWidgetItem(new_status)) + QMessageBox.information(self, 'Готово', 'Статус обновлён')