From 7fd78c0de2992f11dceb0e98305b16b2d904edc8 Mon Sep 17 00:00:00 2001 From: "mamonov.ep" Date: Thu, 11 Dec 2025 14:26:39 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20=D0=BB?= =?UTF-8?q?=D0=B0=D0=B1=D1=8B=20+=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D1=83=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controlWorkVisualProg/main.py | 610 ++++++++++++++++ controlWorkVisualProg/pharmacy.db | Bin 0 -> 20480 bytes controlWorkVisualProg/requirements.txt | 2 + secondLabVisualProg/main.py | 430 +++++++++++ thirdLabVisualProg/faculty.db | Bin 0 -> 28672 bytes thirdLabVisualProg/main.py | 968 +++++++++++++++++++++++++ thirdLabVisualProg/requirements.txt | 2 + 7 files changed, 2012 insertions(+) create mode 100644 controlWorkVisualProg/main.py create mode 100644 controlWorkVisualProg/pharmacy.db create mode 100644 controlWorkVisualProg/requirements.txt create mode 100644 secondLabVisualProg/main.py create mode 100644 thirdLabVisualProg/faculty.db create mode 100644 thirdLabVisualProg/main.py create mode 100644 thirdLabVisualProg/requirements.txt diff --git a/controlWorkVisualProg/main.py b/controlWorkVisualProg/main.py new file mode 100644 index 0000000..137c98a --- /dev/null +++ b/controlWorkVisualProg/main.py @@ -0,0 +1,610 @@ +import sys +import sqlite3 +from PyQt5.QtWidgets import ( + QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QGroupBox, QTableWidget, QTableWidgetItem, QPushButton, QComboBox, + QLabel, QLineEdit, QMessageBox, QTabWidget, QFormLayout, + QHeaderView, QAbstractItemView, QStatusBar, QDoubleSpinBox, QSpinBox +) +from PyQt5.QtCore import Qt + + +class Database: + """Класс для работы с базой данных SQLite - Аптека""" + + def __init__(self, db_name="pharmacy.db"): + self.db_name = db_name + self.conn = None + self.cursor = None + self.connect() + self.create_tables() + self.populate_sample_data() + + def connect(self): + """Подключение к базе данных""" + self.conn = sqlite3.connect(self.db_name) + self.cursor = self.conn.cursor() + + def create_tables(self): + """Создание таблиц базы данных""" + # Главная таблица - Типы медикаментов + self.cursor.execute(''' + CREATE TABLE IF NOT EXISTS medication_types ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + description TEXT, + storage_conditions TEXT, + prescription_required INTEGER DEFAULT 0 + ) + ''') + + # Подчинённая таблица - Медикаменты + self.cursor.execute(''' + CREATE TABLE IF NOT EXISTS medications ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + type_id INTEGER NOT NULL, + manufacturer TEXT, + price REAL, + quantity_received INTEGER DEFAULT 0, + quantity_sold INTEGER DEFAULT 0, + FOREIGN KEY (type_id) REFERENCES medication_types(id) ON DELETE CASCADE + ) + ''') + + self.conn.commit() + + def populate_sample_data(self): + """Заполнение тестовыми данными""" + self.cursor.execute("SELECT COUNT(*) FROM medication_types") + if self.cursor.fetchone()[0] > 0: + return + + # Типы медикаментов (главная таблица) + types = [ + ("Антибиотики", "Препараты для борьбы с бактериальными инфекциями", "Хранить при температуре 2-8°C", 1), + ("Болеутоляющие", "Препараты для снятия боли", "Хранить в сухом месте при комнатной температуре", 0), + ("Жаропонижающие", "Препараты для снижения температуры", "Хранить при температуре до 25°C", 0), + ("Витамины", "Витаминные комплексы и добавки", "Хранить в сухом тёмном месте", 0), + ("Противовирусные", "Препараты для борьбы с вирусами", "Хранить при температуре 15-25°C", 1), + ] + self.cursor.executemany( + "INSERT INTO medication_types (name, description, storage_conditions, prescription_required) VALUES (?, ?, ?, ?)", + types + ) + + # Медикаменты (подчинённая таблица) + medications = [ + ("Амоксициллин", 1, "Фармстандарт", 150.50, 100, 45), + ("Азитромицин", 1, "КРКА", 280.00, 80, 30), + ("Цефтриаксон", 1, "Синтез", 45.00, 200, 120), + ("Ибупрофен", 2, "Борисовский завод", 85.00, 150, 90), + ("Кеторол", 2, "Dr. Reddy's", 120.00, 100, 65), + ("Анальгин", 2, "Фармстандарт", 35.00, 300, 180), + ("Парацетамол", 3, "Медисорб", 25.00, 500, 350), + ("Нурофен", 3, "Reckitt Benckiser", 180.00, 120, 75), + ("Аспирин", 3, "Bayer", 95.00, 200, 140), + ("Витамин C", 4, "Эвалар", 250.00, 100, 40), + ("Компливит", 4, "Фармстандарт", 320.00, 80, 35), + ("Арбидол", 5, "Фармстандарт", 450.00, 60, 25), + ("Ингавирин", 5, "Валента", 550.00, 50, 20), + ] + self.cursor.executemany( + "INSERT INTO medications (name, type_id, manufacturer, price, quantity_received, quantity_sold) VALUES (?, ?, ?, ?, ?, ?)", + medications + ) + + self.conn.commit() + + # === CRUD для типов медикаментов (главная таблица) === + def get_medication_types(self): + self.cursor.execute("SELECT * FROM medication_types ORDER BY name") + return self.cursor.fetchall() + + def add_medication_type(self, name, description, storage_conditions, prescription_required): + self.cursor.execute( + "INSERT INTO medication_types (name, description, storage_conditions, prescription_required) VALUES (?, ?, ?, ?)", + (name, description, storage_conditions, prescription_required) + ) + self.conn.commit() + return self.cursor.lastrowid + + def update_medication_type(self, id, name, description, storage_conditions, prescription_required): + self.cursor.execute( + "UPDATE medication_types SET name=?, description=?, storage_conditions=?, prescription_required=? WHERE id=?", + (name, description, storage_conditions, prescription_required, id) + ) + self.conn.commit() + + def delete_medication_type(self, id): + self.cursor.execute("DELETE FROM medication_types WHERE id=?", (id,)) + self.conn.commit() + + # === CRUD для медикаментов (подчинённая таблица) === + def get_medications(self, type_id=None, search_query=None): + """Получить медикаменты с вычисляемым полем (остаток на складе)""" + if search_query: + query = """ + SELECT m.id, m.name, m.type_id, m.manufacturer, m.price, + m.quantity_received, m.quantity_sold, + (m.quantity_received - m.quantity_sold) as quantity_remaining, + t.name as type_name + FROM medications m + JOIN medication_types t ON m.type_id = t.id + WHERE m.name LIKE ? OR m.manufacturer LIKE ? + ORDER BY m.name + """ + search = f"%{search_query}%" + self.cursor.execute(query, (search, search)) + elif type_id: + self.cursor.execute(""" + SELECT m.id, m.name, m.type_id, m.manufacturer, m.price, + m.quantity_received, m.quantity_sold, + (m.quantity_received - m.quantity_sold) as quantity_remaining, + t.name as type_name + FROM medications m + JOIN medication_types t ON m.type_id = t.id + WHERE m.type_id = ? + ORDER BY m.name + """, (type_id,)) + else: + self.cursor.execute(""" + SELECT m.id, m.name, m.type_id, m.manufacturer, m.price, + m.quantity_received, m.quantity_sold, + (m.quantity_received - m.quantity_sold) as quantity_remaining, + t.name as type_name + FROM medications m + JOIN medication_types t ON m.type_id = t.id + ORDER BY m.name + """) + return self.cursor.fetchall() + + def add_medication(self, name, type_id, manufacturer, price, quantity_received, quantity_sold): + self.cursor.execute( + "INSERT INTO medications (name, type_id, manufacturer, price, quantity_received, quantity_sold) VALUES (?, ?, ?, ?, ?, ?)", + (name, type_id, manufacturer, price, quantity_received, quantity_sold) + ) + self.conn.commit() + return self.cursor.lastrowid + + def update_medication(self, id, name, type_id, manufacturer, price, quantity_received, quantity_sold): + self.cursor.execute( + "UPDATE medications SET name=?, type_id=?, manufacturer=?, price=?, quantity_received=?, quantity_sold=? WHERE id=?", + (name, type_id, manufacturer, price, quantity_received, quantity_sold, id) + ) + self.conn.commit() + + def delete_medication(self, id): + self.cursor.execute("DELETE FROM medications WHERE id=?", (id,)) + self.conn.commit() + + def close(self): + if self.conn: + self.conn.close() + + +class MainWindow(QMainWindow): + """Главное окно приложения Аптека""" + + def __init__(self): + super().__init__() + self.setWindowTitle("Контрольная работа - База данных «Аптека»") + self.setMinimumSize(1100, 700) + + # Инициализация БД + self.db = Database() + + # Создание интерфейса + self.init_ui() + + # Загрузка данных + self.load_all_data() + + def init_ui(self): + """Инициализация интерфейса""" + central_widget = QWidget() + self.setCentralWidget(central_widget) + main_layout = QVBoxLayout(central_widget) + + # Панель поиска медикаментов + search_group = QGroupBox("Поиск медикаментов") + search_layout = QHBoxLayout(search_group) + + search_layout.addWidget(QLabel("Название/Производитель:")) + self.search_input = QLineEdit() + self.search_input.setPlaceholderText("Введите название или производителя...") + self.search_input.returnPressed.connect(self.search_medications) + search_layout.addWidget(self.search_input) + + self.btn_search = QPushButton("Поиск") + self.btn_search.clicked.connect(self.search_medications) + search_layout.addWidget(self.btn_search) + + self.btn_reset_search = QPushButton("Сбросить") + self.btn_reset_search.clicked.connect(self.reset_search) + search_layout.addWidget(self.btn_reset_search) + + main_layout.addWidget(search_group) + + # Вкладки + self.tabs = QTabWidget() + main_layout.addWidget(self.tabs) + + # Вкладка медикаментов (подчинённая таблица) + self.create_medications_tab() + + # Вкладка типов медикаментов (главная таблица) + self.create_types_tab() + + # Статусбар + self.statusBar = QStatusBar() + self.setStatusBar(self.statusBar) + self.statusBar.showMessage("Готово") + + def create_medications_tab(self): + """Вкладка медикаментов (подчинённая таблица)""" + tab = QWidget() + layout = QVBoxLayout(tab) + + # Фильтр по типу (связь главный-подчинённый) + filter_layout = QHBoxLayout() + filter_layout.addWidget(QLabel("Фильтр по типу медикамента:")) + self.medications_type_filter = QComboBox() + self.medications_type_filter.currentIndexChanged.connect(self.filter_medications_by_type) + filter_layout.addWidget(self.medications_type_filter) + filter_layout.addStretch() + layout.addLayout(filter_layout) + + # Таблица медикаментов с вычисляемым полем + self.medications_table = QTableWidget() + self.medications_table.setColumnCount(8) + self.medications_table.setHorizontalHeaderLabels([ + "ID", "Название", "Тип", "Производитель", "Цена (руб.)", + "Поступило", "Продано", "Остаток на складе" + ]) + self.medications_table.setSelectionBehavior(QAbstractItemView.SelectRows) + self.medications_table.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.medications_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + self.medications_table.setColumnHidden(0, True) + layout.addWidget(self.medications_table) + + # Форма редактирования + form_group = QGroupBox("Добавить/Редактировать медикамент") + form_layout = QFormLayout(form_group) + + self.med_name = QLineEdit() + self.med_type = QComboBox() + self.med_manufacturer = QLineEdit() + self.med_price = QDoubleSpinBox() + self.med_price.setRange(0, 100000) + self.med_price.setDecimals(2) + self.med_price.setSuffix(" руб.") + self.med_received = QSpinBox() + self.med_received.setRange(0, 100000) + self.med_sold = QSpinBox() + self.med_sold.setRange(0, 100000) + + form_layout.addRow("Название:", self.med_name) + form_layout.addRow("Тип:", self.med_type) + form_layout.addRow("Производитель:", self.med_manufacturer) + form_layout.addRow("Цена:", self.med_price) + form_layout.addRow("Поступило:", self.med_received) + form_layout.addRow("Продано:", self.med_sold) + + layout.addWidget(form_group) + + # Кнопки + buttons_layout = QHBoxLayout() + btn_add = QPushButton("Добавить") + btn_add.clicked.connect(self.add_medication) + btn_edit = QPushButton("Изменить выбранный") + btn_edit.clicked.connect(self.edit_medication) + btn_delete = QPushButton("Удалить выбранный") + btn_delete.clicked.connect(self.delete_medication) + btn_load = QPushButton("Загрузить в форму") + btn_load.clicked.connect(self.load_medication_to_form) + + buttons_layout.addWidget(btn_add) + buttons_layout.addWidget(btn_edit) + buttons_layout.addWidget(btn_delete) + buttons_layout.addWidget(btn_load) + buttons_layout.addStretch() + layout.addLayout(buttons_layout) + + self.tabs.addTab(tab, "Медикаменты") + + def create_types_tab(self): + """Вкладка типов медикаментов (главная таблица)""" + tab = QWidget() + layout = QVBoxLayout(tab) + + # Таблица типов + self.types_table = QTableWidget() + self.types_table.setColumnCount(5) + self.types_table.setHorizontalHeaderLabels([ + "ID", "Название", "Описание", "Условия хранения", "Рецепт" + ]) + self.types_table.setSelectionBehavior(QAbstractItemView.SelectRows) + self.types_table.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.types_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + self.types_table.setColumnHidden(0, True) + layout.addWidget(self.types_table) + + # Форма редактирования + form_group = QGroupBox("Добавить/Редактировать тип медикамента") + form_layout = QFormLayout(form_group) + + self.type_name = QLineEdit() + self.type_description = QLineEdit() + self.type_storage = QLineEdit() + self.type_prescription = QComboBox() + self.type_prescription.addItems(["Нет", "Да"]) + + form_layout.addRow("Название:", self.type_name) + form_layout.addRow("Описание:", self.type_description) + form_layout.addRow("Условия хранения:", self.type_storage) + form_layout.addRow("Требуется рецепт:", self.type_prescription) + + layout.addWidget(form_group) + + # Кнопки + buttons_layout = QHBoxLayout() + btn_add = QPushButton("Добавить") + btn_add.clicked.connect(self.add_type) + btn_edit = QPushButton("Изменить") + btn_edit.clicked.connect(self.edit_type) + btn_delete = QPushButton("Удалить") + btn_delete.clicked.connect(self.delete_type) + btn_load = QPushButton("Загрузить в форму") + btn_load.clicked.connect(self.load_type_to_form) + + buttons_layout.addWidget(btn_add) + buttons_layout.addWidget(btn_edit) + buttons_layout.addWidget(btn_delete) + buttons_layout.addWidget(btn_load) + buttons_layout.addStretch() + layout.addLayout(buttons_layout) + + self.tabs.addTab(tab, "Типы медикаментов") + + def load_all_data(self): + """Загрузка всех данных""" + self.load_types() + self.load_medications() + self.update_comboboxes() + + def update_comboboxes(self): + """Обновление выпадающих списков""" + types = self.db.get_medication_types() + + # Фильтр по типу + self.medications_type_filter.clear() + self.medications_type_filter.addItem("Все типы", None) + + # Комбобокс для формы + self.med_type.clear() + + for t in types: + self.medications_type_filter.addItem(t[1], t[0]) + self.med_type.addItem(t[1], t[0]) + + # === Типы медикаментов (главная таблица) === + def load_types(self): + types = self.db.get_medication_types() + self.types_table.setRowCount(len(types)) + for row, t in enumerate(types): + self.types_table.setItem(row, 0, QTableWidgetItem(str(t[0]))) + self.types_table.setItem(row, 1, QTableWidgetItem(t[1])) + self.types_table.setItem(row, 2, QTableWidgetItem(t[2] or "")) + self.types_table.setItem(row, 3, QTableWidgetItem(t[3] or "")) + self.types_table.setItem(row, 4, QTableWidgetItem("Да" if t[4] else "Нет")) + + def add_type(self): + name = self.type_name.text().strip() + description = self.type_description.text().strip() + storage = self.type_storage.text().strip() + prescription = self.type_prescription.currentIndex() + + if not name: + QMessageBox.warning(self, "Ошибка", "Введите название типа") + return + try: + self.db.add_medication_type(name, description, storage, prescription) + self.load_all_data() + self.clear_type_form() + self.statusBar.showMessage("Тип медикамента добавлен") + except Exception as e: + QMessageBox.critical(self, "Ошибка", str(e)) + + def edit_type(self): + row = self.types_table.currentRow() + if row < 0: + QMessageBox.warning(self, "Ошибка", "Выберите тип медикамента") + return + + id = int(self.types_table.item(row, 0).text()) + name = self.type_name.text().strip() + description = self.type_description.text().strip() + storage = self.type_storage.text().strip() + prescription = self.type_prescription.currentIndex() + + if not name: + QMessageBox.warning(self, "Ошибка", "Введите название типа") + return + + self.db.update_medication_type(id, name, description, storage, prescription) + self.load_all_data() + self.statusBar.showMessage("Тип медикамента обновлён") + + def delete_type(self): + row = self.types_table.currentRow() + if row < 0: + QMessageBox.warning(self, "Ошибка", "Выберите тип медикамента") + return + + reply = QMessageBox.question( + self, "Подтверждение", + "Удалить тип медикамента? Это также удалит все связанные медикаменты.", + QMessageBox.Yes | QMessageBox.No + ) + if reply == QMessageBox.Yes: + id = int(self.types_table.item(row, 0).text()) + self.db.delete_medication_type(id) + self.load_all_data() + self.statusBar.showMessage("Тип медикамента удалён") + + def load_type_to_form(self): + row = self.types_table.currentRow() + if row < 0: + return + self.type_name.setText(self.types_table.item(row, 1).text()) + self.type_description.setText(self.types_table.item(row, 2).text()) + self.type_storage.setText(self.types_table.item(row, 3).text()) + prescription = self.types_table.item(row, 4).text() + self.type_prescription.setCurrentIndex(1 if prescription == "Да" else 0) + + def clear_type_form(self): + self.type_name.clear() + self.type_description.clear() + self.type_storage.clear() + self.type_prescription.setCurrentIndex(0) + + # === Медикаменты (подчинённая таблица) === + def load_medications(self, type_id=None, search_query=None): + medications = self.db.get_medications(type_id, search_query) + self.medications_table.setRowCount(len(medications)) + for row, m in enumerate(medications): + self.medications_table.setItem(row, 0, QTableWidgetItem(str(m[0]))) # id + self.medications_table.setItem(row, 1, QTableWidgetItem(m[1])) # name + self.medications_table.setItem(row, 2, QTableWidgetItem(m[8])) # type_name + self.medications_table.setItem(row, 3, QTableWidgetItem(m[3] or "")) # manufacturer + self.medications_table.setItem(row, 4, QTableWidgetItem(f"{m[4]:.2f}")) # price + self.medications_table.setItem(row, 5, QTableWidgetItem(str(m[5]))) # received + self.medications_table.setItem(row, 6, QTableWidgetItem(str(m[6]))) # sold + # Вычисляемое поле - остаток на складе + remaining = m[7] + remaining_item = QTableWidgetItem(str(remaining)) + # Подсветка если остаток мал + if remaining <= 10: + remaining_item.setBackground(Qt.red) + elif remaining <= 30: + remaining_item.setBackground(Qt.yellow) + self.medications_table.setItem(row, 7, remaining_item) + + def filter_medications_by_type(self): + """Фильтрация по типу (связь главный-подчинённый)""" + type_id = self.medications_type_filter.currentData() + self.load_medications(type_id) + + def search_medications(self): + """Поиск медикаментов""" + query = self.search_input.text().strip() + if query: + self.load_medications(search_query=query) + self.statusBar.showMessage(f"Поиск: {query}") + else: + self.load_medications() + + def reset_search(self): + """Сброс поиска""" + self.search_input.clear() + self.medications_type_filter.setCurrentIndex(0) + self.load_medications() + self.statusBar.showMessage("Поиск сброшен") + + def add_medication(self): + name = self.med_name.text().strip() + type_id = self.med_type.currentData() + manufacturer = self.med_manufacturer.text().strip() + price = self.med_price.value() + received = self.med_received.value() + sold = self.med_sold.value() + + if not name or not type_id: + QMessageBox.warning(self, "Ошибка", "Заполните обязательные поля (Название, Тип)") + return + + if sold > received: + QMessageBox.warning(self, "Ошибка", "Продано не может быть больше, чем поступило") + return + + self.db.add_medication(name, type_id, manufacturer, price, received, sold) + self.load_medications() + self.clear_medication_form() + self.statusBar.showMessage("Медикамент добавлен") + + def edit_medication(self): + row = self.medications_table.currentRow() + if row < 0: + QMessageBox.warning(self, "Ошибка", "Выберите медикамент") + return + + id = int(self.medications_table.item(row, 0).text()) + name = self.med_name.text().strip() + type_id = self.med_type.currentData() + manufacturer = self.med_manufacturer.text().strip() + price = self.med_price.value() + received = self.med_received.value() + sold = self.med_sold.value() + + if not name or not type_id: + QMessageBox.warning(self, "Ошибка", "Заполните обязательные поля") + return + + if sold > received: + QMessageBox.warning(self, "Ошибка", "Продано не может быть больше, чем поступило") + return + + self.db.update_medication(id, name, type_id, manufacturer, price, received, sold) + self.load_medications() + self.statusBar.showMessage("Медикамент обновлён") + + def delete_medication(self): + row = self.medications_table.currentRow() + if row < 0: + QMessageBox.warning(self, "Ошибка", "Выберите медикамент") + return + + reply = QMessageBox.question( + self, "Подтверждение", "Удалить медикамент?", + QMessageBox.Yes | QMessageBox.No + ) + if reply == QMessageBox.Yes: + id = int(self.medications_table.item(row, 0).text()) + self.db.delete_medication(id) + self.load_medications() + self.statusBar.showMessage("Медикамент удалён") + + def load_medication_to_form(self): + row = self.medications_table.currentRow() + if row < 0: + return + self.med_name.setText(self.medications_table.item(row, 1).text()) + self.med_manufacturer.setText(self.medications_table.item(row, 3).text()) + self.med_price.setValue(float(self.medications_table.item(row, 4).text())) + self.med_received.setValue(int(self.medications_table.item(row, 5).text())) + self.med_sold.setValue(int(self.medications_table.item(row, 6).text())) + # Найти тип + type_name = self.medications_table.item(row, 2).text() + index = self.med_type.findText(type_name) + if index >= 0: + self.med_type.setCurrentIndex(index) + + def clear_medication_form(self): + self.med_name.clear() + self.med_manufacturer.clear() + self.med_price.setValue(0) + self.med_received.setValue(0) + self.med_sold.setValue(0) + + def closeEvent(self, event): + """Закрытие приложения""" + self.db.close() + event.accept() + + +if __name__ == '__main__': + app = QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec_()) diff --git a/controlWorkVisualProg/pharmacy.db b/controlWorkVisualProg/pharmacy.db new file mode 100644 index 0000000000000000000000000000000000000000..396e3f3c67fd557fd26a8488919b269530a8b8a3 GIT binary patch literal 20480 zcmeI4-)~cO6u`gVd)u``+UvuTNsQ#H6J2Em0Z~YtuInzOp>(5b6Niblv=?&&>$d*D zEhb7gBq0zPJ`h7RXrlOJ(!nZYTf2l8dHmkQ7ykhtjE^Q7pFHQbxV2pgXneq&o80yL z?e}}m_ngl;ThiNeHhL^<<_&RjB0HYUi+$`##yJ}hf-$DT?S%XOrNF>(e*&+3!?@j0 zWxGB+?$Q2Wj;(R#J)_q*svgaw}__8Op8z?rXSX$Vk8~QkmqqA!7PlF%cPtJ01?V?#a(g8i@_++wUAtW(p^hseB=8WNodj z$|kdB$`IhzCI?Lwl9{}jpGjnmlwqDR);${14+Y}kn0UEW;om~JiPH}yaA-KHhYm+< zI=yw@d|=TbJ*r27`sfDE30%(gnrWXHj));WtV1w@fze=KNN+&t8&NyDckbk4b}(~O zr{M!*B4>Y5g-DOJ%M7E)5StX{(h&tZk?4?YgR7Fo3aY`l6BUavx>4}U6f^ewJg_U z33ipNS?i*Zi*m)fEaU=6owY8>1$fUYiVcz_xeD68K?zv_eY4gjA=hBKB;cGftggv& zUEdtMmc^d^{=NI}mV!mq(ZT#572NVulmmIxgqAeSYqklw3E6rC<)eBS3p6)b1t5H`F!n|cT!cXAgySPbSm>Y5g-CYfCvzQe?{OqHN+qM?8(`s`g!AnAM2t+s5*bpV;Ob38sP1pj40dG zgKeHh;P?NE_7l^7fgf}c0U|&IhyW2F0z`la5CI}U1c(3;AOepU0ZmbR_@wayABe*X7(CYiRX&1>&!+r7Vg*Sr_JZ+j1TJ)R#u*I|h+B0vO)01+SpM1Tko z0U|&IhyW4z2L${cH`~K~3g_}OC?mgRm+V9L0hV(+;d@^w@`jpjEE_K=FYN8wx7E#j zydSsE!zL^w$4dVdr~KM3(qFZU_p#$(5$Dz{zx(>O*Pg&LY@utgxz;Rzoet1l;Aks2 zFw*1O;%0un15aycJrF^M{0`*NI5>s zZ$A$~6SFfm1BX!Em=fxoW1A$X!&d9RG=_9YW-0UFtqr&U9YbEPE6u#Hm zA;4o*eh$Kma840>Fyz8E{u_Vi)ef|{-!=mv)Jo)UAxx#6qdmNjE$(olZ9N=pyJnw) zD&Z7mD7#BUjdXhE#oTl{FyKIv!(72=Em(7QLr2{W1;1|xDcc}<6U@Tm{yRe6wqshA zi*a`K_%0RP>fC`EZoon3p!=d$%MYy^a9~A#g~noyzr5{d_oiP`aQ`!Qz(EuCk)UBv w+z&WlCz$&s-1GdYk?n)r&H6k0U6>rOql)by^?fULheM?ugMky$&fT>CFAApOxc~qF literal 0 HcmV?d00001 diff --git a/controlWorkVisualProg/requirements.txt b/controlWorkVisualProg/requirements.txt new file mode 100644 index 0000000..a184504 --- /dev/null +++ b/controlWorkVisualProg/requirements.txt @@ -0,0 +1,2 @@ +PyQt5>=5.15.0 +pyinstaller>=6.0.0 diff --git a/secondLabVisualProg/main.py b/secondLabVisualProg/main.py new file mode 100644 index 0000000..8497e6a --- /dev/null +++ b/secondLabVisualProg/main.py @@ -0,0 +1,430 @@ +import sys +from PyQt5.QtWidgets import ( + QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QLabel, QPushButton, QComboBox, QSlider, QFileDialog, + QMessageBox, QColorDialog, QAction, QToolBar, QStatusBar +) +from PyQt5.QtCore import Qt, QPoint +from PyQt5.QtGui import ( + QImage, QPainter, QPen, QColor, QPixmap, QIcon +) + + +class Canvas(QWidget): + """Холст для рисования""" + + def __init__(self, parent=None): + super().__init__(parent) + self.setMinimumSize(800, 600) + + # Создаём изображение для рисования + self.image = QImage(800, 600, QImage.Format_RGB32) + self.image.fill(Qt.white) + + # Параметры рисования + self.drawing = False + self.last_point = QPoint() + self.pen_color = QColor(Qt.black) + self.pen_width = 3 + self.pen_style = Qt.SolidLine + self.eraser_width = 20 + + # История для undo/redo + self.undo_stack = [] + self.redo_stack = [] + self.save_state() # Сохраняем начальное состояние + + def save_state(self): + """Сохранить текущее состояние для undo""" + self.undo_stack.append(self.image.copy()) + self.redo_stack.clear() # Очищаем redo при новом действии + # Ограничиваем размер истории + if len(self.undo_stack) > 50: + self.undo_stack.pop(0) + + def undo(self): + """Отменить последнее действие""" + if len(self.undo_stack) > 1: + # Сохраняем текущее состояние в redo + self.redo_stack.append(self.undo_stack.pop()) + # Восстанавливаем предыдущее состояние + self.image = self.undo_stack[-1].copy() + self.update() + return True + return False + + def redo(self): + """Повторить отменённое действие""" + if self.redo_stack: + state = self.redo_stack.pop() + self.undo_stack.append(state) + self.image = state.copy() + self.update() + return True + return False + + def clear_canvas(self): + """Очистить холст""" + self.save_state() + self.image.fill(Qt.white) + self.update() + + def new_image(self, width=800, height=600): + """Создать новое изображение""" + self.image = QImage(width, height, QImage.Format_RGB32) + self.image.fill(Qt.white) + self.undo_stack.clear() + self.redo_stack.clear() + self.save_state() + self.setMinimumSize(width, height) + self.update() + + def load_image(self, file_path): + """Загрузить изображение из файла""" + loaded_image = QImage(file_path) + if loaded_image.isNull(): + return False + self.image = loaded_image.convertToFormat(QImage.Format_RGB32) + self.setMinimumSize(self.image.width(), self.image.height()) + self.undo_stack.clear() + self.redo_stack.clear() + self.save_state() + self.update() + return True + + def save_image(self, file_path): + """Сохранить изображение в файл""" + return self.image.save(file_path) + + def set_pen_color(self, color): + """Установить цвет кисти""" + self.pen_color = color + + def set_pen_width(self, width): + """Установить толщину кисти""" + self.pen_width = width + + def set_pen_style(self, style): + """Установить стиль линии""" + self.pen_style = style + + def set_eraser_width(self, width): + """Установить размер ластика""" + self.eraser_width = width + + def paintEvent(self, event): + """Отрисовка холста""" + painter = QPainter(self) + painter.drawImage(0, 0, self.image) + + def mousePressEvent(self, event): + """Обработка нажатия кнопки мыши""" + if event.button() == Qt.LeftButton or event.button() == Qt.RightButton: + self.drawing = True + self.last_point = event.pos() + self.save_state() # Сохраняем состояние перед началом рисования + + def mouseMoveEvent(self, event): + """Обработка движения мыши""" + if self.drawing: + painter = QPainter(self.image) + + if event.buttons() & Qt.RightButton: + # Правая кнопка - ластик (стираем белым цветом) + pen = QPen(Qt.white, self.eraser_width, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) + else: + # Левая кнопка - рисуем + pen = QPen(self.pen_color, self.pen_width, self.pen_style, Qt.RoundCap, Qt.RoundJoin) + + painter.setPen(pen) + painter.drawLine(self.last_point, event.pos()) + self.last_point = event.pos() + self.update() + + def mouseReleaseEvent(self, event): + """Обработка отпускания кнопки мыши""" + if event.button() == Qt.LeftButton or event.button() == Qt.RightButton: + self.drawing = False + + +class MainWindow(QMainWindow): + """Главное окно графического редактора""" + + def __init__(self): + super().__init__() + self.setWindowTitle("Лабораторная работа №2 - Графический редактор") + self.setMinimumSize(1000, 750) + + # Создаём холст + self.canvas = Canvas() + + # Центральный виджет с холстом + central_widget = QWidget() + self.setCentralWidget(central_widget) + main_layout = QVBoxLayout(central_widget) + main_layout.addWidget(self.canvas) + + # Создаём меню + self.create_menu() + + # Создаём панель инструментов + self.create_toolbar() + + # Создаём статусбар + self.statusBar = QStatusBar() + self.setStatusBar(self.statusBar) + self.statusBar.showMessage("Готово") + + def create_menu(self): + """Создание меню""" + menubar = self.menuBar() + + # Меню "Файл" + file_menu = menubar.addMenu("Файл") + + new_action = QAction("Создать", self) + new_action.setShortcut("Ctrl+N") + new_action.triggered.connect(self.new_file) + file_menu.addAction(new_action) + + open_action = QAction("Открыть", self) + open_action.setShortcut("Ctrl+O") + open_action.triggered.connect(self.open_file) + file_menu.addAction(open_action) + + save_action = QAction("Сохранить", self) + save_action.setShortcut("Ctrl+S") + save_action.triggered.connect(self.save_file) + file_menu.addAction(save_action) + + save_as_action = QAction("Сохранить как...", self) + save_as_action.setShortcut("Ctrl+Shift+S") + save_as_action.triggered.connect(self.save_file_as) + file_menu.addAction(save_as_action) + + file_menu.addSeparator() + + exit_action = QAction("Выход", self) + exit_action.setShortcut("Ctrl+Q") + exit_action.triggered.connect(self.close) + file_menu.addAction(exit_action) + + # Меню "Редактирование" + edit_menu = menubar.addMenu("Редактирование") + + undo_action = QAction("Отменить", self) + undo_action.setShortcut("Ctrl+Z") + undo_action.triggered.connect(self.undo) + edit_menu.addAction(undo_action) + + redo_action = QAction("Повторить", self) + redo_action.setShortcut("Ctrl+Y") + redo_action.triggered.connect(self.redo) + edit_menu.addAction(redo_action) + + edit_menu.addSeparator() + + clear_action = QAction("Очистить", self) + clear_action.setShortcut("Ctrl+Delete") + clear_action.triggered.connect(self.clear_canvas) + edit_menu.addAction(clear_action) + + # Меню "Инструменты" + tools_menu = menubar.addMenu("Инструменты") + + color_action = QAction("Выбрать цвет...", self) + color_action.triggered.connect(self.choose_color) + tools_menu.addAction(color_action) + + def create_toolbar(self): + """Создание панели инструментов""" + toolbar = QToolBar("Инструменты") + toolbar.setMovable(False) + self.addToolBar(toolbar) + + # Кнопки файловых операций + btn_new = QPushButton("Создать") + btn_new.clicked.connect(self.new_file) + toolbar.addWidget(btn_new) + + btn_open = QPushButton("Открыть") + btn_open.clicked.connect(self.open_file) + toolbar.addWidget(btn_open) + + btn_save = QPushButton("Сохранить") + btn_save.clicked.connect(self.save_file) + toolbar.addWidget(btn_save) + + toolbar.addSeparator() + + # Кнопки undo/redo + btn_undo = QPushButton("↶ Отменить") + btn_undo.clicked.connect(self.undo) + toolbar.addWidget(btn_undo) + + btn_redo = QPushButton("↷ Повторить") + btn_redo.clicked.connect(self.redo) + toolbar.addWidget(btn_redo) + + toolbar.addSeparator() + + # Выбор цвета + toolbar.addWidget(QLabel("Цвет: ")) + self.color_btn = QPushButton() + self.color_btn.setFixedSize(30, 30) + self.color_btn.setStyleSheet("background-color: black;") + self.color_btn.clicked.connect(self.choose_color) + toolbar.addWidget(self.color_btn) + + toolbar.addSeparator() + + # Толщина линии (TrackBar - QSlider) + toolbar.addWidget(QLabel("Толщина: ")) + self.width_slider = QSlider(Qt.Horizontal) + self.width_slider.setMinimum(1) + self.width_slider.setMaximum(50) + self.width_slider.setValue(3) + self.width_slider.setFixedWidth(100) + self.width_slider.valueChanged.connect(self.change_pen_width) + toolbar.addWidget(self.width_slider) + + self.width_label = QLabel("3") + toolbar.addWidget(self.width_label) + + toolbar.addSeparator() + + # Стиль линии (ComboBox) + toolbar.addWidget(QLabel("Стиль: ")) + self.style_combo = QComboBox() + self.style_combo.addItem("Сплошная", Qt.SolidLine) + self.style_combo.addItem("Штриховая", Qt.DashLine) + self.style_combo.addItem("Пунктирная", Qt.DotLine) + self.style_combo.addItem("Штрих-пунктир", Qt.DashDotLine) + self.style_combo.addItem("Штрих-две точки", Qt.DashDotDotLine) + self.style_combo.currentIndexChanged.connect(self.change_pen_style) + toolbar.addWidget(self.style_combo) + + toolbar.addSeparator() + + # Размер ластика + toolbar.addWidget(QLabel("Ластик: ")) + self.eraser_slider = QSlider(Qt.Horizontal) + self.eraser_slider.setMinimum(5) + self.eraser_slider.setMaximum(100) + self.eraser_slider.setValue(20) + self.eraser_slider.setFixedWidth(100) + self.eraser_slider.valueChanged.connect(self.change_eraser_width) + toolbar.addWidget(self.eraser_slider) + + self.eraser_label = QLabel("20") + toolbar.addWidget(self.eraser_label) + + toolbar.addSeparator() + + # Кнопка очистки + btn_clear = QPushButton("Очистить") + btn_clear.clicked.connect(self.clear_canvas) + toolbar.addWidget(btn_clear) + + def new_file(self): + """Создать новое изображение""" + reply = QMessageBox.question( + self, "Новый файл", + "Создать новое изображение? Несохранённые изменения будут потеряны.", + QMessageBox.Yes | QMessageBox.No, QMessageBox.No + ) + if reply == QMessageBox.Yes: + self.canvas.new_image() + self.current_file = None + self.statusBar.showMessage("Создано новое изображение") + + def open_file(self): + """Открыть изображение""" + file_path, _ = QFileDialog.getOpenFileName( + self, "Открыть изображение", "", + "Изображения (*.png *.jpg *.jpeg *.bmp *.gif);;Все файлы (*)" + ) + if file_path: + if self.canvas.load_image(file_path): + self.current_file = file_path + self.statusBar.showMessage(f"Открыто: {file_path}") + else: + QMessageBox.critical(self, "Ошибка", "Не удалось открыть изображение") + + def save_file(self): + """Сохранить изображение""" + if hasattr(self, 'current_file') and self.current_file: + if self.canvas.save_image(self.current_file): + self.statusBar.showMessage(f"Сохранено: {self.current_file}") + else: + QMessageBox.critical(self, "Ошибка", "Не удалось сохранить изображение") + else: + self.save_file_as() + + def save_file_as(self): + """Сохранить изображение как...""" + file_path, _ = QFileDialog.getSaveFileName( + self, "Сохранить изображение", "", + "PNG (*.png);;JPEG (*.jpg *.jpeg);;BMP (*.bmp);;Все файлы (*)" + ) + if file_path: + if self.canvas.save_image(file_path): + self.current_file = file_path + self.statusBar.showMessage(f"Сохранено: {file_path}") + else: + QMessageBox.critical(self, "Ошибка", "Не удалось сохранить изображение") + + def undo(self): + """Отменить действие""" + if self.canvas.undo(): + self.statusBar.showMessage("Отменено") + else: + self.statusBar.showMessage("Нечего отменять") + + def redo(self): + """Повторить действие""" + if self.canvas.redo(): + self.statusBar.showMessage("Повторено") + else: + self.statusBar.showMessage("Нечего повторять") + + def clear_canvas(self): + """Очистить холст""" + reply = QMessageBox.question( + self, "Очистить", + "Очистить холст?", + QMessageBox.Yes | QMessageBox.No, QMessageBox.No + ) + if reply == QMessageBox.Yes: + self.canvas.clear_canvas() + self.statusBar.showMessage("Холст очищен") + + def choose_color(self): + """Выбрать цвет кисти""" + color = QColorDialog.getColor(self.canvas.pen_color, self, "Выберите цвет") + if color.isValid(): + self.canvas.set_pen_color(color) + self.color_btn.setStyleSheet(f"background-color: {color.name()};") + self.statusBar.showMessage(f"Выбран цвет: {color.name()}") + + def change_pen_width(self, value): + """Изменить толщину кисти""" + self.canvas.set_pen_width(value) + self.width_label.setText(str(value)) + + def change_pen_style(self, index): + """Изменить стиль линии""" + style = self.style_combo.itemData(index) + self.canvas.set_pen_style(style) + + def change_eraser_width(self, value): + """Изменить размер ластика""" + self.canvas.set_eraser_width(value) + self.eraser_label.setText(str(value)) + + +if __name__ == '__main__': + app = QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec_()) diff --git a/thirdLabVisualProg/faculty.db b/thirdLabVisualProg/faculty.db new file mode 100644 index 0000000000000000000000000000000000000000..14d04ed8d395e18f354459f8c07edfba5cf4e622 GIT binary patch literal 28672 zcmeI5-%nF#7{||#mQ#r3Eb*c-MmWZhL6jy)oHN@pDjXdd7A=s?C0lc#M`>0-`@`ri z7K^_wbW=b#qArAFw@VmO?S!%$x4R|p8TUq?C|1lvb*z?*9Iy0 z`A2^JPpPiqo}?@KPmK%y%(k~tp^XR-0U|&IhyW2F0z`laR1>(=RHp@Y?UJwRX=^xP zr_!02J)TavuUdD+3=NvbVCZn4X}GOMYlE?Ai^q)K@Su6rj2Oowy~je4lg9hzNh35g z*xwt5$T2fKxUc4@gq2E1$E`8j7&Jc|G{XHTLw$X<15d@1TO2tSkHr#pS+rK`baEnd zCR#SQcEoTznI4T^u&ty!soF92m=#Y5vupc~^heCzqhUdy)jeIC5iyUL5i{Iv4j7^_ z)f$hr8U10S$Luo!xjQt_9qKWwD78(#;ne~=cF32HiVIXcu=re8b#aPqFYMB6<*LN& zGgdM^hBwSpagI!6k}3P2QC8uq(yU@u?%5i+H$7Tl&mQ^mK>@Df=!#u$6~c;+|CHk? zYb29MUwA6SQQL}bjXowEK{m?@&SkdB9;2+V73UHwusmuD&Q-jwqE>LOvz*~f zvYc}T)m65P@&KKAHjlEzR-KDzSv1&u5RZ`Jk*rSkolxY%S1<^A2Fn{z{}F>1*dj!7 z;!HU<2jNLqVlK$$f*;GUEzs%OhQ1ZpGg;PQi_UfD8p}Ib=;XO+Y}vU1y#?oH*-~*@ zh%DlS+z?ne8^nV<`Bl9~(tGrW`n6a064T6x01+SpM1Tko0U|&IhyW2F0z`laJTHO6 zo~$OjKhU4)=7AbiEqUv=36x$>Hc;=L@wsjZaR0wY|5?)i(0|j{u|pdXAOb{y2oM1x zKm>>Y5g-CYfCvx)BJh$D*yd4p$?g)Uh6YvLAs5%&cy&eHBUdaQ@%Ux6SuU>wDE|K6 zF6jmRoZhW(e@Q1uW)T4*Km>>Y5g-CYfCvx)B0vO)01$+e*@*qe-~vqHOAB)7%3}_9G3=38vw-;jTR}Yh`tOpWKauob^(W$Q=|$rtJt9B^ zhyW2F0z`la5CI}U1c(3;AOb|-SqL<^nyNP9b_DO8 z6}<}2^w+erUPfwx+4lYW6;$<7wL|smO_I;wB>Y5%_-+c(1`HwYMCQmDkxlI8whySwi7S)b2T7q7Gjya;@<` zo*hmkfI}~GuX(lgL}qy6To?ae48dgPL_JqBWu=YXMdr6h!YO-|9}lN2cM#{{mR%MO z+Jf-X;l=bdX_ZSB)|GM-4dl3hNZ)V8~w8&{Fn4W`WsIT(TV2O=XKHyL%0 zwho1HtE8a$q}{v4^uB^oJnh{g`x=SkuAp4R9`_&@r^ppxghyNZ%o??lu0l^8w?Qb} z**F*1SYUTB8SZ)JdyDAd?(iD|9okfF-A>qLJC1k(r>JsU_{(_;yu28FTd45lcAI-( zjjFBTUxLI_t~LE0pLC$*5QjV?6vXT|&bz>7U=32_39Dv=F1K%~Y?q>~owgDcfb>PF zzrE!hE;tP&@W_kYdK|_y_8;R4@q~ZVkcALhiq3wH0Ol#$`nmW>dZI+DTjBHUsK*tX z5(n~m6?siLUqFP*RQJF_ZGG5IBuW 0: + return + + # Факультеты + faculties = [ + ("Информатика и вычислительная техника", "Иванов И.И."), + ("Экономика и управление", "Петров П.П."), + ("Инфокоммуникационные технологии", "Сидоров С.С.") + ] + self.cursor.executemany("INSERT INTO faculties (name, dean) VALUES (?, ?)", faculties) + + # Кафедры + departments = [ + ("Программная инженерия", 1, "Козлов А.А."), + ("Информационные системы", 1, "Новиков Б.Б."), + ("Менеджмент", 2, "Смирнова В.В."), + ("Сети связи", 3, "Федоров Г.Г.") + ] + self.cursor.executemany( + "INSERT INTO departments (name, faculty_id, head) VALUES (?, ?, ?)", + departments + ) + + # Группы + groups = [ + ("ПИ-21", 1, 3), + ("ПИ-22", 1, 2), + ("ИС-21", 2, 3), + ("М-21", 3, 3), + ("СС-21", 4, 3) + ] + self.cursor.executemany( + "INSERT INTO groups (name, department_id, course) VALUES (?, ?, ?)", + groups + ) + + # Студенты + students = [ + ("Александров", "Алексей", "Андреевич", 1, 2003, "alex@mail.ru"), + ("Борисова", "Бэлла", "Борисовна", 1, 2004, "bella@mail.ru"), + ("Васильев", "Виктор", "Владимирович", 2, 2004, "victor@mail.ru"), + ("Григорьева", "Галина", "Геннадьевна", 2, 2003, "galina@mail.ru"), + ("Дмитриев", "Денис", "Дмитриевич", 3, 2003, "denis@mail.ru"), + ("Егорова", "Елена", "Евгеньевна", 3, 2004, "elena@mail.ru"), + ("Жуков", "Захар", "Зиновьевич", 4, 2003, "zahar@mail.ru"), + ("Иванова", "Ирина", "Игоревна", 4, 2004, "irina@mail.ru"), + ("Кузнецов", "Кирилл", "Константинович", 5, 2003, "kirill@mail.ru"), + ("Лебедева", "Любовь", "Леонидовна", 5, 2004, "lubov@mail.ru"), + ] + self.cursor.executemany( + "INSERT INTO students (last_name, first_name, middle_name, group_id, birth_year, email) VALUES (?, ?, ?, ?, ?, ?)", + students + ) + + self.conn.commit() + + # === CRUD операции для факультетов === + def get_faculties(self): + self.cursor.execute("SELECT * FROM faculties ORDER BY name") + return self.cursor.fetchall() + + def add_faculty(self, name, dean): + self.cursor.execute("INSERT INTO faculties (name, dean) VALUES (?, ?)", (name, dean)) + self.conn.commit() + return self.cursor.lastrowid + + def update_faculty(self, id, name, dean): + self.cursor.execute("UPDATE faculties SET name=?, dean=? WHERE id=?", (name, dean, id)) + self.conn.commit() + + def delete_faculty(self, id): + self.cursor.execute("DELETE FROM faculties WHERE id=?", (id,)) + self.conn.commit() + + # === CRUD операции для кафедр === + def get_departments(self, faculty_id=None): + if faculty_id: + self.cursor.execute(""" + SELECT d.id, d.name, d.faculty_id, d.head, f.name as faculty_name + FROM departments d + JOIN faculties f ON d.faculty_id = f.id + WHERE d.faculty_id = ? + ORDER BY d.name + """, (faculty_id,)) + else: + self.cursor.execute(""" + SELECT d.id, d.name, d.faculty_id, d.head, f.name as faculty_name + FROM departments d + JOIN faculties f ON d.faculty_id = f.id + ORDER BY d.name + """) + return self.cursor.fetchall() + + def add_department(self, name, faculty_id, head): + self.cursor.execute( + "INSERT INTO departments (name, faculty_id, head) VALUES (?, ?, ?)", + (name, faculty_id, head) + ) + self.conn.commit() + return self.cursor.lastrowid + + def update_department(self, id, name, faculty_id, head): + self.cursor.execute( + "UPDATE departments SET name=?, faculty_id=?, head=? WHERE id=?", + (name, faculty_id, head, id) + ) + self.conn.commit() + + def delete_department(self, id): + self.cursor.execute("DELETE FROM departments WHERE id=?", (id,)) + self.conn.commit() + + # === CRUD операции для групп === + def get_groups(self, department_id=None): + if department_id: + self.cursor.execute(""" + SELECT g.id, g.name, g.department_id, g.course, d.name as department_name + FROM groups g + JOIN departments d ON g.department_id = d.id + WHERE g.department_id = ? + ORDER BY g.name + """, (department_id,)) + else: + self.cursor.execute(""" + SELECT g.id, g.name, g.department_id, g.course, d.name as department_name + FROM groups g + JOIN departments d ON g.department_id = d.id + ORDER BY g.name + """) + return self.cursor.fetchall() + + def add_group(self, name, department_id, course): + self.cursor.execute( + "INSERT INTO groups (name, department_id, course) VALUES (?, ?, ?)", + (name, department_id, course) + ) + self.conn.commit() + return self.cursor.lastrowid + + def update_group(self, id, name, department_id, course): + self.cursor.execute( + "UPDATE groups SET name=?, department_id=?, course=? WHERE id=?", + (name, department_id, course, id) + ) + self.conn.commit() + + def delete_group(self, id): + self.cursor.execute("DELETE FROM groups WHERE id=?", (id,)) + self.conn.commit() + + # === CRUD операции для студентов === + def get_students(self, group_id=None, search_query=None): + if search_query: + query = """ + SELECT s.id, s.last_name, s.first_name, s.middle_name, + s.group_id, s.birth_year, s.email, g.name as group_name + FROM students s + JOIN groups g ON s.group_id = g.id + WHERE s.last_name LIKE ? OR s.first_name LIKE ? OR s.middle_name LIKE ? + ORDER BY s.last_name, s.first_name + """ + search = f"%{search_query}%" + self.cursor.execute(query, (search, search, search)) + elif group_id: + self.cursor.execute(""" + SELECT s.id, s.last_name, s.first_name, s.middle_name, + s.group_id, s.birth_year, s.email, g.name as group_name + FROM students s + JOIN groups g ON s.group_id = g.id + WHERE s.group_id = ? + ORDER BY s.last_name, s.first_name + """, (group_id,)) + else: + self.cursor.execute(""" + SELECT s.id, s.last_name, s.first_name, s.middle_name, + s.group_id, s.birth_year, s.email, g.name as group_name + FROM students s + JOIN groups g ON s.group_id = g.id + ORDER BY s.last_name, s.first_name + """) + return self.cursor.fetchall() + + def add_student(self, last_name, first_name, middle_name, group_id, birth_year, email): + self.cursor.execute( + "INSERT INTO students (last_name, first_name, middle_name, group_id, birth_year, email) VALUES (?, ?, ?, ?, ?, ?)", + (last_name, first_name, middle_name, group_id, birth_year, email) + ) + self.conn.commit() + return self.cursor.lastrowid + + def update_student(self, id, last_name, first_name, middle_name, group_id, birth_year, email): + self.cursor.execute( + "UPDATE students SET last_name=?, first_name=?, middle_name=?, group_id=?, birth_year=?, email=? WHERE id=?", + (last_name, first_name, middle_name, group_id, birth_year, email, id) + ) + self.conn.commit() + + def delete_student(self, id): + self.cursor.execute("DELETE FROM students WHERE id=?", (id,)) + self.conn.commit() + + def search_student_exact(self, last_name): + """Поиск студента по точной фамилии""" + self.cursor.execute(""" + SELECT s.id, s.last_name, s.first_name, s.middle_name, + s.group_id, s.birth_year, s.email, g.name as group_name + FROM students s + JOIN groups g ON s.group_id = g.id + WHERE s.last_name = ? + ORDER BY s.first_name + """, (last_name,)) + return self.cursor.fetchall() + + def close(self): + if self.conn: + self.conn.close() + + +class MainWindow(QMainWindow): + """Главное окно приложения""" + + def __init__(self): + super().__init__() + self.setWindowTitle("Лабораторная работа №3 - База данных «Факультет»") + self.setMinimumSize(1100, 700) + + # Инициализация базы данных + self.db = Database() + + # Создание интерфейса + self.init_ui() + + # Загрузка данных + self.load_all_data() + + def init_ui(self): + """Инициализация интерфейса""" + central_widget = QWidget() + self.setCentralWidget(central_widget) + main_layout = QVBoxLayout(central_widget) + + # Верхняя панель - поиск студентов + search_group = QGroupBox("Поиск студентов") + search_layout = QHBoxLayout(search_group) + + search_layout.addWidget(QLabel("Фамилия:")) + self.search_input = QLineEdit() + self.search_input.setPlaceholderText("Введите фамилию для поиска...") + self.search_input.returnPressed.connect(self.search_students) + search_layout.addWidget(self.search_input) + + self.btn_search = QPushButton("Поиск (частичный)") + self.btn_search.clicked.connect(self.search_students) + search_layout.addWidget(self.btn_search) + + self.btn_search_exact = QPushButton("Точный поиск") + self.btn_search_exact.clicked.connect(self.search_students_exact) + search_layout.addWidget(self.btn_search_exact) + + self.btn_reset_search = QPushButton("Сбросить") + self.btn_reset_search.clicked.connect(self.reset_search) + search_layout.addWidget(self.btn_reset_search) + + main_layout.addWidget(search_group) + + # Вкладки для разных таблиц + self.tabs = QTabWidget() + main_layout.addWidget(self.tabs) + + # Вкладка студентов + self.create_students_tab() + + # Вкладка групп + self.create_groups_tab() + + # Вкладка кафедр + self.create_departments_tab() + + # Вкладка факультетов + self.create_faculties_tab() + + # Статусбар + self.statusBar = QStatusBar() + self.setStatusBar(self.statusBar) + self.statusBar.showMessage("Готово") + + def create_students_tab(self): + """Создание вкладки студентов""" + tab = QWidget() + layout = QVBoxLayout(tab) + + # Фильтр по группе + filter_layout = QHBoxLayout() + filter_layout.addWidget(QLabel("Фильтр по группе:")) + self.students_group_filter = QComboBox() + self.students_group_filter.currentIndexChanged.connect(self.filter_students_by_group) + filter_layout.addWidget(self.students_group_filter) + filter_layout.addStretch() + layout.addLayout(filter_layout) + + # Таблица студентов + self.students_table = QTableWidget() + self.students_table.setColumnCount(7) + self.students_table.setHorizontalHeaderLabels([ + "ID", "Фамилия", "Имя", "Отчество", "Группа", "Год рождения", "Email" + ]) + self.students_table.setSelectionBehavior(QAbstractItemView.SelectRows) + self.students_table.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.students_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + self.students_table.setColumnHidden(0, True) # Скрываем ID + layout.addWidget(self.students_table) + + # Форма редактирования + form_group = QGroupBox("Добавить/Редактировать студента") + form_layout = QFormLayout(form_group) + + self.student_last_name = QLineEdit() + self.student_first_name = QLineEdit() + self.student_middle_name = QLineEdit() + self.student_group = QComboBox() + self.student_birth_year = QSpinBox() + self.student_birth_year.setRange(1990, 2010) + self.student_birth_year.setValue(2003) + self.student_email = QLineEdit() + + form_layout.addRow("Фамилия:", self.student_last_name) + form_layout.addRow("Имя:", self.student_first_name) + form_layout.addRow("Отчество:", self.student_middle_name) + form_layout.addRow("Группа:", self.student_group) + form_layout.addRow("Год рождения:", self.student_birth_year) + form_layout.addRow("Email:", self.student_email) + + layout.addWidget(form_group) + + # Кнопки управления + buttons_layout = QHBoxLayout() + btn_add = QPushButton("Добавить") + btn_add.clicked.connect(self.add_student) + btn_edit = QPushButton("Изменить выбранного") + btn_edit.clicked.connect(self.edit_student) + btn_delete = QPushButton("Удалить выбранного") + btn_delete.clicked.connect(self.delete_student) + btn_load = QPushButton("Загрузить в форму") + btn_load.clicked.connect(self.load_student_to_form) + + buttons_layout.addWidget(btn_add) + buttons_layout.addWidget(btn_edit) + buttons_layout.addWidget(btn_delete) + buttons_layout.addWidget(btn_load) + buttons_layout.addStretch() + layout.addLayout(buttons_layout) + + self.tabs.addTab(tab, "Студенты") + + def create_groups_tab(self): + """Создание вкладки групп""" + tab = QWidget() + layout = QVBoxLayout(tab) + + # Фильтр по кафедре + filter_layout = QHBoxLayout() + filter_layout.addWidget(QLabel("Фильтр по кафедре:")) + self.groups_dept_filter = QComboBox() + self.groups_dept_filter.currentIndexChanged.connect(self.filter_groups_by_department) + filter_layout.addWidget(self.groups_dept_filter) + filter_layout.addStretch() + layout.addLayout(filter_layout) + + # Таблица групп + self.groups_table = QTableWidget() + self.groups_table.setColumnCount(4) + self.groups_table.setHorizontalHeaderLabels(["ID", "Название", "Кафедра", "Курс"]) + self.groups_table.setSelectionBehavior(QAbstractItemView.SelectRows) + self.groups_table.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.groups_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + self.groups_table.setColumnHidden(0, True) + layout.addWidget(self.groups_table) + + # Форма редактирования + form_group = QGroupBox("Добавить/Редактировать группу") + form_layout = QFormLayout(form_group) + + self.group_name = QLineEdit() + self.group_department = QComboBox() + self.group_course = QSpinBox() + self.group_course.setRange(1, 6) + self.group_course.setValue(1) + + form_layout.addRow("Название:", self.group_name) + form_layout.addRow("Кафедра:", self.group_department) + form_layout.addRow("Курс:", self.group_course) + + layout.addWidget(form_group) + + # Кнопки + buttons_layout = QHBoxLayout() + btn_add = QPushButton("Добавить") + btn_add.clicked.connect(self.add_group) + btn_edit = QPushButton("Изменить") + btn_edit.clicked.connect(self.edit_group) + btn_delete = QPushButton("Удалить") + btn_delete.clicked.connect(self.delete_group) + btn_load = QPushButton("Загрузить в форму") + btn_load.clicked.connect(self.load_group_to_form) + + buttons_layout.addWidget(btn_add) + buttons_layout.addWidget(btn_edit) + buttons_layout.addWidget(btn_delete) + buttons_layout.addWidget(btn_load) + buttons_layout.addStretch() + layout.addLayout(buttons_layout) + + self.tabs.addTab(tab, "Группы") + + def create_departments_tab(self): + """Создание вкладки кафедр""" + tab = QWidget() + layout = QVBoxLayout(tab) + + # Фильтр по факультету + filter_layout = QHBoxLayout() + filter_layout.addWidget(QLabel("Фильтр по факультету:")) + self.depts_faculty_filter = QComboBox() + self.depts_faculty_filter.currentIndexChanged.connect(self.filter_departments_by_faculty) + filter_layout.addWidget(self.depts_faculty_filter) + filter_layout.addStretch() + layout.addLayout(filter_layout) + + # Таблица кафедр + self.departments_table = QTableWidget() + self.departments_table.setColumnCount(4) + self.departments_table.setHorizontalHeaderLabels(["ID", "Название", "Факультет", "Заведующий"]) + self.departments_table.setSelectionBehavior(QAbstractItemView.SelectRows) + self.departments_table.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.departments_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + self.departments_table.setColumnHidden(0, True) + layout.addWidget(self.departments_table) + + # Форма редактирования + form_group = QGroupBox("Добавить/Редактировать кафедру") + form_layout = QFormLayout(form_group) + + self.dept_name = QLineEdit() + self.dept_faculty = QComboBox() + self.dept_head = QLineEdit() + + form_layout.addRow("Название:", self.dept_name) + form_layout.addRow("Факультет:", self.dept_faculty) + form_layout.addRow("Заведующий:", self.dept_head) + + layout.addWidget(form_group) + + # Кнопки + buttons_layout = QHBoxLayout() + btn_add = QPushButton("Добавить") + btn_add.clicked.connect(self.add_department) + btn_edit = QPushButton("Изменить") + btn_edit.clicked.connect(self.edit_department) + btn_delete = QPushButton("Удалить") + btn_delete.clicked.connect(self.delete_department) + btn_load = QPushButton("Загрузить в форму") + btn_load.clicked.connect(self.load_department_to_form) + + buttons_layout.addWidget(btn_add) + buttons_layout.addWidget(btn_edit) + buttons_layout.addWidget(btn_delete) + buttons_layout.addWidget(btn_load) + buttons_layout.addStretch() + layout.addLayout(buttons_layout) + + self.tabs.addTab(tab, "Кафедры") + + def create_faculties_tab(self): + """Создание вкладки факультетов""" + tab = QWidget() + layout = QVBoxLayout(tab) + + # Таблица факультетов + self.faculties_table = QTableWidget() + self.faculties_table.setColumnCount(3) + self.faculties_table.setHorizontalHeaderLabels(["ID", "Название", "Декан"]) + self.faculties_table.setSelectionBehavior(QAbstractItemView.SelectRows) + self.faculties_table.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.faculties_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + self.faculties_table.setColumnHidden(0, True) + layout.addWidget(self.faculties_table) + + # Форма редактирования + form_group = QGroupBox("Добавить/Редактировать факультет") + form_layout = QFormLayout(form_group) + + self.faculty_name = QLineEdit() + self.faculty_dean = QLineEdit() + + form_layout.addRow("Название:", self.faculty_name) + form_layout.addRow("Декан:", self.faculty_dean) + + layout.addWidget(form_group) + + # Кнопки + buttons_layout = QHBoxLayout() + btn_add = QPushButton("Добавить") + btn_add.clicked.connect(self.add_faculty) + btn_edit = QPushButton("Изменить") + btn_edit.clicked.connect(self.edit_faculty) + btn_delete = QPushButton("Удалить") + btn_delete.clicked.connect(self.delete_faculty) + btn_load = QPushButton("Загрузить в форму") + btn_load.clicked.connect(self.load_faculty_to_form) + + buttons_layout.addWidget(btn_add) + buttons_layout.addWidget(btn_edit) + buttons_layout.addWidget(btn_delete) + buttons_layout.addWidget(btn_load) + buttons_layout.addStretch() + layout.addLayout(buttons_layout) + + self.tabs.addTab(tab, "Факультеты") + + def load_all_data(self): + """Загрузка всех данных""" + self.load_faculties() + self.load_departments() + self.load_groups() + self.load_students() + self.update_comboboxes() + + def update_comboboxes(self): + """Обновление выпадающих списков""" + # Факультеты + faculties = self.db.get_faculties() + + self.depts_faculty_filter.clear() + self.depts_faculty_filter.addItem("Все факультеты", None) + self.dept_faculty.clear() + + for f in faculties: + self.depts_faculty_filter.addItem(f[1], f[0]) + self.dept_faculty.addItem(f[1], f[0]) + + # Кафедры + departments = self.db.get_departments() + + self.groups_dept_filter.clear() + self.groups_dept_filter.addItem("Все кафедры", None) + self.group_department.clear() + + for d in departments: + self.groups_dept_filter.addItem(d[1], d[0]) + self.group_department.addItem(d[1], d[0]) + + # Группы + groups = self.db.get_groups() + + self.students_group_filter.clear() + self.students_group_filter.addItem("Все группы", None) + self.student_group.clear() + + for g in groups: + self.students_group_filter.addItem(g[1], g[0]) + self.student_group.addItem(g[1], g[0]) + + # === Факультеты === + def load_faculties(self): + faculties = self.db.get_faculties() + self.faculties_table.setRowCount(len(faculties)) + for row, f in enumerate(faculties): + self.faculties_table.setItem(row, 0, QTableWidgetItem(str(f[0]))) + self.faculties_table.setItem(row, 1, QTableWidgetItem(f[1])) + self.faculties_table.setItem(row, 2, QTableWidgetItem(f[2] or "")) + + def add_faculty(self): + name = self.faculty_name.text().strip() + dean = self.faculty_dean.text().strip() + if not name: + QMessageBox.warning(self, "Ошибка", "Введите название факультета") + return + try: + self.db.add_faculty(name, dean) + self.load_all_data() + self.faculty_name.clear() + self.faculty_dean.clear() + self.statusBar.showMessage("Факультет добавлен") + except Exception as e: + QMessageBox.critical(self, "Ошибка", str(e)) + + def edit_faculty(self): + row = self.faculties_table.currentRow() + if row < 0: + QMessageBox.warning(self, "Ошибка", "Выберите факультет") + return + id = int(self.faculties_table.item(row, 0).text()) + name = self.faculty_name.text().strip() + dean = self.faculty_dean.text().strip() + if not name: + QMessageBox.warning(self, "Ошибка", "Введите название факультета") + return + self.db.update_faculty(id, name, dean) + self.load_all_data() + self.statusBar.showMessage("Факультет обновлен") + + def delete_faculty(self): + row = self.faculties_table.currentRow() + if row < 0: + QMessageBox.warning(self, "Ошибка", "Выберите факультет") + return + reply = QMessageBox.question(self, "Подтверждение", + "Удалить факультет? Это также удалит все связанные кафедры, группы и студентов.", + QMessageBox.Yes | QMessageBox.No) + if reply == QMessageBox.Yes: + id = int(self.faculties_table.item(row, 0).text()) + self.db.delete_faculty(id) + self.load_all_data() + self.statusBar.showMessage("Факультет удален") + + def load_faculty_to_form(self): + row = self.faculties_table.currentRow() + if row < 0: + return + self.faculty_name.setText(self.faculties_table.item(row, 1).text()) + self.faculty_dean.setText(self.faculties_table.item(row, 2).text()) + + # === Кафедры === + def load_departments(self, faculty_id=None): + departments = self.db.get_departments(faculty_id) + self.departments_table.setRowCount(len(departments)) + for row, d in enumerate(departments): + self.departments_table.setItem(row, 0, QTableWidgetItem(str(d[0]))) + self.departments_table.setItem(row, 1, QTableWidgetItem(d[1])) + self.departments_table.setItem(row, 2, QTableWidgetItem(d[4])) # faculty_name + self.departments_table.setItem(row, 3, QTableWidgetItem(d[3] or "")) + + def filter_departments_by_faculty(self): + faculty_id = self.depts_faculty_filter.currentData() + self.load_departments(faculty_id) + + def add_department(self): + name = self.dept_name.text().strip() + faculty_id = self.dept_faculty.currentData() + head = self.dept_head.text().strip() + if not name or not faculty_id: + QMessageBox.warning(self, "Ошибка", "Заполните все обязательные поля") + return + self.db.add_department(name, faculty_id, head) + self.load_all_data() + self.dept_name.clear() + self.dept_head.clear() + self.statusBar.showMessage("Кафедра добавлена") + + def edit_department(self): + row = self.departments_table.currentRow() + if row < 0: + QMessageBox.warning(self, "Ошибка", "Выберите кафедру") + return + id = int(self.departments_table.item(row, 0).text()) + name = self.dept_name.text().strip() + faculty_id = self.dept_faculty.currentData() + head = self.dept_head.text().strip() + if not name or not faculty_id: + QMessageBox.warning(self, "Ошибка", "Заполните все обязательные поля") + return + self.db.update_department(id, name, faculty_id, head) + self.load_all_data() + self.statusBar.showMessage("Кафедра обновлена") + + def delete_department(self): + row = self.departments_table.currentRow() + if row < 0: + QMessageBox.warning(self, "Ошибка", "Выберите кафедру") + return + reply = QMessageBox.question(self, "Подтверждение", + "Удалить кафедру? Это также удалит все связанные группы и студентов.", + QMessageBox.Yes | QMessageBox.No) + if reply == QMessageBox.Yes: + id = int(self.departments_table.item(row, 0).text()) + self.db.delete_department(id) + self.load_all_data() + self.statusBar.showMessage("Кафедра удалена") + + def load_department_to_form(self): + row = self.departments_table.currentRow() + if row < 0: + return + self.dept_name.setText(self.departments_table.item(row, 1).text()) + self.dept_head.setText(self.departments_table.item(row, 3).text()) + # Найти факультет в комбобоксе + faculty_name = self.departments_table.item(row, 2).text() + index = self.dept_faculty.findText(faculty_name) + if index >= 0: + self.dept_faculty.setCurrentIndex(index) + + # === Группы === + def load_groups(self, department_id=None): + groups = self.db.get_groups(department_id) + self.groups_table.setRowCount(len(groups)) + for row, g in enumerate(groups): + self.groups_table.setItem(row, 0, QTableWidgetItem(str(g[0]))) + self.groups_table.setItem(row, 1, QTableWidgetItem(g[1])) + self.groups_table.setItem(row, 2, QTableWidgetItem(g[4])) # department_name + self.groups_table.setItem(row, 3, QTableWidgetItem(str(g[3]))) + + def filter_groups_by_department(self): + dept_id = self.groups_dept_filter.currentData() + self.load_groups(dept_id) + + def add_group(self): + name = self.group_name.text().strip() + dept_id = self.group_department.currentData() + course = self.group_course.value() + if not name or not dept_id: + QMessageBox.warning(self, "Ошибка", "Заполните все обязательные поля") + return + self.db.add_group(name, dept_id, course) + self.load_all_data() + self.group_name.clear() + self.statusBar.showMessage("Группа добавлена") + + def edit_group(self): + row = self.groups_table.currentRow() + if row < 0: + QMessageBox.warning(self, "Ошибка", "Выберите группу") + return + id = int(self.groups_table.item(row, 0).text()) + name = self.group_name.text().strip() + dept_id = self.group_department.currentData() + course = self.group_course.value() + if not name or not dept_id: + QMessageBox.warning(self, "Ошибка", "Заполните все обязательные поля") + return + self.db.update_group(id, name, dept_id, course) + self.load_all_data() + self.statusBar.showMessage("Группа обновлена") + + def delete_group(self): + row = self.groups_table.currentRow() + if row < 0: + QMessageBox.warning(self, "Ошибка", "Выберите группу") + return + reply = QMessageBox.question(self, "Подтверждение", + "Удалить группу? Это также удалит всех студентов группы.", + QMessageBox.Yes | QMessageBox.No) + if reply == QMessageBox.Yes: + id = int(self.groups_table.item(row, 0).text()) + self.db.delete_group(id) + self.load_all_data() + self.statusBar.showMessage("Группа удалена") + + def load_group_to_form(self): + row = self.groups_table.currentRow() + if row < 0: + return + self.group_name.setText(self.groups_table.item(row, 1).text()) + self.group_course.setValue(int(self.groups_table.item(row, 3).text())) + dept_name = self.groups_table.item(row, 2).text() + index = self.group_department.findText(dept_name) + if index >= 0: + self.group_department.setCurrentIndex(index) + + # === Студенты === + def load_students(self, group_id=None, search_query=None): + students = self.db.get_students(group_id, search_query) + self.students_table.setRowCount(len(students)) + for row, s in enumerate(students): + self.students_table.setItem(row, 0, QTableWidgetItem(str(s[0]))) + self.students_table.setItem(row, 1, QTableWidgetItem(s[1])) + self.students_table.setItem(row, 2, QTableWidgetItem(s[2])) + self.students_table.setItem(row, 3, QTableWidgetItem(s[3] or "")) + self.students_table.setItem(row, 4, QTableWidgetItem(s[7])) # group_name + self.students_table.setItem(row, 5, QTableWidgetItem(str(s[5]))) + self.students_table.setItem(row, 6, QTableWidgetItem(s[6] or "")) + + def filter_students_by_group(self): + group_id = self.students_group_filter.currentData() + self.load_students(group_id) + + def search_students(self): + query = self.search_input.text().strip() + if query: + self.load_students(search_query=query) + self.statusBar.showMessage(f"Поиск: {query}") + else: + self.load_students() + + def search_students_exact(self): + query = self.search_input.text().strip() + if query: + students = self.db.search_student_exact(query) + self.students_table.setRowCount(len(students)) + for row, s in enumerate(students): + self.students_table.setItem(row, 0, QTableWidgetItem(str(s[0]))) + self.students_table.setItem(row, 1, QTableWidgetItem(s[1])) + self.students_table.setItem(row, 2, QTableWidgetItem(s[2])) + self.students_table.setItem(row, 3, QTableWidgetItem(s[3] or "")) + self.students_table.setItem(row, 4, QTableWidgetItem(s[7])) + self.students_table.setItem(row, 5, QTableWidgetItem(str(s[5]))) + self.students_table.setItem(row, 6, QTableWidgetItem(s[6] or "")) + self.statusBar.showMessage(f"Точный поиск: {query} (найдено: {len(students)})") + + def reset_search(self): + self.search_input.clear() + self.students_group_filter.setCurrentIndex(0) + self.load_students() + self.statusBar.showMessage("Поиск сброшен") + + def add_student(self): + last_name = self.student_last_name.text().strip() + first_name = self.student_first_name.text().strip() + middle_name = self.student_middle_name.text().strip() + group_id = self.student_group.currentData() + birth_year = self.student_birth_year.value() + email = self.student_email.text().strip() + + if not last_name or not first_name or not group_id: + QMessageBox.warning(self, "Ошибка", "Заполните обязательные поля (Фамилия, Имя, Группа)") + return + + self.db.add_student(last_name, first_name, middle_name, group_id, birth_year, email) + self.load_students() + self.clear_student_form() + self.statusBar.showMessage("Студент добавлен") + + def edit_student(self): + row = self.students_table.currentRow() + if row < 0: + QMessageBox.warning(self, "Ошибка", "Выберите студента") + return + + id = int(self.students_table.item(row, 0).text()) + last_name = self.student_last_name.text().strip() + first_name = self.student_first_name.text().strip() + middle_name = self.student_middle_name.text().strip() + group_id = self.student_group.currentData() + birth_year = self.student_birth_year.value() + email = self.student_email.text().strip() + + if not last_name or not first_name or not group_id: + QMessageBox.warning(self, "Ошибка", "Заполните обязательные поля") + return + + self.db.update_student(id, last_name, first_name, middle_name, group_id, birth_year, email) + self.load_students() + self.statusBar.showMessage("Студент обновлен") + + def delete_student(self): + row = self.students_table.currentRow() + if row < 0: + QMessageBox.warning(self, "Ошибка", "Выберите студента") + return + reply = QMessageBox.question(self, "Подтверждение", "Удалить студента?", + QMessageBox.Yes | QMessageBox.No) + if reply == QMessageBox.Yes: + id = int(self.students_table.item(row, 0).text()) + self.db.delete_student(id) + self.load_students() + self.statusBar.showMessage("Студент удален") + + def load_student_to_form(self): + row = self.students_table.currentRow() + if row < 0: + return + self.student_last_name.setText(self.students_table.item(row, 1).text()) + self.student_first_name.setText(self.students_table.item(row, 2).text()) + self.student_middle_name.setText(self.students_table.item(row, 3).text()) + self.student_birth_year.setValue(int(self.students_table.item(row, 5).text())) + self.student_email.setText(self.students_table.item(row, 6).text()) + # Найти группу + group_name = self.students_table.item(row, 4).text() + index = self.student_group.findText(group_name) + if index >= 0: + self.student_group.setCurrentIndex(index) + + def clear_student_form(self): + self.student_last_name.clear() + self.student_first_name.clear() + self.student_middle_name.clear() + self.student_email.clear() + self.student_birth_year.setValue(2003) + + def closeEvent(self, event): + """Закрытие приложения""" + self.db.close() + event.accept() + + +if __name__ == '__main__': + app = QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec_()) diff --git a/thirdLabVisualProg/requirements.txt b/thirdLabVisualProg/requirements.txt new file mode 100644 index 0000000..a184504 --- /dev/null +++ b/thirdLabVisualProg/requirements.txt @@ -0,0 +1,2 @@ +PyQt5>=5.15.0 +pyinstaller>=6.0.0