From 361b934e8a77c172aff5a4dd881b7f85dcf2d822 Mon Sep 17 00:00:00 2001 From: Artemiy Date: Wed, 6 May 2026 00:30:44 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=88=D0=B8=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE=D0=BD=D0=B0?= =?UTF-8?q?=D0=BB,=20=D0=BD=D0=B0=D1=87=D0=B0=D1=82=D0=B0=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B7=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B0=20=D1=84=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D1=86=D0=B8=D0=BE=D0=BD=D0=B0=D0=BB=D0=B0=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D0=BE=D0=BF=D1=82=D0=B8=D0=BC=D0=B8=D0=B7=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 232 ++++++++------------------------ controller.py | 11 +- experiment_design.py | 312 +++++++++++++++++++++++++++++++++++++++++++ main.py | 14 +- main_window.py | 145 ++++++++++++++++++++ 5 files changed, 522 insertions(+), 192 deletions(-) create mode 100644 experiment_design.py create mode 100644 main_window.py diff --git a/README.md b/README.md index 7387f72..6215ded 100644 --- a/README.md +++ b/README.md @@ -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. Профессиональный вид - современный интерфейс, подходящий для лаборатории diff --git a/controller.py b/controller.py index a9b965d..08e4412 100644 --- a/controller.py +++ b/controller.py @@ -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,9 +21,8 @@ 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): """Добавляет новую строку для реагента""" self.view.add_new_row() diff --git a/experiment_design.py b/experiment_design.py new file mode 100644 index 0000000..5bdd856 --- /dev/null +++ b/experiment_design.py @@ -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" + "Следите за обновлениями!" + ) diff --git a/main.py b/main.py index 91576e5..f2dee33 100755 --- a/main.py +++ b/main.py @@ -1,17 +1,15 @@ # 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_()) diff --git a/main_window.py b/main_window.py new file mode 100644 index 0000000..ee729a3 --- /dev/null +++ b/main_window.py @@ -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()