Оптимизмрован код, исправлены параметры отображения факторов экспримента

This commit is contained in:
2026-05-26 22:50:16 +05:00
parent 63b5f0a49f
commit 1ddfe20a8d
3 changed files with 75 additions and 100 deletions
+8 -8
View File
@@ -16,7 +16,7 @@ import numpy as np
# Типы расчёта шага
FACTOR_TYPES = {
'absolute': 'абс', # абсолютный шаг
'absolute': 'ед.', # абсолютный шаг
'relative': '%', # относительный шаг (процент от нулевого уровня)
}
@@ -33,7 +33,7 @@ def calculate_factor_levels(
Параметры:
center_value: нулевой уровень фактора (центральная точка)
step_value: значение шага
step_type: тип шага ("абс" - абсолютный, "%" - относительный)
step_type: тип шага ("ед." - абсолютный, "%" - относительный)
base_value: базовое значение для относительного шага (если None, используется center_value)
Возвращает:
@@ -42,14 +42,14 @@ def calculate_factor_levels(
Пример:
>>> calculate_factor_levels(100, 10, "%")
(110.0, 90.0)
>>> calculate_factor_levels(100, 20, "абс")
>>> calculate_factor_levels(100, 20, "ед.")
(120.0, 80.0)
"""
# Определяем абсолютное значение шага
if step_type == "%":
base = base_value if base_value is not None else center_value
step_abs = center_value * step_value / 100
else: # "абс" или "absolute"
else: # "ед." или "absolute"
step_abs = step_value
high_level = center_value + step_abs
@@ -79,7 +79,7 @@ def generate_factorial_design(
- center (float): нулевой уровень (0)
- unit (str): единица измерения
- step (float, опционально): шаг варьирования
- step_type (str, опционально): тип шага ("абс" или "%")
- step_type (str, опционально): тип шага ("ед." или "%")
Минимально необходимые ключи: name, low, high, center, unit
@@ -297,7 +297,7 @@ def create_factor_from_reagent(
step_value = center_value * step_percent / 100
high_level, low_level = calculate_factor_levels(
center_value, step_value, "абс"
center_value, step_value, "ед."
)
return {
@@ -306,7 +306,7 @@ def create_factor_from_reagent(
'low': low_level,
'high': high_level,
'step': step_value,
'step_type': 'абс',
'step_type': 'ед.',
'unit': reagent.get('unit', volume_unit),
'percentage': reagent.get('percentage', 0),
'dilution_factor': reagent.get('dilution_factor', 1.0)
@@ -336,7 +336,7 @@ def create_factor_from_reagent(
step_value = center_value * step_percent / 100
high_level, low_level = calculate_factor_levels(
center_value, step_value, "абс"
center_value, step_value, "ед."
)
return {
+55 -86
View File
@@ -189,24 +189,22 @@ class MainWindow(QMainWindow):
return tab
def _add_reagent_row(self):
def _add_reagent_row(self, name = "", percentage = "0", unit = "мл", dilution = "1", amount = ""):
row = self.reagents_table.rowCount()
self.reagents_table.insertRow(row)
self.reagents_table.setItem(row, 0, QTableWidgetItem(f"Реагент_{row+1}"))
self.reagents_table.setItem(row, 1, QTableWidgetItem("0"))
if name == "":
self.reagents_table.setItem(row, 0, QTableWidgetItem(f"Реагент_{row+1}"))
else:
self.reagents_table.setItem(row, 0, QTableWidgetItem(name))
self.reagents_table.setItem(row, 1, QTableWidgetItem(percentage))
unit_combo = QComboBox()
unit_combo.setStyleSheet("""
QComboBox {
padding: 1px;
}
""")
unit_combo.addItems(["мг", "г", "кг", "мкг", "нг", "мл", "мкл", "л", "нл"])
unit_combo.setCurrentText("мл")
unit_combo.addItems(["мг", "г", "кг", "мкг", "нг", "мл", "мкл", "л"])
unit_combo.setCurrentText(unit)
self.reagents_table.setCellWidget(row, 2, unit_combo)
self.reagents_table.setItem(row, 3, QTableWidgetItem("1"))
self.reagents_table.setItem(row, 4, QTableWidgetItem(""))
self.reagents_table.setItem(row, 3, QTableWidgetItem(dilution))
self.reagents_table.setItem(row, 4, QTableWidgetItem(amount))
def _remove_reagent_row(self):
for row in sorted(set(i.row() for i in self.reagents_table.selectedItems()), reverse=True):
@@ -280,13 +278,12 @@ class MainWindow(QMainWindow):
# Сохраняем результаты для передачи в DoE
self.last_medium_result = result
QMessageBox.information(self, "Успех", "Расчёт выполнен успешно!")
# QMessageBox.information(self, "Успех", "Расчёт выполнен успешно!")
except Exception as e:
QMessageBox.critical(self, "Ошибка", str(e))
# ========== ВКЛАДКА 2: ФАКТОРЫ ЭКСПЕРИМЕНТА ==========
def _create_factors_tab(self):
tab = QWidget()
layout = QVBoxLayout(tab)
@@ -313,7 +310,7 @@ class MainWindow(QMainWindow):
"Фактор", "%", "Разбавление", "Нулевой уровень", "Шаг",
"Тип шага", "Верхний (+1)", "Нижний (-1)", "Ед. изм."
])
self.factors_table.setAlternatingRowColors(True)
#self.factors_table.setAlternatingRowColors(True)
factors_layout.addWidget(self.factors_table)
@@ -361,30 +358,38 @@ class MainWindow(QMainWindow):
return tab
def _add_factor_row(self):
def _add_factor_row(self, name = "", percentage = "0", dilution = "0", center = "0", step ="0", step_type = "%", high = "", low = "", unit= "г"):
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("0"))
self.factors_table.setItem(row, 4, QTableWidgetItem("1"))
if name == "":
self.factors_table.setItem(row, 0, QTableWidgetItem(f"Фактор_{row+1}"))
else:
self.factors_table.setItem(row, 0, QTableWidgetItem(name))
self.factors_table.setItem(row, 1, QTableWidgetItem(percentage))
self.factors_table.setItem(row, 2, QTableWidgetItem(dilution))
self.factors_table.setItem(row, 3, QTableWidgetItem(center))
self.factors_table.setItem(row, 4, QTableWidgetItem(step))
step_type_combo = QComboBox()
step_type_combo.addItems(["абс", "%"])
step_type_combo.addItems(["%", "ед."])
step_type_combo.setCurrentText(step_type)
self.factors_table.setCellWidget(row, 5, step_type_combo)
high_item = QTableWidgetItem("1")
high_item = QTableWidgetItem(high)
high_item.setFlags(high_item.flags() & ~Qt.ItemIsEditable)
high_item.setBackground(QColor(240, 240, 240))
self.factors_table.setItem(row, 6, high_item)
low_item = QTableWidgetItem("-1")
low_item = QTableWidgetItem(low)
low_item.setFlags(low_item.flags() & ~Qt.ItemIsEditable)
low_item.setBackground(QColor(240, 240, 240))
self.factors_table.setItem(row, 7, low_item)
self.factors_table.setItem(row, 8, QTableWidgetItem(""))
unit_measure = QComboBox()
unit_measure.addItems(["мкл", "мл", "л", "мг", "г", "кг"])
unit_measure.setCurrentText(unit)
self.factors_table.setCellWidget(row, 8, unit_measure)
return row
def _remove_factor_row(self):
for row in sorted(set(i.row() for i in self.factors_table.selectedItems()), reverse=True):
@@ -404,37 +409,20 @@ class MainWindow(QMainWindow):
self.factors_table.setRowCount(0)
for reagent in result['reagents']:
row = self.factors_table.rowCount()
self.factors_table.insertRow(row)
name = reagent['name']
if reagent.get('dilution_factor', 1.0) != 1.0:
name += f" (разб. ×{reagent['dilution_factor']:.2f})"
self.factors_table.setItem(row, 0, QTableWidgetItem(name))
self.factors_table.setItem(row, 1, QTableWidgetItem(f"{reagent['percentage']:.2f}"))
self.factors_table.setItem(row, 2, QTableWidgetItem(f"{reagent.get('dilution_factor', 1.0):.3f}"))
# Нулевой уровень - исходное количество (неразбавленное)
center = reagent.get('undiluted_amount', reagent['calculated_amount'])
self.factors_table.setItem(row, 3, QTableWidgetItem(self._format_number(center)))
# Шаг - 10% от нулевого уровня
step = center * 0.1
self.factors_table.setItem(row, 4, QTableWidgetItem(self._format_number(step)))
# Тип шага - абсолютный
step_combo = self.factors_table.cellWidget(row, 5)
if step_combo:
step_combo.setCurrentText("абс")
# Верхний и нижний уровни
self.factors_table.setItem(row, 6, QTableWidgetItem(self._format_number(center + step)))
self.factors_table.setItem(row, 7, QTableWidgetItem(self._format_number(center - step)))
self.factors_table.setItem(row, 8, QTableWidgetItem(result['total_unit']))
QMessageBox.information(self, "Успех",
f"Импортировано {len(result['reagents'])} факторов из калькулятора сред")
name += f" (разб. ×{reagent['dilution_factor']:.g})"
percentage = f"{reagent['percentage']:.g}"
dilution = f"{reagent.get('dilution_factor', 1.0):g}"
center = str(reagent.get('calculated_amount'))
step = ""
step_type = "%"
high = ""
low = ""
unit = reagent["unit"]
self._add_factor_row(name, percentage, dilution, center, step, step_type, high, low, unit)
# QMessageBox.information(self, "Успех",
# f"Импортировано {len(result['reagents'])} факторов из калькулятора сред")
def _get_factors_from_table(self) -> List[Dict]:
"""Собирает данные факторов из таблицы"""
@@ -466,7 +454,7 @@ class MainWindow(QMainWindow):
'low': float(low_text),
'unit': unit_item.text() if unit_item else "",
'step': float(step_item.text()) if step_item and step_item.text() else 0,
'step_type': step_combo.currentText() if step_combo else "абс"
'step_type': step_combo.currentText() if step_combo else "ед."
}
factors.append(factor)
except (ValueError, AttributeError) as e:
@@ -750,7 +738,7 @@ class MainWindow(QMainWindow):
try:
project = ProjectData.load_from_file(filename)
self._apply_project_data(project)
QMessageBox.information(self, "Успех", f"Проект загружен из {filename}")
# QMessageBox.information(self, "Успех", f"Проект загружен из {filename}")
except FileNotFoundError:
QMessageBox.critical(self, "Ошибка", f"Файл не найден: {filename}")
except Exception as e:
@@ -788,7 +776,7 @@ class MainWindow(QMainWindow):
low_item = self.factors_table.item(row, 7)
high_item = self.factors_table.item(row, 6)
step_item = self.factors_table.item(row, 4)
unit_item = self.factors_table.item(row, 8)
unit_item = self.factors_table.cellWidget(row, 8)
step_combo = self.factors_table.cellWidget(row, 5)
percent_item = self.factors_table.item(row, 1)
dilution_item = self.factors_table.item(row, 2)
@@ -801,8 +789,8 @@ class MainWindow(QMainWindow):
low=float(low_item.text()) if low_item and low_item.text() else 0,
high=float(high_item.text()) if high_item and high_item.text() else 0,
step=float(step_item.text()) if step_item and step_item.text() else 0,
step_type=step_combo.currentText() if step_combo else "абс",
unit=unit_item.text() if unit_item else "",
step_type=step_combo.currentText() if step_combo else "ед.",
unit=unit_item.currentText() if unit_item else "",
percentage=float(percent_item.text()) if percent_item and percent_item.text() else None,
dilution_factor=float(dilution_item.text()) if dilution_item and dilution_item.text() else None
)
@@ -874,32 +862,13 @@ class MainWindow(QMainWindow):
# Применяем данные факторов эксперимента
self.factors_table.setRowCount(0)
for factor in project.experiment_factors:
row = self.factors_table.rowCount()
self.factors_table.insertRow(row)
self.factors_table.setItem(row, 0, QTableWidgetItem(factor.name))
if factor.percentage is not None:
self.factors_table.setItem(row, 1, QTableWidgetItem(str(factor.percentage)))
else:
self.factors_table.setItem(row, 1, QTableWidgetItem("0"))
percentage = str(factor.percentage)
if factor.dilution_factor is not None:
self.factors_table.setItem(row, 2, QTableWidgetItem(str(factor.dilution_factor)))
else:
self.factors_table.setItem(row, 2, QTableWidgetItem("1"))
self.factors_table.setItem(row, 3, QTableWidgetItem(str(factor.center)))
self.factors_table.setItem(row, 4, QTableWidgetItem(str(factor.step)))
step_combo = QComboBox()
step_combo.addItems(["абс", "%"])
step_combo.setCurrentText(factor.step_type)
self.factors_table.setCellWidget(row, 5, step_combo)
self.factors_table.setItem(row, 6, QTableWidgetItem(str(factor.high)))
self.factors_table.setItem(row, 7, QTableWidgetItem(str(factor.low)))
self.factors_table.setItem(row, 8, QTableWidgetItem(factor.unit))
dilution = str(factor.dilution_factor)
self._add_factor_row(factor.name, percentage, dilution, str(factor.center),
str(factor.step), factor.step_type, str(factor.high),
str(factor.low), factor.unit)
# Применяем настройки эксперимента
self.center_points_spin.setValue(project.experiment_center_points)
self.randomize_check.setChecked(project.experiment_randomize)
@@ -916,9 +885,9 @@ class MainWindow(QMainWindow):
if i < self.results_table.rowCount() and row_results:
self.results_table.setItem(i, 1, QTableWidgetItem(str(row_results[0])))
# Переключаемся на вкладку с факторами
# Переключаемся на вкладку с экспериментом
if self.tab_widget:
self.tab_widget.setCurrentIndex(1)
self.tab_widget.setCurrentIndex(0)
def _refresh_design_matrix(self):
"""Обновляет отображение матрицы планирования"""
+12 -6
View File
@@ -12,8 +12,8 @@ class Colors:
"""Цветовая палитра приложения"""
# Основные цвета (Primary)
PRIMARY = "#3498db" # Синий - основной акцент
PRIMARY_DARK = "#2980b9" # Тёмно-синий (наведение)
PRIMARY = "#999999" # Синий - основной акцент
PRIMARY_DARK = "#777777" # Тёмно-синий (наведение)
PRIMARY_LIGHT = "#5dade2" # Светло-синий
PRIMARY_BG = "#ebf5fb" # Фоновый для primary элементов
@@ -216,10 +216,10 @@ class ButtonStyles:
{ButtonStyles._base_style()}
}}
QPushButton:hover {{
background-color: {Colors.PRIMARY_DARK};
background-color: {Colors.PRIMARY_LIGHT};
}}
QPushButton:pressed {{
background-color: {Colors.PRIMARY_LIGHT};
background-color: {Colors.PRIMARY_DARK};
}}
QPushButton:disabled {{
background-color: {Colors.GRAY_400};
@@ -326,7 +326,14 @@ class InputStyles:
@staticmethod
def default():
return f"""
QLineEdit, QDoubleSpinBox, QSpinBox, QComboBox {{
QComboBox {{
border: 1px solid {Colors.BORDER_DEFAULT};
border-radius: {Spacing.BORDER_RADIUS_SM}px;
padding: 1px;
background-color: {Colors.WHITE};
min-height: {Spacing.INPUT_HEIGHT - 16}px;
}}
QLineEdit, QDoubleSpinBox, QSpinBox {{
border: 1px solid {Colors.BORDER_DEFAULT};
border-radius: {Spacing.BORDER_RADIUS_SM}px;
padding: {Spacing.SM}px;
@@ -342,7 +349,6 @@ class InputStyles:
color: {Colors.GRAY_600};
}}
"""
@staticmethod
def error():
return f"""