AMS-BP 0.4.31__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.
Files changed (74) hide show
  1. AMS_BP/__init__.py +1 -1
  2. AMS_BP/{configio → core/configio}/experiments.py +5 -0
  3. AMS_BP/{motion → core/motion}/condensate_movement.py +1 -1
  4. AMS_BP/{motion → core/motion}/movement/boundary_conditions.py +1 -1
  5. AMS_BP/{photophysics → core/photophysics}/photon_physics.py +1 -1
  6. AMS_BP/{sample → core/sample}/flurophores/flurophore_schema.py +1 -1
  7. AMS_BP/{sim_microscopy.py → core/sim_microscopy.py} +1 -1
  8. AMS_BP/gui/main.py +4 -4
  9. AMS_BP/gui/sim_worker.py +3 -4
  10. AMS_BP/gui/widgets/camera_config_widget.py +4 -5
  11. AMS_BP/gui/widgets/cell_config_widget.py +3 -3
  12. AMS_BP/gui/widgets/channel_config_widget.py +2 -2
  13. AMS_BP/gui/widgets/condensate_config_widget.py +4 -4
  14. AMS_BP/gui/widgets/experiment_config_widget.py +53 -2
  15. AMS_BP/gui/widgets/flurophore_config_widget.py +1 -2
  16. AMS_BP/gui/widgets/global_config_widget.py +2 -2
  17. AMS_BP/gui/widgets/laser_config_widget.py +2 -2
  18. AMS_BP/gui/widgets/molecule_config_widget.py +3 -6
  19. AMS_BP/gui/windows/__init__.py +0 -0
  20. AMS_BP/gui/{configuration_window.py → windows/configuration_window.py} +15 -12
  21. AMS_BP/gui/{template_window_selection.py → windows/template_window_selection.py} +35 -5
  22. AMS_BP/main_cli.py +2 -2
  23. AMS_BP/resources/template_configs/metadata_configs.json +21 -3
  24. AMS_BP/resources/template_configs/twocolor_confocal_timeseries_live.toml +399 -0
  25. AMS_BP/resources/template_configs/twocolor_confocal_zstack_fixed.toml +406 -0
  26. AMS_BP/{sim_config.toml → resources/template_configs/twocolor_confocal_zstack_live.toml} +111 -111
  27. AMS_BP/tools/logging/__init__.py +0 -0
  28. {ams_bp-0.4.31.dist-info → ams_bp-0.4.40.dist-info}/METADATA +6 -2
  29. ams_bp-0.4.40.dist-info/RECORD +110 -0
  30. ams_bp-0.4.31.dist-info/RECORD +0 -106
  31. /AMS_BP/{configio → core}/__init__.py +0 -0
  32. /AMS_BP/{cells → core/cells}/__init__.py +0 -0
  33. /AMS_BP/{cells → core/cells}/budding_yeast_cell.py +0 -0
  34. /AMS_BP/{cells → core/cells}/cell_factory.py +0 -0
  35. /AMS_BP/{logging → core/configio}/__init__.py +0 -0
  36. /AMS_BP/{configio → core/configio}/configmodels.py +0 -0
  37. /AMS_BP/{configio → core/configio}/convertconfig.py +0 -0
  38. /AMS_BP/{configio → core/configio}/saving.py +0 -0
  39. /AMS_BP/{groundtruth_generators → core/groundtruth_generators}/__init__.py +0 -0
  40. /AMS_BP/{groundtruth_generators → core/groundtruth_generators}/nuclearporecomplexes.py +0 -0
  41. /AMS_BP/{metadata → core/metadata}/__init__.py +0 -0
  42. /AMS_BP/{metadata → core/metadata}/metadata.py +0 -0
  43. /AMS_BP/{motion → core/motion}/__init__.py +0 -0
  44. /AMS_BP/{motion → core/motion}/movement/__init__.py +0 -0
  45. /AMS_BP/{motion → core/motion}/track_gen.py +0 -0
  46. /AMS_BP/{optics → core/optics}/__init__.py +0 -0
  47. /AMS_BP/{optics → core/optics}/camera/__init__.py +0 -0
  48. /AMS_BP/{optics → core/optics}/camera/detectors.py +0 -0
  49. /AMS_BP/{optics → core/optics}/camera/quantum_eff.py +0 -0
  50. /AMS_BP/{optics → core/optics}/filters/__init__.py +0 -0
  51. /AMS_BP/{optics → core/optics}/filters/channels/__init__.py +0 -0
  52. /AMS_BP/{optics → core/optics}/filters/channels/channelschema.py +0 -0
  53. /AMS_BP/{optics → core/optics}/filters/filters.py +0 -0
  54. /AMS_BP/{optics → core/optics}/lasers/__init__.py +0 -0
  55. /AMS_BP/{optics → core/optics}/lasers/laser_profiles.py +0 -0
  56. /AMS_BP/{optics → core/optics}/lasers/scanning_patterns.py +0 -0
  57. /AMS_BP/{optics → core/optics}/psf/__init__.py +0 -0
  58. /AMS_BP/{optics → core/optics}/psf/psf_engine.py +0 -0
  59. /AMS_BP/{photophysics → core/photophysics}/__init__.py +0 -0
  60. /AMS_BP/{photophysics → core/photophysics}/state_kinetics.py +0 -0
  61. /AMS_BP/{probabilityfuncs → core/probabilityfuncs}/__init__.py +0 -0
  62. /AMS_BP/{probabilityfuncs → core/probabilityfuncs}/markov_chain.py +0 -0
  63. /AMS_BP/{probabilityfuncs → core/probabilityfuncs}/probability_functions.py +0 -0
  64. /AMS_BP/{run_sim_util.py → core/run_sim_util.py} +0 -0
  65. /AMS_BP/{sample → core/sample}/__init__.py +0 -0
  66. /AMS_BP/{sample → core/sample}/flurophores/__init__.py +0 -0
  67. /AMS_BP/{sample → core/sample}/sim_sampleplane.py +0 -0
  68. /AMS_BP/gui/{help_window.py → windows/help_window.py} +0 -0
  69. /AMS_BP/gui/{logging_window.py → windows/logging_window.py} +0 -0
  70. /AMS_BP/{logging → tools/logging}/logutil.py +0 -0
  71. /AMS_BP/{logging → tools/logging}/setup_run_directory.py +0 -0
  72. {ams_bp-0.4.31.dist-info → ams_bp-0.4.40.dist-info}/WHEEL +0 -0
  73. {ams_bp-0.4.31.dist-info → ams_bp-0.4.40.dist-info}/entry_points.txt +0 -0
  74. {ams_bp-0.4.31.dist-info → ams_bp-0.4.40.dist-info}/licenses/LICENSE +0 -0
AMS_BP/__init__.py CHANGED
@@ -10,4 +10,4 @@ Last updated: 2024-12-16
10
10
 
11
11
  """
12
12
 
13
- __version__ = "0.4.31"
13
+ __version__ = "0.4.40"
@@ -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}
@@ -25,8 +25,8 @@ from typing import Optional
25
25
 
26
26
  import numpy as np
27
27
 
28
+ from ...utils.decorators import cache
28
29
  from ..cells import BaseCell
29
- from ..utils.decorators import cache
30
30
  from .track_gen import Track_generator
31
31
 
32
32
 
@@ -5,7 +5,7 @@ Removal Time: NDY (not determined yet)
5
5
 
6
6
  import numpy as np
7
7
 
8
- from ...utils.decorators import _catch_recursion_error, deprecated
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 ...utils.constants import H_C_COM, N_A
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
@@ -18,12 +18,12 @@ from PyQt6.QtWidgets import (
18
18
  QWidget,
19
19
  )
20
20
 
21
- from ..logging.logutil import LoggerManager
22
- from ..logging.setup_run_directory import setup_run_directory
23
- from .logging_window import LogWindow
21
+ from ..tools.logging.logutil import LoggerManager
22
+ from ..tools.logging.setup_run_directory import setup_run_directory
24
23
  from .sim_worker import SimulationWorker
25
- from .template_window_selection import TemplateSelectionWindow
26
24
  from .widgets.utility_widgets.toggleswitch_widget import ToggleSwitch
25
+ from .windows.logging_window import LogWindow
26
+ from .windows.template_window_selection import TemplateSelectionWindow
27
27
 
28
28
  LOGO_PATH = str(Path(__file__).parent / "assets" / "drawing.svg")
29
29
 
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 ..logging.logutil import LogEmitter
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:
@@ -14,6 +14,10 @@ from PyQt6.QtWidgets import (
14
14
  QWidget,
15
15
  )
16
16
 
17
+ from ...core.configio.convertconfig import create_quantum_efficiency_from_config
18
+ from ...core.optics.camera.detectors import (
19
+ CMOSDetector,
20
+ )
17
21
  from .utility_widgets.spectrum_widget import SpectrumEditorDialog
18
22
 
19
23
 
@@ -122,11 +126,6 @@ class CameraConfigWidget(QWidget):
122
126
  # You can now use this data wherever needed, e.g., saving or validation
123
127
 
124
128
  def validate(self) -> bool:
125
- from ...configio.convertconfig import create_quantum_efficiency_from_config
126
- from ...optics.camera.detectors import (
127
- CMOSDetector,
128
- )
129
-
130
129
  try:
131
130
  data = self.get_data()
132
131
 
@@ -14,6 +14,9 @@ from PyQt6.QtWidgets import (
14
14
  QWidget,
15
15
  )
16
16
 
17
+ from ...core.cells import create_cell
18
+ from ...core.configio.configmodels import CellParameters
19
+
17
20
 
18
21
  class CellConfigWidget(QWidget):
19
22
  def __init__(self):
@@ -53,9 +56,6 @@ class CellConfigWidget(QWidget):
53
56
  layout.addWidget(self.validate_button)
54
57
 
55
58
  def validate(self) -> bool:
56
- from ...cells import create_cell
57
- from ...configio.configmodels import CellParameters
58
-
59
59
  try:
60
60
  data = self.get_data()
61
61
 
@@ -15,6 +15,8 @@ from PyQt6.QtWidgets import (
15
15
  QWidget,
16
16
  )
17
17
 
18
+ from ...core.configio.convertconfig import create_channels
19
+
18
20
 
19
21
  class ChannelConfigWidget(QWidget):
20
22
  def __init__(self):
@@ -48,8 +50,6 @@ class ChannelConfigWidget(QWidget):
48
50
 
49
51
  def validate(self) -> bool:
50
52
  try:
51
- from ...configio.convertconfig import create_channels
52
-
53
53
  data = self.get_data()
54
54
 
55
55
  # Full simulation-level validation
@@ -18,6 +18,10 @@ from PyQt6.QtWidgets import (
18
18
  QWidget,
19
19
  )
20
20
 
21
+ from ...core.cells import create_cell
22
+ from ...core.configio.configmodels import CondensateParameters
23
+ from ...core.motion import create_condensate_dict
24
+
21
25
 
22
26
  class CondensateConfigWidget(QWidget):
23
27
  # Signal to notify when molecule count changes
@@ -300,10 +304,6 @@ class CondensateConfigWidget(QWidget):
300
304
  }
301
305
 
302
306
  def validate(self) -> bool:
303
- from ...cells import create_cell
304
- from ...configio.configmodels import CondensateParameters
305
- from ...motion import create_condensate_dict
306
-
307
307
  try:
308
308
  data = self.get_data()
309
309
 
@@ -1,10 +1,12 @@
1
1
  from pathlib import Path
2
2
  from typing import List
3
3
 
4
+ from PyQt6.QtCore import Qt, QTimer
4
5
  from PyQt6.QtWidgets import (
5
6
  QComboBox,
6
7
  QDoubleSpinBox,
7
8
  QFormLayout,
9
+ QGraphicsOpacityEffect,
8
10
  QHBoxLayout,
9
11
  QLabel,
10
12
  QLineEdit,
@@ -18,6 +20,8 @@ from PyQt6.QtWidgets import (
18
20
  QWidget,
19
21
  )
20
22
 
23
+ from ...core.configio.convertconfig import create_experiment_from_config
24
+
21
25
 
22
26
  class ExperimentConfigWidget(QWidget):
23
27
  def __init__(self):
@@ -31,6 +35,32 @@ class ExperimentConfigWidget(QWidget):
31
35
  layout.setSpacing(10)
32
36
  self.setLayout(layout)
33
37
  self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
38
+
39
+ self.scanning_area = QWidget()
40
+ self.scanning_area_layout = QVBoxLayout()
41
+ self.scanning_area_layout.setContentsMargins(0, 0, 0, 0)
42
+ self.scanning_area_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
43
+ self.scanning_area.setLayout(self.scanning_area_layout)
44
+ layout.addWidget(self.scanning_area)
45
+ # Setup scanning label
46
+ self.scanning_label = QLabel("Scanning Confocal Selected")
47
+ self.scanning_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
48
+ self.scanning_label.setStyleSheet(
49
+ "color: green; font-weight: bold; font-size: 16px;"
50
+ )
51
+ layout.addWidget(self.scanning_label)
52
+ # Reserve space by always having label active, even if invisible
53
+ self.opacity_effect = QGraphicsOpacityEffect(self.scanning_label)
54
+ self.scanning_label.setGraphicsEffect(self.opacity_effect)
55
+ self.opacity_effect.setOpacity(0.0) # start invisible but reserved
56
+
57
+ # Blinking mode control
58
+ self._scanning_mode = False
59
+ self._blinking = False
60
+ self.blink_timer = QTimer(self)
61
+ self.blink_timer.setInterval(500) # ms
62
+ self.blink_timer.timeout.connect(self._toggle_scanning_label_visibility)
63
+
34
64
  form = QFormLayout()
35
65
 
36
66
  # Experiment Info
@@ -103,6 +133,24 @@ class ExperimentConfigWidget(QWidget):
103
133
  self.validate_button.clicked.connect(self.validate)
104
134
  layout.addWidget(self.validate_button)
105
135
 
136
+ def set_scanning_mode(self, enabled: bool):
137
+ self._scanning_mode = enabled
138
+ if enabled:
139
+ self.opacity_effect.setOpacity(1.0)
140
+ self.blink_timer.start()
141
+ else:
142
+ self.opacity_effect.setOpacity(0.0)
143
+ self.blink_timer.stop()
144
+
145
+ def _toggle_scanning_label_visibility(self):
146
+ """Toggles label opacity to create blinking without layout shifting."""
147
+ if self._scanning_mode:
148
+ current_opacity = self.opacity_effect.opacity()
149
+ new_opacity = 0.0 if current_opacity > 0.5 else 1.0
150
+ self.opacity_effect.setOpacity(new_opacity)
151
+ else:
152
+ self.opacity_effect.setOpacity(0.0)
153
+
106
154
  def update_z_position_mode(self, mode: str):
107
155
  # Clear existing
108
156
  for i in reversed(range(self.z_position_layout.count())):
@@ -202,6 +250,7 @@ class ExperimentConfigWidget(QWidget):
202
250
  for name in self.laser_position_widgets
203
251
  ],
204
252
  "xyoffset": [w.value() for w in self.xyoffset],
253
+ "scanning": self._scanning_mode,
205
254
  }
206
255
 
207
256
  if data["experiment_type"] == "z-stack":
@@ -249,10 +298,12 @@ class ExperimentConfigWidget(QWidget):
249
298
  positions = data.get("laser_positions_active", [])
250
299
  self.set_active_lasers(laser_names, powers=powers, positions=positions)
251
300
 
301
+ # Scanning Confocal
302
+ scanning_mode = data.get("scanning", False)
303
+ self.set_scanning_mode(scanning_mode)
304
+
252
305
  def validate(self) -> bool:
253
306
  try:
254
- from ...configio.convertconfig import create_experiment_from_config
255
-
256
307
  data = self.get_data()
257
308
  config_dict = {"experiment": data}
258
309
 
@@ -18,6 +18,7 @@ from PyQt6.QtWidgets import (
18
18
  QWidget,
19
19
  )
20
20
 
21
+ from ...core.configio.convertconfig import create_fluorophores_from_config
21
22
  from .utility_widgets.scinotation_widget import scientific_input_field
22
23
  from .utility_widgets.spectrum_widget import SpectrumEditorDialog
23
24
 
@@ -495,8 +496,6 @@ class FluorophoreConfigWidget(QWidget):
495
496
 
496
497
  def validate(self) -> bool:
497
498
  try:
498
- from ...configio.convertconfig import create_fluorophores_from_config
499
-
500
499
  data = self.get_data()
501
500
  # Try to build fluorophores with the backend logic
502
501
 
@@ -11,6 +11,8 @@ from PyQt6.QtWidgets import (
11
11
  QWidget,
12
12
  )
13
13
 
14
+ from ...core.configio.configmodels import GlobalParameters
15
+
14
16
 
15
17
  class GlobalConfigWidget(QWidget):
16
18
  def __init__(self):
@@ -122,8 +124,6 @@ class GlobalConfigWidget(QWidget):
122
124
 
123
125
  def validate(self) -> bool:
124
126
  try:
125
- from ...configio.configmodels import GlobalParameters
126
-
127
127
  data = self.get_data()
128
128
 
129
129
  GlobalParameters(**data)
@@ -15,6 +15,8 @@ from PyQt6.QtWidgets import (
15
15
  QWidget,
16
16
  )
17
17
 
18
+ from ...core.configio.convertconfig import create_lasers_from_config
19
+
18
20
 
19
21
  class LaserConfigWidget(QWidget):
20
22
  laser_names_updated = pyqtSignal(list)
@@ -81,8 +83,6 @@ class LaserConfigWidget(QWidget):
81
83
 
82
84
  def validate(self) -> bool:
83
85
  try:
84
- from ...configio.convertconfig import create_lasers_from_config
85
-
86
86
  data = self.get_data()
87
87
  create_lasers_from_config({"lasers": data})
88
88
 
@@ -20,6 +20,9 @@ from PyQt6.QtWidgets import (
20
20
  QWidget,
21
21
  )
22
22
 
23
+ from ...core.configio.configmodels import MoleculeParameters
24
+ from ...core.configio.convertconfig import create_dataclass_schema
25
+
23
26
 
24
27
  class MoleculeConfigWidget(QWidget):
25
28
  # Signal to notify when molecule count changes
@@ -99,8 +102,6 @@ class MoleculeConfigWidget(QWidget):
99
102
  data = self.get_data()
100
103
 
101
104
  # This will validate the schema using the backend logic
102
- from ...configio.configmodels import MoleculeParameters
103
- from ...configio.convertconfig import create_dataclass_schema
104
105
 
105
106
  _ = create_dataclass_schema(MoleculeParameters, data)
106
107
 
@@ -120,10 +121,6 @@ class MoleculeConfigWidget(QWidget):
120
121
  Load molecule configuration from TOML config format.
121
122
  """
122
123
  try:
123
- # Validate format first
124
- from ...configio.configmodels import MoleculeParameters
125
- from ...configio.convertconfig import create_dataclass_schema
126
-
127
124
  validated = create_dataclass_schema(MoleculeParameters, config)
128
125
 
129
126
  # Determine number of types
File without changes
@@ -18,19 +18,19 @@ from PyQt6.QtWidgets import (
18
18
  QWidget,
19
19
  )
20
20
 
21
+ from ..widgets.camera_config_widget import CameraConfigWidget
22
+ from ..widgets.cell_config_widget import CellConfigWidget
23
+ from ..widgets.channel_config_widget import ChannelConfigWidget
24
+ from ..widgets.condensate_config_widget import CondensateConfigWidget
25
+ from ..widgets.experiment_config_widget import ExperimentConfigWidget
26
+ from ..widgets.flurophore_config_widget import FluorophoreConfigWidget
27
+ from ..widgets.general_config_widget import GeneralConfigWidget
28
+ from ..widgets.global_config_widget import GlobalConfigWidget
29
+ from ..widgets.laser_config_widget import LaserConfigWidget
30
+ from ..widgets.molecule_config_widget import MoleculeConfigWidget
31
+ from ..widgets.output_config_widget import OutputConfigWidget
32
+ from ..widgets.psf_config_widget import PSFConfigWidget
21
33
  from .help_window import HelpWindow
22
- from .widgets.camera_config_widget import CameraConfigWidget
23
- from .widgets.cell_config_widget import CellConfigWidget
24
- from .widgets.channel_config_widget import ChannelConfigWidget
25
- from .widgets.condensate_config_widget import CondensateConfigWidget
26
- from .widgets.experiment_config_widget import ExperimentConfigWidget
27
- from .widgets.flurophore_config_widget import FluorophoreConfigWidget
28
- from .widgets.general_config_widget import GeneralConfigWidget
29
- from .widgets.global_config_widget import GlobalConfigWidget
30
- from .widgets.laser_config_widget import LaserConfigWidget
31
- from .widgets.molecule_config_widget import MoleculeConfigWidget
32
- from .widgets.output_config_widget import OutputConfigWidget
33
- from .widgets.psf_config_widget import PSFConfigWidget
34
34
 
35
35
 
36
36
  class ConfigEditor(QWidget):
@@ -110,6 +110,9 @@ class ConfigEditor(QWidget):
110
110
 
111
111
  # === Widget interconnections ===
112
112
  self.psf_tab.confocal_mode_changed.connect(self.laser_tab.set_confocal_mode)
113
+ self.psf_tab.confocal_mode_changed.connect(
114
+ self.experiment_tab.set_scanning_mode
115
+ )
113
116
 
114
117
  self.molecule_tab.molecule_count_changed.connect(
115
118
  self.fluorophore_tab.set_mfluorophore_count
@@ -11,13 +11,14 @@ from PyQt6.QtWidgets import (
11
11
  QMessageBox,
12
12
  QPushButton,
13
13
  QScrollArea,
14
+ QSizePolicy,
14
15
  QVBoxLayout,
15
16
  QWidget,
16
17
  )
17
18
 
18
19
  from .configuration_window import ConfigEditor
19
20
 
20
- TEMPLATE_DIR = Path(__file__).parent.parent / "resources" / "template_configs"
21
+ TEMPLATE_DIR = Path(__file__).parent.parent.parent / "resources" / "template_configs"
21
22
  METADATA_PATH = TEMPLATE_DIR / "metadata_configs.json"
22
23
 
23
24
 
@@ -52,32 +53,61 @@ class TemplateSelectionWindow(QWidget):
52
53
 
53
54
  def create_template_card(self, entry: dict) -> QWidget:
54
55
  group = QGroupBox(entry["label"])
56
+ group.setMinimumHeight(150)
57
+ group.setMaximumHeight(200)
55
58
  layout = QHBoxLayout()
59
+ layout.setAlignment(Qt.AlignmentFlag.AlignTop)
60
+ layout.setSpacing(15)
56
61
 
57
62
  # Image
58
63
  img_label = QLabel()
59
64
  img_path = TEMPLATE_DIR / entry["image"]
60
65
  if img_path.exists():
61
66
  pixmap = QPixmap(str(img_path)).scaled(
62
- 200, 200, Qt.AspectRatioMode.KeepAspectRatio
67
+ 150,
68
+ 150,
69
+ Qt.AspectRatioMode.KeepAspectRatio,
70
+ Qt.TransformationMode.SmoothTransformation,
63
71
  )
64
72
  img_label.setPixmap(pixmap)
65
73
  else:
66
74
  img_label.setText("[Missing image assets]")
75
+ img_label.setFixedSize(150, 150)
76
+ img_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
67
77
  layout.addWidget(img_label)
68
78
 
69
- # Description + Button
79
+ # Description + Button (in a VBox)
70
80
  vbox = QVBoxLayout()
81
+ vbox.setAlignment(Qt.AlignmentFlag.AlignTop)
82
+
83
+ # Scrollable description
84
+ desc_scroll = QScrollArea()
85
+ desc_scroll.setFixedHeight(100) # Height for the scroll area
86
+ desc_scroll.setWidgetResizable(True)
87
+
88
+ desc_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
89
+ desc_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
71
90
 
72
91
  description_label = QLabel(entry.get("description", ""))
73
- description_label.setWordWrap(True) # <--- this is the key!
74
- vbox.addWidget(description_label)
92
+ description_label.setWordWrap(True)
93
+ description_label.setAlignment(
94
+ Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignLeft
95
+ )
96
+ description_label.setContentsMargins(5, 5, 5, 5)
97
+ description_label.setSizePolicy(
98
+ description_label.sizePolicy().horizontalPolicy(),
99
+ QSizePolicy.Policy.Maximum,
100
+ )
101
+
102
+ desc_scroll.setWidget(description_label)
103
+ vbox.addWidget(desc_scroll)
75
104
 
76
105
  btn = QPushButton("Use This Template")
77
106
  btn.clicked.connect(
78
107
  lambda _, config=entry["config"]: self.load_template(config)
79
108
  )
80
109
  vbox.addWidget(btn)
110
+
81
111
  layout.addLayout(vbox)
82
112
 
83
113
  group.setLayout(layout)
AMS_BP/main_cli.py CHANGED
@@ -34,9 +34,9 @@ from rich.progress import Progress, SpinnerColumn, TextColumn
34
34
  from typing_extensions import Annotated
35
35
 
36
36
  from . import __version__
37
+ from .core.run_sim_util import run_simulation_from_file
37
38
  from .gui.main import MainWindow
38
- from .logging.logutil import cleanup_old_logs
39
- from .run_sim_util import run_simulation_from_file
39
+ from .tools.logging.logutil import cleanup_old_logs
40
40
 
41
41
  cli_help_doc = str(
42
42
  """
@@ -1,20 +1,38 @@
1
1
  {
2
2
  "PAmCherry_EGFP_Widefield_Zstack_Fixed": {
3
- "label": "PAmCherry with EGFP (Fixed)",
3
+ "label": "PAmCherry with EGFP (Widefield Fixed)",
4
4
  "description": "Two-Color Widefield Microscopy of a RodCell with PAmCherry and EGFP as molecules. Cell is fixed and the experiment protocol is a Z-Stack.",
5
5
  "image": "palm_egfp.png",
6
6
  "config": "twocolor_widefield_zstack_fixed.toml"
7
7
  },
8
8
  "PAmCherry_EGFP_Widefield_Zstack_Live": {
9
- "label": "PAmCherry with EGFP (Live)",
9
+ "label": "PAmCherry with EGFP (Widefield Live)",
10
10
  "description": "Two-Color Widefield Microscopy of a RodCell with PAmCherry and EGFP as molecules. Cell is live (with motion described by bounded FBM models (differently for each molecule) and the experiment protocol is a Z-Stack.",
11
11
  "image": "palm_egfp.png",
12
12
  "config": "twocolor_widefield_zstack_live.toml"
13
13
  },
14
14
  "PAmCherry_EGFP_Widefield_Timeseries_Live": {
15
- "label": "PAmCherry with EGFP (Live timeseries)",
15
+ "label": "PAmCherry with EGFP (Widefield Live timeseries)",
16
16
  "description": "Two-Color Widefield Microscopy of a RodCell with PAmCherry and EGFP as molecules. Cell is live (with motion described by bounded FBM models (differently for each molecule) and the experiment protocol is a time-series.",
17
17
  "image": "palm_egfp.png",
18
18
  "config": "twocolor_widefield_timeseries_live.toml"
19
+ },
20
+ "PAmCherry_EGFP_Confocal_Timeseries_Live": {
21
+ "label": "PAmCherry with EGFP (Confocal Live timeseries)",
22
+ "description": "Two-Color Confocal (1 AU pinhole diameter) Microscopy of a RodCell with PAmCherry and EGFP as molecules. Cell is live (with motion described by bounded FBM models (differently for each molecule) and the experiment protocol is a time-series.",
23
+ "image": "palm_egfp.png",
24
+ "config": "twocolor_confocal_timeseries_live.toml"
25
+ },
26
+ "PAmCherry_EGFP_Confocal_Zstack_Live": {
27
+ "label": "PAmCherry with EGFP (Confocal Live)",
28
+ "description": "Two-Color Confocal (1 AU pinhole diameter) Microscopy of a RodCell with PAmCherry and EGFP as molecules. Cell is live (with motion described by bounded FBM models (differently for each molecule) and the experiment protocol is a Z-Stack.",
29
+ "image": "palm_egfp.png",
30
+ "config": "twocolor_confocal_zstack_live.toml"
31
+ },
32
+ "PAmCherry_EGFP_Confocal_Zstack_Fixed": {
33
+ "label": "PAmCherry with EGFP (Confocal Fixed)",
34
+ "description": "Two-Color Confocal (1 AU pinhole diameter) Microscopy of a RodCell with PAmCherry and EGFP as molecules. Cell is fixed and the experiment protocol is a Z-Stack.",
35
+ "image": "palm_egfp.png",
36
+ "config": "twocolor_confocal_zstack_fixed.toml"
19
37
  }
20
38
  }