Добавленна поддержка отображения, сохранинения, и загрузки растворителя во вкладке факторнго эксперимента. Убрана галочка рАндомизировать по умолчанию

This commit is contained in:
2026-05-27 15:41:09 +05:00
parent a2bc606336
commit acf3ad0dd5
5 changed files with 110 additions and 26 deletions
Binary file not shown.
+17 -2
View File
@@ -7,7 +7,13 @@ from dataclasses import dataclass, asdict
from datetime import datetime from datetime import datetime
import json import json
VERSION="alpha_0.3"
"""
Условно
0.1 - Разработка калькулятора сред
0.2 - Разработка факторов эксперимента
0.3 - разработка матрицы планирования
"""
@dataclass @dataclass
class ReagentData: class ReagentData:
"""Данные реагента""" """Данные реагента"""
@@ -111,7 +117,7 @@ class ProjectData:
project_name: str project_name: str
created_at: str created_at: str
modified_at: str modified_at: str
version: str = "1.0" version: str = VERSION
# Данные калькулятора сред # Данные калькулятора сред
medium_total_volume: float = 1000.0 medium_total_volume: float = 1000.0
@@ -120,6 +126,9 @@ class ProjectData:
medium_reagents: List[ReagentData] = None medium_reagents: List[ReagentData] = None
# Данные эксперимента # Данные эксперимента
experiment_total_volume: float = 1000.0
experiment_volume_unit: str = "мл"
experiment_solvent: str = "Вода"
experiment_factors: List[FactorData] = None experiment_factors: List[FactorData] = None
experiment_responses: List[ResponseData] = None experiment_responses: List[ResponseData] = None
experiment_center_points: int = 3 experiment_center_points: int = 3
@@ -150,6 +159,9 @@ class ProjectData:
'reagents': [r.to_dict() for r in self.medium_reagents] 'reagents': [r.to_dict() for r in self.medium_reagents]
}, },
'experiment': { 'experiment': {
'total_volume': self.medium_total_volume,
'volume_unit': self.medium_volume_unit,
'solvent': self.medium_solvent,
'factors': [f.to_dict() for f in self.experiment_factors], 'factors': [f.to_dict() for f in self.experiment_factors],
'responses': [r.to_dict() for r in self.experiment_responses], 'responses': [r.to_dict() for r in self.experiment_responses],
'center_points': self.experiment_center_points, 'center_points': self.experiment_center_points,
@@ -175,6 +187,9 @@ class ProjectData:
medium_volume_unit=medium.get('volume_unit', 'мл'), medium_volume_unit=medium.get('volume_unit', 'мл'),
medium_solvent=medium.get('solvent', 'Вода'), medium_solvent=medium.get('solvent', 'Вода'),
medium_reagents=[ReagentData.from_dict(r) for r in medium.get('reagents', [])], medium_reagents=[ReagentData.from_dict(r) for r in medium.get('reagents', [])],
experiment_total_volume=experiment.get('total_volume', 1000.0),
experiment_volume_unit=experiment.get('volume_unit', 'мл'),
experiment_solvent=experiment.get('solvent', 'Вода'),
experiment_factors=[FactorData.from_dict(f) for f in experiment.get('factors', [])], experiment_factors=[FactorData.from_dict(f) for f in experiment.get('factors', [])],
experiment_responses=[ResponseData.from_dict(r) for r in experiment.get('responses', [])], experiment_responses=[ResponseData.from_dict(r) for r in experiment.get('responses', [])],
experiment_center_points=experiment.get('center_points', 3), experiment_center_points=experiment.get('center_points', 3),
+44 -16
View File
@@ -2,10 +2,9 @@
""" """
Единый графический интерфейс для калькулятора сред и DoE Единый графический интерфейс для калькулятора сред и DoE
""" """
from theme import Colors, Fonts, Spacing, ButtonStyles, get_full_stylesheet, apply_theme from theme import Colors, Fonts, Spacing, ButtonStyles, get_full_stylesheet, apply_theme, TitleStyles
import sys import sys
from typing import List, Dict, Optional from typing import List, Dict, Optional
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QTabWidget, QGroupBox, QLabel, QPushButton, QTableWidget, QTabWidget, QGroupBox, QLabel, QPushButton, QTableWidget,
@@ -86,12 +85,8 @@ class MainWindow(QMainWindow):
# Заголовок # Заголовок
title = QLabel("Цифровой помощник биохимика") title = QLabel("Цифровой помощник биохимика")
title_font = QFont() title.setObjectName("mainTitle") # Стиль подтянется из theme
title_font.setPointSize(18)
title_font.setBold(True)
title.setFont(title_font)
title.setAlignment(Qt.AlignCenter) title.setAlignment(Qt.AlignCenter)
title.setStyleSheet("color: #2c3e50; padding: 10px;")
layout.addWidget(title) layout.addWidget(title)
# Вкладки # Вкладки
@@ -275,7 +270,7 @@ class MainWindow(QMainWindow):
) )
self.info_label.setText(solvent_text) self.info_label.setText(solvent_text)
self.info_label.setStyleSheet("background-color: #d5f5e3; padding: 8px; border-radius: 5px;") self.info_label.setStyleSheet("background-color: #d5f5e3; padding: 8px; border-radius: 5px;")
print(result)
# Сохраняем результаты для передачи в DoE # Сохраняем результаты для передачи в DoE
self.last_medium_result = result self.last_medium_result = result
@@ -332,19 +327,43 @@ class MainWindow(QMainWindow):
# Настройки эксперимента # Настройки эксперимента
settings_box = QGroupBox("Настройки эксперимента") settings_box = QGroupBox("Настройки эксперимента")
settings_layout = QHBoxLayout() settings_layout = QVBoxLayout()
settings_layout.addWidget(QLabel("Центральных точек:")) # Параметры среды (из калькулятора)
env_layout = QHBoxLayout()
env_layout.addWidget(QLabel("Общий объём:"))
self.exp_total_volume = QDoubleSpinBox()
self.exp_total_volume.setRange(0.001, 1000000)
self.exp_total_volume.setValue(100)
self.exp_total_volume.setSuffix(" ")
env_layout.addWidget(self.exp_total_volume)
self.exp_volume_unit = QComboBox()
self.exp_volume_unit.addItems(["мл", "л", "мкл", "нл"])
self.exp_volume_unit.setCurrentText("мл")
env_layout.addWidget(self.exp_volume_unit)
env_layout.addSpacing(20)
env_layout.addWidget(QLabel("Растворитель:"))
self.exp_solvent = QLineEdit("Вода")
env_layout.addWidget(self.exp_solvent)
env_layout.addStretch()
settings_layout.addLayout(env_layout)
# Настройки эксперимента
exp_layout = QHBoxLayout()
exp_layout.addWidget(QLabel("Центральных точек:"))
self.center_points_spin = QSpinBox() self.center_points_spin = QSpinBox()
self.center_points_spin.setRange(0, 10) self.center_points_spin.setRange(0, 10)
self.center_points_spin.setValue(3) self.center_points_spin.setValue(3)
settings_layout.addWidget(self.center_points_spin) exp_layout.addWidget(self.center_points_spin)
settings_layout.addSpacing(20) exp_layout.addSpacing(20)
self.randomize_check = QCheckBox("Рэндомизировать порядок") self.randomize_check = QCheckBox("Рандомизировать порядок")
self.randomize_check.setChecked(True) self.randomize_check.setChecked(False)
settings_layout.addWidget(self.randomize_check) exp_layout.addWidget(self.randomize_check)
settings_layout.addStretch() exp_layout.addStretch()
settings_layout.addLayout(exp_layout)
settings_box.setLayout(settings_layout) settings_box.setLayout(settings_layout)
layout.addWidget(settings_box) layout.addWidget(settings_box)
@@ -415,6 +434,10 @@ class MainWindow(QMainWindow):
# Очищаем таблицу факторов # Очищаем таблицу факторов
self.factors_table.setRowCount(0) self.factors_table.setRowCount(0)
self.exp_total_volume.setValue(result['total_volume'])
self.exp_volume_unit.setCurrentText(result['total_unit'])
self.exp_solvent.setText(result['solvent_name'])
for reagent in result['reagents']: for reagent in result['reagents']:
name = reagent['name'] name = reagent['name']
if reagent.get('dilution_factor', 1.0) != 1.0: if reagent.get('dilution_factor', 1.0) != 1.0:
@@ -906,7 +929,12 @@ class MainWindow(QMainWindow):
self.reagents_table.setItem(row, 4, QTableWidgetItem(str(reagent.dilution_factor))) self.reagents_table.setItem(row, 4, QTableWidgetItem(str(reagent.dilution_factor)))
self.reagents_table.setItem(row, 5, QTableWidgetItem("")) self.reagents_table.setItem(row, 5, QTableWidgetItem(""))
# Применяем данные факторов эксперимента # Применяем данные факторов эксперимента
self.exp_total_volume.setValue(project.experiment_total_volume)
self.exp_volume_unit.setCurrentText(project.experiment_volume_unit)
self.exp_solvent.setText(project.experiment_solvent)
self.factors_table.setRowCount(0) self.factors_table.setRowCount(0)
for factor in project.experiment_factors: for factor in project.experiment_factors:
if factor.percentage is not None: if factor.percentage is not None:
+1
View File
@@ -14,6 +14,7 @@ from PyQt5.QtWidgets import QApplication
from gui import MainWindow from gui import MainWindow
from theme import Fonts, setup_emoji_support from theme import Fonts, setup_emoji_support
# Добавляем текущую директорию в путь # Добавляем текущую директорию в путь
sys.path.insert(0, os.path.dirname(__file__)) sys.path.insert(0, os.path.dirname(__file__))
+44 -4
View File
@@ -12,7 +12,7 @@ class Colors:
"""Цветовая палитра приложения""" """Цветовая палитра приложения"""
# Основные цвета (Primary) # Основные цвета (Primary)
PRIMARY = "#999999" # Синий - основной акцент PRIMARY = "#3498db" # Синий - основной акцент
PRIMARY_DARK = "#777777" # Тёмно-синий (наведение) PRIMARY_DARK = "#777777" # Тёмно-синий (наведение)
PRIMARY_LIGHT = "#5dade2" # Светло-синий PRIMARY_LIGHT = "#5dade2" # Светло-синий
PRIMARY_BG = "#ebf5fb" # Фоновый для primary элементов PRIMARY_BG = "#ebf5fb" # Фоновый для primary элементов
@@ -69,6 +69,9 @@ class Colors:
BORDER_DEFAULT = GRAY_400 BORDER_DEFAULT = GRAY_400
BORDER_DARK = GRAY_600 BORDER_DARK = GRAY_600
# Цвет для заголовка
TITLE_COLOR = "#2c3e50"
# Прозрачность # Прозрачность
TRANSPARENT = "transparent" TRANSPARENT = "transparent"
OVERLAY = "rgba(0, 0, 0, 0.5)" OVERLAY = "rgba(0, 0, 0, 0.5)"
@@ -100,7 +103,7 @@ class Fonts:
def get_title_font(cls): def get_title_font(cls):
"""Возвращает шрифт для заголовков""" """Возвращает шрифт для заголовков"""
font = QFont(cls.FAMILY_PRIMARY.split(',')[0]) font = QFont(cls.FAMILY_PRIMARY.split(',')[0])
font.setPointSize(cls.SIZE_XLARGE) font.setPointSize(cls.SIZE_XXLARGE)
font.setBold(True) font.setBold(True)
return font return font
@@ -192,6 +195,21 @@ class Spacing:
BORDER_RADIUS_LG = 8 BORDER_RADIUS_LG = 8
BORDER_RADIUS_XL = 12 BORDER_RADIUS_XL = 12
# ========== СТИЛЬ ЗАГОЛОВКА ==========
class TitleStyles:
@staticmethod
def main_title():
return f"""
color: {Colors.TITLE_COLOR};
padding: {Spacing.XL}px;
font-size: {Fonts.SIZE_XLARGE}px;
font-weight: {Fonts.WEIGHT_BOLD};
"""
# ========== СТИЛИ КНОПОК ========== # ========== СТИЛИ КНОПОК ==========
class ButtonStyles: class ButtonStyles:
"""Стили для разных типов кнопок""" """Стили для разных типов кнопок"""
@@ -464,9 +482,10 @@ class GroupBoxStyles:
font-size: {Fonts.SIZE_MEDIUM}px; font-size: {Fonts.SIZE_MEDIUM}px;
}} }}
QGroupBox::title {{ QGroupBox::title {{
subcontrol-origin: margin; subcontrol-origin: margin;
left: {Spacing.LG}px; left: {Spacing.LG}px;
padding: 0 {Spacing.MD}px; padding: 10 {Spacing.MD}px;
color: {Colors.INFO}; color: {Colors.INFO};
}} }}
""" """
@@ -485,7 +504,7 @@ class GroupBoxStyles:
QGroupBox::title {{ QGroupBox::title {{
subcontrol-origin: margin; subcontrol-origin: margin;
left: {Spacing.LG}px; left: {Spacing.LG}px;
padding: 0 {Spacing.MD}px; padding: 10 {Spacing.MD}px;
color: {Colors.PRIMARY}; color: {Colors.PRIMARY};
}} }}
""" """
@@ -566,8 +585,29 @@ def get_full_stylesheet():
""" """
Возвращает полную таблицу стилей для приложения Возвращает полную таблицу стилей для приложения
""" """
return f""" return f"""
/* СТИЛИ ДЛЯ ВСЕХ КНОПОК ПО УМОЛЧАНИЮ */
QPushButton {{
background-color: {Colors.PRIMARY};
color: {Colors.TEXT_ON_PRIMARY};
border: none;
border-radius: {Spacing.BORDER_RADIUS_MD}px;
padding: {Spacing.SM}px {Spacing.LG}px;
font-weight: {Fonts.WEIGHT_SEMIBOLD};
font-size: {Fonts.SIZE_NORMAL}px;
}}
QPushButton:hover {{
background-color: {Colors.PRIMARY_LIGHT};
}}
QPushButton:pressed {{
background-color: {Colors.PRIMARY_DARK};
}}
/* Глобальные стили */ /* Глобальные стили */
QLabel#mainTitle {{
{TitleStyles.main_title()}
}}
QMainWindow {{ QMainWindow {{
background-color: {Colors.GRAY_200}; background-color: {Colors.GRAY_200};
}} }}