celldetective 1.3.9.post5__tar.gz → 1.4.0__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.
Files changed (140) hide show
  1. {celldetective-1.3.9.post5 → celldetective-1.4.0}/PKG-INFO +24 -16
  2. {celldetective-1.3.9.post5 → celldetective-1.4.0}/README.md +17 -14
  3. celldetective-1.4.0/celldetective/__init__.py +1 -0
  4. celldetective-1.4.0/celldetective/_version.py +1 -0
  5. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/events.py +2 -4
  6. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/extra_properties.py +132 -0
  7. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/InitWindow.py +33 -45
  8. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/__init__.py +1 -0
  9. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/about.py +19 -15
  10. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/analyze_block.py +34 -19
  11. celldetective-1.4.0/celldetective/gui/base_components.py +23 -0
  12. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/btrack_options.py +26 -34
  13. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/classifier_widget.py +68 -81
  14. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/configure_new_exp.py +113 -17
  15. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/control_panel.py +68 -141
  16. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/generic_signal_plot.py +9 -12
  17. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/gui_utils.py +49 -21
  18. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/json_readers.py +5 -4
  19. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/layouts.py +246 -22
  20. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/measurement_options.py +32 -17
  21. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/neighborhood_options.py +10 -13
  22. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/plot_measurements.py +21 -17
  23. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/plot_signals_ui.py +125 -72
  24. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/process_block.py +180 -123
  25. celldetective-1.4.0/celldetective/gui/processes/compute_neighborhood.py +594 -0
  26. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/processes/measure_cells.py +5 -0
  27. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/processes/segment_cells.py +27 -6
  28. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/processes/track_cells.py +6 -0
  29. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/retrain_segmentation_model_options.py +12 -20
  30. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/retrain_signal_model_options.py +57 -56
  31. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/seg_model_loader.py +21 -62
  32. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/signal_annotator.py +129 -70
  33. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/signal_annotator2.py +431 -635
  34. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/signal_annotator_options.py +8 -11
  35. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/survival_ui.py +49 -95
  36. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/tableUI.py +28 -25
  37. celldetective-1.4.0/celldetective/gui/thresholds_gui.py +714 -0
  38. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/viewers.py +106 -39
  39. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/workers.py +9 -3
  40. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/io.py +57 -20
  41. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/measure.py +63 -27
  42. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/neighborhood.py +342 -268
  43. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/preprocessing.py +25 -17
  44. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/relative_measurements.py +50 -29
  45. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/scripts/analyze_signals.py +4 -1
  46. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/scripts/measure_relative.py +4 -1
  47. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/scripts/segment_cells.py +0 -6
  48. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/scripts/track_cells.py +3 -1
  49. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/scripts/train_segmentation_model.py +7 -4
  50. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/signals.py +29 -14
  51. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/tracking.py +7 -2
  52. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/utils.py +36 -8
  53. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective.egg-info/PKG-INFO +24 -16
  54. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective.egg-info/SOURCES.txt +2 -0
  55. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective.egg-info/requires.txt +4 -0
  56. {celldetective-1.3.9.post5 → celldetective-1.4.0}/tests/test_qt.py +21 -21
  57. celldetective-1.3.9.post5/celldetective/__init__.py +0 -4
  58. celldetective-1.3.9.post5/celldetective/_version.py +0 -1
  59. celldetective-1.3.9.post5/celldetective/gui/thresholds_gui.py +0 -1318
  60. {celldetective-1.3.9.post5 → celldetective-1.4.0}/LICENSE +0 -0
  61. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/__main__.py +0 -0
  62. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/datasets/segmentation_annotations/blank +0 -0
  63. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/datasets/signal_annotations/blank +0 -0
  64. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/filters.py +0 -0
  65. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/help/DL-segmentation-strategy.json +0 -0
  66. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/help/Threshold-vs-DL.json +0 -0
  67. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/help/cell-populations.json +0 -0
  68. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/help/exp-structure.json +0 -0
  69. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/help/feature-btrack.json +0 -0
  70. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/help/neighborhood.json +0 -0
  71. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/help/prefilter-for-segmentation.json +0 -0
  72. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/help/preprocessing.json +0 -0
  73. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/help/propagate-classification.json +0 -0
  74. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/help/track-postprocessing.json +0 -0
  75. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/help/tracking.json +0 -0
  76. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/processes/downloader.py +0 -0
  77. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/processes/train_segmentation_model.py +0 -0
  78. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/processes/train_signal_model.py +0 -0
  79. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/gui/styles.py +0 -0
  80. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/icons/logo-large.png +0 -0
  81. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/icons/logo.png +0 -0
  82. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/icons/signals_icon.png +0 -0
  83. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/icons/splash-test.png +0 -0
  84. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/icons/splash.png +0 -0
  85. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/icons/splash0.png +0 -0
  86. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/icons/survival2.png +0 -0
  87. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/icons/vignette_signals2.png +0 -0
  88. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/icons/vignette_signals2.svg +0 -0
  89. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/links/zenodo.json +0 -0
  90. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/pair_signal_detection/blank +0 -0
  91. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/segmentation_effectors/blank +0 -0
  92. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -0
  93. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
  94. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -0
  95. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -0
  96. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
  97. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/segmentation_generic/blank +0 -0
  98. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/segmentation_targets/blank +0 -0
  99. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
  100. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
  101. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/signal_detection/NucCond/config_input.json +0 -0
  102. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/signal_detection/NucCond/log_classifier.csv +0 -0
  103. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/signal_detection/NucCond/log_regressor.csv +0 -0
  104. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
  105. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
  106. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/signal_detection/NucCond/scores.npy +0 -0
  107. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
  108. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
  109. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
  110. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
  111. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/signal_detection/blank +0 -0
  112. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/tracking_configs/biased_motion.json +0 -0
  113. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/tracking_configs/mcf7.json +0 -0
  114. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/tracking_configs/no_z_motion.json +0 -0
  115. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/tracking_configs/ricm.json +0 -0
  116. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/models/tracking_configs/ricm2.json +0 -0
  117. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/regionprops/__init__.py +0 -0
  118. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/regionprops/_regionprops.py +0 -0
  119. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/regionprops/props.json +0 -0
  120. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/scripts/measure_cells.py +0 -0
  121. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/scripts/segment_cells_thresholds.py +0 -0
  122. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/scripts/train_signal_model.py +0 -0
  123. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective/segmentation.py +0 -0
  124. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective.egg-info/dependency_links.txt +0 -0
  125. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective.egg-info/entry_points.txt +0 -0
  126. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective.egg-info/not-zip-safe +0 -0
  127. {celldetective-1.3.9.post5 → celldetective-1.4.0}/celldetective.egg-info/top_level.txt +0 -0
  128. {celldetective-1.3.9.post5 → celldetective-1.4.0}/setup.cfg +0 -0
  129. {celldetective-1.3.9.post5 → celldetective-1.4.0}/setup.py +0 -0
  130. {celldetective-1.3.9.post5 → celldetective-1.4.0}/tests/__init__.py +0 -0
  131. {celldetective-1.3.9.post5 → celldetective-1.4.0}/tests/test_events.py +0 -0
  132. {celldetective-1.3.9.post5 → celldetective-1.4.0}/tests/test_filters.py +0 -0
  133. {celldetective-1.3.9.post5 → celldetective-1.4.0}/tests/test_io.py +0 -0
  134. {celldetective-1.3.9.post5 → celldetective-1.4.0}/tests/test_measure.py +0 -0
  135. {celldetective-1.3.9.post5 → celldetective-1.4.0}/tests/test_neighborhood.py +0 -0
  136. {celldetective-1.3.9.post5 → celldetective-1.4.0}/tests/test_preprocessing.py +0 -0
  137. {celldetective-1.3.9.post5 → celldetective-1.4.0}/tests/test_segmentation.py +0 -0
  138. {celldetective-1.3.9.post5 → celldetective-1.4.0}/tests/test_signals.py +0 -0
  139. {celldetective-1.3.9.post5 → celldetective-1.4.0}/tests/test_tracking.py +0 -0
  140. {celldetective-1.3.9.post5 → celldetective-1.4.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: celldetective
3
- Version: 1.3.9.post5
3
+ Version: 1.4.0
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://www.biorxiv.org/content/10.1101/2024.03.15.585250v3)
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 {Torro2024.03.15.585250,
191
- author = {Torro, R{\'e}my and D{\'\i}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},
192
- title = {Celldetective: an AI-enhanced image analysis tool for unraveling dynamic cell interactions},
193
- elocation-id = {2024.03.15.585250},
194
- year = {2024},
195
- doi = {10.1101/2024.03.15.585250},
196
- publisher = {Cold Spring Harbor Laboratory},
197
- 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.Competing Interest StatementThe authors have declared no competing interest.},
198
- URL = {https://www.biorxiv.org/content/early/2024/11/13/2024.03.15.585250},
199
- eprint = {https://www.biorxiv.org/content/early/2024/11/13/2024.03.15.585250.full.pdf},
200
- journal = {bioRxiv}
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://www.biorxiv.org/content/10.1101/2024.03.15.585250v3)
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 {Torro2024.03.15.585250,
136
- author = {Torro, R{\'e}my and D{\'\i}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},
137
- title = {Celldetective: an AI-enhanced image analysis tool for unraveling dynamic cell interactions},
138
- elocation-id = {2024.03.15.585250},
139
- year = {2024},
140
- doi = {10.1101/2024.03.15.585250},
141
- publisher = {Cold Spring Harbor Laboratory},
142
- 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.Competing Interest StatementThe authors have declared no competing interest.},
143
- URL = {https://www.biorxiv.org/content/early/2024/11/13/2024.03.15.585250},
144
- eprint = {https://www.biorxiv.org/content/early/2024/11/13/2024.03.15.585250.full.pdf},
145
- journal = {bioRxiv}
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.0"
@@ -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 = switch_to_events_v2(classes, event_times, max_times, origin_times, FrameToMin=0.5)
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:
@@ -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,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 superqt.fonticon import icon
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 gc
19
- from subprocess import check_output, Popen
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 json
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
- class AppInitWindow(QMainWindow, Styles):
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 = QWidget()
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,7 +76,7 @@ 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)
@@ -266,7 +263,7 @@ class AppInitWindow(QMainWindow, Styles):
266
263
 
267
264
  print('setting memory and threads')
268
265
 
269
- self.ThreadsWidget = QWidget()
266
+ self.ThreadsWidget = CelldetectiveWidget()
270
267
  self.ThreadsWidget.setWindowTitle("Threads")
271
268
  layout = QVBoxLayout()
272
269
  self.ThreadsWidget.setLayout(layout)
@@ -382,24 +379,21 @@ class AppInitWindow(QMainWindow, Styles):
382
379
  wells = glob(os.sep.join([self.exp_dir,"W*"]))
383
380
  self.number_of_wells = len(wells)
384
381
  if self.number_of_wells==0:
385
- msgBox = QMessageBox()
386
- msgBox.setIcon(QMessageBox.Critical)
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
382
+ generic_message("No well was found in the experiment folder.\nPlease respect the W*/ nomenclature...", msg_type="critical")
383
+ return None
393
384
  else:
394
385
  if self.number_of_wells==1:
395
386
  print(f"Found {self.number_of_wells} well...")
396
387
  elif self.number_of_wells>1:
397
388
  print(f"Found {self.number_of_wells} wells...")
398
- number_pos = []
389
+
390
+ number_pos = {}
399
391
  for w in wells:
400
- position_folders = glob(os.sep.join([w,f"{w.split(os.sep)[-1][1]}*", os.sep]))
401
- number_pos.append(len(position_folders))
402
- print(f"Number of positions per well: {number_pos}")
392
+ well_name, well_nbr = extract_well_name_and_number(w)
393
+ position_folders = glob(os.sep.join([w,f"{well_nbr}*", os.sep]))
394
+ number_pos.update({well_name: len(position_folders)})
395
+ print(f"Number of positions per well:")
396
+ pretty_table(number_pos)
403
397
 
404
398
  with open(os.sep.join([self.soft_path,'celldetective','recent.txt']), 'a+') as f:
405
399
  f.write(self.exp_dir+'\n')
@@ -423,12 +417,6 @@ class AppInitWindow(QMainWindow, Styles):
423
417
  else:
424
418
  return None
425
419
  if not os.path.exists(os.sep.join([self.foldername,"config.ini"])):
426
- msgBox = QMessageBox()
427
- msgBox.setIcon(QMessageBox.Warning)
428
- msgBox.setText("No configuration can be found in the selected folder...")
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
420
+ generic_message("No configuration can be found in the selected folder...", msg_type="warning")
421
+ self.experiment_path_selection.setText('')
422
+ return None
@@ -1,4 +1,5 @@
1
1
  from .styles import Styles
2
+ from .base_components import CelldetectiveWidget, CelldetectiveMainWindow
2
3
  from .btrack_options import ConfigTracking
3
4
  from .json_readers import ConfigEditor
4
5
  from .tableUI import TableUI
@@ -1,44 +1,48 @@
1
- from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel
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.setMinimumWidth(300)
17
+ self.setMaximumWidth(320)
16
18
  center_window(self)
17
- logo = QPixmap(os.sep.join([get_software_location(),'celldetective','icons','logo.png']))
18
-
19
+
20
+ logo = QPixmap(os.sep.join([get_software_location(), 'celldetective', 'icons', 'logo.png']))
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/remyeltorro/celldetective/releases\">(release notes)</a>")
33
+
34
+ self.version_lbl = QLabel(f"Version {__version__} <a href=\"https://github.com/remyeltorro/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("The project leading to this publication has received funding from France 2030, the French Government program managed by the French National Research Agency (ANR-16-CONV-0001) and from Excellence Initiative of Aix-Marseille University - A*MIDEX')")
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
-
@@ -1,11 +1,14 @@
1
1
  from PyQt5.QtWidgets import QFrame, QLabel, QPushButton, QVBoxLayout, \
2
- QSpacerItem, QSizePolicy
2
+ QSpacerItem, QSizePolicy
3
3
  from PyQt5.QtCore import Qt, QSize
4
4
  from PyQt5.QtGui import QIcon
5
- from celldetective.gui.plot_measurements import ConfigMeasurementsPlot
6
5
  from celldetective.gui import ConfigSurvival, ConfigSignalPlot
7
6
  import os
8
7
  from celldetective.gui import Styles
8
+ from glob import glob
9
+
10
+ from celldetective.gui.gui_utils import generic_message
11
+
9
12
 
10
13
  class AnalysisPanel(QFrame, Styles):
11
14
  def __init__(self, parent_window, title=None):
@@ -14,10 +17,11 @@ class AnalysisPanel(QFrame, Styles):
14
17
  self.parent_window = parent_window
15
18
  self.title = title
16
19
  if self.title is None:
17
- self.title=''
20
+ self.title = ''
18
21
  self.exp_channels = self.parent_window.exp_channels
19
22
  self.exp_dir = self.parent_window.exp_dir
20
23
  self.soft_path = self.parent_window.parent_window.soft_path
24
+ self.pop_exists = False
21
25
 
22
26
  self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
23
27
  self.grid = QVBoxLayout(self)
@@ -40,7 +44,7 @@ class AnalysisPanel(QFrame, Styles):
40
44
  self.grid.addWidget(panel_title, alignment=Qt.AlignCenter)
41
45
 
42
46
  self.survival_btn = QPushButton("plot survival")
43
- self.survival_btn.setIcon(QIcon(QIcon(os.sep.join([self.soft_path,'celldetective','icons','survival2.png']))))
47
+ self.survival_btn.setIcon(QIcon(QIcon(os.sep.join([self.soft_path, 'celldetective', 'icons', 'survival2.png']))))
44
48
  self.survival_btn.setStyleSheet(self.button_style_sheet_2)
45
49
  self.survival_btn.setIconSize(QSize(35, 35))
46
50
  self.survival_btn.clicked.connect(self.configure_survival)
@@ -55,27 +59,38 @@ class AnalysisPanel(QFrame, Styles):
55
59
  self.grid.addWidget(signal_lbl, alignment=Qt.AlignCenter)
56
60
 
57
61
  self.plot_signal_btn = QPushButton("plot signals")
58
- self.plot_signal_btn.setIcon(QIcon(QIcon(os.sep.join([self.soft_path,'celldetective','icons','signals_icon.png']))))
62
+ self.plot_signal_btn.setIcon(QIcon(QIcon(os.sep.join([self.soft_path, 'celldetective', 'icons', 'signals_icon.png']))))
59
63
  self.plot_signal_btn.setStyleSheet(self.button_style_sheet_2)
60
64
  self.plot_signal_btn.setIconSize(QSize(35, 35))
61
65
  self.plot_signal_btn.clicked.connect(self.configure_plot_signals)
62
66
  self.grid.addWidget(self.plot_signal_btn)
63
67
 
64
- verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
65
- self.grid.addItem(verticalSpacer)
68
+ vertical_spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
69
+ self.grid.addItem(vertical_spacer)
66
70
 
71
+ def check_for_tables(self):
72
+
73
+ for population in self.parent_window.populations:
74
+ tables = glob(self.exp_dir + os.sep.join(['W*', '*', 'output', 'tables', f'trajectories_{population}.csv']))
75
+ if len(tables) > 0:
76
+ self.pop_exists = True
77
+
67
78
  def configure_survival(self):
68
- print('survival analysis starting!!!')
69
- self.configSurvival = ConfigSurvival(self)
70
- self.configSurvival.show()
79
+
80
+ self.check_for_tables()
81
+ if self.pop_exists:
82
+ self.configSurvival = ConfigSurvival(self)
83
+ self.configSurvival.show()
84
+ else:
85
+ generic_message("No population table could be found... Abort...")
86
+ return None
71
87
 
72
88
  def configure_plot_signals(self):
73
- print('Configure a signal collapse representation...')
74
- self.ConfigSignalPlot = ConfigSignalPlot(self)
75
- self.ConfigSignalPlot.show()
76
-
77
- def configure_plot_measurements(self):
78
-
79
- print('plot measurements analysis starting!!!')
80
- self.ConfigMeasurementsPlot_wg = ConfigMeasurementsPlot(self)
81
- self.ConfigMeasurementsPlot_wg.show()
89
+
90
+ self.check_for_tables()
91
+ if self.pop_exists:
92
+ self.ConfigSignalPlot = ConfigSignalPlot(self)
93
+ self.ConfigSignalPlot.show()
94
+ else:
95
+ generic_message("No population table could be found... Abort...")
96
+ return None
@@ -0,0 +1,23 @@
1
+ from PyQt5.QtWidgets import QMainWindow, QWidget, QDialog
2
+ from PyQt5.QtCore import Qt
3
+ from celldetective.gui import Styles
4
+
5
+
6
+ class CelldetectiveWidget(QWidget, Styles):
7
+ def __init__(self, *args, **kwargs):
8
+ super().__init__(*args, **kwargs)
9
+ self.setWindowIcon(self.celldetective_icon)
10
+ self.setAttribute(Qt.WA_DeleteOnClose)
11
+
12
+
13
+ class CelldetectiveMainWindow(QMainWindow, Styles):
14
+ def __init__(self, *args, **kwargs):
15
+ super().__init__(*args, **kwargs)
16
+ self.setWindowIcon(self.celldetective_icon)
17
+ self.setAttribute(Qt.WA_DeleteOnClose)
18
+
19
+
20
+ class CelldetectiveDialog(QDialog, Styles):
21
+ def __init__(self, *args, **kwargs):
22
+ super().__init__(*args, **kwargs)
23
+ self.setWindowIcon(self.celldetective_icon)