celldetective 1.3.9.post5__tar.gz → 1.4.1__tar.gz
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.
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/PKG-INFO +24 -16
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/README.md +17 -14
- celldetective-1.4.1/celldetective/__init__.py +1 -0
- celldetective-1.4.1/celldetective/_version.py +1 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/events.py +2 -4
- celldetective-1.4.1/celldetective/exceptions.py +11 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/extra_properties.py +132 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/filters.py +7 -1
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/InitWindow.py +37 -46
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/__init__.py +3 -9
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/about.py +19 -15
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/analyze_block.py +34 -19
- celldetective-1.4.1/celldetective/gui/base_annotator.py +786 -0
- celldetective-1.4.1/celldetective/gui/base_components.py +23 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/classifier_widget.py +86 -94
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/configure_new_exp.py +163 -46
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/control_panel.py +76 -146
- celldetective-1.3.9.post5/celldetective/gui/signal_annotator.py → celldetective-1.4.1/celldetective/gui/event_annotator.py +533 -1438
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/generic_signal_plot.py +11 -13
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/gui_utils.py +54 -23
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/help/neighborhood.json +2 -2
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/json_readers.py +5 -4
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/layouts.py +265 -31
- celldetective-1.3.9.post5/celldetective/gui/signal_annotator2.py → celldetective-1.4.1/celldetective/gui/pair_event_annotator.py +433 -635
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/plot_measurements.py +21 -17
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/plot_signals_ui.py +125 -72
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/process_block.py +283 -188
- celldetective-1.4.1/celldetective/gui/processes/compute_neighborhood.py +594 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/processes/downloader.py +37 -34
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/processes/measure_cells.py +19 -8
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/processes/segment_cells.py +47 -11
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/processes/track_cells.py +18 -13
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/seg_model_loader.py +21 -62
- celldetective-1.4.1/celldetective/gui/settings/__init__.py +7 -0
- celldetective-1.4.1/celldetective/gui/settings/_settings_base.py +70 -0
- celldetective-1.3.9.post5/celldetective/gui/retrain_signal_model_options.py → celldetective-1.4.1/celldetective/gui/settings/_settings_event_model_training.py +54 -109
- celldetective-1.3.9.post5/celldetective/gui/measurement_options.py → celldetective-1.4.1/celldetective/gui/settings/_settings_measurements.py +54 -92
- celldetective-1.3.9.post5/celldetective/gui/neighborhood_options.py → celldetective-1.4.1/celldetective/gui/settings/_settings_neighborhood.py +10 -13
- celldetective-1.4.1/celldetective/gui/settings/_settings_segmentation.py +49 -0
- celldetective-1.3.9.post5/celldetective/gui/retrain_segmentation_model_options.py → celldetective-1.4.1/celldetective/gui/settings/_settings_segmentation_model_training.py +38 -92
- celldetective-1.3.9.post5/celldetective/gui/signal_annotator_options.py → celldetective-1.4.1/celldetective/gui/settings/_settings_signal_annotator.py +78 -103
- celldetective-1.3.9.post5/celldetective/gui/btrack_options.py → celldetective-1.4.1/celldetective/gui/settings/_settings_tracking.py +85 -116
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/styles.py +2 -1
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/survival_ui.py +49 -95
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/tableUI.py +53 -25
- celldetective-1.4.1/celldetective/gui/table_ops/merge_groups.py +118 -0
- celldetective-1.4.1/celldetective/gui/thresholds_gui.py +714 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/viewers.py +107 -42
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/workers.py +8 -4
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/io.py +137 -57
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/links/zenodo.json +145 -144
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/measure.py +94 -53
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/neighborhood.py +342 -268
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/preprocessing.py +56 -35
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/regionprops/_regionprops.py +16 -5
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/relative_measurements.py +50 -29
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/scripts/analyze_signals.py +4 -1
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/scripts/measure_cells.py +5 -5
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/scripts/measure_relative.py +20 -12
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/scripts/segment_cells.py +4 -10
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/scripts/segment_cells_thresholds.py +3 -3
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/scripts/track_cells.py +10 -8
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/scripts/train_segmentation_model.py +18 -6
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/signals.py +29 -14
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/tracking.py +14 -3
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/utils.py +91 -62
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective.egg-info/PKG-INFO +24 -16
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective.egg-info/SOURCES.txt +21 -27
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective.egg-info/requires.txt +4 -0
- celldetective-1.4.1/tests/__init__.py +0 -0
- celldetective-1.4.1/tests/gui/__init__.py +0 -0
- celldetective-1.4.1/tests/gui/test_new_project.py +228 -0
- celldetective-1.4.1/tests/gui/test_project.py +99 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/tests/test_preprocessing.py +2 -2
- celldetective-1.3.9.post5/celldetective/__init__.py +0 -4
- celldetective-1.3.9.post5/celldetective/_version.py +0 -1
- celldetective-1.3.9.post5/celldetective/gui/thresholds_gui.py +0 -1318
- celldetective-1.3.9.post5/celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -79
- celldetective-1.3.9.post5/celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
- celldetective-1.3.9.post5/celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -37
- celldetective-1.3.9.post5/celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -39
- celldetective-1.3.9.post5/celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
- celldetective-1.3.9.post5/celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
- celldetective-1.3.9.post5/celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
- celldetective-1.3.9.post5/celldetective/models/signal_detection/NucCond/config_input.json +0 -1
- celldetective-1.3.9.post5/celldetective/models/signal_detection/NucCond/log_classifier.csv +0 -126
- celldetective-1.3.9.post5/celldetective/models/signal_detection/NucCond/log_regressor.csv +0 -282
- celldetective-1.3.9.post5/celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
- celldetective-1.3.9.post5/celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
- celldetective-1.3.9.post5/celldetective/models/signal_detection/NucCond/scores.npy +0 -0
- celldetective-1.3.9.post5/celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
- celldetective-1.3.9.post5/celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
- celldetective-1.3.9.post5/celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
- celldetective-1.3.9.post5/celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
- celldetective-1.3.9.post5/tests/test_qt.py +0 -103
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/LICENSE +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/__main__.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/datasets/segmentation_annotations/blank +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/datasets/signal_annotations/blank +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/help/DL-segmentation-strategy.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/help/Threshold-vs-DL.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/help/cell-populations.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/help/exp-structure.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/help/feature-btrack.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/help/prefilter-for-segmentation.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/help/preprocessing.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/help/propagate-classification.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/help/track-postprocessing.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/help/tracking.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/processes/train_segmentation_model.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/gui/processes/train_signal_model.py +0 -0
- {celldetective-1.3.9.post5/tests → celldetective-1.4.1/celldetective/gui/table_ops}/__init__.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/icons/logo-large.png +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/icons/logo.png +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/icons/signals_icon.png +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/icons/splash-test.png +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/icons/splash.png +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/icons/splash0.png +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/icons/survival2.png +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/icons/vignette_signals2.png +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/icons/vignette_signals2.svg +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/models/pair_signal_detection/blank +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/models/segmentation_effectors/blank +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/models/segmentation_generic/blank +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/models/segmentation_targets/blank +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/models/signal_detection/blank +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/models/tracking_configs/biased_motion.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/models/tracking_configs/mcf7.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/models/tracking_configs/no_z_motion.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/models/tracking_configs/ricm.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/models/tracking_configs/ricm2.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/regionprops/__init__.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/regionprops/props.json +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/scripts/train_signal_model.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective/segmentation.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective.egg-info/dependency_links.txt +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective.egg-info/entry_points.txt +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective.egg-info/not-zip-safe +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/celldetective.egg-info/top_level.txt +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/setup.cfg +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/setup.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/tests/test_events.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/tests/test_filters.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/tests/test_io.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/tests/test_measure.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/tests/test_neighborhood.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/tests/test_segmentation.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/tests/test_signals.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/tests/test_tracking.py +0 -0
- {celldetective-1.3.9.post5 → celldetective-1.4.1}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: celldetective
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: description
|
|
5
5
|
Home-page: http://github.com/remyeltorro/celldetective
|
|
6
6
|
Author: Rémy Torro
|
|
@@ -44,12 +44,17 @@ Requires-Dist: h5py
|
|
|
44
44
|
Requires-Dist: cliffs_delta
|
|
45
45
|
Requires-Dist: requests
|
|
46
46
|
Requires-Dist: trackpy
|
|
47
|
+
Requires-Dist: prettyprint
|
|
48
|
+
Requires-Dist: pandas
|
|
49
|
+
Requires-Dist: matplotlib
|
|
50
|
+
Requires-Dist: prettytable
|
|
47
51
|
Dynamic: author
|
|
48
52
|
Dynamic: author-email
|
|
49
53
|
Dynamic: description
|
|
50
54
|
Dynamic: description-content-type
|
|
51
55
|
Dynamic: home-page
|
|
52
56
|
Dynamic: license
|
|
57
|
+
Dynamic: license-file
|
|
53
58
|
Dynamic: requires-dist
|
|
54
59
|
Dynamic: summary
|
|
55
60
|
|
|
@@ -183,26 +188,29 @@ For more information about how to get started, please check the [documentation](
|
|
|
183
188
|
# How to cite?
|
|
184
189
|
|
|
185
190
|
If you use this software in your research, please cite the
|
|
186
|
-
[Celldetective](https://
|
|
187
|
-
paper (currently preprint):
|
|
191
|
+
[Celldetective](https://elifesciences.org/reviewed-preprints/105302)
|
|
192
|
+
paper (currently a reviewed preprint at eLife):
|
|
188
193
|
|
|
189
194
|
``` raw
|
|
190
|
-
@article
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
195
|
+
@article{torroCelldetectiveAIenhancedImage2025,
|
|
196
|
+
title = {Celldetective: An {{AI-enhanced}} Image Analysis Tool for Unraveling Dynamic Cell Interactions},
|
|
197
|
+
shorttitle = {Celldetective},
|
|
198
|
+
author = {Torro, Rémy and Díaz-Bello, Beatriz and Arawi, Dalia El and Dervanova, Ksenija and Ammer, Lorna and Dupuy, Florian and Chames, Patrick and Sengupta, Kheya and Limozin, Laurent},
|
|
199
|
+
date = {2025-03-10},
|
|
200
|
+
journaltitle = {eLife},
|
|
201
|
+
volume = {14},
|
|
202
|
+
publisher = {eLife Sciences Publications Limited},
|
|
203
|
+
doi = {10.7554/eLife.105302.1},
|
|
204
|
+
url = {https://elifesciences.org/reviewed-preprints/105302},
|
|
205
|
+
urldate = {2025-03-20},
|
|
206
|
+
abstract = {A current challenge in bioimaging for immunology and immunotherapy research lies in analyzing multimodal and multidimensional data that capture dynamic interactions between diverse cell populations. Here, we introduce Celldetective, an open-source Python-based software designed for high-performance, end-to-end analysis of image-based in vitro immune and immunotherapy assays. Purpose-built for multicondition, 2D multichannel time-lapse microscopy of mixed cell populations, Celldetective is optimized for the needs of immunology assays. The software seamlessly integrates AI-based segmentation, Bayesian tracking, and automated single-cell event detection, all within an intuitive graphical interface that supports interactive visualization, annotation, and training capabilities. We demonstrate its utility with original data on immune effector cell interactions with an activating surface, mediated by bispecific antibodies, and further showcase its potential for analyzing extensive sets of pairwise interactions in antibody-dependent cell cytotoxicity events.},
|
|
207
|
+
langid = {english},
|
|
208
|
+
file = {/home/torro/Zotero/storage/VFYBBMQF/Torro et al. - 2025 - Celldetective an AI-enhanced image analysis tool .pdf;/home/torro/Zotero/storage/UGMCKKST/105302.html}
|
|
201
209
|
}
|
|
202
210
|
```
|
|
203
211
|
|
|
204
212
|
Make sure you to cite the papers of any segmentation model (StarDist,
|
|
205
|
-
Cellpose) or tracker (bTrack) you used through Celldetective.
|
|
213
|
+
Cellpose) or tracker (bTrack, TrackPy) you used through Celldetective.
|
|
206
214
|
|
|
207
215
|
# Bibliography
|
|
208
216
|
|
|
@@ -128,26 +128,29 @@ For more information about how to get started, please check the [documentation](
|
|
|
128
128
|
# How to cite?
|
|
129
129
|
|
|
130
130
|
If you use this software in your research, please cite the
|
|
131
|
-
[Celldetective](https://
|
|
132
|
-
paper (currently preprint):
|
|
131
|
+
[Celldetective](https://elifesciences.org/reviewed-preprints/105302)
|
|
132
|
+
paper (currently a reviewed preprint at eLife):
|
|
133
133
|
|
|
134
134
|
``` raw
|
|
135
|
-
@article
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
135
|
+
@article{torroCelldetectiveAIenhancedImage2025,
|
|
136
|
+
title = {Celldetective: An {{AI-enhanced}} Image Analysis Tool for Unraveling Dynamic Cell Interactions},
|
|
137
|
+
shorttitle = {Celldetective},
|
|
138
|
+
author = {Torro, Rémy and Díaz-Bello, Beatriz and Arawi, Dalia El and Dervanova, Ksenija and Ammer, Lorna and Dupuy, Florian and Chames, Patrick and Sengupta, Kheya and Limozin, Laurent},
|
|
139
|
+
date = {2025-03-10},
|
|
140
|
+
journaltitle = {eLife},
|
|
141
|
+
volume = {14},
|
|
142
|
+
publisher = {eLife Sciences Publications Limited},
|
|
143
|
+
doi = {10.7554/eLife.105302.1},
|
|
144
|
+
url = {https://elifesciences.org/reviewed-preprints/105302},
|
|
145
|
+
urldate = {2025-03-20},
|
|
146
|
+
abstract = {A current challenge in bioimaging for immunology and immunotherapy research lies in analyzing multimodal and multidimensional data that capture dynamic interactions between diverse cell populations. Here, we introduce Celldetective, an open-source Python-based software designed for high-performance, end-to-end analysis of image-based in vitro immune and immunotherapy assays. Purpose-built for multicondition, 2D multichannel time-lapse microscopy of mixed cell populations, Celldetective is optimized for the needs of immunology assays. The software seamlessly integrates AI-based segmentation, Bayesian tracking, and automated single-cell event detection, all within an intuitive graphical interface that supports interactive visualization, annotation, and training capabilities. We demonstrate its utility with original data on immune effector cell interactions with an activating surface, mediated by bispecific antibodies, and further showcase its potential for analyzing extensive sets of pairwise interactions in antibody-dependent cell cytotoxicity events.},
|
|
147
|
+
langid = {english},
|
|
148
|
+
file = {/home/torro/Zotero/storage/VFYBBMQF/Torro et al. - 2025 - Celldetective an AI-enhanced image analysis tool .pdf;/home/torro/Zotero/storage/UGMCKKST/105302.html}
|
|
146
149
|
}
|
|
147
150
|
```
|
|
148
151
|
|
|
149
152
|
Make sure you to cite the papers of any segmentation model (StarDist,
|
|
150
|
-
Cellpose) or tracker (bTrack) you used through Celldetective.
|
|
153
|
+
Cellpose) or tracker (bTrack, TrackPy) you used through Celldetective.
|
|
151
154
|
|
|
152
155
|
# Bibliography
|
|
153
156
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from ._version import __version__
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.4.1"
|
|
@@ -52,7 +52,7 @@ def switch_to_events(classes, event_times, max_times, origin_times=None, left_ce
|
|
|
52
52
|
>>> event_times = [5, 10, 15]
|
|
53
53
|
>>> max_times = [20, 20, 20]
|
|
54
54
|
>>> origin_times = [0, 0, 5]
|
|
55
|
-
>>> events, survival_times =
|
|
55
|
+
>>> events, survival_times = switch_to_events(classes, event_times, max_times, origin_times, FrameToMin=0.5)
|
|
56
56
|
# This would process the events considering left censorship and convert survival times to minutes.
|
|
57
57
|
|
|
58
58
|
"""
|
|
@@ -189,6 +189,7 @@ def compute_survival(df, class_of_interest, t_event, t_reference=None, FrameToMi
|
|
|
189
189
|
assert class_of_interest in cols,"The requested class cannot be found in the dataframe..."
|
|
190
190
|
assert t_event in cols,"The event time cannot be found in the dataframe..."
|
|
191
191
|
left_censored = False
|
|
192
|
+
first_detections = None
|
|
192
193
|
|
|
193
194
|
if not pairs:
|
|
194
195
|
groupby_cols = ['position','TRACK_ID']
|
|
@@ -209,10 +210,7 @@ def compute_survival(df, class_of_interest, t_event, t_reference=None, FrameToMi
|
|
|
209
210
|
assert t_reference in cols,"The reference time cannot be found in the dataframe..."
|
|
210
211
|
first_detections = df.groupby(groupby_cols)[t_reference].max().values
|
|
211
212
|
|
|
212
|
-
|
|
213
|
-
print(f"{classes=} {event_times=} {max_times=} {first_detections=}")
|
|
214
213
|
events, survival_times = switch_to_events(classes, event_times, max_times, origin_times=first_detections, left_censored=left_censored, FrameToMin=FrameToMin, cut_observation_time=cut_observation_time)
|
|
215
|
-
print(f"{events=} {survival_times=}")
|
|
216
214
|
|
|
217
215
|
ks = KaplanMeierFitter()
|
|
218
216
|
if len(events)>0:
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class QueryError(Exception):
|
|
2
|
+
"""Base class for query-related errors."""
|
|
3
|
+
|
|
4
|
+
class EmptyQueryError(QueryError):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
class MissingColumnsError(QueryError):
|
|
8
|
+
def __init__(self, missing_cols):
|
|
9
|
+
msg = f"The following columns are missing from the DataFrame: {missing_cols}"
|
|
10
|
+
super().__init__(msg)
|
|
11
|
+
self.missing_cols = missing_cols
|
|
@@ -245,6 +245,138 @@ def fraction_of_area_dark_intensity(regionmask, intensity_image, target_channel=
|
|
|
245
245
|
return float(np.sum(subregion)) / float(np.sum(regionmask))
|
|
246
246
|
|
|
247
247
|
|
|
248
|
+
def area_dark_intensity_nintyfive(regionmask, intensity_image, target_channel='adhesion_channel', fill_holes=True): #, target_channel='adhesion_channel'
|
|
249
|
+
|
|
250
|
+
subregion = (intensity_image < 0.95)*regionmask # under one, under 0.8, under 0.6, whatever value!
|
|
251
|
+
if fill_holes:
|
|
252
|
+
subregion = skm.label(subregion, connectivity=2, background=0)
|
|
253
|
+
subregion = fill_label_holes(subregion)
|
|
254
|
+
subregion[subregion>0] = 1
|
|
255
|
+
|
|
256
|
+
return float(np.sum(subregion))
|
|
257
|
+
|
|
258
|
+
def area_dark_intensity_ninty(regionmask, intensity_image, target_channel='adhesion_channel', fill_holes=True): #, target_channel='adhesion_channel'
|
|
259
|
+
|
|
260
|
+
subregion = (intensity_image < 0.90)*regionmask # under one, under 0.8, under 0.6, whatever value!
|
|
261
|
+
if fill_holes:
|
|
262
|
+
subregion = skm.label(subregion, connectivity=2, background=0)
|
|
263
|
+
subregion = fill_label_holes(subregion)
|
|
264
|
+
subregion[subregion>0] = 1
|
|
265
|
+
|
|
266
|
+
return float(np.sum(subregion))
|
|
267
|
+
|
|
268
|
+
def mean_dark_intensity_nintyfive(regionmask, intensity_image, target_channel='adhesion_channel', fill_holes=True):
|
|
269
|
+
"""
|
|
270
|
+
Calculate the mean intensity in a dark subregion below 95, handling NaN values.
|
|
271
|
+
|
|
272
|
+
"""
|
|
273
|
+
subregion = (intensity_image < 0.95) * regionmask
|
|
274
|
+
|
|
275
|
+
if fill_holes:
|
|
276
|
+
subregion = skm.label(subregion, connectivity=2, background=0)
|
|
277
|
+
subregion = fill_label_holes(subregion)
|
|
278
|
+
subregion[subregion > 0] = 1
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
masked_intensity = intensity_image[subregion == 1]
|
|
282
|
+
|
|
283
|
+
return float(np.nanmean(masked_intensity))
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def mean_dark_intensity_nintyfive_fillhole_false(regionmask, intensity_image, target_channel='adhesion_channel'):
|
|
287
|
+
"""
|
|
288
|
+
Calculate the mean intensity in a dark subregion below 95, handling NaN values.
|
|
289
|
+
"""
|
|
290
|
+
subregion = (intensity_image < 0.95) * regionmask # Select dark regions within the mask
|
|
291
|
+
|
|
292
|
+
masked_intensity = intensity_image[subregion == 1] # Extract pixel values from the selected region
|
|
293
|
+
|
|
294
|
+
return float(np.nanmean(masked_intensity)) # Compute mean, ignoring NaNs
|
|
295
|
+
|
|
296
|
+
def mean_dark_intensity_ninty_fillhole_false(regionmask, intensity_image, target_channel='adhesion_channel'):
|
|
297
|
+
"""
|
|
298
|
+
Calculate the mean intensity in a dark subregion, handling NaN values.
|
|
299
|
+
"""
|
|
300
|
+
subregion = (intensity_image < 0.90) * regionmask # Select dark regions within the mask
|
|
301
|
+
|
|
302
|
+
masked_intensity = intensity_image[subregion == 1] # Extract pixel values from the selected region
|
|
303
|
+
|
|
304
|
+
return float(np.nanmean(masked_intensity)) # Compute mean, ignoring NaNs
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def mean_dark_intensity_ninty(regionmask, intensity_image, target_channel='adhesion_channel', fill_holes=True):
|
|
308
|
+
"""
|
|
309
|
+
Calculate the mean intensity in a dark subregion below 90, handling NaN values.
|
|
310
|
+
|
|
311
|
+
"""
|
|
312
|
+
subregion = (intensity_image < 0.90) * regionmask
|
|
313
|
+
|
|
314
|
+
if fill_holes:
|
|
315
|
+
subregion = skm.label(subregion, connectivity=2, background=0)
|
|
316
|
+
subregion = fill_label_holes(subregion)
|
|
317
|
+
subregion[subregion > 0] = 1
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
masked_intensity = intensity_image[subregion == 1]
|
|
321
|
+
|
|
322
|
+
return float(np.nanmean(masked_intensity))
|
|
323
|
+
|
|
324
|
+
def mean_dark_intensity_eight_five(regionmask, intensity_image, target_channel='adhesion_channel', fill_holes=True):
|
|
325
|
+
"""
|
|
326
|
+
Calculate the mean intensity in a dark subregion below 85, handling NaN values.
|
|
327
|
+
|
|
328
|
+
"""
|
|
329
|
+
subregion = (intensity_image < 0.85) * regionmask
|
|
330
|
+
|
|
331
|
+
if fill_holes:
|
|
332
|
+
subregion = skm.label(subregion, connectivity=2, background=0)
|
|
333
|
+
subregion = fill_label_holes(subregion)
|
|
334
|
+
subregion[subregion > 0] = 1
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
masked_intensity = intensity_image[subregion == 1]
|
|
338
|
+
|
|
339
|
+
return float(np.nanmean(masked_intensity))
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def mean_dark_intensity_eight_five_fillhole_false(regionmask, intensity_image, target_channel='adhesion_channel'):
|
|
343
|
+
|
|
344
|
+
subregion = (intensity_image < 0.85) * regionmask # Select dark regions within the mask
|
|
345
|
+
|
|
346
|
+
masked_intensity = intensity_image[subregion == 1] # Extract pixel values from the selected region
|
|
347
|
+
|
|
348
|
+
return float(np.nanmean(masked_intensity)) # Compute mean, ignoring NaNs
|
|
349
|
+
|
|
350
|
+
def percentile_zero_one_dark_intensity_ninty(regionmask, intensity_image, target_channel='adhesion_channel'):
|
|
351
|
+
|
|
352
|
+
subregion = (intensity_image < 0.95) * regionmask
|
|
353
|
+
return float(np.nanpercentile(intensity_image[subregion],0.1))
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def percentile_one_dark_intensity_ninty(regionmask, intensity_image, target_channel='adhesion_channel'):
|
|
357
|
+
|
|
358
|
+
subregion = (intensity_image < 0.95) * regionmask
|
|
359
|
+
return float(np.nanpercentile(intensity_image[subregion],1))
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def percentile_five_dark_intensity_ninty(regionmask, intensity_image, target_channel='adhesion_channel'):
|
|
363
|
+
|
|
364
|
+
subregion = (intensity_image < 0.95) * regionmask
|
|
365
|
+
return float(np.nanpercentile(intensity_image[subregion],5))
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def percentile_ten_dark_intensity_ninty(regionmask, intensity_image, target_channel='adhesion_channel'):
|
|
369
|
+
|
|
370
|
+
subregion = (intensity_image < 0.95) * regionmask
|
|
371
|
+
return float(np.nanpercentile(intensity_image[subregion],10))
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def percentile_ninty_five_dark_intensity_ninty(regionmask, intensity_image, target_channel='adhesion_channel'):
|
|
375
|
+
|
|
376
|
+
subregion = (intensity_image < 0.95) * regionmask
|
|
377
|
+
return float(np.nanpercentile(intensity_image[subregion],95))
|
|
378
|
+
|
|
379
|
+
|
|
248
380
|
def intensity_percentile_ninety_nine(regionmask, intensity_image):
|
|
249
381
|
return np.nanpercentile(intensity_image[regionmask],99)
|
|
250
382
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from skimage.filters import difference_of_gaussians, threshold_otsu, threshold_local,
|
|
1
|
+
from skimage.filters import difference_of_gaussians, threshold_multiotsu, threshold_otsu, threshold_local, \
|
|
2
|
+
threshold_niblack, threshold_sauvola
|
|
2
3
|
from celldetective.utils import interpolate_nan
|
|
3
4
|
import scipy.ndimage as snd
|
|
4
5
|
import numpy as np
|
|
@@ -102,6 +103,11 @@ def otsu_filter(img, *kwargs):
|
|
|
102
103
|
binary = img >= thresh
|
|
103
104
|
return binary.astype(float)
|
|
104
105
|
|
|
106
|
+
def multiotsu_filter(img, classes=3, *kwargs):
|
|
107
|
+
thresholds = threshold_multiotsu(img, classes=classes)
|
|
108
|
+
regions = np.digitize(img, bins=thresholds)
|
|
109
|
+
return regions.astype(float)
|
|
110
|
+
|
|
105
111
|
def local_filter(img, *kwargs):
|
|
106
112
|
thresh = threshold_local(img.astype(float), *kwargs)
|
|
107
113
|
binary = img >= thresh
|
|
@@ -1,29 +1,27 @@
|
|
|
1
|
+
import gc
|
|
2
|
+
import json
|
|
1
3
|
import os
|
|
2
|
-
|
|
3
|
-
from PyQt5.QtWidgets import QApplication, QMainWindow
|
|
4
|
-
from PyQt5.QtWidgets import QFileDialog, QDialog, QWidget, QVBoxLayout, QCheckBox, QHBoxLayout, QLabel, QLineEdit, QPushButton, QMessageBox, QMenu, QAction
|
|
5
|
-
from PyQt5.QtCore import Qt, QUrl
|
|
6
|
-
from PyQt5.QtGui import QIcon, QDesktopServices, QIntValidator
|
|
7
|
-
|
|
8
4
|
from glob import glob
|
|
9
|
-
from
|
|
10
|
-
from fonticon_mdi6 import MDI6
|
|
11
|
-
|
|
12
|
-
from celldetective.gui.about import AboutWidget
|
|
13
|
-
from celldetective.io import correct_annotation
|
|
14
|
-
from celldetective.utils import download_zenodo_file
|
|
15
|
-
from celldetective.gui.gui_utils import center_window
|
|
16
|
-
from celldetective.gui import Styles, ControlPanel, ConfigNewExperiment
|
|
5
|
+
from subprocess import Popen, check_output
|
|
17
6
|
|
|
18
|
-
import
|
|
19
|
-
from
|
|
7
|
+
from PyQt5.QtCore import QUrl, Qt
|
|
8
|
+
from PyQt5.QtGui import QDesktopServices, QIntValidator
|
|
9
|
+
from PyQt5.QtWidgets import QAction, QApplication, QCheckBox, QDialog, QFileDialog, QHBoxLayout, QLabel, QLineEdit, \
|
|
10
|
+
QMenu, QPushButton, QVBoxLayout
|
|
11
|
+
from fonticon_mdi6 import MDI6
|
|
20
12
|
from psutil import cpu_count
|
|
21
|
-
import
|
|
13
|
+
from superqt.fonticon import icon
|
|
22
14
|
|
|
15
|
+
from celldetective.gui import ConfigNewExperiment, ControlPanel, CelldetectiveMainWindow, CelldetectiveWidget
|
|
16
|
+
from celldetective.gui.about import AboutWidget
|
|
17
|
+
from celldetective.gui.gui_utils import center_window, generic_message
|
|
23
18
|
from celldetective.gui.processes.downloader import DownloadProcess
|
|
24
19
|
from celldetective.gui.workers import ProgressWindow
|
|
20
|
+
from celldetective.io import correct_annotation, extract_well_name_and_number
|
|
21
|
+
from celldetective.utils import download_zenodo_file, pretty_table
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
|
|
24
|
+
class AppInitWindow(CelldetectiveMainWindow):
|
|
27
25
|
|
|
28
26
|
"""
|
|
29
27
|
Initial window to set the experiment folder or create a new one.
|
|
@@ -36,7 +34,7 @@ class AppInitWindow(QMainWindow, Styles):
|
|
|
36
34
|
self.parent_window = parent_window
|
|
37
35
|
self.setWindowTitle("celldetective")
|
|
38
36
|
|
|
39
|
-
self.n_threads = min([1,cpu_count()])
|
|
37
|
+
self.n_threads = min([1, cpu_count()])
|
|
40
38
|
|
|
41
39
|
try:
|
|
42
40
|
check_output('nvidia-smi')
|
|
@@ -48,7 +46,6 @@ class AppInitWindow(QMainWindow, Styles):
|
|
|
48
46
|
|
|
49
47
|
self.soft_path = software_location
|
|
50
48
|
self.onlyInt = QIntValidator()
|
|
51
|
-
self.setWindowIcon(QIcon(os.sep.join([self.soft_path,'celldetective','icons','logo.png'])))
|
|
52
49
|
|
|
53
50
|
self._createActions()
|
|
54
51
|
self._createMenuBar()
|
|
@@ -58,9 +55,9 @@ class AppInitWindow(QMainWindow, Styles):
|
|
|
58
55
|
self.geometry = self.screen.availableGeometry()
|
|
59
56
|
self.screen_width, self.screen_height = self.geometry.getRect()[-2:]
|
|
60
57
|
|
|
61
|
-
central_widget =
|
|
58
|
+
central_widget = CelldetectiveWidget()
|
|
62
59
|
self.vertical_layout = QVBoxLayout(central_widget)
|
|
63
|
-
self.vertical_layout.setContentsMargins(15,15,15,15)
|
|
60
|
+
self.vertical_layout.setContentsMargins(15, 15, 15, 15)
|
|
64
61
|
self.vertical_layout.addWidget(QLabel("Experiment folder:"))
|
|
65
62
|
self.create_locate_exp_hbox()
|
|
66
63
|
self.create_buttons_hbox()
|
|
@@ -79,14 +76,17 @@ class AppInitWindow(QMainWindow, Styles):
|
|
|
79
76
|
def create_locate_exp_hbox(self):
|
|
80
77
|
|
|
81
78
|
self.locate_exp_layout = QHBoxLayout()
|
|
82
|
-
self.locate_exp_layout.setContentsMargins(0,5,0,0)
|
|
79
|
+
self.locate_exp_layout.setContentsMargins(0, 5, 0, 0)
|
|
83
80
|
self.experiment_path_selection = QLineEdit()
|
|
84
81
|
self.experiment_path_selection.setAlignment(Qt.AlignLeft)
|
|
85
82
|
self.experiment_path_selection.setEnabled(True)
|
|
86
83
|
self.experiment_path_selection.setDragEnabled(True)
|
|
87
84
|
self.experiment_path_selection.setFixedWidth(430)
|
|
88
85
|
self.experiment_path_selection.textChanged[str].connect(self.check_path_and_enable_opening)
|
|
89
|
-
|
|
86
|
+
try:
|
|
87
|
+
self.foldername = os.getcwd()
|
|
88
|
+
except FileNotFoundError as e:
|
|
89
|
+
self.foldername = ""
|
|
90
90
|
self.experiment_path_selection.setPlaceholderText('/path/to/experiment/folder/')
|
|
91
91
|
self.locate_exp_layout.addWidget(self.experiment_path_selection, 90)
|
|
92
92
|
|
|
@@ -266,7 +266,7 @@ class AppInitWindow(QMainWindow, Styles):
|
|
|
266
266
|
|
|
267
267
|
print('setting memory and threads')
|
|
268
268
|
|
|
269
|
-
self.ThreadsWidget =
|
|
269
|
+
self.ThreadsWidget = CelldetectiveWidget()
|
|
270
270
|
self.ThreadsWidget.setWindowTitle("Threads")
|
|
271
271
|
layout = QVBoxLayout()
|
|
272
272
|
self.ThreadsWidget.setLayout(layout)
|
|
@@ -382,24 +382,21 @@ class AppInitWindow(QMainWindow, Styles):
|
|
|
382
382
|
wells = glob(os.sep.join([self.exp_dir,"W*"]))
|
|
383
383
|
self.number_of_wells = len(wells)
|
|
384
384
|
if self.number_of_wells==0:
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
msgBox.setText("No well was found in the experiment folder.\nPlease respect the W*/ nomenclature...")
|
|
388
|
-
msgBox.setWindowTitle("Error")
|
|
389
|
-
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
390
|
-
returnValue = msgBox.exec()
|
|
391
|
-
if returnValue == QMessageBox.Ok:
|
|
392
|
-
return None
|
|
385
|
+
generic_message("No well was found in the experiment folder.\nPlease respect the W*/ nomenclature...", msg_type="critical")
|
|
386
|
+
return None
|
|
393
387
|
else:
|
|
394
388
|
if self.number_of_wells==1:
|
|
395
389
|
print(f"Found {self.number_of_wells} well...")
|
|
396
390
|
elif self.number_of_wells>1:
|
|
397
391
|
print(f"Found {self.number_of_wells} wells...")
|
|
398
|
-
|
|
392
|
+
|
|
393
|
+
number_pos = {}
|
|
399
394
|
for w in wells:
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
395
|
+
well_name, well_nbr = extract_well_name_and_number(w)
|
|
396
|
+
position_folders = glob(os.sep.join([w,f"{well_nbr}*", os.sep]))
|
|
397
|
+
number_pos.update({well_name: len(position_folders)})
|
|
398
|
+
print(f"Number of positions per well:")
|
|
399
|
+
pretty_table(number_pos)
|
|
403
400
|
|
|
404
401
|
with open(os.sep.join([self.soft_path,'celldetective','recent.txt']), 'a+') as f:
|
|
405
402
|
f.write(self.exp_dir+'\n')
|
|
@@ -423,12 +420,6 @@ class AppInitWindow(QMainWindow, Styles):
|
|
|
423
420
|
else:
|
|
424
421
|
return None
|
|
425
422
|
if not os.path.exists(os.sep.join([self.foldername,"config.ini"])):
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
msgBox.setWindowTitle("Warning")
|
|
430
|
-
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
431
|
-
returnValue = msgBox.exec()
|
|
432
|
-
if returnValue == QMessageBox.Ok:
|
|
433
|
-
self.experiment_path_selection.setText('')
|
|
434
|
-
return None
|
|
423
|
+
generic_message("No configuration can be found in the selected folder...", msg_type="warning")
|
|
424
|
+
self.experiment_path_selection.setText('')
|
|
425
|
+
return None
|
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
from .styles import Styles
|
|
2
|
-
from .
|
|
2
|
+
from .base_components import CelldetectiveWidget, CelldetectiveMainWindow
|
|
3
3
|
from .json_readers import ConfigEditor
|
|
4
4
|
from .tableUI import TableUI
|
|
5
|
-
from .measurement_options import ConfigMeasurements
|
|
6
|
-
from .neighborhood_options import ConfigNeighborhoods
|
|
7
5
|
from .classifier_widget import ClassifierWidget
|
|
8
6
|
from .survival_ui import ConfigSurvival
|
|
9
7
|
from .plot_signals_ui import ConfigSignalPlot
|
|
10
|
-
from .
|
|
11
|
-
from .
|
|
12
|
-
from .signal_annotator2 import SignalAnnotator2
|
|
13
|
-
from .retrain_signal_model_options import ConfigSignalModelTraining
|
|
14
|
-
from .retrain_segmentation_model_options import ConfigSegmentationModelTraining
|
|
8
|
+
from .event_annotator import EventAnnotator
|
|
9
|
+
from .pair_event_annotator import PairEventAnnotator
|
|
15
10
|
from .thresholds_gui import ThresholdConfigWizard
|
|
16
11
|
from .seg_model_loader import SegmentationModelLoader
|
|
17
12
|
from .process_block import ProcessPanel, NeighPanel, PreprocessingPanel
|
|
18
13
|
from .analyze_block import AnalysisPanel
|
|
19
14
|
from .control_panel import ControlPanel
|
|
20
15
|
from .configure_new_exp import ConfigNewExperiment
|
|
21
|
-
|
|
@@ -1,44 +1,48 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import
|
|
1
|
+
from PyQt5.QtWidgets import QVBoxLayout, QLabel
|
|
2
2
|
from PyQt5.QtGui import QPixmap
|
|
3
3
|
from PyQt5.QtCore import Qt
|
|
4
|
+
|
|
5
|
+
from celldetective.gui import CelldetectiveWidget
|
|
4
6
|
from celldetective.utils import get_software_location
|
|
5
7
|
import os
|
|
6
8
|
from celldetective.gui.gui_utils import center_window
|
|
7
9
|
from celldetective._version import __version__
|
|
8
10
|
|
|
9
|
-
class AboutWidget(QWidget):
|
|
10
11
|
|
|
12
|
+
class AboutWidget(CelldetectiveWidget):
|
|
13
|
+
|
|
11
14
|
def __init__(self):
|
|
12
|
-
|
|
13
15
|
super().__init__()
|
|
14
16
|
self.setWindowTitle("About celldetective")
|
|
15
|
-
self.
|
|
17
|
+
self.setMaximumWidth(320)
|
|
16
18
|
center_window(self)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
|
|
20
|
+
logo = QPixmap(self.celldetective_logo_path)
|
|
21
|
+
|
|
19
22
|
# Create the layout
|
|
20
23
|
layout = QVBoxLayout(self)
|
|
21
24
|
img_label = QLabel('')
|
|
22
25
|
img_label.setPixmap(logo)
|
|
23
26
|
layout.addWidget(img_label, alignment=Qt.AlignCenter)
|
|
24
|
-
|
|
27
|
+
|
|
25
28
|
self.soft_name = QLabel('celldetective')
|
|
26
29
|
self.soft_name.setStyleSheet("""font-weight: bold;
|
|
27
30
|
font-size: 18px;
|
|
28
31
|
""")
|
|
29
32
|
layout.addWidget(self.soft_name, alignment=Qt.AlignCenter)
|
|
30
|
-
|
|
31
|
-
self.version_lbl = QLabel(f"Version {__version__} <a href=\"https://github.com/
|
|
33
|
+
|
|
34
|
+
self.version_lbl = QLabel(f"Version {__version__} <a href=\"https://github.com/celldetective/celldetective"
|
|
35
|
+
f"/releases\">(release notes)</a>")
|
|
32
36
|
self.version_lbl.setOpenExternalLinks(True)
|
|
33
37
|
layout.addWidget(self.version_lbl, alignment=Qt.AlignCenter)
|
|
34
|
-
|
|
38
|
+
|
|
35
39
|
self.lab_lbl = QLabel("Developed at Laboratoire Adhésion et Inflammation (LAI) INSERM U1067 CNRS UMR 7333")
|
|
36
40
|
self.lab_lbl.setWordWrap(True)
|
|
37
41
|
layout.addWidget(self.lab_lbl, alignment=Qt.AlignCenter)
|
|
38
|
-
|
|
39
|
-
self.centuri_mention = QLabel(
|
|
42
|
+
|
|
43
|
+
self.centuri_mention = QLabel(
|
|
44
|
+
"The project leading to this publication has received funding from France 2030, the French Government "
|
|
45
|
+
"program managed by the French National Research Agency (ANR-16-CONV-0001) and from Excellence Initiative "
|
|
46
|
+
"of Aix-Marseille University - A*MIDEX')")
|
|
40
47
|
self.centuri_mention.setWordWrap(True)
|
|
41
48
|
layout.addWidget(self.centuri_mention, alignment=Qt.AlignCenter)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|