AMS-BP 0.3.0__py3-none-any.whl → 0.4.0__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/configmodels.py +32 -18
- AMS_BP/configio/convertconfig.py +508 -632
- AMS_BP/gui/README.md +77 -0
- AMS_BP/gui/__init__.py +0 -0
- AMS_BP/gui/assets/__init__.py +0 -0
- AMS_BP/gui/assets/drawing.svg +107 -0
- AMS_BP/gui/configuration_window.py +333 -0
- AMS_BP/gui/help_docs/__init__.py +0 -0
- AMS_BP/gui/help_docs/cell_help.md +45 -0
- AMS_BP/gui/help_docs/channels_help.md +78 -0
- AMS_BP/gui/help_docs/condensate_help.md +59 -0
- AMS_BP/gui/help_docs/detector_help.md +57 -0
- AMS_BP/gui/help_docs/experiment_help.md +92 -0
- AMS_BP/gui/help_docs/fluorophore_help.md +128 -0
- AMS_BP/gui/help_docs/general_help.md +43 -0
- AMS_BP/gui/help_docs/global_help.md +47 -0
- AMS_BP/gui/help_docs/laser_help.md +76 -0
- AMS_BP/gui/help_docs/molecule_help.md +78 -0
- AMS_BP/gui/help_docs/output_help.md +5 -0
- AMS_BP/gui/help_docs/psf_help.md +51 -0
- AMS_BP/gui/help_window.py +26 -0
- AMS_BP/gui/logging_window.py +93 -0
- AMS_BP/gui/main.py +255 -0
- AMS_BP/gui/sim_worker.py +58 -0
- AMS_BP/gui/template_window_selection.py +100 -0
- AMS_BP/gui/widgets/__init__.py +0 -0
- AMS_BP/gui/widgets/camera_config_widget.py +213 -0
- AMS_BP/gui/widgets/cell_config_widget.py +225 -0
- AMS_BP/gui/widgets/channel_config_widget.py +307 -0
- AMS_BP/gui/widgets/condensate_config_widget.py +341 -0
- AMS_BP/gui/widgets/experiment_config_widget.py +259 -0
- AMS_BP/gui/widgets/flurophore_config_widget.py +513 -0
- AMS_BP/gui/widgets/general_config_widget.py +47 -0
- AMS_BP/gui/widgets/global_config_widget.py +142 -0
- AMS_BP/gui/widgets/laser_config_widget.py +255 -0
- AMS_BP/gui/widgets/molecule_config_widget.py +714 -0
- AMS_BP/gui/widgets/output_config_widget.py +61 -0
- AMS_BP/gui/widgets/psf_config_widget.py +128 -0
- AMS_BP/gui/widgets/utility_widgets/__init__.py +0 -0
- AMS_BP/gui/widgets/utility_widgets/scinotation_widget.py +21 -0
- AMS_BP/gui/widgets/utility_widgets/spectrum_widget.py +115 -0
- AMS_BP/logging/__init__.py +0 -0
- AMS_BP/logging/logutil.py +83 -0
- AMS_BP/logging/setup_run_directory.py +35 -0
- AMS_BP/{run_cell_simulation.py → main_cli.py} +27 -72
- AMS_BP/optics/filters/filters.py +2 -0
- AMS_BP/resources/template_configs/metadata_configs.json +20 -0
- AMS_BP/resources/template_configs/sim_config.toml +408 -0
- AMS_BP/resources/template_configs/twocolor_widefield_timeseries_live.toml +399 -0
- AMS_BP/resources/template_configs/twocolor_widefield_zstack_fixed.toml +406 -0
- AMS_BP/resources/template_configs/twocolor_widefield_zstack_live.toml +408 -0
- AMS_BP/run_sim_util.py +76 -0
- AMS_BP/sim_microscopy.py +2 -2
- {ams_bp-0.3.0.dist-info → ams_bp-0.4.0.dist-info}/METADATA +59 -34
- ams_bp-0.4.0.dist-info/RECORD +103 -0
- ams_bp-0.4.0.dist-info/entry_points.txt +2 -0
- ams_bp-0.3.0.dist-info/RECORD +0 -55
- ams_bp-0.3.0.dist-info/entry_points.txt +0 -2
- {ams_bp-0.3.0.dist-info → ams_bp-0.4.0.dist-info}/WHEEL +0 -0
- {ams_bp-0.3.0.dist-info → ams_bp-0.4.0.dist-info}/licenses/LICENSE +0 -0
AMS_BP/gui/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# AMS-BP GUI
|
2
|
+
|
3
|
+
The AMS-BP GUI provides a user-friendly interface for constructing and validating configuration files for the AMS-BP fluorescence microscopy simulation framework. It integrates simulation execution, configuration design, log packaging, and data visualization in one cohesive window.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
This GUI supports:
|
8
|
+
|
9
|
+
A template-based configuration builder for quick setup.
|
10
|
+
A visual editor for each simulation parameter block (cells, molecules, fluorophores, lasers, etc.).
|
11
|
+
Interactive help sections for each tab.
|
12
|
+
Simulation execution using prebuilt configs.
|
13
|
+
Napari integration for visualization of resulting microscopy data.
|
14
|
+
Packaging of simulation logs for easy sharing.
|
15
|
+
|
16
|
+
## Launching the GUI
|
17
|
+
|
18
|
+
```bash
|
19
|
+
run_AMS_BP gui
|
20
|
+
```
|
21
|
+
## GUI Structure
|
22
|
+
|
23
|
+
### Main Window
|
24
|
+
The main window (MainWindow) acts as the launchpad for:
|
25
|
+
|
26
|
+
Configuration Builder — Launches a template selector and opens a full editor.
|
27
|
+
Run Simulation from Config — Lets you pick a .toml file and start the simulation.
|
28
|
+
Visualize Microscopy Data — Opens TIFF and other images in a Napari viewer.
|
29
|
+
Package Logs for Sharing — Archives a run_* folder into a .zip.
|
30
|
+
#### Configuration Editor
|
31
|
+
Once a template is selected, the ConfigEditor is launched. It contains:
|
32
|
+
|
33
|
+
A dropdown navigation system replacing traditional tabs.
|
34
|
+
A floating tab counter and preview/save buttons.
|
35
|
+
A help button per section (reads *.md files from help_docs/).
|
36
|
+
Live validation using internal config models (convertconfig.py).
|
37
|
+
##### Tabs & Widgets
|
38
|
+
Each configuration section is implemented as a modular PyQt widget, including:
|
39
|
+
|
40
|
+
GlobalConfigWidget
|
41
|
+
CellConfigWidget
|
42
|
+
MoleculeConfigWidget
|
43
|
+
FluorophoreConfigWidget
|
44
|
+
CondensateConfigWidget
|
45
|
+
LaserConfigWidget
|
46
|
+
ExperimentConfigWidget
|
47
|
+
...and more
|
48
|
+
Each widget supports:
|
49
|
+
|
50
|
+
get_data() → Extract validated dictionary data
|
51
|
+
set_data(data: dict) → Load existing config into the UI
|
52
|
+
validate() → Validate using backend logic
|
53
|
+
get_help_path() → Load the corresponding markdown help page
|
54
|
+
|
55
|
+
#### Running a Simulation
|
56
|
+
|
57
|
+
Once you've completed the config file setup via the GUI:
|
58
|
+
|
59
|
+
Click "Preview Configuration TOML" to confirm contents.
|
60
|
+
Click "Ready to Save Configuration" and choose a .toml path.
|
61
|
+
Return to the main window, click "Run Simulation from Config".
|
62
|
+
Simulation will launch in a background thread and print logs to a live window.
|
63
|
+
Once done, results will be saved in a run_*/ folder.
|
64
|
+
|
65
|
+
#### Viewing Results
|
66
|
+
|
67
|
+
Click "Visualize Microscopy Data (Napari)"
|
68
|
+
Select any .tif, .tiff, .nd2, or .zarr file
|
69
|
+
The data will be loaded into a new Napari viewer session
|
70
|
+
|
71
|
+
#### Packaging Logs
|
72
|
+
|
73
|
+
To share or archive a completed simulation:
|
74
|
+
|
75
|
+
Click "Package Logs for Sharing".
|
76
|
+
Select the run_* folder you want.
|
77
|
+
Choose a destination for the .zip file.
|
AMS_BP/gui/__init__.py
ADDED
File without changes
|
File without changes
|
@@ -0,0 +1,107 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
3
|
+
|
4
|
+
<svg
|
5
|
+
width="125.97778mm"
|
6
|
+
height="53.644855mm"
|
7
|
+
viewBox="0 0 125.97776 53.644857"
|
8
|
+
version="1.1"
|
9
|
+
id="svg5"
|
10
|
+
xml:space="preserve"
|
11
|
+
inkscape:version="1.2.1 (9c6d41e4, 2022-07-14)"
|
12
|
+
sodipodi:docname="drawing.svg"
|
13
|
+
inkscape:export-filename="drawing.png"
|
14
|
+
inkscape:export-xdpi="96"
|
15
|
+
inkscape:export-ydpi="96"
|
16
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
17
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
18
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
19
|
+
xmlns="http://www.w3.org/2000/svg"
|
20
|
+
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
21
|
+
id="namedview7"
|
22
|
+
pagecolor="#ffffff"
|
23
|
+
bordercolor="#000000"
|
24
|
+
borderopacity="0.25"
|
25
|
+
inkscape:showpageshadow="2"
|
26
|
+
inkscape:pageopacity="0.0"
|
27
|
+
inkscape:pagecheckerboard="0"
|
28
|
+
inkscape:deskcolor="#d1d1d1"
|
29
|
+
inkscape:document-units="mm"
|
30
|
+
showgrid="false"
|
31
|
+
inkscape:zoom="0.73986917"
|
32
|
+
inkscape:cx="160.83925"
|
33
|
+
inkscape:cy="87.85337"
|
34
|
+
inkscape:window-width="1309"
|
35
|
+
inkscape:window-height="456"
|
36
|
+
inkscape:window-x="0"
|
37
|
+
inkscape:window-y="37"
|
38
|
+
inkscape:window-maximized="0"
|
39
|
+
inkscape:current-layer="g3684" /><defs
|
40
|
+
id="defs2" /><g
|
41
|
+
inkscape:label="Layer 1"
|
42
|
+
inkscape:groupmode="layer"
|
43
|
+
id="layer1"
|
44
|
+
transform="translate(-27.086214,-76.914915)"><g
|
45
|
+
id="g3684"
|
46
|
+
transform="matrix(1.9992994,0,0,2.2020296,-94.700072,-124.92117)"><rect
|
47
|
+
style="fill:#000000;fill-opacity:1;stroke:#020003;stroke-width:0.995148;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1"
|
48
|
+
id="rect3488"
|
49
|
+
width="38.09753"
|
50
|
+
height="22.701815"
|
51
|
+
x="62.086613"
|
52
|
+
y="92.491959"
|
53
|
+
ry="2.0249722" /><g
|
54
|
+
id="g3010"
|
55
|
+
transform="matrix(0.06365389,0,0,0.06365391,96.148715,87.667151)"><path
|
56
|
+
d="M 402.773,182.731 272.587,122.942 v -4.525 A 35.36,35.36 0 0 0 237.267,83.1 h -8.619 a 35.36,35.36 0 0 0 -35.32,35.321 v 64.531 a 21.723,21.723 0 0 0 14.946,20.643 v 34.972 a 24.684,24.684 0 0 0 49.367,0 V 203.6 a 21.732,21.732 0 0 0 14.946,-20.648 v -4.1 l 37.313,46.205 c 0.027,0.032 0.053,0.065 0.08,0.1 a 33.611,33.611 0 0 1 7.9,21.628 v 28.13 h -160.9 a 18.456,18.456 0 1 0 0,36.911 h 95.32 a 31.079,31.079 0 0 0 -6.655,19.241 v 30.479 a 31.079,31.079 0 0 0 6.655,19.241 H 104.373 a 21.924,21.924 0 0 0 -21.9,21.9 v 4.323 a 21.924,21.924 0 0 0 21.9,21.9 h 319.153 a 6,6 0 0 0 6,-6 V 224.23 a 45.635,45.635 0 0 0 -26.753,-41.499 z m -169.815,68.514 a 12.7,12.7 0 0 1 -12.684,-12.682 v -33.847 l 0.006,-0.011 h 25.361 v 33.858 a 12.7,12.7 0 0 1 -12.683,12.682 z m 27.629,-68.3 a 9.74,9.74 0 0 1 -9.229,9.739 c -0.178,0.008 -0.349,0.018 -0.527,0.018 h -35.748 c -0.178,0 -0.348,-0.01 -0.5,-0.017 a 9.744,9.744 0 0 1 -9.25,-9.74 V 118.417 A 23.347,23.347 0 0 1 228.648,95.1 h 8.619 a 23.348,23.348 0 0 1 23.32,23.321 z M 150.523,293.366 a 6.463,6.463 0 0 1 6.456,-6.455 h 160.9 v 12.911 h -160.9 a 6.463,6.463 0 0 1 -6.456,-6.456 z m 107.118,68.176 v -30.479 a 19.261,19.261 0 0 1 19.24,-19.241 h 41 v 68.961 h -41 a 19.262,19.262 0 0 1 -19.24,-19.241 z M 417.526,416.9 H 104.373 a 9.91,9.91 0 0 1 -9.9,-9.9 v -4.323 a 9.911,9.911 0 0 1 9.9,-9.9 h 313.153 z m 0,-36.121 h -87.643 v -134 a 45.622,45.622 0 0 0 -10.68,-29.31 l -45.948,-56.9 a 6.023,6.023 0 0 0 -0.668,-0.694 v -23.727 l 125.21,57.5 a 33.626,33.626 0 0 1 19.729,30.58 z"
|
57
|
+
id="path2881"
|
58
|
+
style="fill:#d0ce2d;fill-opacity:1;stroke:#648ddb;stroke-opacity:1" /><path
|
59
|
+
d="m 374,288 a 38,38 0 1 0 38,38 38.043,38.043 0 0 0 -38,-38 z m 0,64 a 26,26 0 1 1 26,-26 26.029,26.029 0 0 1 -26,26 z"
|
60
|
+
id="path2883"
|
61
|
+
style="fill:#000000;fill-opacity:1" /></g><g
|
62
|
+
id="g3652"
|
63
|
+
transform="matrix(0.9,0,0,0.9,8.29375,10.389063)"><g
|
64
|
+
id="g1060"><image
|
65
|
+
width="15.875"
|
66
|
+
height="22.489582"
|
67
|
+
preserveAspectRatio="none"
|
68
|
+
xlink:href="data:image/PNG;base64, iVBORw0KGgoAAAANSUhEUgAAADwAAABVCAIAAACaZRC3AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAA B6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJ TUUH6AwYDDYCCJaoAwAAF0FJREFUaN6Nm2tvJceRpp+IyKw6ZLcsyQN7PcbYwCx2doH9/79nsLvj8U 2W+kqeU5kZEfshs6hWq0kNQTTQRDeZjIqMeG8lfPIhgillY7vn8jX7V+w7e6M+EI1u3O7IgjjmWOBJ d26DY+CJCKoUpQgKVimvqV9RNmj4QQ9uQU+AGrxq3HUkGYW2k3eMHRf6jfEe/8B4oN8YjXAifzxn+f HEIJBJOjkYjeL0gQzoZKcHuUOgA2uUwASSEXQhFTGsUIQKlqhgQQ02Q14xLuCMG/1gDOjcOgxIAlDU 2JIAa/TG6DBwR5LkJx/l6cTzQwUREFLwwJwR6yFkQkMS7RRnD0IJxQolEUMqZlSlONaxpDpfJfeKXO jJtZEND6LjnSMYgggCNqgPFEWT0skbHPQOTgafnbp8+hcVVLGCVrQgiszfoYIgiSYMNClCrWQhCyPw QBUKpaKKOSVQZ0+24B4MQijgQQ6ycTgDXEFRoYAG2sjBGHhnOJHkZ0X+YnuIogWrFGOD4lTQjSioEw OCEGKHim2oYZ16UOb/V0pBFYJsREInr6iDYDf2G/vB6ETQjZyVMlSJ5Ejoq9+G0Q2fleb5SiOIoEY1 NmFzdrAEoxk+65RExXbahhUCZFAEIANx1KDgSToy+Hglg91I4Rj4FQ6sY5BCKlUohm2k4EpLBnTHjQ ERZIA/d2gBQZWq7MkebJ0NUglIpQcjCUUrsoFgTjjuq7WYvZcAaYRxa8SNflALgCfeGUHMUSOYUoVN MHClF4aBkYpDQg5ywE/b+vOeLsbFuFMugSieeNCFJrR5lwsEw8mkDCTIRBQUHB2IEAJJCihtMOZlUE giicTPaSNKgRqUQcyedjJIIZQoSAEl5blDzzIXLhv3G7Uy4HB6ZwxGEk4mAnRy/vKOndMGyPlVUEAR g41I3BmKKBmI4ELMHydYoomCCuaUQekU52oIa4ilfFbon15EBCnIjr6GHYfxiA/SIZFAEhlwIIEZRd gTldXuPpBAHElkgw0KruQBgSZpJIgjgKONqmwbpaA71uG2BvwcXLOZf3YPPzl0QiQjacLtgt4zAhoG JXFA6YMM1KGsOSO59mgmKWis505BNmQ2aMCBChSikEIG0dFBnRsUVMj50BRRqjKCbdAdj3Xu/PmhST LwztGRhlUksEFxNOlCggueRCLzqlWaIEoWMhBIQYysZAEjK0Bp61lhmGLAYAQ5IBFHG6LgRBKzeRIb WMcGEpDPtMc8dzi9IVeqYkntlKBCQhPE1nExYqPfQSUKFKRjiclqMw9kQIGC7uQED3a2e0UFH+QgWT c4nRF0J+f1cGSwhshLczrJIJ10YiAwAstV41k8mXe5kJV+R+7EBgI3ym3twvlNwhFBCqFIZQQuaDAE raSS+rTSsMBBnHR89rSBfQIwvnzop19IQMEgcaUbAm7EdrZpLGSyek1IyGCAgiY46WvWakXnsBc86f PfOBpoYhC6psQIxtlgsqNAwO3H2f/lSufcUrYqKg7KKEghClRkRwV3opM3FMpAO1GIjvqaYhaMTsi6 taJIru0TwZggsa9BUSpquOKBKxi24RckyeN8Gl+sdD4dOkmHjl4poA4G5fxNhFAkCNBG6exX5A7fiA DHhKrIIEACdaxRQBJJQvGEThxkB8F29kKtuEAiFZTYyEobuODzMX7x0PL0GUhDHrDGJqiAMgbuUMiE gggmWFIaelAGutMEdOGeDEYyQJPi3Dk6aAmKK+4QZCBGLdhGqYhg0BNTXBlOc5rj+eKcnk9BQBw6CF kxSEc6gBd8Qg5DKxrIWEhybtM0stDnsE+GsIEmNSiBJE2Z+35uuDDcGDqvDyhFiYF3XNYadshFT57Z iDKvbYFKbnhdIA4nAhcikFy3VBRsfVNxxNHEk5arSCKURCZQBlFsAvREcy28BlcnWNdOOjLQwIScy1 A+O/DPLqIIYlDhgt9RDE9USUhf19+CdDSQgFndOU8G2XBwVgPM4wpkkApK6I/DaWKsSMZAxhqUMT8h g+KUwH6hPea3m0/fsDkmAy1YInKO3gaFkgt1ZMHLWrYMJChKNagU2JKLU841GQoDBbXFfaphUILodK cLYaBIUJ3q9E/aOr986ESd0tk71diMPRdTGsExiMnn4FXyDXwlEFyTB+O6IYollw27wzYwpFEPotMq ZUMdeaAMKuyFS+FS0MCdPkjnKPgkewM51rlXZz+7EQMG2rDCJbkzLoIqDi04ghBK8jr5Lfwx+G+CJO +V74yPhdy4v/Dtt7z+Fn1NKuMjtzc8fuS98bay3xiD7GzCa+OVcQcR3JzmHIMGIxCoiTg5Tlb73HKR s9ji1M4OF2NTsuCKKZuRwq78Gv6Q/G/hj0I13iR/GvxQkXu++T2/+SNf/47tK0ZyfM+7P/H3f/DXAx ceB10JYQ8ucEk2oTsZeDCC5gxHnHDUF7vLl7FHnkvfZC3kETg0WxtxT74S/gn+Wfgfxv8yNuVd8I3z n8L4Fd/+C7/5N779HfsdOnjceHNQHrje+EdDD6QjjgySxayG0IxecV0kbT5wGfgkeP+V6WG2iFAT3G jGccE3zLHO7tzDa3hd+HXhG+FXTgi58/41d9+y/Zrya+7vqAf3D/jObjAYH2g3+oGPtdWHIEYoV6Fv 5BRPbMGmdDx/iQQ8NYklJZFgKIdwLfiGXFDHjBok9KAlDVS4L7za2F6hd3Q4BuKEMJSb8hB8GLw7eH /weONwwohCN6yA4qz16QYbZuTjCSuEmPSCnzTJT5jLpH4SMAhjwJGLx6sjCXBUPhrfDf585ZuBFsbO u8q7wscD/Z7yiroxghi8e8v/e8d/fOSvN943jmRUKHhZYN+daESuSZd2rhIhCzmZ23MK09MXp8ggMJ JDGDCcPNZ+OhQR3hb+Ey6OHLwtkPwd/nLlzRuK8955CF4/cMDbv/Hv3/F/3/GPK22gyrbDBZWljI3O GEvVUEUPMshOCFEXLn/CF1+mW7PSOeigQg/aYCiZ0BGoSiQRxCAOHg5+15DBm8Fb4+MV/cCrR/5+5f 4HmvDD9/zlB/7xyLVhwv0deg+FmCLTDW6EE0YKdLJB4BAbMfWWAf3l6ZFkMpIOCE3oiQ8iFoLDiE46 HvTkIfhbYMHNuVaOgdywxl8O9jd44fHKhwe6sxt3BdtRozf6A48PjIPbIOcWFCKwQSZZyG3dSG4LUj /b0wkOXSgGFd9AyVzacChpoAwlFYEIHjuaxGAIQ+hJHOhb9EDrGl6XwtfJPxkXQQ4eH3j7EblxOAXU +BH7BZJYoAEsNJafwP0vVDoEF7phlXKHXBDFpk4s5GRcRlE2oW4UQ29kW5zyKNyUJsTADmywK6+Ub4 x/EX6ffDOg86bx50CUR+HjFJdPJpuxIOGExKOTA39B6l1ajhJGFqRi92D4gVzJIAOb0m1wD7uxV0zQ 4BiMjhf6haMygGQTpPKVcZ/8pvGvjd8GEfx9oxtvk80pHRnIIAbp6KAkNhG8w5TW4ydl/sJGDCNtIU kEnSzLiCAHpbHDnXMH+0bZqUYYI9EpqMr6a7CkylbojTK4b3ydUPlwR1XMkSsaWFsYWgIJjOV++IAn wSSfmx5CKjlFICOAIMb6DwaZWD93bK7h6kqe81UVsVNASnpwBB+d953vO287r4HKx8JjpR2nXjMVM3 4U7+bknfD6Cwzx52t8NlTqUgXmn+epiaQn1yACO9iUraDgG+moobm0gQiy05Vr8kPjTweXzgehdL4/ eNt5vDGuRCdZTkMmHmubzIViZ4vLC+0hJ22ehZzgKx05bRegCx+VFpizH1yCvcDEypOiOwmSuOMHD5 PSOzf4W7A/0hrv4dqJAwnUkB3KOf4H0am5mEvNxYbyueUyxTvtaFmEL84dbo4lCCPpSTc2QZOLk3qq o44dbFCFLhxBgw7vhC5chX8I905thDMcHVQW4kUZU1EBD0pgZ9vM9eJfrPTUD3RQBiWWrKgJ8xkNLA ghhW54oSgl2ZI6yWzijjp3CXATIuhT6yiMSivcjNfw2tk7dDTZHAbloCRNOZKcinViiioaC/M809MJ iSYlKdO8KasAE91ORyYq7MgFK+zOZaCN5vRBH5SgTKadjKRCGN0YhaNiG6WwgTXySl7RG3dBHVThWt CkTd1e8UJM9Dc+d+U+h6YTYZlSFLUlg/sgEgYh5AZ36D2logEHDa4P3A76QCYvzuWEbEZs5LZ4eASh pDKS6GvFirIJCuGraiGgdCGEIxn+vBOwLIgnVUBOWDilgrpkcwxVpOAXjuSD0A4+OrfGCMJIxRSMYt RCVtIYiTgMGIyCJt0ZIPUkzqBOdRSGLUQwoCtDnmcueYJuN/qU7QYj12qVOXoCHWRHHRc+KjehOTfn iKVoTNFMz4krvtYyjjh+wye/glbJtcQIB0eFoqiQyRhEX/7nS3QrBRcaaBD9HJZCCYqgwghiAkVFpo n2iF8ZnUxSGQpKOeFEdHwgFS0L+oTTg1FpO20jZKEOEkuKsFWsUp0RlOkF5wvtAQGeNEcPMqnGpmwb e8ESFwrErPQD0QjwG1ypA1GGkcIATzSXO+EK5ZwAyhAOQzf6PX1fBGcuARMSrCAVUWpbNpDn52LvF5 yACMb0Ioztwitjr8sDXvox4NRODLRjTp2MEFou8O0BTkwJT1Algkx8gplKzM++PFkBbCmXcbpbRZZQ /9Ian8WY4ue8iAWKoZUx6EGfFoQtyXQ9Vluy4ghG4LrE/RBUEENtlWp93dCJbRwOuCEDUWRDYhnE05 eJghdyvGAzgyhSkXvYycB9Xne60JLHweikoXO2TJBU2RQDGdjcSpO16SqR6BI/p8sRiuiPrSyOdTKR StmoQmFZLZ74hThwfXFOi6AF2eEVPhhXjiQ6qRyd3smOTlVzasYbtS5HSwZ2UJ0iaMUrXXHgSd0q6x mGLF0lDSmIkY4p28ZlwxQ1RpKCHMTjQm9fqPTpFK+jI4TSlAx6J5LR8IY4BiZrHstG39C6yikH+0EJ tBCFw7gFfeLPKV9tRCVtzXvm0q2c6H0dXeqSq/uGlnOBvHARGcSB1CXNr6zOIBuc0kfMH7mTd/g9x0 YUDPSB8rCIQoIE4eTAHc31PbvSz2uqTsTiywg4DEQphTovUuEm/Nwq+kT3OB1bDijohhhhmGFjMTlO 2yHu4Svia/JXxB0USOQN/BVztAOURB3xNea3JIMjIIlBBgHmSKCT6ife1iSpFQ1aUHJND3mO2E5rKx pcQNCKGAYbVF2Bg270neMb+Gfid+S3cA8FuaH/QXzAH5YVfUBzPCjJBbaBNBAGxHSeBpqoUSu2Y9Nx dLihMHyJ/PIyCXiSxQRkOvKKJXtwKWwFGXTj8RX5W9q/wn8nfke+Ih39Hjvof0VtEZzH4AjSl+drgT h7YwCdMcgZh6qUiu3IKzTgSjhHowtdcCOUlOcrLXOXOmWgT08NdKMGd4MKxwbf4H8g/if+b8jv4Sv4 gBvtz+SFKEjQO0fSTpd66pHTGLnMtMW5yGbCK3fkFTmzdE4EB1znrnlB6p38sCQXZ+voIBwpy9GRxJ xN0XviW/z3xB/JP9B/S17WvQzDK6kwaDfG+aNSicKoxIZt7EFNIpZbPlcBgU5T9IDGMK5wHbTA88UU gkBhafTiDAhDNxBkQwTd2C9kXVY5hs2UzUfiPVyXZj7d5ekiq64US+MMGUIVHBTaZFmd8QCd2shHGL SNQ1eoJ+IXRXVBjWJLDECxis5ltiGBOdt7Lt/R/0bu9HcI9DeMvxDfoR/QBiDGU8bAWOQ8gD5jGOTk fbqgTvsIQnSigdCDrnhfGaRfMvSVMHxm6wRzrGEgjbziBwrxHf7vZMW+xy4k6AfkT/ifkO+RK4DsWK KBOQF9alwH0w2usSJuM/AzgtEBsq/UlU9j/Elees6xnSRgCIcClCAPynSdgzyIK0ejJbfO1Tke4P9Q dkTRG/IG/Tv8QN5gwgGwcY6jQBwPWtKhyMpKzskw1dq5JmdeKHJlp6ZY+tLIi3logSQ6hTMZADm4OS 3x5Bi0d/gN+Su+ITOneCU/wgMSSMFO+3Sq9JZLQJrXLoyo1A3kR9k8lVFOVehYsYYJJPO59pj5NBeG UGTts9IBQjkUtqXT9bkC3mMf8A3f12WStozhen5fP3WILVBowi1pZ/DTd1TIgfhabRMbppMyT3OS1J eI7fk5M7E7lIGBV0bFy0LMTG1zThilb/SK26JYlmihFGKmGAtl4z7ZoScfg8fOmLGaJBWdPzFWCIs4 bc/8sZqfffyMBIAlNdlgT0pAEODKrS5iIglJkTMxWMk7QlaEqgRR6IV4knWEAtvAGt4JaIF09Iy8TE VGWBJCDhgnPnk51fvpfqnBnXOXlFyh2i5E0gJvWMcc9xV3nlx9mQzTYQJV+rRMN6pwOzFddBjYSZmR U08SUs9iN+L0bTNW6PvFNR7UwZ1xUXZdMWgRbrqUX2/kIHLFlWSgA3VKkp061QXoxjC8IgV1tNMb2v A5EOQUZhNLZlxDT0A7dV73ldSTfH56TLHUnOrsg61gA52CdmKznH0Ba07h3RSd/2BQnN0xx4VbLKxT IYPbQMfSzFeaVnFZ4dUt1wKagCQnO56EN55vjyc3f6Z3tkSdTI7lItCSmPg4Vp2i4pXc8R3AnLvOJa jCTRmGKybsUJ4WhKBQlZzqa0BgU5GC5gtq/0RL/xlt+UJPP51+vvBwEw6jCYcQidpSj6LSLsgdeY8X 6JTGNriDYkTl2MmdYuxBVdIYFXFqcAk8uTk9CCiCCUxjbeC+yixfuoLPTo+AlkufviqPM6auuKywqE AW/EJ7Tb6Ggj5ij5BogFIqdUMqNbk7N/kUXSdmyk9cbRXqKW/PuHZOnVdW1Gl29Bd6+ul6zmZ4mIq6 cCsclTGTPImxNtai04W8Q7ZFAUXpIDNeoGigB3mQfclOs4OnejYgZsxqFkiW4hFgitmipxLIeHGN56 mfqOCCGL2QdRkDM/e8BlCstxVC0EkoK164lZWGGEEc5BW/kk4XhoBzQClQcUULZaKxSsz0qS9sOEm4 grbnE5Dr0J+qpgUzVLnMCOdAHXU4veqEaGSsxDOFsUOugk0lPDr05ee6LCRdDd0wWzGpyvKVlyN1QJ AG0xjXXzr0uV1gRwpWKIWqM7iJggUZTMLeHB/4QAMHM+SOUDwIPaMMhaj4ybgQXKhKVbZ56A3JlfRI 0EpciXaSsTMV99L0YL7nUtEdqUg5M2NQC3WezDmCDzCEcOKRUGTQHVFix6cqkKhSKz1A1jJSpcKmXK AkpkgllVYWT8ty3tH5etSaZS/09AQuihbYF6aLyWVy6QfFkAmI57Ru+Du4Ang/E8GKDapT54tlZ65/ joEqXGBzNNdxW6FvtI1UTE7tJVZPf3q6fK7SUxALpRupdKUK3ReEd4FKr7QZqej4I3lduZjZQ7OiEp SgzBdVOh5LwitGNSq405KbcwhDl21g57s25JKA9eU1voZ04gP6gral4AUXWpIdL1CInZswbkRDZgqm rLTvnFMTFcIyymLuv/kKh5DKEEbSYvkeMSi+kIw1oq/PpeZ8rvT+bORF4jO4mhRbDDyN7gyjVXwjdn xW+nwPJWcK1tG+JHR0fXH+1cuPGH8orss57x2/QicD6QufxcGY+YPJ9H5xI2YSjnU0sMJWqRNnFlpy 3fELuS8+lhty9t3M8BYojkwNLk9rQYi68rVDOFhjxCEdbec7Czdk/pLzNZHE88XQ96c9PV8QLM4G20 Gty49zW5oLkzVe8E7Y2lsECpdc9Wa+TXEmGzq4Er5iVpO9rrDs7NqONMTPCzufhi6WyHM9neecloIW SlJBO/LIJH051SMnO6Kn3jBfopj/NSGoiRzkgUz3ZCPBlBAiiEafK93QwILylJBnURVXuiwH8b9W6W k0Faou35sZmK6UjTJjZ9e18XVm/yuyn4T0YAzMyJlrMTiX1PT7VrfMOFQup8YcnWpWrID62c/LXvrs 0P8fB+jge9r6bpIAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjQtMTItMjRUMTI6NTQ6MDIrMDA6MDBTk1 0pAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDI0LTEyLTI0VDEyOjU0OjAyKzAwOjAwIs7llQAAAABJRU5E rkJggg== "
|
69
|
+
id="image140"
|
70
|
+
x="86.665413"
|
71
|
+
y="92.645836" /><image
|
72
|
+
width="15.875"
|
73
|
+
height="22.489582"
|
74
|
+
preserveAspectRatio="none"
|
75
|
+
xlink:href="data:image/PNG;base64, iVBORw0KGgoAAAANSUhEUgAAADwAAABVCAMAAAAi2XfSAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAA B6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACN1BMVEUAAAABAAAEAAAIAAAOAAAW AAAdAAAkAAArAAAwAAAuAAApAAAhAAAgAAAnAAAfAAAXAAAQAAANAAAUAAAYAAAaAAAmAAAqAAAiAA AVAAAlAAAvAAARAAACAAADAAAHAAASAAAjAAAbAAAPAAATAAAxAAAyAAAeAAAKAAAFAAAoAAAsAAA5 AABDAABEAAA/AAA2AAAZAAALAAA3AAAcAAAzAABNAABQAABOAAAJAAAtAABMAABSAABGAAA6AAAGAA A0AAA+AABFAAA9AAAMAAA1AAA4AAA7AAA8AABAAABCAABPAABBAABKAABIAABmAAB9AAB+AABwAABb AABYAABpAAB1AAB3AAB2AAByAABqAABgAABLAABJAABlAACQAACuAACzAACiAACGAAB0AACFAACaAA CpAACvAACrAACkAACVAAB/AABnAABRAAC0AADbAADhAADSAACyAACXAACTAAC5AADKAADRAADTAADJ AAC4AACZAABTAACKAADAAADtAAD9AADvAADQAACxAACjAACtAAC/AADOAADgAADdAADGAACgAABaAA C6AADnAAD/AAD5AAC3AAClAACoAADPAADZAADcAADUAAC7AADpAADlAADMAACbAACsAAC9AADLAACc AABVAAB4AAChAACLAAB6AACNAACmAACWAABtAABZAACBAAB7AABWAABUAABHAABfAABoAABeAACDAA CCAABXAABvAACeAACfAACRAABhAAD///81K/IXAAAAAWJLR0S8StLi7wAAAAd0SU1FB+gMGAw1NgIP D3UAAA3LSURBVFjDbViHe+PmeT+R2MAHgMQekrBIUAAlESRwd8T5KOpEUuN0lziOR5u7NE3itnFq1x 1uWjvubrrS60r3dtKZjqtbd/5z/UFXH6k8pR6Jkp7n5fd+7/gN3LiB11arTdEMy/GCKBGJlVlF7XSp jqYbjGRalmwbjmO4gse5LGF0ze+2W9s3/u+1tb2z2w0MxeUslpBQxlsUxJ1EZ4jtcr2+IKeO7zCuG4 auyZJI9Qd7Wx/Hbm3vtbu+ykiuGRLJ5fp8ltKJhn+wcj7cPzgcmVES0IURpaFsskoUDFrr4NbuOE70 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 OjUzOjU0KzAwOjAw6yLFsgAAAABJRU5ErkJggg== "
|
76
|
+
id="image152"
|
77
|
+
x="73.332962"
|
78
|
+
y="92.645836" /><image
|
79
|
+
width="15.875"
|
80
|
+
height="22.489584"
|
81
|
+
preserveAspectRatio="none"
|
82
|
+
xlink:href="data:image/PNG;base64, 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 "
|
83
|
+
id="image164"
|
84
|
+
x="60"
|
85
|
+
y="92.645836" /></g><text
|
86
|
+
xml:space="preserve"
|
87
|
+
style="font-weight:bold;font-size:17.0365px;line-height:11.6365px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';text-align:center;letter-spacing:0px;text-anchor:middle;fill:none;fill-opacity:1;stroke:#fefcff;stroke-width:0.252758;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
88
|
+
x="89.708412"
|
89
|
+
y="99.697433"
|
90
|
+
id="text501"
|
91
|
+
transform="scale(0.90157797,1.1091664)"><tspan
|
92
|
+
sodipodi:role="line"
|
93
|
+
id="tspan499"
|
94
|
+
style="fill:none;fill-opacity:1;stroke:#fefcff;stroke-width:0.252758;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
95
|
+
x="89.708412"
|
96
|
+
y="99.697433">AMS</tspan></text></g><text
|
97
|
+
xml:space="preserve"
|
98
|
+
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.18572px;line-height:1.60776px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Italic';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-anchor:middle;fill:#000000;stroke:none;stroke-width:0.290723;stroke-linecap:round;paint-order:fill markers stroke"
|
99
|
+
x="114.30348"
|
100
|
+
y="114.54252"
|
101
|
+
id="text738"
|
102
|
+
transform="scale(1.0494764,0.95285612)"><tspan
|
103
|
+
sodipodi:role="line"
|
104
|
+
id="tspan736"
|
105
|
+
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.18572px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Italic';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke:none;stroke-width:0.290723"
|
106
|
+
x="114.30348"
|
107
|
+
y="114.54252">BP</tspan></text></g></g></svg>
|
@@ -0,0 +1,333 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
|
3
|
+
import tomli
|
4
|
+
import tomlkit
|
5
|
+
from PyQt6.QtWidgets import (
|
6
|
+
QComboBox,
|
7
|
+
QDialog,
|
8
|
+
QFileDialog,
|
9
|
+
QHBoxLayout,
|
10
|
+
QLabel,
|
11
|
+
QMessageBox,
|
12
|
+
QPushButton,
|
13
|
+
QStackedWidget,
|
14
|
+
QTextEdit,
|
15
|
+
QVBoxLayout,
|
16
|
+
QWidget,
|
17
|
+
)
|
18
|
+
|
19
|
+
from .help_window import HelpWindow
|
20
|
+
from .widgets.camera_config_widget import CameraConfigWidget
|
21
|
+
from .widgets.cell_config_widget import CellConfigWidget
|
22
|
+
from .widgets.channel_config_widget import ChannelConfigWidget
|
23
|
+
from .widgets.condensate_config_widget import CondensateConfigWidget
|
24
|
+
from .widgets.experiment_config_widget import ExperimentConfigWidget
|
25
|
+
from .widgets.flurophore_config_widget import FluorophoreConfigWidget
|
26
|
+
from .widgets.general_config_widget import GeneralConfigWidget
|
27
|
+
from .widgets.global_config_widget import GlobalConfigWidget
|
28
|
+
from .widgets.laser_config_widget import LaserConfigWidget
|
29
|
+
from .widgets.molecule_config_widget import MoleculeConfigWidget
|
30
|
+
from .widgets.output_config_widget import OutputConfigWidget
|
31
|
+
from .widgets.psf_config_widget import PSFConfigWidget
|
32
|
+
|
33
|
+
|
34
|
+
class ConfigEditor(QWidget):
|
35
|
+
def __init__(self):
|
36
|
+
super().__init__()
|
37
|
+
self.setWindowTitle("Simulation Configuration Editor")
|
38
|
+
|
39
|
+
# Create the main layout for the window
|
40
|
+
layout = QVBoxLayout()
|
41
|
+
|
42
|
+
# Create a horizontal layout for the dropdown and the tab index label
|
43
|
+
dropdown_layout = QHBoxLayout()
|
44
|
+
|
45
|
+
# Add a QLabel for the instruction/title about the dropdown
|
46
|
+
dropdown_title = QLabel(
|
47
|
+
"Use the dropdown below to set the parameters for each tab:"
|
48
|
+
)
|
49
|
+
dropdown_layout.addWidget(dropdown_title)
|
50
|
+
|
51
|
+
# Create a QComboBox (dropdown menu) for selecting tabs
|
52
|
+
self.dropdown = QComboBox()
|
53
|
+
self.dropdown.addItems(
|
54
|
+
[
|
55
|
+
"General",
|
56
|
+
"Global Parameters",
|
57
|
+
"Cell Parameters",
|
58
|
+
"Molecule Parameters",
|
59
|
+
"Condensate Parameters",
|
60
|
+
"Define fluorophores",
|
61
|
+
"Camera Parameters",
|
62
|
+
"PSF Parameters",
|
63
|
+
"Laser Parameters",
|
64
|
+
"Channels Parameters",
|
65
|
+
"Saving Instructions",
|
66
|
+
"Experiment Builder",
|
67
|
+
]
|
68
|
+
)
|
69
|
+
self.dropdown.currentIndexChanged.connect(
|
70
|
+
self.on_dropdown_change
|
71
|
+
) # Connect to the change event
|
72
|
+
|
73
|
+
# Create a QLabel for displaying the current tab index
|
74
|
+
self.tab_index_label = QLabel("1/10")
|
75
|
+
|
76
|
+
# Add the dropdown and label to the layout
|
77
|
+
dropdown_layout.addWidget(self.dropdown)
|
78
|
+
dropdown_layout.addWidget(self.tab_index_label)
|
79
|
+
|
80
|
+
# Add the dropdown layout to the main layout
|
81
|
+
layout.addLayout(dropdown_layout)
|
82
|
+
|
83
|
+
# Create a QStackedWidget to hold the content for each "tab"
|
84
|
+
self.stacked_widget = QStackedWidget()
|
85
|
+
|
86
|
+
# Initialize the widgets for each "tab"
|
87
|
+
self.general_tab = GeneralConfigWidget()
|
88
|
+
self.global_tab = GlobalConfigWidget()
|
89
|
+
self.cell_tab = CellConfigWidget()
|
90
|
+
self.molecule_tab = MoleculeConfigWidget()
|
91
|
+
self.condensate_tab = CondensateConfigWidget()
|
92
|
+
self.output_tab = OutputConfigWidget()
|
93
|
+
self.fluorophore_tab = FluorophoreConfigWidget()
|
94
|
+
self.psf_tab = PSFConfigWidget()
|
95
|
+
self.laser_tab = LaserConfigWidget()
|
96
|
+
self.channel_tab = ChannelConfigWidget()
|
97
|
+
self.detector_tab = CameraConfigWidget()
|
98
|
+
self.experiment_tab = ExperimentConfigWidget()
|
99
|
+
|
100
|
+
# connections
|
101
|
+
# PSF -> confocal -> lasers
|
102
|
+
self.psf_tab.confocal_mode_changed.connect(self.laser_tab.set_confocal_mode)
|
103
|
+
# === Molecule -> Fluorophore & Condensate ===
|
104
|
+
self.molecule_tab.molecule_count_changed.connect(
|
105
|
+
self.fluorophore_tab.set_mfluorophore_count
|
106
|
+
)
|
107
|
+
self.molecule_tab.molecule_count_changed.connect(
|
108
|
+
self.condensate_tab.set_molecule_count
|
109
|
+
)
|
110
|
+
# === Fluorophore -> Molecule & Condensate ===
|
111
|
+
self.fluorophore_tab.mfluorophore_count_changed.connect(
|
112
|
+
self.molecule_tab.set_molecule_count
|
113
|
+
)
|
114
|
+
self.fluorophore_tab.mfluorophore_count_changed.connect(
|
115
|
+
self.condensate_tab.set_molecule_count
|
116
|
+
)
|
117
|
+
# === Condensate -> Molecule & Fluorophore ===
|
118
|
+
self.condensate_tab.molecule_count_changed.connect(
|
119
|
+
self.molecule_tab.set_molecule_count
|
120
|
+
)
|
121
|
+
self.condensate_tab.molecule_count_changed.connect(
|
122
|
+
self.fluorophore_tab.set_mfluorophore_count
|
123
|
+
)
|
124
|
+
# === Laser -> Experiment
|
125
|
+
self.laser_tab.laser_names_updated.connect(
|
126
|
+
self.experiment_tab.set_active_lasers
|
127
|
+
)
|
128
|
+
|
129
|
+
# Add each tab's widget to the stacked widget
|
130
|
+
self.stacked_widget.addWidget(self.general_tab)
|
131
|
+
self.stacked_widget.addWidget(self.global_tab)
|
132
|
+
self.stacked_widget.addWidget(self.cell_tab)
|
133
|
+
self.stacked_widget.addWidget(self.molecule_tab)
|
134
|
+
self.stacked_widget.addWidget(self.condensate_tab)
|
135
|
+
self.stacked_widget.addWidget(self.fluorophore_tab)
|
136
|
+
self.stacked_widget.addWidget(self.detector_tab)
|
137
|
+
self.stacked_widget.addWidget(self.psf_tab)
|
138
|
+
self.stacked_widget.addWidget(self.laser_tab)
|
139
|
+
self.stacked_widget.addWidget(self.channel_tab)
|
140
|
+
self.stacked_widget.addWidget(self.output_tab)
|
141
|
+
self.stacked_widget.addWidget(self.experiment_tab)
|
142
|
+
|
143
|
+
# Set the stacked widget as the central widget
|
144
|
+
layout.addWidget(self.stacked_widget)
|
145
|
+
|
146
|
+
# Create and add the save and help buttons at the bottom
|
147
|
+
self.save_button = QPushButton("Ready to save configuration?")
|
148
|
+
self.save_button.clicked.connect(self.save_config)
|
149
|
+
layout.addWidget(self.save_button)
|
150
|
+
|
151
|
+
self.preview_button = QPushButton("Preview Configuration TOML")
|
152
|
+
self.preview_button.clicked.connect(self.preview_config)
|
153
|
+
layout.addWidget(self.preview_button)
|
154
|
+
|
155
|
+
self.help_button = QPushButton("Get Help on this section")
|
156
|
+
self.help_button.clicked.connect(self.show_help)
|
157
|
+
layout.addWidget(self.help_button)
|
158
|
+
|
159
|
+
# Set the layout for the main window
|
160
|
+
self.setLayout(layout)
|
161
|
+
|
162
|
+
# Set initial display
|
163
|
+
self.on_dropdown_change(0) # Show the first tab (index 0)
|
164
|
+
|
165
|
+
def set_data(self, config: dict):
|
166
|
+
if "Cell_Parameters" in config:
|
167
|
+
self.cell_tab.set_data(config["Cell_Parameters"])
|
168
|
+
|
169
|
+
if "Global_Parameters" in config:
|
170
|
+
self.global_tab.set_data(config["Global_Parameters"])
|
171
|
+
|
172
|
+
if "Molecule_Parameters" in config:
|
173
|
+
self.molecule_tab.set_data(config["Molecule_Parameters"])
|
174
|
+
|
175
|
+
if "fluorophores" in config:
|
176
|
+
self.fluorophore_tab.set_data(config["fluorophores"])
|
177
|
+
|
178
|
+
if "Condensate_Parameters" in config:
|
179
|
+
self.condensate_tab.set_data(config["Condensate_Parameters"])
|
180
|
+
|
181
|
+
if "Output_Parameters" in config:
|
182
|
+
self.output_tab.set_data(config["Output_Parameters"])
|
183
|
+
|
184
|
+
if "lasers" in config:
|
185
|
+
self.laser_tab.set_data(config["lasers"])
|
186
|
+
|
187
|
+
if "experiment" in config:
|
188
|
+
self.experiment_tab.set_data(config["experiment"])
|
189
|
+
|
190
|
+
if "channels" in config:
|
191
|
+
self.channel_tab.set_data(config["channels"])
|
192
|
+
|
193
|
+
if "camera" in config:
|
194
|
+
self.detector_tab.set_data(config["camera"])
|
195
|
+
|
196
|
+
if "psf" in config:
|
197
|
+
self.psf_tab.set_data(config["psf"])
|
198
|
+
|
199
|
+
def preview_config(self):
|
200
|
+
"""Preview the full TOML config in a dialog before saving."""
|
201
|
+
try:
|
202
|
+
# Step 1: Validate tabs
|
203
|
+
if not self.validate_all_tabs():
|
204
|
+
QMessageBox.warning(
|
205
|
+
self, "Validation Error", "Fix errors before preview."
|
206
|
+
)
|
207
|
+
return
|
208
|
+
|
209
|
+
# Step 2: Collect config data
|
210
|
+
config = self.collect_all_config()
|
211
|
+
|
212
|
+
# Step 3: Convert to TOML string
|
213
|
+
toml_doc = tomlkit.document()
|
214
|
+
for key, value in config.items():
|
215
|
+
toml_doc[key] = value
|
216
|
+
toml_str = tomlkit.dumps(toml_doc)
|
217
|
+
|
218
|
+
# Step 4: Show preview dialog
|
219
|
+
preview_dialog = QDialog(self)
|
220
|
+
preview_dialog.setWindowTitle("Preview TOML Configuration")
|
221
|
+
layout = QVBoxLayout(preview_dialog)
|
222
|
+
text_edit = QTextEdit()
|
223
|
+
text_edit.setPlainText(toml_str)
|
224
|
+
text_edit.setReadOnly(True)
|
225
|
+
layout.addWidget(text_edit)
|
226
|
+
preview_dialog.resize(800, 600)
|
227
|
+
preview_dialog.exec()
|
228
|
+
|
229
|
+
except Exception as e:
|
230
|
+
QMessageBox.critical(self, "Error", f"Preview failed: {str(e)}")
|
231
|
+
|
232
|
+
def build_toml_doc(self, config: dict) -> tomlkit.TOMLDocument:
|
233
|
+
"""Build a TOML document from the configuration dictionary."""
|
234
|
+
doc = tomlkit.document()
|
235
|
+
for key, val in config.items():
|
236
|
+
doc[key] = val
|
237
|
+
return doc
|
238
|
+
|
239
|
+
def on_dropdown_change(self, index):
|
240
|
+
"""Change the displayed widget based on the dropdown selection."""
|
241
|
+
self.stacked_widget.setCurrentIndex(index)
|
242
|
+
# Update the tab index label (1-based index)
|
243
|
+
total_tabs = (
|
244
|
+
self.dropdown.count()
|
245
|
+
) # Corrected way to get the total number of items
|
246
|
+
self.tab_index_label.setText(f"{index + 1}/{total_tabs}")
|
247
|
+
|
248
|
+
def validate_all_tabs(self) -> bool:
|
249
|
+
return all(
|
250
|
+
[
|
251
|
+
self.global_tab.validate(),
|
252
|
+
self.cell_tab.validate(),
|
253
|
+
self.molecule_tab.validate(),
|
254
|
+
self.condensate_tab.validate(),
|
255
|
+
self.fluorophore_tab.validate(),
|
256
|
+
self.psf_tab.validate(),
|
257
|
+
self.laser_tab.validate(),
|
258
|
+
self.channel_tab.validate(),
|
259
|
+
self.detector_tab.validate(),
|
260
|
+
self.experiment_tab.validate(),
|
261
|
+
]
|
262
|
+
)
|
263
|
+
|
264
|
+
def collect_all_config(self) -> dict:
|
265
|
+
return {
|
266
|
+
**self.general_tab.get_data(),
|
267
|
+
"Global_Parameters": self.global_tab.get_data(),
|
268
|
+
"Cell_Parameters": self.cell_tab.get_data(),
|
269
|
+
"Molecule_Parameters": self.molecule_tab.get_data(),
|
270
|
+
"Condensate_Parameters": self.condensate_tab.get_data(),
|
271
|
+
"Output_Parameters": self.output_tab.get_data(),
|
272
|
+
"fluorophores": self.fluorophore_tab.get_data(),
|
273
|
+
"psf": self.psf_tab.get_data(),
|
274
|
+
"lasers": self.laser_tab.get_data(),
|
275
|
+
"channels": self.channel_tab.get_data(),
|
276
|
+
"camera": self.detector_tab.get_data(),
|
277
|
+
"experiment": self.experiment_tab.get_data(),
|
278
|
+
}
|
279
|
+
|
280
|
+
def show_help(self):
|
281
|
+
current_widget = self.stacked_widget.currentWidget()
|
282
|
+
if hasattr(current_widget, "get_help_path"):
|
283
|
+
help_path = current_widget.get_help_path()
|
284
|
+
if help_path.exists():
|
285
|
+
help_window = HelpWindow(help_path, self)
|
286
|
+
help_window.exec()
|
287
|
+
return
|
288
|
+
|
289
|
+
QMessageBox.warning(self, "Help", "Help content not found for this section.")
|
290
|
+
|
291
|
+
def save_config(self):
|
292
|
+
"""Collect data from all tabs and save the configuration."""
|
293
|
+
try:
|
294
|
+
# Validate all required tabs
|
295
|
+
|
296
|
+
if self.validate_all_tabs:
|
297
|
+
config = self.collect_all_config()
|
298
|
+
toml_doc = self.build_toml_doc(config)
|
299
|
+
|
300
|
+
# Ask user where to save
|
301
|
+
file_path, _ = QFileDialog.getSaveFileName(
|
302
|
+
self, "Save Configuration", "", "TOML Files (*.toml);;All Files (*)"
|
303
|
+
)
|
304
|
+
|
305
|
+
if file_path:
|
306
|
+
if not file_path.endswith(".toml"):
|
307
|
+
file_path += ".toml"
|
308
|
+
|
309
|
+
with open(file_path, "w") as f:
|
310
|
+
tomlkit.dump(toml_doc, f)
|
311
|
+
|
312
|
+
QMessageBox.information(
|
313
|
+
self, "Success", "Configuration has been saved successfully."
|
314
|
+
)
|
315
|
+
else:
|
316
|
+
QMessageBox.warning(
|
317
|
+
self, "Save Cancelled", "No file selected. Save was aborted."
|
318
|
+
)
|
319
|
+
else:
|
320
|
+
QMessageBox.warning(
|
321
|
+
self,
|
322
|
+
"Validation Error",
|
323
|
+
"Please correct the errors in all tabs before saving.",
|
324
|
+
)
|
325
|
+
except Exception as e:
|
326
|
+
QMessageBox.critical(
|
327
|
+
self, "Error", f"An error occurred while saving: {str(e)}"
|
328
|
+
)
|
329
|
+
|
330
|
+
def load_config_from_toml(self, path: Path):
|
331
|
+
with open(path, "rb") as f:
|
332
|
+
config = tomli.load(f)
|
333
|
+
self.set_data(config)
|
File without changes
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Cell Configuration Help
|
2
|
+
|
3
|
+
This section defines the geometric model of the cell used in the simulation. Each cell type has specific parameters that describe its spatial extent and shape.
|
4
|
+
|
5
|
+
## Fields
|
6
|
+
|
7
|
+
### `cell_type`
|
8
|
+
- **Type**: `String`
|
9
|
+
- **Options**: `"RectangularCell"`, `"SphericalCell"`, `"OvoidCell"`, `"RodCell"`, `"BuddingCell"`
|
10
|
+
- **Description**: Specifies the type of cell geometry to simulate.
|
11
|
+
|
12
|
+
---
|
13
|
+
|
14
|
+
## Parameters by Cell Type
|
15
|
+
|
16
|
+
### **SphericalCell**
|
17
|
+
- `center`: `[x, y, z]` — 3D coordinates of the center of the sphere.
|
18
|
+
- `radius`: `float` — Radius of the sphere in micrometers.
|
19
|
+
|
20
|
+
### **RodCell**
|
21
|
+
- `center`: `[x, y, z]` — 3D center of the rod.
|
22
|
+
- `direction`: `[dx, dy, dz]` — Direction vector of the rod’s long axis (will be normalized).
|
23
|
+
- `height`: `float` — Length of the rod in micrometers.
|
24
|
+
- `radius`: `float` — Radius of the rod in micrometers.
|
25
|
+
|
26
|
+
### **RectangularCell**
|
27
|
+
- `bounds`: `[xmin, xmax, ymin, ymax, zmin, zmax]` — Bounds of the cell volume in micrometers. Useful for simulating flat, box-like geometries.
|
28
|
+
|
29
|
+
### **OvoidCell**
|
30
|
+
- `center`: `[x, y, z]` — 3D center of the ovoid.
|
31
|
+
- `xradius`: `float` — Radius along the x-axis.
|
32
|
+
- `yradius`: `float` — Radius along the y-axis.
|
33
|
+
- `zradius`: `float` — Radius along the z-axis.
|
34
|
+
|
35
|
+
### **BuddingCell**
|
36
|
+
- *Not currently implemented in the UI.*
|
37
|
+
|
38
|
+
---
|
39
|
+
|
40
|
+
## Notes
|
41
|
+
- The selected `cell_type` determines which fields are editable.
|
42
|
+
- All units are in micrometers (`μm`).
|
43
|
+
- The coordinate system is 3D and consistent with the z-position defined in the experiment setup.
|
44
|
+
- These values define the spatial domain for molecule simulation, diffusion, and tracking.
|
45
|
+
|