финал

This commit is contained in:
2025-12-11 15:39:41 +03:00
parent 7fd78c0de2
commit 84b934036b
12 changed files with 27 additions and 52 deletions

View 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_())