Сделал лабы + контрольную
This commit is contained in:
610
controlWorkVisualProg/main.py
Normal file
610
controlWorkVisualProg/main.py
Normal file
@@ -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_())
|
||||
BIN
controlWorkVisualProg/pharmacy.db
Normal file
BIN
controlWorkVisualProg/pharmacy.db
Normal file
Binary file not shown.
2
controlWorkVisualProg/requirements.txt
Normal file
2
controlWorkVisualProg/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
PyQt5>=5.15.0
|
||||
pyinstaller>=6.0.0
|
||||
430
secondLabVisualProg/main.py
Normal file
430
secondLabVisualProg/main.py
Normal file
@@ -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_())
|
||||
BIN
thirdLabVisualProg/faculty.db
Normal file
BIN
thirdLabVisualProg/faculty.db
Normal file
Binary file not shown.
968
thirdLabVisualProg/main.py
Normal file
968
thirdLabVisualProg/main.py
Normal file
@@ -0,0 +1,968 @@
|
||||
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, QSpinBox
|
||||
)
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
|
||||
class Database:
|
||||
"""Класс для работы с базой данных SQLite"""
|
||||
|
||||
def __init__(self, db_name="faculty.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 faculties (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
dean TEXT
|
||||
)
|
||||
''')
|
||||
|
||||
# Таблица кафедр
|
||||
self.cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS departments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
faculty_id INTEGER,
|
||||
head TEXT,
|
||||
FOREIGN KEY (faculty_id) REFERENCES faculties(id) ON DELETE CASCADE
|
||||
)
|
||||
''')
|
||||
|
||||
# Таблица групп
|
||||
self.cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS groups (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
department_id INTEGER,
|
||||
course INTEGER,
|
||||
FOREIGN KEY (department_id) REFERENCES departments(id) ON DELETE CASCADE
|
||||
)
|
||||
''')
|
||||
|
||||
# Таблица студентов
|
||||
self.cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS students (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
last_name TEXT NOT NULL,
|
||||
first_name TEXT NOT NULL,
|
||||
middle_name TEXT,
|
||||
group_id INTEGER,
|
||||
birth_year INTEGER,
|
||||
email TEXT,
|
||||
FOREIGN KEY (group_id) REFERENCES groups(id) ON DELETE CASCADE
|
||||
)
|
||||
''')
|
||||
|
||||
self.conn.commit()
|
||||
|
||||
def populate_sample_data(self):
|
||||
"""Заполнение тестовыми данными (если таблицы пустые)"""
|
||||
self.cursor.execute("SELECT COUNT(*) FROM faculties")
|
||||
if self.cursor.fetchone()[0] > 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_())
|
||||
2
thirdLabVisualProg/requirements.txt
Normal file
2
thirdLabVisualProg/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
PyQt5>=5.15.0
|
||||
pyinstaller>=6.0.0
|
||||
Reference in New Issue
Block a user