Расширен функционал, начата разработка функционала по оптимизации
This commit is contained in:
@@ -1,194 +1,55 @@
|
||||
# Калькулятор питательных сред
|
||||
markdown
|
||||
# 🧬 Цифровой помощник биохимика
|
||||
|
||||
Программа для расчёта состава питательных сред с поддержкой разбавленных реагентов.
|
||||
**Биотехнологические инструменты для лаборатории**
|
||||
|
||||
## 📋 Функционал
|
||||
## 📋 О проекте
|
||||
|
||||
- **Расчёт состава среды** по процентному содержанию компонентов
|
||||
- **Поддержка разных единиц измерения** (нг, мкг, мг, г, кг, нл, мкл, мл, л)
|
||||
- **Коэффициент пересчёта** для каждого реагента (например, для перевода объёма в массу)
|
||||
- **Разбавление реагентов** — возможность указать, во сколько раз разбавлен исходный реактив
|
||||
- **Автоматический расчёт растворителя** с учётом объёма вносимых разбавленных реагентов
|
||||
- **Сохранение и загрузка** состава в JSON-файл
|
||||
- **Интерактивная таблица** с возможностью добавления/удаления реагентов
|
||||
Цифровой помощник биохимика — это программный комплекс для автоматизации лабораторных расчётов и планирования экспериментов в области биотехнологии и биохимии.
|
||||
|
||||
## 🚀 Установка и запуск
|
||||
## 🚀 Возможности
|
||||
|
||||
### Требования
|
||||
- Python 3.6 или выше
|
||||
- PyQt5
|
||||
### 1. 🥼 Калькулятор питательных сред
|
||||
- Расчёт состава питательных сред по процентному содержанию компонентов
|
||||
- Поддержка различных единиц измерения (нг, мкг, мг, г, кг, нл, мкл, мл, л)
|
||||
- Учёт разбавления исходных реактивов
|
||||
- Автоматический расчёт количества растворителя
|
||||
- Сохранение и загрузка рецептов в JSON
|
||||
|
||||
### Установка зависимостей
|
||||
```bash
|
||||
pip install PyQt5
|
||||
```
|
||||
### 2. 📊 Планирование эксперимента (DoE)
|
||||
- Полнофакторный дизайн эксперимента (2^k факторный план)
|
||||
- Интерактивное создание матрицы планирования
|
||||
- Регрессионный анализ (в разработке)
|
||||
- Визуализация результатов (в разработке)
|
||||
|
||||
### Запуск программы
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
## 🛠 Технологии
|
||||
|
||||
## 📖 Использование
|
||||
|
||||
### Основные параметры
|
||||
1. **Общее количество** — конечный объём/масса готовой среды
|
||||
2. **Единица измерения** — нл, мкл, мл, л
|
||||
3. **Растворитель** — название растворителя (отображается в первой строке таблицы)
|
||||
|
||||
### Таблица реагентов
|
||||
|
||||
| Колонка | Описание |
|
||||
|---------|----------|
|
||||
| Название | Имя реагента |
|
||||
| % | Процентное содержание в конечной среде |
|
||||
| Единица | В каких единицах измеряется реагент |
|
||||
| Коэфф. | Коэффициент пересчёта (например, для перевода объёма в массу) |
|
||||
| Разбавление (x) | Во сколько раз разбавлен исходный реактив (1 = не разбавлен) |
|
||||
| Нужно взять (разб.) | Рассчитанное количество разбавленного реагента |
|
||||
|
||||
### Пример расчёта
|
||||
|
||||
**Задача:** Приготовить 100 мл питательной среды с 10% соли.
|
||||
|
||||
**Вариант 1 — соль не разбавлена:**
|
||||
- Разбавление = 1
|
||||
- Нужно взять: 10 г соли
|
||||
- Растворителя: 100 мл
|
||||
|
||||
**Вариант 2 — есть 10-кратно разбавленная соль:**
|
||||
- Разбавление = 10
|
||||
- Нужно взять: 1 мл разбавленной соли (10 г / 10)
|
||||
- Растворителя: 99 мл (100 мл - 1 мл)
|
||||
|
||||
## 🧠 Особенности реализации
|
||||
|
||||
### Расчёт растворителя
|
||||
|
||||
**Важное примечание:** При расчёте объёма растворителя учитываются только те реагенты, которые заданы в **объёмных единицах** (нл, мкл, мл, л). Реагенты в **единицах массы** (нг, мкг, мг, г, кг) считаются не имеющими объёма и не влияют на расчёт растворителя.
|
||||
|
||||
Это сделано потому, что:
|
||||
1. Массовые реагенты обычно добавляются в твёрдом виде и их объём пренебрежимо мал
|
||||
2. Для корректного учёта объёма твёрдых веществ потребовалась бы плотность
|
||||
|
||||
```python
|
||||
# Пример из model.py
|
||||
if is_volume:
|
||||
# Реагент в объёмных единицах — учитываем его объём
|
||||
reagent_volume_base = diluted_amount * VOLUME_UNITS[reagent.unit]
|
||||
else:
|
||||
# Реагент в массовых единицах — не учитываем (объём = 0)
|
||||
reagent_volume_base = 0
|
||||
```
|
||||
|
||||
### Алгоритм расчёта
|
||||
|
||||
1. **Расчёт неразбавленного состава**
|
||||
- Количество чистого вещества = (процент / 100) × общий объём
|
||||
|
||||
2. **Пересчёт с учётом разбавления**
|
||||
- Количество разбавленного реагента = чистое вещество / фактор разбавления
|
||||
|
||||
3. **Расчёт объёма вносимых реагентов**
|
||||
- Для объёмных реагентов: объём = количество разбавленного реагента
|
||||
- Для массовых реагентов: объём = 0
|
||||
|
||||
4. **Расчёт растворителя**
|
||||
- Объём растворителя = общий объём − сумма объёмов всех реагентов
|
||||
- Python 3.6+
|
||||
- PyQt5 — графический интерфейс
|
||||
- JSON — хранение данных
|
||||
|
||||
## 📁 Структура проекта
|
||||
digital_biochemist_assistant/
|
||||
├── main.py # Точка входа
|
||||
├── main_window.py # Главное окно с выбором инструментов
|
||||
├── controller.py # Контроллер калькулятора сред
|
||||
├── model.py # Модель расчётов
|
||||
├── view.py # Интерфейс калькулятора
|
||||
├── reagent.py # Класс реагента
|
||||
├── experiment_design.py # Инструмент планирования эксперимента
|
||||
└── README.md # Документация
|
||||
|
||||
```
|
||||
nutrient_medium_pyqt/
|
||||
├── main.py # Точка входа, инициализация приложения
|
||||
├── controller.py # Контроллер — связь между моделью и представлением
|
||||
├── model.py # Модель — бизнес-логика и расчёты
|
||||
├── view.py # Представление — GUI на PyQt5
|
||||
├── reagent.py # Класс Reagent для хранения данных
|
||||
└── README.md # Документация
|
||||
```
|
||||
text
|
||||
|
||||
## 🔧 Описание кода
|
||||
## 🎯 Планы развития
|
||||
|
||||
### main.py
|
||||
Инициализирует QApplication, создаёт экземпляр Controller и запускает главный цикл обработки событий.
|
||||
|
||||
### reagent.py
|
||||
```python
|
||||
class Reagent:
|
||||
def __init__(self, name: str, percentage: float, unit: str, conversion_factor: float = 1.0)
|
||||
```
|
||||
Хранит данные о реагенте:
|
||||
- `name` — название
|
||||
- `percentage` — процентное содержание
|
||||
- `unit` — единица измерения
|
||||
- `conversion_factor` — коэффициент пересчёта
|
||||
- `dilution_factor` — коэффициент разбавления (добавляется динамически)
|
||||
|
||||
### model.py
|
||||
Содержит бизнес-логику программы:
|
||||
|
||||
**Константы:**
|
||||
```python
|
||||
VOLUME_UNITS = {'нл': 0.001, 'мкл': 1.0, 'мл': 1000.0, 'л': 1000000.0}
|
||||
MASS_UNITS = {'нг': 0.000001, 'мкг': 0.001, 'мг': 1.0, 'г': 1000.0, 'кг': 1000000.0}
|
||||
```
|
||||
|
||||
**Основные методы:**
|
||||
- `convert_amount()` — пересчёт между единицами измерения через базовые (мкл для объёма, мкг для массы)
|
||||
- `calculate_amounts()` — главный расчётный метод
|
||||
- `save_to_file()` / `load_from_file()` — сериализация в JSON
|
||||
|
||||
### view.py
|
||||
Реализует графический интерфейс на PyQt5:
|
||||
|
||||
**Основные компоненты:**
|
||||
- `QDoubleSpinBox` для общего количества
|
||||
- `QComboBox` для выбора единиц измерения
|
||||
- `QTableWidget` для таблицы реагентов (6 колонок)
|
||||
- Кнопки: Добавить, Удалить, Рассчитать, Сохранить, Загрузить
|
||||
|
||||
**Ключевые методы:**
|
||||
- `add_solvent_row()` — создаёт нередактируемую строку растворителя
|
||||
- `add_new_row()` — добавляет строку реагента
|
||||
- `update_results()` — обновляет результаты расчёта
|
||||
- `update_solvent_result()` — обновляет количество растворителя
|
||||
|
||||
### controller.py
|
||||
Координирует взаимодействие Model и View:
|
||||
|
||||
**Сигналы (слоты):**
|
||||
- `add_row_btn.clicked` → `add_reagent_row()`
|
||||
- `calculate_btn.clicked` → `_perform_calculation()`
|
||||
- `save_btn.clicked` → `save_composition()`
|
||||
- `load_btn.clicked` → `load_composition()`
|
||||
|
||||
**Основные методы:**
|
||||
- `_update_model_from_view()` — переносит данные из таблицы в модель
|
||||
- `_update_view_from_model()` — заполняет таблицу из модели
|
||||
- `_perform_calculation()` — вызывает расчёт и обновляет интерфейс
|
||||
|
||||
## 📝 Формат JSON-файла
|
||||
|
||||
```json
|
||||
{
|
||||
"total_amount": 1000.0,
|
||||
"amount_unit": "мл",
|
||||
"solvent": "Вода",
|
||||
"reagents": [
|
||||
{
|
||||
"name": "NaCl",
|
||||
"percentage": 0.9,
|
||||
"unit": "г",
|
||||
"conversion_factor": 1.0,
|
||||
"dilution_factor": 1.0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 🤝 Вклад в проект
|
||||
|
||||
При обнаружении ошибок или наличии предложений создавайте Issue или Pull Request.
|
||||
- [ ] Расширенный статистический анализ
|
||||
- [ ] Визуализация поверхностей отклика
|
||||
- [ ] Экспорт в Excel и PDF
|
||||
- [ ] База данных реагентов и рецептов
|
||||
- [ ] Графический редактор плана эксперимента
|
||||
- [ ] Модуль концентраций и разбавлений
|
||||
- [ ] Калькулятор растворов
|
||||
|
||||
## 📄 Лицензия
|
||||
|
||||
@@ -196,4 +57,17 @@ MIT License
|
||||
|
||||
## 👨💻 Автор
|
||||
|
||||
Kolobov Artem
|
||||
Kolobov artem
|
||||
|
||||
|
||||
Основные изменения:
|
||||
|
||||
1. Новое название - "Цифровой помощник биохимика" (Digital Biotechnologist Assistant)
|
||||
|
||||
2. Современный дизайн - градиентный фон, иконки, улучшенная стилизация
|
||||
|
||||
3. Расширенный DoE модуль - уже умеет генерировать матрицу планирования для 2^k факторного плана
|
||||
|
||||
4. Улучшенная навигация - вкладки, понятные описания
|
||||
|
||||
5. Профессиональный вид - современный интерфейс, подходящий для лаборатории
|
||||
|
||||
+5
-4
@@ -1,7 +1,7 @@
|
||||
from PyQt5.QtWidgets import QMessageBox, QFileDialog, QTableWidgetItem, QComboBox, QDoubleSpinBox, QLineEdit
|
||||
from PyQt5.QtWidgets import QMessageBox, QFileDialog, QTableWidgetItem, QComboBox, QLineEdit
|
||||
from PyQt5.QtCore import Qt
|
||||
from model import Model
|
||||
from view import MainWindow
|
||||
from view import MainWindow as MediumCalculatorView
|
||||
import json
|
||||
from reagent import Reagent
|
||||
|
||||
@@ -9,8 +9,10 @@ from reagent import Reagent
|
||||
class Controller:
|
||||
def __init__(self):
|
||||
self.model = Model()
|
||||
self.view = MainWindow()
|
||||
self.view = MediumCalculatorView()
|
||||
self._connect_signals()
|
||||
# Убираем автоматический показ окна - теперь он вызывается из главного меню
|
||||
# self.view.show()
|
||||
|
||||
def _connect_signals(self):
|
||||
"""Подключает обработчики событий интерфейса"""
|
||||
@@ -19,7 +21,6 @@ class Controller:
|
||||
self.view.calculate_btn.clicked.connect(self._perform_calculation)
|
||||
self.view.save_btn.clicked.connect(self.save_composition)
|
||||
self.view.load_btn.clicked.connect(self.load_composition)
|
||||
# Подключаем обновление названия растворителя в таблице при изменении поля
|
||||
self.view.solvent_input.textChanged.connect(self.view.update_solvent_name)
|
||||
|
||||
def add_reagent_row(self):
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
from PyQt5.QtWidgets import (QMainWindow, QVBoxLayout, QHBoxLayout,
|
||||
QPushButton, QLabel, QWidget, QMessageBox,
|
||||
QTableWidget, QTableWidgetItem, QGroupBox,
|
||||
QSpinBox, QDoubleSpinBox, QComboBox, QLineEdit,
|
||||
QTextEdit, QTabWidget, QFormLayout)
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QFont
|
||||
|
||||
|
||||
class ExperimentDesignWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Планирование эксперимента - Цифровой помощник биохимика")
|
||||
self.setGeometry(200, 100, 1000, 750)
|
||||
self.setStyleSheet("""
|
||||
QMainWindow {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
QGroupBox {
|
||||
font-weight: bold;
|
||||
border: 2px solid #ccc;
|
||||
border-radius: 5px;
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
left: 10px;
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
QTableWidget {
|
||||
gridline-color: #ddd;
|
||||
}
|
||||
""")
|
||||
self._init_ui()
|
||||
|
||||
def _init_ui(self):
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
layout = QVBoxLayout(central_widget)
|
||||
|
||||
# Заголовок
|
||||
title_label = QLabel("📈 Планирование полнофакторного эксперимента (DoE)")
|
||||
title_font = QFont()
|
||||
title_font.setPointSize(18)
|
||||
title_font.setBold(True)
|
||||
title_label.setFont(title_font)
|
||||
title_label.setAlignment(Qt.AlignCenter)
|
||||
title_label.setStyleSheet("color: #2E7D32;")
|
||||
layout.addWidget(title_label)
|
||||
|
||||
# Вкладки
|
||||
tabs = QTabWidget()
|
||||
|
||||
# Вкладка 1: Параметры эксперимента
|
||||
params_tab = QWidget()
|
||||
params_layout = QVBoxLayout(params_tab)
|
||||
|
||||
# Группа факторов
|
||||
factors_group = QGroupBox("Факторы эксперимента (независимые переменные)")
|
||||
factors_layout = QVBoxLayout()
|
||||
|
||||
# Информация
|
||||
info_label = QLabel("Определите факторы, которые влияют на ваш эксперимент:")
|
||||
info_label.setStyleSheet("color: #555; font-weight: normal;")
|
||||
factors_layout.addWidget(info_label)
|
||||
|
||||
self.factors_table = QTableWidget()
|
||||
self.factors_table.setColumnCount(4)
|
||||
self.factors_table.setHorizontalHeaderLabels(["Фактор", "Нижний уровень (-1)", "Верхний уровень (+1)", "Единица измерения"])
|
||||
self.factors_table.setRowCount(3)
|
||||
|
||||
# Пример данных
|
||||
sample_factors = [
|
||||
["Температура", "25", "37", "°C"],
|
||||
["pH", "6.5", "7.5", ""],
|
||||
["Концентрация глюкозы", "5", "20", "г/л"]
|
||||
]
|
||||
|
||||
for i, factor in enumerate(sample_factors):
|
||||
for j, value in enumerate(factor):
|
||||
self.factors_table.setItem(i, j, QTableWidgetItem(value))
|
||||
|
||||
factors_layout.addWidget(self.factors_table)
|
||||
|
||||
# Кнопки для управления факторами
|
||||
factor_buttons = QHBoxLayout()
|
||||
add_factor_btn = QPushButton("+ Добавить фактор")
|
||||
add_factor_btn.clicked.connect(self.add_factor_row)
|
||||
remove_factor_btn = QPushButton("- Удалить последний")
|
||||
remove_factor_btn.clicked.connect(self.remove_factor_row)
|
||||
factor_buttons.addWidget(add_factor_btn)
|
||||
factor_buttons.addWidget(remove_factor_btn)
|
||||
factor_buttons.addStretch()
|
||||
factors_layout.addLayout(factor_buttons)
|
||||
|
||||
factors_group.setLayout(factors_layout)
|
||||
params_layout.addWidget(factors_group)
|
||||
|
||||
# Группа откликов
|
||||
responses_group = QGroupBox("Отклики (зависимые переменные)")
|
||||
responses_layout = QVBoxLayout()
|
||||
|
||||
self.responses_table = QTableWidget()
|
||||
self.responses_table.setColumnCount(2)
|
||||
self.responses_table.setHorizontalHeaderLabels(["Отклик", "Единица измерения"])
|
||||
self.responses_table.setRowCount(2)
|
||||
|
||||
sample_responses = [
|
||||
["Оптическая плотность (OD600)", ""],
|
||||
["Концентрация целевого продукта", "мг/мл"]
|
||||
]
|
||||
|
||||
for i, response in enumerate(sample_responses):
|
||||
for j, value in enumerate(response):
|
||||
self.responses_table.setItem(i, j, QTableWidgetItem(value))
|
||||
|
||||
responses_layout.addWidget(self.responses_table)
|
||||
|
||||
# Кнопки для управления откликами
|
||||
response_buttons = QHBoxLayout()
|
||||
add_response_btn = QPushButton("+ Добавить отклик")
|
||||
add_response_btn.clicked.connect(self.add_response_row)
|
||||
remove_response_btn = QPushButton("- Удалить последний")
|
||||
remove_response_btn.clicked.connect(self.remove_response_row)
|
||||
response_buttons.addWidget(add_response_btn)
|
||||
response_buttons.addWidget(remove_response_btn)
|
||||
response_buttons.addStretch()
|
||||
responses_layout.addLayout(response_buttons)
|
||||
|
||||
responses_group.setLayout(responses_layout)
|
||||
params_layout.addWidget(responses_group)
|
||||
|
||||
tabs.addTab(params_tab, "📝 Параметры эксперимента")
|
||||
|
||||
# Вкладка 2: Матрица планирования
|
||||
plan_tab = QWidget()
|
||||
plan_layout = QVBoxLayout(plan_tab)
|
||||
|
||||
plan_info = QLabel("Полнофакторный план эксперимента (Full Factorial Design)")
|
||||
plan_info.setAlignment(Qt.AlignCenter)
|
||||
plan_info.setStyleSheet("font-weight: bold; font-size: 14px; padding: 10px;")
|
||||
plan_layout.addWidget(plan_info)
|
||||
|
||||
self.design_matrix = QTableWidget()
|
||||
plan_layout.addWidget(self.design_matrix)
|
||||
|
||||
generate_btn = QPushButton("🔄 Сгенерировать план эксперимента")
|
||||
generate_btn.clicked.connect(self.generate_design_matrix)
|
||||
plan_layout.addWidget(generate_btn)
|
||||
|
||||
tabs.addTab(plan_tab, "📊 Матрица планирования")
|
||||
|
||||
# Вкладка 3: Анализ результатов
|
||||
analysis_tab = QWidget()
|
||||
analysis_layout = QVBoxLayout(analysis_tab)
|
||||
|
||||
analysis_info = QLabel("Регрессионный анализ и визуализация результатов")
|
||||
analysis_info.setAlignment(Qt.AlignCenter)
|
||||
analysis_info.setStyleSheet("font-weight: bold; font-size: 14px; padding: 10px;")
|
||||
analysis_layout.addWidget(analysis_info)
|
||||
|
||||
# Заглушка для анализа
|
||||
placeholder = QLabel("Здесь будет:\n\n"
|
||||
"• Множественная линейная регрессия\n"
|
||||
"• Анализ взаимодействий факторов\n"
|
||||
"• ANOVA (дисперсионный анализ)\n"
|
||||
"• Построение поверхностей отклика\n"
|
||||
"• Графики главных эффектов\n"
|
||||
"• Оптимизация параметров")
|
||||
placeholder.setAlignment(Qt.AlignCenter)
|
||||
placeholder.setStyleSheet("color: #666; font-size: 12px; border: 1px dashed #ccc; padding: 20px;")
|
||||
analysis_layout.addWidget(placeholder)
|
||||
|
||||
analyze_btn = QPushButton("📈 Провести анализ (в разработке)")
|
||||
analyze_btn.clicked.connect(self.show_placeholder_message)
|
||||
analysis_layout.addWidget(analyze_btn)
|
||||
|
||||
tabs.addTab(analysis_tab, "📐 Анализ результатов")
|
||||
|
||||
# Вкладка 4: Визуализация
|
||||
viz_tab = QWidget()
|
||||
viz_layout = QVBoxLayout(viz_tab)
|
||||
|
||||
viz_info = QLabel("Интерактивная визуализация данных")
|
||||
viz_info.setAlignment(Qt.AlignCenter)
|
||||
viz_info.setStyleSheet("font-weight: bold; font-size: 14px; padding: 10px;")
|
||||
viz_layout.addWidget(viz_info)
|
||||
|
||||
viz_placeholder = QLabel("Здесь будут:\n\n"
|
||||
"• 2D и 3D графики поверхностей отклика\n"
|
||||
"• Контурные графики\n"
|
||||
"• Диаграммы Парето\n"
|
||||
"• Графики нормальной вероятности")
|
||||
viz_placeholder.setAlignment(Qt.AlignCenter)
|
||||
viz_placeholder.setStyleSheet("color: #666; font-size: 12px; border: 1px dashed #ccc; padding: 20px;")
|
||||
viz_layout.addWidget(viz_placeholder)
|
||||
|
||||
tabs.addTab(viz_tab, "📈 Визуализация")
|
||||
|
||||
layout.addWidget(tabs)
|
||||
|
||||
# Кнопки управления
|
||||
btn_layout = QHBoxLayout()
|
||||
|
||||
export_btn = QPushButton("💾 Экспорт отчёта (PDF/Excel)")
|
||||
export_btn.clicked.connect(self.show_placeholder_message)
|
||||
btn_layout.addWidget(export_btn)
|
||||
|
||||
save_btn = QPushButton("💿 Сохранить проект")
|
||||
save_btn.clicked.connect(self.show_placeholder_message)
|
||||
btn_layout.addWidget(save_btn)
|
||||
|
||||
load_btn = QPushButton("📂 Загрузить проект")
|
||||
load_btn.clicked.connect(self.show_placeholder_message)
|
||||
btn_layout.addWidget(load_btn)
|
||||
|
||||
btn_layout.addStretch()
|
||||
|
||||
close_btn = QPushButton("❌ Закрыть")
|
||||
close_btn.clicked.connect(self.close)
|
||||
btn_layout.addWidget(close_btn)
|
||||
|
||||
layout.addLayout(btn_layout)
|
||||
|
||||
def add_factor_row(self):
|
||||
"""Добавляет строку для нового фактора"""
|
||||
row = self.factors_table.rowCount()
|
||||
self.factors_table.insertRow(row)
|
||||
self.factors_table.setItem(row, 0, QTableWidgetItem(f"Фактор_{row+1}"))
|
||||
self.factors_table.setItem(row, 1, QTableWidgetItem("0"))
|
||||
self.factors_table.setItem(row, 2, QTableWidgetItem("1"))
|
||||
self.factors_table.setItem(row, 3, QTableWidgetItem(""))
|
||||
|
||||
def remove_factor_row(self):
|
||||
"""Удаляет последнюю строку факторов"""
|
||||
if self.factors_table.rowCount() > 1:
|
||||
self.factors_table.removeRow(self.factors_table.rowCount() - 1)
|
||||
|
||||
def add_response_row(self):
|
||||
"""Добавляет строку для нового отклика"""
|
||||
row = self.responses_table.rowCount()
|
||||
self.responses_table.insertRow(row)
|
||||
self.responses_table.setItem(row, 0, QTableWidgetItem(f"Отклик_{row+1}"))
|
||||
self.responses_table.setItem(row, 1, QTableWidgetItem(""))
|
||||
|
||||
def remove_response_row(self):
|
||||
"""Удаляет последнюю строку откликов"""
|
||||
if self.responses_table.rowCount() > 1:
|
||||
self.responses_table.removeRow(self.responses_table.rowCount() - 1)
|
||||
|
||||
def generate_design_matrix(self):
|
||||
"""Генерирует матрицу планирования (заглушка)"""
|
||||
n_factors = self.factors_table.rowCount()
|
||||
|
||||
if n_factors == 0:
|
||||
QMessageBox.warning(self, "Предупреждение", "Добавьте хотя бы один фактор!")
|
||||
return
|
||||
|
||||
# Полнофакторный план: 2^k опытов
|
||||
n_experiments = 2 ** n_factors
|
||||
|
||||
self.design_matrix.setRowCount(n_experiments)
|
||||
self.design_matrix.setColumnCount(n_factors + 1)
|
||||
|
||||
# Заголовки
|
||||
headers = ["Опыт №"] + [self.factors_table.item(i, 0).text() if self.factors_table.item(i, 0) else f"Фактор_{i+1}"
|
||||
for i in range(n_factors)]
|
||||
self.design_matrix.setHorizontalHeaderLabels(headers)
|
||||
|
||||
# Заполняем матрицу (простой 2^k план)
|
||||
for exp in range(n_experiments):
|
||||
# Номер опыта
|
||||
self.design_matrix.setItem(exp, 0, QTableWidgetItem(str(exp + 1)))
|
||||
|
||||
# Кодированные уровни факторов (-1 или +1)
|
||||
for factor in range(n_factors):
|
||||
level = -1 if (exp // (2 ** factor)) % 2 == 0 else 1
|
||||
self.design_matrix.setItem(exp, factor + 1, QTableWidgetItem(str(level)))
|
||||
|
||||
self.design_matrix.resizeColumnsToContents()
|
||||
|
||||
QMessageBox.information(self, "Успех",
|
||||
f"Сгенерирован план для {n_factors} факторов\n"
|
||||
f"Общее количество опытов: {n_experiments}")
|
||||
|
||||
def show_placeholder_message(self):
|
||||
"""Показывает сообщение о том, что функция в разработке"""
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"В разработке",
|
||||
"🧪 Биотехнологические инструменты в стадии активной разработки!\n\n"
|
||||
"В ближайшее время здесь появится:\n\n"
|
||||
"✅ Полнофакторный план (2^k факторный дизайн)\n"
|
||||
"✅ Регрессионный анализ и ANOVA\n"
|
||||
"✅ Построение поверхностей отклика\n"
|
||||
"✅ Оптимизация параметров\n"
|
||||
"✅ Экспорт в Excel и PDF\n"
|
||||
"✅ Визуализация 2D/3D графиков\n\n"
|
||||
"Следите за обновлениями!"
|
||||
)
|
||||
@@ -1,16 +1,14 @@
|
||||
# main.py
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from controller import Controller
|
||||
from main_window import MainWindow
|
||||
|
||||
def main():
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
# Создаём контроллер - он сам создаст модель и представление
|
||||
controller = Controller()
|
||||
|
||||
# Показываем главное окно
|
||||
controller.view.show()
|
||||
# Создаём главное окно с выбором режима
|
||||
main_window = MainWindow()
|
||||
main_window.show()
|
||||
|
||||
# Запускаем цикл обработки событий
|
||||
sys.exit(app.exec_())
|
||||
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
from PyQt5.QtWidgets import (QMainWindow, QVBoxLayout, QHBoxLayout,
|
||||
QPushButton, QLabel, QWidget, QFrame)
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QFont, QIcon
|
||||
from controller import Controller
|
||||
from experiment_design import ExperimentDesignWindow
|
||||
|
||||
|
||||
class DigitalBiochemistAssistant(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Цифровой помощник биохимика - Главное меню")
|
||||
self.setGeometry(300, 200, 700, 500)
|
||||
self.setStyleSheet("""
|
||||
QMainWindow {
|
||||
background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #e8f4f8, stop:1 #f0f0f0);
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #2196F3;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 15px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
border-radius: 8px;
|
||||
font-family: 'Segoe UI', Arial;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #1976D2;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #0D47A1;
|
||||
}
|
||||
QLabel {
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
font-family: 'Segoe UI', Arial;
|
||||
}
|
||||
""")
|
||||
self._init_ui()
|
||||
|
||||
def _init_ui(self):
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
layout = QVBoxLayout(central_widget)
|
||||
layout.setSpacing(20)
|
||||
layout.setContentsMargins(50, 50, 50, 50)
|
||||
|
||||
# Заголовок
|
||||
title_label = QLabel("🧬 Цифровой помощник биохимика 🧪")
|
||||
title_font = QFont()
|
||||
title_font.setPointSize(20)
|
||||
title_font.setBold(True)
|
||||
title_label.setFont(title_font)
|
||||
title_label.setAlignment(Qt.AlignCenter)
|
||||
title_label.setStyleSheet("color: #1565C0;")
|
||||
layout.addWidget(title_label)
|
||||
|
||||
# Подзаголовок
|
||||
subtitle_label = QLabel("Биотехнологические инструменты для лаборатории")
|
||||
subtitle_font = QFont()
|
||||
subtitle_font.setPointSize(12)
|
||||
subtitle_label.setFont(subtitle_font)
|
||||
subtitle_label.setAlignment(Qt.AlignCenter)
|
||||
subtitle_label.setStyleSheet("color: #666;")
|
||||
layout.addWidget(subtitle_label)
|
||||
|
||||
layout.addSpacing(20)
|
||||
|
||||
# Кнопка 1: Калькулятор питательных сред
|
||||
btn_medium = QPushButton("🥼 Калькулятор питательных сред")
|
||||
btn_medium.setMinimumHeight(80)
|
||||
btn_medium.clicked.connect(self.open_medium_calculator)
|
||||
layout.addWidget(btn_medium)
|
||||
|
||||
# Описание кнопки 1
|
||||
desc1_label = QLabel("Расчёт состава питательной среды с учётом процентного содержания,\n"
|
||||
"разбавления реагентов и автоматическим расчётом растворителя")
|
||||
desc1_label.setAlignment(Qt.AlignCenter)
|
||||
desc1_label.setWordWrap(True)
|
||||
desc1_label.setStyleSheet("color: #555; font-size: 11px;")
|
||||
layout.addWidget(desc1_label)
|
||||
|
||||
layout.addSpacing(15)
|
||||
|
||||
# Кнопка 2: Планирование эксперимента
|
||||
btn_experiment = QPushButton("📊 Планирование эксперимента (DoE)")
|
||||
btn_experiment.setMinimumHeight(80)
|
||||
btn_experiment.clicked.connect(self.open_experiment_designer)
|
||||
layout.addWidget(btn_experiment)
|
||||
|
||||
# Описание кнопки 2
|
||||
desc2_label = QLabel("Дизайн эксперимента, оптимизация процессов,\n"
|
||||
"многомерный анализ и визуализация")
|
||||
desc2_label.setAlignment(Qt.AlignCenter)
|
||||
desc2_label.setWordWrap(True)
|
||||
desc2_label.setStyleSheet("color: #555; font-size: 11px;")
|
||||
layout.addWidget(desc2_label)
|
||||
|
||||
layout.addSpacing(15)
|
||||
|
||||
# Линия-разделитель
|
||||
line = QFrame()
|
||||
line.setFrameShape(QFrame.HLine)
|
||||
line.setFrameShadow(QFrame.Sunken)
|
||||
layout.addWidget(line)
|
||||
|
||||
# Нижняя панель
|
||||
bottom_layout = QHBoxLayout()
|
||||
|
||||
# Информация о версии
|
||||
version_label = QLabel("Версия 2.0 | © 2024 Цифровой помощник биохимика")
|
||||
version_label.setStyleSheet("color: #999; font-size: 10px;")
|
||||
bottom_layout.addWidget(version_label)
|
||||
|
||||
bottom_layout.addStretch()
|
||||
|
||||
# Кнопка выхода
|
||||
btn_exit = QPushButton("Выход")
|
||||
btn_exit.setMaximumWidth(150)
|
||||
btn_exit.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #f44336;
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #da190b;
|
||||
}
|
||||
""")
|
||||
btn_exit.clicked.connect(self.close)
|
||||
bottom_layout.addWidget(btn_exit)
|
||||
|
||||
layout.addLayout(bottom_layout)
|
||||
|
||||
def open_medium_calculator(self):
|
||||
"""Открывает калькулятор питательной среды"""
|
||||
self.medium_calculator = Controller()
|
||||
self.medium_calculator.view.show()
|
||||
|
||||
def open_experiment_designer(self):
|
||||
"""Открывает окно планирования эксперимента"""
|
||||
self.experiment_window = ExperimentDesignWindow()
|
||||
self.experiment_window.show()
|
||||
Reference in New Issue
Block a user