diff --git a/calculations/.INFO.py.kate-swp b/calculations/.INFO.py.kate-swp new file mode 100644 index 0000000..4834a67 Binary files /dev/null and b/calculations/.INFO.py.kate-swp differ diff --git a/calculations/models/project_data.py b/calculations/models/project_data.py index a83fc69..17d1408 100644 --- a/calculations/models/project_data.py +++ b/calculations/models/project_data.py @@ -7,7 +7,13 @@ from dataclasses import dataclass, asdict from datetime import datetime import json - +VERSION="alpha_0.3" +""" +Условно +0.1 - Разработка калькулятора сред +0.2 - Разработка факторов эксперимента +0.3 - разработка матрицы планирования +""" @dataclass class ReagentData: """Данные реагента""" @@ -111,7 +117,7 @@ class ProjectData: project_name: str created_at: str modified_at: str - version: str = "1.0" + version: str = VERSION # Данные калькулятора сред medium_total_volume: float = 1000.0 @@ -120,6 +126,9 @@ class ProjectData: medium_reagents: List[ReagentData] = None # Данные эксперимента + experiment_total_volume: float = 1000.0 + experiment_volume_unit: str = "мл" + experiment_solvent: str = "Вода" experiment_factors: List[FactorData] = None experiment_responses: List[ResponseData] = None experiment_center_points: int = 3 @@ -150,6 +159,9 @@ class ProjectData: 'reagents': [r.to_dict() for r in self.medium_reagents] }, '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], 'responses': [r.to_dict() for r in self.experiment_responses], 'center_points': self.experiment_center_points, @@ -175,6 +187,9 @@ class ProjectData: medium_volume_unit=medium.get('volume_unit', 'мл'), medium_solvent=medium.get('solvent', 'Вода'), 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_responses=[ResponseData.from_dict(r) for r in experiment.get('responses', [])], experiment_center_points=experiment.get('center_points', 3), diff --git a/gui.py b/gui.py index 45d5eeb..a5d5701 100644 --- a/gui.py +++ b/gui.py @@ -2,10 +2,9 @@ """ Единый графический интерфейс для калькулятора сред и 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 from typing import List, Dict, Optional - from PyQt5.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTabWidget, QGroupBox, QLabel, QPushButton, QTableWidget, @@ -86,12 +85,8 @@ class MainWindow(QMainWindow): # Заголовок title = QLabel("Цифровой помощник биохимика") - title_font = QFont() - title_font.setPointSize(18) - title_font.setBold(True) - title.setFont(title_font) + title.setObjectName("mainTitle") # Стиль подтянется из theme title.setAlignment(Qt.AlignCenter) - title.setStyleSheet("color: #2c3e50; padding: 10px;") layout.addWidget(title) # Вкладки @@ -275,7 +270,7 @@ class MainWindow(QMainWindow): ) self.info_label.setText(solvent_text) self.info_label.setStyleSheet("background-color: #d5f5e3; padding: 8px; border-radius: 5px;") - + print(result) # Сохраняем результаты для передачи в DoE self.last_medium_result = result @@ -329,23 +324,47 @@ class MainWindow(QMainWindow): factors_layout.addLayout(btn_layout) factors_box.setLayout(factors_layout) layout.addWidget(factors_box) - + # Настройки эксперимента settings_box = QGroupBox("Настройки эксперимента") - settings_layout = QHBoxLayout() - - settings_layout.addWidget(QLabel("Центральных точек:")) + settings_layout = QVBoxLayout() + + # Параметры среды (из калькулятора) + 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.setRange(0, 10) self.center_points_spin.setValue(3) - settings_layout.addWidget(self.center_points_spin) - settings_layout.addSpacing(20) - - self.randomize_check = QCheckBox("Рэндомизировать порядок") - self.randomize_check.setChecked(True) - settings_layout.addWidget(self.randomize_check) - settings_layout.addStretch() + exp_layout.addWidget(self.center_points_spin) + exp_layout.addSpacing(20) + self.randomize_check = QCheckBox("Рандомизировать порядок") + self.randomize_check.setChecked(False) + exp_layout.addWidget(self.randomize_check) + exp_layout.addStretch() + settings_layout.addLayout(exp_layout) + settings_box.setLayout(settings_layout) layout.addWidget(settings_box) @@ -414,7 +433,11 @@ class MainWindow(QMainWindow): # Очищаем таблицу факторов 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']: name = reagent['name'] 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, 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) for factor in project.experiment_factors: if factor.percentage is not None: diff --git a/main.py b/main.py index f66d3a3..4a7c163 100644 --- a/main.py +++ b/main.py @@ -14,6 +14,7 @@ from PyQt5.QtWidgets import QApplication from gui import MainWindow from theme import Fonts, setup_emoji_support + # Добавляем текущую директорию в путь sys.path.insert(0, os.path.dirname(__file__)) diff --git a/theme.py b/theme.py index cd35b02..9d28645 100644 --- a/theme.py +++ b/theme.py @@ -12,7 +12,7 @@ class Colors: """Цветовая палитра приложения""" # Основные цвета (Primary) - PRIMARY = "#999999" # Синий - основной акцент + PRIMARY = "#3498db" # Синий - основной акцент PRIMARY_DARK = "#777777" # Тёмно-синий (наведение) PRIMARY_LIGHT = "#5dade2" # Светло-синий PRIMARY_BG = "#ebf5fb" # Фоновый для primary элементов @@ -69,6 +69,9 @@ class Colors: BORDER_DEFAULT = GRAY_400 BORDER_DARK = GRAY_600 + # Цвет для заголовка + TITLE_COLOR = "#2c3e50" + # Прозрачность TRANSPARENT = "transparent" OVERLAY = "rgba(0, 0, 0, 0.5)" @@ -100,7 +103,7 @@ class Fonts: def get_title_font(cls): """Возвращает шрифт для заголовков""" font = QFont(cls.FAMILY_PRIMARY.split(',')[0]) - font.setPointSize(cls.SIZE_XLARGE) + font.setPointSize(cls.SIZE_XXLARGE) font.setBold(True) return font @@ -192,6 +195,21 @@ class Spacing: BORDER_RADIUS_LG = 8 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: """Стили для разных типов кнопок""" @@ -464,9 +482,10 @@ class GroupBoxStyles: font-size: {Fonts.SIZE_MEDIUM}px; }} QGroupBox::title {{ + subcontrol-origin: margin; left: {Spacing.LG}px; - padding: 0 {Spacing.MD}px; + padding: 10 {Spacing.MD}px; color: {Colors.INFO}; }} """ @@ -485,7 +504,7 @@ class GroupBoxStyles: QGroupBox::title {{ subcontrol-origin: margin; left: {Spacing.LG}px; - padding: 0 {Spacing.MD}px; + padding: 10 {Spacing.MD}px; color: {Colors.PRIMARY}; }} """ @@ -566,8 +585,29 @@ def get_full_stylesheet(): """ Возвращает полную таблицу стилей для приложения """ + 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 {{ background-color: {Colors.GRAY_200}; }}