Расширен функционал, начата разработка функционала по оптимизации

This commit is contained in:
2026-05-06 00:30:44 +05:00
parent e8282a72a1
commit 361b934e8a
5 changed files with 522 additions and 192 deletions
+312
View File
@@ -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"
"Следите за обновлениями!"
)