AMS-BP 0.4.22__py3-none-any.whl → 0.4.40__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- AMS_BP/__init__.py +1 -1
- AMS_BP/{configio → core/configio}/experiments.py +5 -0
- AMS_BP/{motion → core/motion}/condensate_movement.py +1 -1
- AMS_BP/{motion → core/motion}/movement/boundary_conditions.py +1 -1
- AMS_BP/{photophysics → core/photophysics}/photon_physics.py +1 -1
- AMS_BP/{sample → core/sample}/flurophores/flurophore_schema.py +1 -1
- AMS_BP/{sim_microscopy.py → core/sim_microscopy.py} +1 -1
- AMS_BP/gui/main.py +36 -5
- AMS_BP/gui/sim_worker.py +3 -4
- AMS_BP/gui/themes/dark_theme.qss +86 -0
- AMS_BP/gui/themes/light_theme.qss +85 -0
- AMS_BP/gui/widgets/camera_config_widget.py +15 -9
- AMS_BP/gui/widgets/cell_config_widget.py +13 -7
- AMS_BP/gui/widgets/channel_config_widget.py +12 -6
- AMS_BP/gui/widgets/condensate_config_widget.py +24 -15
- AMS_BP/gui/widgets/experiment_config_widget.py +71 -7
- AMS_BP/gui/widgets/flurophore_config_widget.py +6 -2
- AMS_BP/gui/widgets/global_config_widget.py +13 -6
- AMS_BP/gui/widgets/laser_config_widget.py +12 -6
- AMS_BP/gui/widgets/molecule_config_widget.py +14 -11
- AMS_BP/gui/widgets/utility_widgets/toggleswitch_widget.py +60 -0
- AMS_BP/gui/windows/__init__.py +0 -0
- AMS_BP/gui/{configuration_window.py → windows/configuration_window.py} +86 -89
- AMS_BP/gui/{template_window_selection.py → windows/template_window_selection.py} +35 -5
- AMS_BP/main_cli.py +2 -2
- AMS_BP/resources/template_configs/metadata_configs.json +21 -3
- AMS_BP/resources/template_configs/twocolor_confocal_timeseries_live.toml +399 -0
- AMS_BP/resources/template_configs/twocolor_confocal_zstack_fixed.toml +406 -0
- AMS_BP/{sim_config.toml → resources/template_configs/twocolor_confocal_zstack_live.toml} +111 -111
- AMS_BP/tools/logging/__init__.py +0 -0
- {ams_bp-0.4.22.dist-info → ams_bp-0.4.40.dist-info}/METADATA +7 -3
- ams_bp-0.4.40.dist-info/RECORD +110 -0
- ams_bp-0.4.22.dist-info/RECORD +0 -103
- /AMS_BP/{configio → core}/__init__.py +0 -0
- /AMS_BP/{cells → core/cells}/__init__.py +0 -0
- /AMS_BP/{cells → core/cells}/budding_yeast_cell.py +0 -0
- /AMS_BP/{cells → core/cells}/cell_factory.py +0 -0
- /AMS_BP/{logging → core/configio}/__init__.py +0 -0
- /AMS_BP/{configio → core/configio}/configmodels.py +0 -0
- /AMS_BP/{configio → core/configio}/convertconfig.py +0 -0
- /AMS_BP/{configio → core/configio}/saving.py +0 -0
- /AMS_BP/{groundtruth_generators → core/groundtruth_generators}/__init__.py +0 -0
- /AMS_BP/{groundtruth_generators → core/groundtruth_generators}/nuclearporecomplexes.py +0 -0
- /AMS_BP/{metadata → core/metadata}/__init__.py +0 -0
- /AMS_BP/{metadata → core/metadata}/metadata.py +0 -0
- /AMS_BP/{motion → core/motion}/__init__.py +0 -0
- /AMS_BP/{motion → core/motion}/movement/__init__.py +0 -0
- /AMS_BP/{motion → core/motion}/track_gen.py +0 -0
- /AMS_BP/{optics → core/optics}/__init__.py +0 -0
- /AMS_BP/{optics → core/optics}/camera/__init__.py +0 -0
- /AMS_BP/{optics → core/optics}/camera/detectors.py +0 -0
- /AMS_BP/{optics → core/optics}/camera/quantum_eff.py +0 -0
- /AMS_BP/{optics → core/optics}/filters/__init__.py +0 -0
- /AMS_BP/{optics → core/optics}/filters/channels/__init__.py +0 -0
- /AMS_BP/{optics → core/optics}/filters/channels/channelschema.py +0 -0
- /AMS_BP/{optics → core/optics}/filters/filters.py +0 -0
- /AMS_BP/{optics → core/optics}/lasers/__init__.py +0 -0
- /AMS_BP/{optics → core/optics}/lasers/laser_profiles.py +0 -0
- /AMS_BP/{optics → core/optics}/lasers/scanning_patterns.py +0 -0
- /AMS_BP/{optics → core/optics}/psf/__init__.py +0 -0
- /AMS_BP/{optics → core/optics}/psf/psf_engine.py +0 -0
- /AMS_BP/{photophysics → core/photophysics}/__init__.py +0 -0
- /AMS_BP/{photophysics → core/photophysics}/state_kinetics.py +0 -0
- /AMS_BP/{probabilityfuncs → core/probabilityfuncs}/__init__.py +0 -0
- /AMS_BP/{probabilityfuncs → core/probabilityfuncs}/markov_chain.py +0 -0
- /AMS_BP/{probabilityfuncs → core/probabilityfuncs}/probability_functions.py +0 -0
- /AMS_BP/{run_sim_util.py → core/run_sim_util.py} +0 -0
- /AMS_BP/{sample → core/sample}/__init__.py +0 -0
- /AMS_BP/{sample → core/sample}/flurophores/__init__.py +0 -0
- /AMS_BP/{sample → core/sample}/sim_sampleplane.py +0 -0
- /AMS_BP/gui/{help_window.py → windows/help_window.py} +0 -0
- /AMS_BP/gui/{logging_window.py → windows/logging_window.py} +0 -0
- /AMS_BP/{logging → tools/logging}/logutil.py +0 -0
- /AMS_BP/{logging → tools/logging}/setup_run_directory.py +0 -0
- {ams_bp-0.4.22.dist-info → ams_bp-0.4.40.dist-info}/WHEEL +0 -0
- {ams_bp-0.4.22.dist-info → ams_bp-0.4.40.dist-info}/entry_points.txt +0 -0
- {ams_bp-0.4.22.dist-info → ams_bp-0.4.40.dist-info}/licenses/LICENSE +0 -0
AMS_BP/__init__.py
CHANGED
@@ -20,6 +20,7 @@ class TimeSeriesExpConfig(BaseExpConfig):
|
|
20
20
|
laser_powers_active: List[float]
|
21
21
|
laser_positions_active: List
|
22
22
|
xyoffset: Tuple[float, float]
|
23
|
+
scanning: bool = False
|
23
24
|
|
24
25
|
exposure_time: Optional[int] = None
|
25
26
|
interval_time: Optional[int] = None
|
@@ -61,6 +62,8 @@ class zStackExpConfig(BaseExpConfig):
|
|
61
62
|
exposure_time: int
|
62
63
|
interval_time: int
|
63
64
|
|
65
|
+
scanning: bool = False
|
66
|
+
|
64
67
|
def __post_init__(self):
|
65
68
|
len_ln = len(self.laser_names_active)
|
66
69
|
len_lpow = len(self.laser_powers_active)
|
@@ -94,6 +97,7 @@ def timeseriesEXP(
|
|
94
97
|
duration_total=config.duration_time,
|
95
98
|
exposure_time=config.exposure_time,
|
96
99
|
interval_time=config.interval_time,
|
100
|
+
scanning=config.scanning,
|
97
101
|
)
|
98
102
|
return np.array([frames]), metadata
|
99
103
|
|
@@ -112,6 +116,7 @@ def zseriesEXP(
|
|
112
116
|
duration_total=config.exposure_time + config.interval_time,
|
113
117
|
exposure_time=config.exposure_time,
|
114
118
|
interval_time=config.interval_time,
|
119
|
+
scanning=config.scanning,
|
115
120
|
)
|
116
121
|
frames.append(f)
|
117
122
|
# m.Channel = {"name": microscope.channels.names}
|
@@ -5,7 +5,7 @@ Removal Time: NDY (not determined yet)
|
|
5
5
|
|
6
6
|
import numpy as np
|
7
7
|
|
8
|
-
from
|
8
|
+
from ....utils.decorators import _catch_recursion_error, deprecated
|
9
9
|
|
10
10
|
# Reflecting boundary condition which is a recursive function so that even if the first candidate
|
11
11
|
# is out of the space limit, the function will keep calling itself until the candidate is within the space limit
|
@@ -3,6 +3,7 @@ from typing import Callable, List, Optional, Tuple
|
|
3
3
|
|
4
4
|
import numpy as np
|
5
5
|
|
6
|
+
from ...utils.constants import H_C_COM
|
6
7
|
from ..optics.camera.detectors import photon_noise
|
7
8
|
from ..optics.camera.quantum_eff import QuantumEfficiency
|
8
9
|
from ..optics.filters.filters import FilterSpectrum
|
@@ -11,7 +12,6 @@ from ..sample.flurophores.flurophore_schema import (
|
|
11
12
|
SpectralData,
|
12
13
|
WavelengthDependentProperty,
|
13
14
|
)
|
14
|
-
from ..utils.constants import H_C_COM
|
15
15
|
|
16
16
|
|
17
17
|
@dataclass
|
@@ -4,7 +4,7 @@ from typing import Any, Callable, Dict, List, Optional, TypeVar
|
|
4
4
|
import numpy as np
|
5
5
|
from pydantic import BaseModel, Field, field_validator
|
6
6
|
|
7
|
-
from
|
7
|
+
from ....utils.constants import H_C_COM, N_A
|
8
8
|
|
9
9
|
NumericType = TypeVar("NumericType", float, np.ndarray, List[float])
|
10
10
|
|
@@ -3,6 +3,7 @@ from typing import Callable, Dict, List, Literal, Optional, Tuple, Union
|
|
3
3
|
|
4
4
|
import numpy as np
|
5
5
|
|
6
|
+
from ..utils.util_functions import ms_to_seconds
|
6
7
|
from .configio.configmodels import ConfigList
|
7
8
|
from .metadata.metadata import MetaData
|
8
9
|
from .optics.camera.detectors import Detector
|
@@ -18,7 +19,6 @@ from .photophysics.photon_physics import (
|
|
18
19
|
from .photophysics.state_kinetics import StateTransitionCalculator
|
19
20
|
from .sample.flurophores.flurophore_schema import StateType, WavelengthDependentProperty
|
20
21
|
from .sample.sim_sampleplane import EMPTY_STATE_HISTORY_DICT, SamplePlane
|
21
|
-
from .utils.util_functions import ms_to_seconds
|
22
22
|
|
23
23
|
|
24
24
|
class VirtualMicroscope:
|
AMS_BP/gui/main.py
CHANGED
@@ -4,10 +4,11 @@ from zipfile import ZipFile
|
|
4
4
|
|
5
5
|
import napari
|
6
6
|
import tifffile
|
7
|
-
from PyQt6.QtCore import Qt, QThread
|
7
|
+
from PyQt6.QtCore import QSettings, Qt, QThread
|
8
8
|
from PyQt6.QtGui import QPainter, QPixmap
|
9
9
|
from PyQt6.QtSvg import QSvgRenderer
|
10
10
|
from PyQt6.QtWidgets import (
|
11
|
+
QApplication,
|
11
12
|
QFileDialog,
|
12
13
|
QLabel,
|
13
14
|
QMainWindow,
|
@@ -17,11 +18,12 @@ from PyQt6.QtWidgets import (
|
|
17
18
|
QWidget,
|
18
19
|
)
|
19
20
|
|
20
|
-
from ..logging.logutil import LoggerManager
|
21
|
-
from ..logging.setup_run_directory import setup_run_directory
|
22
|
-
from .logging_window import LogWindow
|
21
|
+
from ..tools.logging.logutil import LoggerManager
|
22
|
+
from ..tools.logging.setup_run_directory import setup_run_directory
|
23
23
|
from .sim_worker import SimulationWorker
|
24
|
-
from .
|
24
|
+
from .widgets.utility_widgets.toggleswitch_widget import ToggleSwitch
|
25
|
+
from .windows.logging_window import LogWindow
|
26
|
+
from .windows.template_window_selection import TemplateSelectionWindow
|
25
27
|
|
26
28
|
LOGO_PATH = str(Path(__file__).parent / "assets" / "drawing.svg")
|
27
29
|
|
@@ -77,6 +79,20 @@ class MainWindow(QMainWindow):
|
|
77
79
|
self.package_logs_button.clicked.connect(self.package_logs)
|
78
80
|
layout.addWidget(self.package_logs_button)
|
79
81
|
|
82
|
+
# Load theme preference
|
83
|
+
self.settings = QSettings("AMS", "AMSConfig")
|
84
|
+
theme_pref = self.settings.value("theme", "light")
|
85
|
+
|
86
|
+
# Add toggle switch with label
|
87
|
+
self.theme_toggle = ToggleSwitch(checked=(theme_pref == "dark"))
|
88
|
+
self.theme_toggle.toggled.connect(self.toggle_theme)
|
89
|
+
self.theme_label = QLabel("Dark Mode" if theme_pref == "dark" else "Light Mode")
|
90
|
+
layout.addWidget(self.theme_label)
|
91
|
+
layout.addWidget(self.theme_toggle, alignment=Qt.AlignmentFlag.AlignCenter)
|
92
|
+
|
93
|
+
# Apply initial theme
|
94
|
+
self.apply_theme(theme_pref)
|
95
|
+
|
80
96
|
def package_logs(self):
|
81
97
|
log_dir = Path.home() / "AMS_runs"
|
82
98
|
|
@@ -245,6 +261,21 @@ class MainWindow(QMainWindow):
|
|
245
261
|
else:
|
246
262
|
print("Failed to load SVG file.")
|
247
263
|
|
264
|
+
def toggle_theme(self, is_dark: bool):
|
265
|
+
theme = "dark" if is_dark else "light"
|
266
|
+
self.settings.setValue("theme", theme)
|
267
|
+
self.apply_theme(theme)
|
268
|
+
self.theme_label.setText("Dark Mode" if is_dark else "Light Mode")
|
269
|
+
|
270
|
+
def apply_theme(self, theme: str):
|
271
|
+
theme_file = Path(__file__).parent / "themes" / f"{theme}_theme.qss"
|
272
|
+
try:
|
273
|
+
with open(theme_file, "r") as f:
|
274
|
+
stylesheet = f.read()
|
275
|
+
QApplication.instance().setStyleSheet(stylesheet)
|
276
|
+
except Exception as e:
|
277
|
+
QMessageBox.warning(self, "Theme Error", f"Failed to load theme:\n{e}")
|
278
|
+
|
248
279
|
def open_config_editor(self):
|
249
280
|
"""Launch template selection first, then open ConfigEditor."""
|
250
281
|
self.template_window = TemplateSelectionWindow()
|
AMS_BP/gui/sim_worker.py
CHANGED
@@ -2,7 +2,9 @@ from pathlib import Path
|
|
2
2
|
|
3
3
|
from PyQt6.QtCore import QObject, pyqtSignal
|
4
4
|
|
5
|
-
from ..
|
5
|
+
from ..core.configio.convertconfig import load_config, setup_microscope
|
6
|
+
from ..core.configio.saving import save_config_frames
|
7
|
+
from ..tools.logging.logutil import LogEmitter
|
6
8
|
|
7
9
|
|
8
10
|
class SimulationWorker(QObject):
|
@@ -20,9 +22,6 @@ class SimulationWorker(QObject):
|
|
20
22
|
try:
|
21
23
|
self.emitter.message.emit(f"Starting simulation for {self.config_path}")
|
22
24
|
|
23
|
-
from ..configio.convertconfig import load_config, setup_microscope
|
24
|
-
from ..configio.saving import save_config_frames
|
25
|
-
|
26
25
|
loadedconfig = load_config(self.config_path)
|
27
26
|
|
28
27
|
if "version" in loadedconfig:
|
@@ -0,0 +1,86 @@
|
|
1
|
+
/* Dark Theme */
|
2
|
+
* {
|
3
|
+
font-family: "Segoe UI", "Arial", sans-serif;
|
4
|
+
font-size: 14px;
|
5
|
+
color: #e0e0e0;
|
6
|
+
}
|
7
|
+
|
8
|
+
QWidget {
|
9
|
+
background-color: #2b2b2b;
|
10
|
+
}
|
11
|
+
|
12
|
+
QLabel {
|
13
|
+
color: #dddddd;
|
14
|
+
}
|
15
|
+
|
16
|
+
QPushButton {
|
17
|
+
background-color: #3daee9;
|
18
|
+
color: #ffffff;
|
19
|
+
border-radius: 6px;
|
20
|
+
padding: 6px 12px;
|
21
|
+
}
|
22
|
+
QPushButton:hover {
|
23
|
+
background-color: #5dbff0;
|
24
|
+
}
|
25
|
+
QPushButton:pressed {
|
26
|
+
background-color: #2a97cc;
|
27
|
+
}
|
28
|
+
|
29
|
+
QLineEdit, QComboBox, QSpinBox, QDoubleSpinBox, QTextEdit {
|
30
|
+
background-color: #3c3f41;
|
31
|
+
border: 1px solid #555;
|
32
|
+
border-radius: 4px;
|
33
|
+
padding: 4px;
|
34
|
+
color: #eeeeee;
|
35
|
+
}
|
36
|
+
|
37
|
+
QGroupBox {
|
38
|
+
border: 1px solid #444;
|
39
|
+
border-radius: 6px;
|
40
|
+
margin-top: 10px;
|
41
|
+
}
|
42
|
+
QGroupBox:title {
|
43
|
+
subcontrol-origin: margin;
|
44
|
+
subcontrol-position: top left;
|
45
|
+
padding: 0 6px;
|
46
|
+
font-weight: bold;
|
47
|
+
color: #aaaaaa;
|
48
|
+
}
|
49
|
+
|
50
|
+
QTabWidget::pane {
|
51
|
+
border: 1px solid #444;
|
52
|
+
background: #3c3f41;
|
53
|
+
border-radius: 6px;
|
54
|
+
}
|
55
|
+
|
56
|
+
QTabBar::tab {
|
57
|
+
background: #444;
|
58
|
+
border: 1px solid #555;
|
59
|
+
padding: 6px 12px;
|
60
|
+
border-top-left-radius: 6px;
|
61
|
+
border-top-right-radius: 6px;
|
62
|
+
}
|
63
|
+
QTabBar::tab:selected {
|
64
|
+
background: #2b2b2b;
|
65
|
+
border-bottom-color: #2b2b2b;
|
66
|
+
}
|
67
|
+
|
68
|
+
QScrollArea {
|
69
|
+
border: none;
|
70
|
+
}
|
71
|
+
|
72
|
+
QListWidget {
|
73
|
+
background-color: #2f2f2f;
|
74
|
+
border-right: 1px solid #444;
|
75
|
+
}
|
76
|
+
|
77
|
+
QListWidget::item {
|
78
|
+
padding: 6px;
|
79
|
+
border-radius: 4px;
|
80
|
+
color: #dddddd;
|
81
|
+
}
|
82
|
+
|
83
|
+
QListWidget::item:selected {
|
84
|
+
background-color: #3daee9;
|
85
|
+
color: #ffffff;
|
86
|
+
}
|
@@ -0,0 +1,85 @@
|
|
1
|
+
/* Light Theme */
|
2
|
+
* {
|
3
|
+
font-family: "Segoe UI", "Arial", sans-serif;
|
4
|
+
font-size: 14px;
|
5
|
+
color: #2b2b2b;
|
6
|
+
}
|
7
|
+
|
8
|
+
QWidget {
|
9
|
+
background-color: #f5f7fa;
|
10
|
+
}
|
11
|
+
|
12
|
+
QLabel {
|
13
|
+
color: #333;
|
14
|
+
}
|
15
|
+
|
16
|
+
QPushButton {
|
17
|
+
background-color: #007acc;
|
18
|
+
color: white;
|
19
|
+
border-radius: 6px;
|
20
|
+
padding: 6px 12px;
|
21
|
+
}
|
22
|
+
QPushButton:hover {
|
23
|
+
background-color: #005f9e;
|
24
|
+
}
|
25
|
+
QPushButton:pressed {
|
26
|
+
background-color: #004d80;
|
27
|
+
}
|
28
|
+
|
29
|
+
QLineEdit, QComboBox, QSpinBox, QDoubleSpinBox, QTextEdit {
|
30
|
+
background-color: white;
|
31
|
+
border: 1px solid #ccc;
|
32
|
+
border-radius: 4px;
|
33
|
+
padding: 4px;
|
34
|
+
}
|
35
|
+
|
36
|
+
QGroupBox {
|
37
|
+
border: 1px solid #d6d9dc;
|
38
|
+
border-radius: 6px;
|
39
|
+
margin-top: 10px;
|
40
|
+
}
|
41
|
+
QGroupBox:title {
|
42
|
+
subcontrol-origin: margin;
|
43
|
+
subcontrol-position: top left;
|
44
|
+
padding: 0 6px;
|
45
|
+
font-weight: bold;
|
46
|
+
color: #555;
|
47
|
+
}
|
48
|
+
|
49
|
+
QTabWidget::pane {
|
50
|
+
border: 1px solid #d1dbe3;
|
51
|
+
background: #ffffff;
|
52
|
+
border-radius: 6px;
|
53
|
+
}
|
54
|
+
|
55
|
+
QTabBar::tab {
|
56
|
+
background: #e6ebf1;
|
57
|
+
border: 1px solid #cfd7df;
|
58
|
+
padding: 6px 12px;
|
59
|
+
border-top-left-radius: 6px;
|
60
|
+
border-top-right-radius: 6px;
|
61
|
+
}
|
62
|
+
QTabBar::tab:selected {
|
63
|
+
background: #ffffff;
|
64
|
+
border-bottom-color: #ffffff;
|
65
|
+
}
|
66
|
+
|
67
|
+
QScrollArea {
|
68
|
+
border: none;
|
69
|
+
}
|
70
|
+
|
71
|
+
QListWidget {
|
72
|
+
background-color: #f0f0f0;
|
73
|
+
border-right: 1px solid #ccc;
|
74
|
+
}
|
75
|
+
|
76
|
+
QListWidget::item {
|
77
|
+
padding: 6px;
|
78
|
+
border-radius: 4px;
|
79
|
+
}
|
80
|
+
|
81
|
+
QListWidget::item:selected {
|
82
|
+
background-color: #007acc;
|
83
|
+
color: white;
|
84
|
+
}
|
85
|
+
|
@@ -8,23 +8,30 @@ from PyQt6.QtWidgets import (
|
|
8
8
|
QHBoxLayout,
|
9
9
|
QMessageBox,
|
10
10
|
QPushButton,
|
11
|
+
QSizePolicy,
|
11
12
|
QSpinBox,
|
12
13
|
QVBoxLayout,
|
13
14
|
QWidget,
|
14
15
|
)
|
15
16
|
|
17
|
+
from ...core.configio.convertconfig import create_quantum_efficiency_from_config
|
18
|
+
from ...core.optics.camera.detectors import (
|
19
|
+
CMOSDetector,
|
20
|
+
)
|
16
21
|
from .utility_widgets.spectrum_widget import SpectrumEditorDialog
|
17
22
|
|
18
23
|
|
19
24
|
class CameraConfigWidget(QWidget):
|
20
25
|
def __init__(self):
|
21
26
|
super().__init__()
|
27
|
+
|
22
28
|
layout = QVBoxLayout()
|
23
|
-
|
29
|
+
layout.setContentsMargins(10, 10, 10, 10)
|
30
|
+
layout.setSpacing(10)
|
31
|
+
self.setLayout(layout)
|
32
|
+
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
24
33
|
|
25
|
-
|
26
|
-
self.validate_button.clicked.connect(self.validate)
|
27
|
-
layout.addWidget(self.validate_button)
|
34
|
+
form = QFormLayout()
|
28
35
|
|
29
36
|
# Camera type (Only "CMOS" is available)
|
30
37
|
self.camera_type = QComboBox()
|
@@ -89,6 +96,10 @@ class CameraConfigWidget(QWidget):
|
|
89
96
|
layout.addLayout(form)
|
90
97
|
self.setLayout(layout)
|
91
98
|
|
99
|
+
self.validate_button = QPushButton("Validate Parameters")
|
100
|
+
self.validate_button.clicked.connect(self.validate)
|
101
|
+
layout.addWidget(self.validate_button)
|
102
|
+
|
92
103
|
def _hbox(self, widgets):
|
93
104
|
box = QHBoxLayout()
|
94
105
|
for w in widgets:
|
@@ -115,11 +126,6 @@ class CameraConfigWidget(QWidget):
|
|
115
126
|
# You can now use this data wherever needed, e.g., saving or validation
|
116
127
|
|
117
128
|
def validate(self) -> bool:
|
118
|
-
from ...configio.convertconfig import create_quantum_efficiency_from_config
|
119
|
-
from ...optics.camera.detectors import (
|
120
|
-
CMOSDetector,
|
121
|
-
)
|
122
|
-
|
123
129
|
try:
|
124
130
|
data = self.get_data()
|
125
131
|
|
@@ -8,21 +8,26 @@ from PyQt6.QtWidgets import (
|
|
8
8
|
QHBoxLayout,
|
9
9
|
QMessageBox,
|
10
10
|
QPushButton,
|
11
|
+
QSizePolicy,
|
11
12
|
QStackedWidget,
|
12
13
|
QVBoxLayout,
|
13
14
|
QWidget,
|
14
15
|
)
|
15
16
|
|
17
|
+
from ...core.cells import create_cell
|
18
|
+
from ...core.configio.configmodels import CellParameters
|
19
|
+
|
16
20
|
|
17
21
|
class CellConfigWidget(QWidget):
|
18
22
|
def __init__(self):
|
19
23
|
super().__init__()
|
20
24
|
layout = QVBoxLayout()
|
21
|
-
|
25
|
+
layout.setContentsMargins(10, 10, 10, 10)
|
26
|
+
layout.setSpacing(10)
|
27
|
+
self.setLayout(layout)
|
28
|
+
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
22
29
|
|
23
|
-
|
24
|
-
self.validate_button.clicked.connect(self.validate)
|
25
|
-
layout.addWidget(self.validate_button)
|
30
|
+
form = QFormLayout()
|
26
31
|
|
27
32
|
self.cell_type = QComboBox()
|
28
33
|
self.cell_type.addItems(
|
@@ -46,10 +51,11 @@ class CellConfigWidget(QWidget):
|
|
46
51
|
layout.addWidget(self.param_stack)
|
47
52
|
self.setLayout(layout)
|
48
53
|
|
49
|
-
|
50
|
-
|
51
|
-
|
54
|
+
self.validate_button = QPushButton("Validate Parameters")
|
55
|
+
self.validate_button.clicked.connect(self.validate)
|
56
|
+
layout.addWidget(self.validate_button)
|
52
57
|
|
58
|
+
def validate(self) -> bool:
|
53
59
|
try:
|
54
60
|
data = self.get_data()
|
55
61
|
|
@@ -8,12 +8,15 @@ from PyQt6.QtWidgets import (
|
|
8
8
|
QLineEdit,
|
9
9
|
QMessageBox,
|
10
10
|
QPushButton,
|
11
|
+
QSizePolicy,
|
11
12
|
QSpinBox,
|
12
13
|
QTabWidget,
|
13
14
|
QVBoxLayout,
|
14
15
|
QWidget,
|
15
16
|
)
|
16
17
|
|
18
|
+
from ...core.configio.convertconfig import create_channels
|
19
|
+
|
17
20
|
|
18
21
|
class ChannelConfigWidget(QWidget):
|
19
22
|
def __init__(self):
|
@@ -21,11 +24,12 @@ class ChannelConfigWidget(QWidget):
|
|
21
24
|
self.channel_widgets = []
|
22
25
|
|
23
26
|
layout = QVBoxLayout()
|
24
|
-
|
27
|
+
layout.setContentsMargins(10, 10, 10, 10)
|
28
|
+
layout.setSpacing(10)
|
29
|
+
self.setLayout(layout)
|
30
|
+
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
25
31
|
|
26
|
-
|
27
|
-
self.validate_button.clicked.connect(self.validate)
|
28
|
-
layout.addWidget(self.validate_button)
|
32
|
+
form = QFormLayout()
|
29
33
|
|
30
34
|
self.num_channels = QSpinBox()
|
31
35
|
self.num_channels.setRange(1, 10)
|
@@ -40,10 +44,12 @@ class ChannelConfigWidget(QWidget):
|
|
40
44
|
layout.addWidget(self.channel_tabs)
|
41
45
|
self.setLayout(layout)
|
42
46
|
|
47
|
+
self.validate_button = QPushButton("Validate Parameters")
|
48
|
+
self.validate_button.clicked.connect(self.validate)
|
49
|
+
layout.addWidget(self.validate_button)
|
50
|
+
|
43
51
|
def validate(self) -> bool:
|
44
52
|
try:
|
45
|
-
from ...configio.convertconfig import create_channels
|
46
|
-
|
47
53
|
data = self.get_data()
|
48
54
|
|
49
55
|
# Full simulation-level validation
|
@@ -11,12 +11,17 @@ from PyQt6.QtWidgets import (
|
|
11
11
|
QMessageBox,
|
12
12
|
QPushButton,
|
13
13
|
QScrollArea,
|
14
|
+
QSizePolicy,
|
14
15
|
QSpinBox,
|
15
16
|
QTabWidget,
|
16
17
|
QVBoxLayout,
|
17
18
|
QWidget,
|
18
19
|
)
|
19
20
|
|
21
|
+
from ...core.cells import create_cell
|
22
|
+
from ...core.configio.configmodels import CondensateParameters
|
23
|
+
from ...core.motion import create_condensate_dict
|
24
|
+
|
20
25
|
|
21
26
|
class CondensateConfigWidget(QWidget):
|
22
27
|
# Signal to notify when molecule count changes
|
@@ -31,6 +36,10 @@ class CondensateConfigWidget(QWidget):
|
|
31
36
|
|
32
37
|
def setup_ui(self):
|
33
38
|
layout = QVBoxLayout()
|
39
|
+
layout.setContentsMargins(10, 10, 10, 10)
|
40
|
+
layout.setSpacing(10)
|
41
|
+
self.setLayout(layout)
|
42
|
+
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
34
43
|
|
35
44
|
# Instructions label
|
36
45
|
instructions = QLabel(
|
@@ -113,6 +122,18 @@ class CondensateConfigWidget(QWidget):
|
|
113
122
|
condensate_controls.addWidget(condensate_count)
|
114
123
|
layout.addLayout(condensate_controls)
|
115
124
|
|
125
|
+
# === Density Difference field RIGHT AFTER the condensate count ===
|
126
|
+
density_layout = QHBoxLayout()
|
127
|
+
density_layout.addWidget(QLabel("Density Difference:"))
|
128
|
+
|
129
|
+
density_spin = QDoubleSpinBox()
|
130
|
+
density_spin.setRange(0, 100)
|
131
|
+
density_spin.setValue(1.0)
|
132
|
+
density_spin.setDecimals(3)
|
133
|
+
density_layout.addWidget(density_spin)
|
134
|
+
layout.addLayout(density_layout)
|
135
|
+
|
136
|
+
# === Condensate containers BELOW this point ===
|
116
137
|
condensate_container = QVBoxLayout()
|
117
138
|
layout.addLayout(condensate_container)
|
118
139
|
|
@@ -126,25 +147,16 @@ class CondensateConfigWidget(QWidget):
|
|
126
147
|
)
|
127
148
|
)
|
128
149
|
|
129
|
-
# Density Difference per molecule type
|
130
|
-
density_layout = QHBoxLayout()
|
131
|
-
density_layout.addWidget(QLabel("Density Difference:"))
|
132
|
-
|
133
|
-
density_spin = QDoubleSpinBox()
|
134
|
-
density_spin.setRange(0, 100)
|
135
|
-
density_spin.setValue(1.0)
|
136
|
-
density_spin.setDecimals(3)
|
137
|
-
density_layout.addWidget(density_spin)
|
138
|
-
layout.addLayout(density_layout)
|
139
150
|
self.condensate_widgets.append(
|
140
151
|
{
|
141
152
|
"condensates": condensate_widgets,
|
142
153
|
"density_widget": density_spin,
|
154
|
+
"condensate_count_spinner": condensate_count,
|
143
155
|
}
|
144
156
|
)
|
157
|
+
|
145
158
|
molecule_widget.setLayout(layout)
|
146
159
|
scroll_area.setWidget(molecule_widget)
|
147
|
-
|
148
160
|
self.tab_widget.addTab(scroll_area, f"Molecule Type {index + 1}")
|
149
161
|
|
150
162
|
def add_condensate_group(self, index, condensate_widgets, condensate_container):
|
@@ -242,6 +254,7 @@ class CondensateConfigWidget(QWidget):
|
|
242
254
|
molecule_group["condensates"],
|
243
255
|
molecule_group_layout,
|
244
256
|
)
|
257
|
+
molecule_group["condensate_count_spinner"].setValue(num_condensates)
|
245
258
|
|
246
259
|
for j in range(num_condensates):
|
247
260
|
condensate = molecule_group["condensates"][j]
|
@@ -291,10 +304,6 @@ class CondensateConfigWidget(QWidget):
|
|
291
304
|
}
|
292
305
|
|
293
306
|
def validate(self) -> bool:
|
294
|
-
from ...cells import create_cell
|
295
|
-
from ...configio.configmodels import CondensateParameters
|
296
|
-
from ...motion import create_condensate_dict
|
297
|
-
|
298
307
|
try:
|
299
308
|
data = self.get_data()
|
300
309
|
|