celldetective 1.4.2__py3-none-any.whl → 1.5.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. celldetective/__init__.py +25 -0
  2. celldetective/__main__.py +62 -43
  3. celldetective/_version.py +1 -1
  4. celldetective/extra_properties.py +477 -399
  5. celldetective/filters.py +192 -97
  6. celldetective/gui/InitWindow.py +541 -411
  7. celldetective/gui/__init__.py +0 -15
  8. celldetective/gui/about.py +44 -39
  9. celldetective/gui/analyze_block.py +120 -84
  10. celldetective/gui/base/__init__.py +0 -0
  11. celldetective/gui/base/channel_norm_generator.py +335 -0
  12. celldetective/gui/base/components.py +249 -0
  13. celldetective/gui/base/feature_choice.py +92 -0
  14. celldetective/gui/base/figure_canvas.py +52 -0
  15. celldetective/gui/base/list_widget.py +133 -0
  16. celldetective/gui/{styles.py → base/styles.py} +92 -36
  17. celldetective/gui/base/utils.py +33 -0
  18. celldetective/gui/base_annotator.py +900 -767
  19. celldetective/gui/classifier_widget.py +6 -22
  20. celldetective/gui/configure_new_exp.py +777 -671
  21. celldetective/gui/control_panel.py +635 -524
  22. celldetective/gui/dynamic_progress.py +449 -0
  23. celldetective/gui/event_annotator.py +2023 -1662
  24. celldetective/gui/generic_signal_plot.py +1292 -944
  25. celldetective/gui/gui_utils.py +899 -1289
  26. celldetective/gui/interactions_block.py +658 -0
  27. celldetective/gui/interactive_timeseries_viewer.py +447 -0
  28. celldetective/gui/json_readers.py +48 -15
  29. celldetective/gui/layouts/__init__.py +5 -0
  30. celldetective/gui/layouts/background_model_free_layout.py +537 -0
  31. celldetective/gui/layouts/channel_offset_layout.py +134 -0
  32. celldetective/gui/layouts/local_correction_layout.py +91 -0
  33. celldetective/gui/layouts/model_fit_layout.py +372 -0
  34. celldetective/gui/layouts/operation_layout.py +68 -0
  35. celldetective/gui/layouts/protocol_designer_layout.py +96 -0
  36. celldetective/gui/pair_event_annotator.py +3130 -2435
  37. celldetective/gui/plot_measurements.py +586 -267
  38. celldetective/gui/plot_signals_ui.py +724 -506
  39. celldetective/gui/preprocessing_block.py +395 -0
  40. celldetective/gui/process_block.py +1678 -1831
  41. celldetective/gui/seg_model_loader.py +580 -473
  42. celldetective/gui/settings/__init__.py +0 -7
  43. celldetective/gui/settings/_cellpose_model_params.py +181 -0
  44. celldetective/gui/settings/_event_detection_model_params.py +95 -0
  45. celldetective/gui/settings/_segmentation_model_params.py +159 -0
  46. celldetective/gui/settings/_settings_base.py +77 -65
  47. celldetective/gui/settings/_settings_event_model_training.py +752 -526
  48. celldetective/gui/settings/_settings_measurements.py +1133 -964
  49. celldetective/gui/settings/_settings_neighborhood.py +574 -488
  50. celldetective/gui/settings/_settings_segmentation_model_training.py +779 -564
  51. celldetective/gui/settings/_settings_signal_annotator.py +329 -305
  52. celldetective/gui/settings/_settings_tracking.py +1304 -1094
  53. celldetective/gui/settings/_stardist_model_params.py +98 -0
  54. celldetective/gui/survival_ui.py +422 -312
  55. celldetective/gui/tableUI.py +1665 -1701
  56. celldetective/gui/table_ops/_maths.py +295 -0
  57. celldetective/gui/table_ops/_merge_groups.py +140 -0
  58. celldetective/gui/table_ops/_merge_one_hot.py +95 -0
  59. celldetective/gui/table_ops/_query_table.py +43 -0
  60. celldetective/gui/table_ops/_rename_col.py +44 -0
  61. celldetective/gui/thresholds_gui.py +382 -179
  62. celldetective/gui/viewers/__init__.py +0 -0
  63. celldetective/gui/viewers/base_viewer.py +700 -0
  64. celldetective/gui/viewers/channel_offset_viewer.py +331 -0
  65. celldetective/gui/viewers/contour_viewer.py +394 -0
  66. celldetective/gui/viewers/size_viewer.py +153 -0
  67. celldetective/gui/viewers/spot_detection_viewer.py +341 -0
  68. celldetective/gui/viewers/threshold_viewer.py +309 -0
  69. celldetective/gui/workers.py +403 -126
  70. celldetective/log_manager.py +92 -0
  71. celldetective/measure.py +1895 -1478
  72. celldetective/napari/__init__.py +0 -0
  73. celldetective/napari/utils.py +1025 -0
  74. celldetective/neighborhood.py +1914 -1448
  75. celldetective/preprocessing.py +1620 -1220
  76. celldetective/processes/__init__.py +0 -0
  77. celldetective/processes/background_correction.py +271 -0
  78. celldetective/processes/compute_neighborhood.py +894 -0
  79. celldetective/processes/detect_events.py +246 -0
  80. celldetective/processes/downloader.py +137 -0
  81. celldetective/processes/measure_cells.py +565 -0
  82. celldetective/processes/segment_cells.py +760 -0
  83. celldetective/processes/track_cells.py +435 -0
  84. celldetective/processes/train_segmentation_model.py +694 -0
  85. celldetective/processes/train_signal_model.py +265 -0
  86. celldetective/processes/unified_process.py +292 -0
  87. celldetective/regionprops/_regionprops.py +358 -317
  88. celldetective/relative_measurements.py +987 -710
  89. celldetective/scripts/measure_cells.py +313 -212
  90. celldetective/scripts/measure_relative.py +90 -46
  91. celldetective/scripts/segment_cells.py +165 -104
  92. celldetective/scripts/segment_cells_thresholds.py +96 -68
  93. celldetective/scripts/track_cells.py +198 -149
  94. celldetective/scripts/train_segmentation_model.py +324 -201
  95. celldetective/scripts/train_signal_model.py +87 -45
  96. celldetective/segmentation.py +844 -749
  97. celldetective/signals.py +3514 -2861
  98. celldetective/tracking.py +30 -15
  99. celldetective/utils/__init__.py +0 -0
  100. celldetective/utils/cellpose_utils/__init__.py +133 -0
  101. celldetective/utils/color_mappings.py +42 -0
  102. celldetective/utils/data_cleaning.py +630 -0
  103. celldetective/utils/data_loaders.py +450 -0
  104. celldetective/utils/dataset_helpers.py +207 -0
  105. celldetective/utils/downloaders.py +235 -0
  106. celldetective/utils/event_detection/__init__.py +8 -0
  107. celldetective/utils/experiment.py +1782 -0
  108. celldetective/utils/image_augmenters.py +308 -0
  109. celldetective/utils/image_cleaning.py +74 -0
  110. celldetective/utils/image_loaders.py +926 -0
  111. celldetective/utils/image_transforms.py +335 -0
  112. celldetective/utils/io.py +62 -0
  113. celldetective/utils/mask_cleaning.py +348 -0
  114. celldetective/utils/mask_transforms.py +5 -0
  115. celldetective/utils/masks.py +184 -0
  116. celldetective/utils/maths.py +351 -0
  117. celldetective/utils/model_getters.py +325 -0
  118. celldetective/utils/model_loaders.py +296 -0
  119. celldetective/utils/normalization.py +380 -0
  120. celldetective/utils/parsing.py +465 -0
  121. celldetective/utils/plots/__init__.py +0 -0
  122. celldetective/utils/plots/regression.py +53 -0
  123. celldetective/utils/resources.py +34 -0
  124. celldetective/utils/stardist_utils/__init__.py +104 -0
  125. celldetective/utils/stats.py +90 -0
  126. celldetective/utils/types.py +21 -0
  127. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/METADATA +1 -1
  128. celldetective-1.5.0b1.dist-info/RECORD +187 -0
  129. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/WHEEL +1 -1
  130. tests/gui/test_new_project.py +129 -117
  131. tests/gui/test_project.py +127 -79
  132. tests/test_filters.py +39 -15
  133. tests/test_notebooks.py +8 -0
  134. tests/test_tracking.py +232 -13
  135. tests/test_utils.py +123 -77
  136. celldetective/gui/base_components.py +0 -23
  137. celldetective/gui/layouts.py +0 -1602
  138. celldetective/gui/processes/compute_neighborhood.py +0 -594
  139. celldetective/gui/processes/downloader.py +0 -111
  140. celldetective/gui/processes/measure_cells.py +0 -360
  141. celldetective/gui/processes/segment_cells.py +0 -499
  142. celldetective/gui/processes/track_cells.py +0 -303
  143. celldetective/gui/processes/train_segmentation_model.py +0 -270
  144. celldetective/gui/processes/train_signal_model.py +0 -108
  145. celldetective/gui/table_ops/merge_groups.py +0 -118
  146. celldetective/gui/viewers.py +0 -1354
  147. celldetective/io.py +0 -3663
  148. celldetective/utils.py +0 -3108
  149. celldetective-1.4.2.dist-info/RECORD +0 -123
  150. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/entry_points.txt +0 -0
  151. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/licenses/LICENSE +0 -0
  152. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/top_level.txt +0 -0
@@ -1,15 +0,0 @@
1
- from .styles import Styles
2
- from .base_components import CelldetectiveWidget, CelldetectiveMainWindow
3
- from .json_readers import ConfigEditor
4
- from .tableUI import TableUI
5
- from .classifier_widget import ClassifierWidget
6
- from .survival_ui import ConfigSurvival
7
- from .plot_signals_ui import ConfigSignalPlot
8
- from .event_annotator import EventAnnotator
9
- from .pair_event_annotator import PairEventAnnotator
10
- from .thresholds_gui import ThresholdConfigWizard
11
- from .seg_model_loader import SegmentationModelLoader
12
- from .process_block import ProcessPanel, NeighPanel, PreprocessingPanel
13
- from .analyze_block import AnalysisPanel
14
- from .control_panel import ControlPanel
15
- from .configure_new_exp import ConfigNewExperiment
@@ -2,47 +2,52 @@ from PyQt5.QtWidgets import QVBoxLayout, QLabel
2
2
  from PyQt5.QtGui import QPixmap
3
3
  from PyQt5.QtCore import Qt
4
4
 
5
- from celldetective.gui import CelldetectiveWidget
6
- from celldetective.utils import get_software_location
7
- import os
8
- from celldetective.gui.gui_utils import center_window
5
+ from celldetective.gui.base.components import CelldetectiveWidget
6
+ from celldetective.gui.base.utils import center_window
9
7
  from celldetective._version import __version__
10
8
 
11
9
 
12
10
  class AboutWidget(CelldetectiveWidget):
13
-
14
- def __init__(self):
15
- super().__init__()
16
- self.setWindowTitle("About celldetective")
17
- self.setMaximumWidth(320)
18
- center_window(self)
19
-
20
- logo = QPixmap(self.celldetective_logo_path)
21
-
22
- # Create the layout
23
- layout = QVBoxLayout(self)
24
- img_label = QLabel('')
25
- img_label.setPixmap(logo)
26
- layout.addWidget(img_label, alignment=Qt.AlignCenter)
27
-
28
- self.soft_name = QLabel('celldetective')
29
- self.soft_name.setStyleSheet("""font-weight: bold;
11
+
12
+ def __init__(self):
13
+ super().__init__()
14
+ self.setWindowTitle("About celldetective")
15
+ self.setMaximumWidth(320)
16
+ center_window(self)
17
+
18
+ logo = QPixmap(self.celldetective_logo_path)
19
+
20
+ # Create the layout
21
+ layout = QVBoxLayout(self)
22
+ img_label = QLabel("")
23
+ img_label.setPixmap(logo)
24
+ layout.addWidget(img_label, alignment=Qt.AlignCenter)
25
+
26
+ self.soft_name = QLabel("celldetective")
27
+ self.soft_name.setStyleSheet(
28
+ """font-weight: bold;
30
29
  font-size: 18px;
31
- """)
32
- layout.addWidget(self.soft_name, alignment=Qt.AlignCenter)
33
-
34
- self.version_lbl = QLabel(f"Version {__version__} <a href=\"https://github.com/celldetective/celldetective"
35
- f"/releases\">(release notes)</a>")
36
- self.version_lbl.setOpenExternalLinks(True)
37
- layout.addWidget(self.version_lbl, alignment=Qt.AlignCenter)
38
-
39
- self.lab_lbl = QLabel("Developed at Laboratoire Adhésion et Inflammation (LAI) INSERM U1067 CNRS UMR 7333")
40
- self.lab_lbl.setWordWrap(True)
41
- layout.addWidget(self.lab_lbl, alignment=Qt.AlignCenter)
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')")
47
- self.centuri_mention.setWordWrap(True)
48
- layout.addWidget(self.centuri_mention, alignment=Qt.AlignCenter)
30
+ """
31
+ )
32
+ layout.addWidget(self.soft_name, alignment=Qt.AlignCenter)
33
+
34
+ self.version_lbl = QLabel(
35
+ f'Version {__version__} <a href="https://github.com/celldetective/celldetective'
36
+ f'/releases">(release notes)</a>'
37
+ )
38
+ self.version_lbl.setOpenExternalLinks(True)
39
+ layout.addWidget(self.version_lbl, alignment=Qt.AlignCenter)
40
+
41
+ self.lab_lbl = QLabel(
42
+ "Developed at Laboratoire Adhésion et Inflammation (LAI) INSERM U1067 CNRS UMR 7333"
43
+ )
44
+ self.lab_lbl.setWordWrap(True)
45
+ layout.addWidget(self.lab_lbl, alignment=Qt.AlignCenter)
46
+
47
+ self.centuri_mention = QLabel(
48
+ "The project leading to this publication has received funding from France 2030, the French Government "
49
+ "program managed by the French National Research Agency (ANR-16-CONV-0001) and from Excellence Initiative "
50
+ "of Aix-Marseille University - AMIDEX')"
51
+ )
52
+ self.centuri_mention.setWordWrap(True)
53
+ layout.addWidget(self.centuri_mention, alignment=Qt.AlignCenter)
@@ -1,96 +1,132 @@
1
- from PyQt5.QtWidgets import QFrame, QLabel, QPushButton, QVBoxLayout, \
2
- QSpacerItem, QSizePolicy
1
+ from PyQt5.QtWidgets import (
2
+ QFrame,
3
+ QLabel,
4
+ QPushButton,
5
+ QVBoxLayout,
6
+ QSpacerItem,
7
+ QSizePolicy,
8
+ )
3
9
  from PyQt5.QtCore import Qt, QSize
4
10
  from PyQt5.QtGui import QIcon
5
- from celldetective.gui import ConfigSurvival, ConfigSignalPlot
6
11
  import os
7
- from celldetective.gui import Styles
12
+ from celldetective.gui.base.styles import Styles
8
13
  from glob import glob
9
14
 
10
- from celldetective.gui.gui_utils import generic_message
15
+ from celldetective.gui.base.components import generic_message
16
+ from celldetective.gui.base.utils import center_window
11
17
 
12
18
 
13
19
  class AnalysisPanel(QFrame, Styles):
14
- def __init__(self, parent_window, title=None):
15
-
16
- super().__init__()
17
- self.parent_window = parent_window
18
- self.title = title
19
- if self.title is None:
20
- self.title = ''
21
- self.exp_channels = self.parent_window.exp_channels
22
- self.exp_dir = self.parent_window.exp_dir
23
- self.soft_path = self.parent_window.parent_window.soft_path
24
- self.pop_exists = False
25
-
26
- self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
27
- self.grid = QVBoxLayout(self)
28
- self.grid.setSpacing(20)
29
- self.generate_header()
30
-
31
- def generate_header(self):
32
-
33
- """
34
- Read the mode and prepare a collapsable block to process a specific cell population.
35
-
36
- """
37
-
38
- panel_title = QLabel("Survival")
39
- panel_title.setStyleSheet("""
20
+ def __init__(self, parent_window, title=None):
21
+
22
+ super().__init__()
23
+ self.parent_window = parent_window
24
+ self.title = title
25
+ if self.title is None:
26
+ self.title = ""
27
+ self.exp_channels = self.parent_window.exp_channels
28
+ self.exp_dir = self.parent_window.exp_dir
29
+ self.soft_path = self.parent_window.parent_window.soft_path
30
+ self.pop_exists = False
31
+
32
+ self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
33
+ self.grid = QVBoxLayout(self)
34
+ self.grid.setSpacing(20)
35
+ self.generate_header()
36
+
37
+ def generate_header(self):
38
+ """
39
+ Read the mode and prepare a collapsable block to process a specific cell population.
40
+
41
+ """
42
+
43
+ panel_title = QLabel("Survival")
44
+ panel_title.setStyleSheet(
45
+ """
40
46
  font-weight: bold;
41
47
  padding: 0px;
42
- """)
43
-
44
- self.grid.addWidget(panel_title, alignment=Qt.AlignCenter)
45
-
46
- self.survival_btn = QPushButton("plot survival")
47
- self.survival_btn.setIcon(QIcon(QIcon(os.sep.join([self.soft_path, 'celldetective', 'icons', 'survival2.png']))))
48
- self.survival_btn.setStyleSheet(self.button_style_sheet_2)
49
- self.survival_btn.setIconSize(QSize(35, 35))
50
- self.survival_btn.clicked.connect(self.configure_survival)
51
- self.grid.addWidget(self.survival_btn)
52
-
53
- signal_lbl = QLabel("Single-cell signals")
54
- signal_lbl.setStyleSheet("""
48
+ """
49
+ )
50
+
51
+ self.grid.addWidget(panel_title, alignment=Qt.AlignCenter)
52
+
53
+ self.survival_btn = QPushButton("plot survival")
54
+ self.survival_btn.setIcon(
55
+ QIcon(
56
+ QIcon(
57
+ os.sep.join(
58
+ [self.soft_path, "celldetective", "icons", "survival2.png"]
59
+ )
60
+ )
61
+ )
62
+ )
63
+ self.survival_btn.setStyleSheet(self.button_style_sheet_2)
64
+ self.survival_btn.setIconSize(QSize(35, 35))
65
+ self.survival_btn.clicked.connect(self.configure_survival)
66
+ self.grid.addWidget(self.survival_btn)
67
+
68
+ signal_lbl = QLabel("Single-cell signals")
69
+ signal_lbl.setStyleSheet(
70
+ """
55
71
  font-weight: bold;
56
72
  padding: 0px;
57
- """)
58
-
59
- self.grid.addWidget(signal_lbl, alignment=Qt.AlignCenter)
60
-
61
- self.plot_signal_btn = QPushButton("plot signals")
62
- self.plot_signal_btn.setIcon(QIcon(QIcon(os.sep.join([self.soft_path, 'celldetective', 'icons', 'signals_icon.png']))))
63
- self.plot_signal_btn.setStyleSheet(self.button_style_sheet_2)
64
- self.plot_signal_btn.setIconSize(QSize(35, 35))
65
- self.plot_signal_btn.clicked.connect(self.configure_plot_signals)
66
- self.grid.addWidget(self.plot_signal_btn)
67
-
68
- vertical_spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
69
- self.grid.addItem(vertical_spacer)
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
-
78
- def configure_survival(self):
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
87
-
88
- def configure_plot_signals(self):
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
73
+ """
74
+ )
75
+
76
+ self.grid.addWidget(signal_lbl, alignment=Qt.AlignCenter)
77
+
78
+ self.plot_signal_btn = QPushButton("plot signals")
79
+ self.plot_signal_btn.setIcon(
80
+ QIcon(
81
+ QIcon(
82
+ os.sep.join(
83
+ [self.soft_path, "celldetective", "icons", "signals_icon.png"]
84
+ )
85
+ )
86
+ )
87
+ )
88
+ self.plot_signal_btn.setStyleSheet(self.button_style_sheet_2)
89
+ self.plot_signal_btn.setIconSize(QSize(35, 35))
90
+ self.plot_signal_btn.clicked.connect(self.configure_plot_signals)
91
+ self.grid.addWidget(self.plot_signal_btn)
92
+
93
+ vertical_spacer = QSpacerItem(
94
+ 20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding
95
+ )
96
+ self.grid.addItem(vertical_spacer)
97
+
98
+ def check_for_tables(self):
99
+
100
+ for population in self.parent_window.populations:
101
+ tables = glob(
102
+ self.exp_dir
103
+ + os.sep.join(
104
+ ["W*", "*", "output", "tables", f"trajectories_{population}.csv"]
105
+ )
106
+ )
107
+ if len(tables) > 0:
108
+ self.pop_exists = True
109
+
110
+ def configure_survival(self):
111
+ from celldetective.gui.survival_ui import ConfigSurvival
112
+
113
+ self.check_for_tables()
114
+ if self.pop_exists:
115
+ self.config_survival = ConfigSurvival(self)
116
+ self.config_survival.show()
117
+ center_window(self.config_survival)
118
+ else:
119
+ generic_message("No population table could be found... Abort...")
120
+ return None
121
+
122
+ def configure_plot_signals(self):
123
+ from celldetective.gui.plot_signals_ui import ConfigSignalPlot
124
+
125
+ self.check_for_tables()
126
+ if self.pop_exists:
127
+ self.config_signal_plot = ConfigSignalPlot(self)
128
+ self.config_signal_plot.show()
129
+ center_window(self.config_signal_plot)
130
+ else:
131
+ generic_message("No population table could be found... Abort...")
132
+ return None
File without changes
@@ -0,0 +1,335 @@
1
+ import os
2
+ from functools import partial
3
+ from glob import glob
4
+
5
+ import numpy as np
6
+ from PyQt5.QtCore import QSize, Qt
7
+ from PyQt5.QtWidgets import QVBoxLayout, QLabel, QPushButton, QLineEdit, QHBoxLayout
8
+ from fonticon_mdi6 import MDI6
9
+ from superqt import QSearchableComboBox
10
+ from superqt.fonticon import icon
11
+
12
+ from celldetective.gui.base.styles import Styles
13
+
14
+
15
+ class ChannelNormGenerator(QVBoxLayout, Styles):
16
+ """Generator for list of channels"""
17
+
18
+ def __init__(self, parent_window=None, init_n_channels=4, mode="signals", *args):
19
+ super().__init__(*args)
20
+
21
+ self.parent_window = parent_window
22
+ self.mode = mode
23
+ self.init_n_channels = init_n_channels
24
+
25
+ if hasattr(self.parent_window.parent_window, "locate_image"):
26
+ self.attr_parent = self.parent_window.parent_window
27
+ elif hasattr(self.parent_window.parent_window.parent_window, "locate_image"):
28
+ self.attr_parent = self.parent_window.parent_window.parent_window
29
+ else:
30
+ self.attr_parent = (
31
+ self.parent_window.parent_window.parent_window.parent_window
32
+ )
33
+
34
+ self.channel_names = self.attr_parent.exp_channels
35
+ self.setContentsMargins(15, 15, 15, 15)
36
+ self.generate_widgets()
37
+ self.add_to_layout()
38
+
39
+ def generate_widgets(self):
40
+
41
+ self.channel_cbs = [QSearchableComboBox() for i in range(self.init_n_channels)]
42
+ self.channel_labels = [QLabel() for i in range(self.init_n_channels)]
43
+
44
+ self.normalization_mode_btns = [
45
+ QPushButton("") for i in range(self.init_n_channels)
46
+ ]
47
+ self.normalization_mode = [True for i in range(self.init_n_channels)]
48
+ self.normalization_clip_btns = [
49
+ QPushButton("") for i in range(self.init_n_channels)
50
+ ]
51
+ self.clip_option = [False for i in range(self.init_n_channels)]
52
+
53
+ for i in range(self.init_n_channels):
54
+ self.normalization_mode_btns[i].setIcon(
55
+ icon(MDI6.percent_circle, color="#1565c0")
56
+ )
57
+ self.normalization_mode_btns[i].setIconSize(QSize(20, 20))
58
+ self.normalization_mode_btns[i].setStyleSheet(self.button_select_all)
59
+ self.normalization_mode_btns[i].setToolTip(
60
+ "Switch to absolute normalization values."
61
+ )
62
+ self.normalization_mode_btns[i].clicked.connect(
63
+ partial(self.switch_normalization_mode, i)
64
+ )
65
+
66
+ self.normalization_clip_btns[i].setIcon(
67
+ icon(MDI6.content_cut, color="black")
68
+ )
69
+ self.normalization_clip_btns[i].setIconSize(QSize(20, 20))
70
+ self.normalization_clip_btns[i].setStyleSheet(self.button_select_all)
71
+ self.normalization_clip_btns[i].clicked.connect(
72
+ partial(self.switch_clipping_mode, i)
73
+ )
74
+ self.normalization_clip_btns[i].setToolTip("clip")
75
+
76
+ self.normalization_min_value_lbl = [
77
+ QLabel("Min %: ") for i in range(self.init_n_channels)
78
+ ]
79
+ self.normalization_min_value_le = [
80
+ QLineEdit("0.1") for i in range(self.init_n_channels)
81
+ ]
82
+ self.normalization_max_value_lbl = [
83
+ QLabel("Max %: ") for i in range(self.init_n_channels)
84
+ ]
85
+ self.normalization_max_value_le = [
86
+ QLineEdit("99.99") for i in range(self.init_n_channels)
87
+ ]
88
+
89
+ if self.mode == "signals":
90
+ tables = glob(
91
+ self.parent_window.exp_dir
92
+ + os.sep.join(
93
+ [
94
+ "W*",
95
+ "*",
96
+ "output",
97
+ "tables",
98
+ f"trajectories_{self.parent_window.mode}.csv",
99
+ ]
100
+ )
101
+ )
102
+ all_measurements = []
103
+ for tab in tables:
104
+ import pandas as pd
105
+
106
+ cols = pd.read_csv(tab, nrows=1).columns.tolist()
107
+ all_measurements.extend(cols)
108
+ all_measurements = np.unique(all_measurements)
109
+
110
+ if self.mode == "signals":
111
+ generic_measurements = [
112
+ "brightfield_channel",
113
+ "live_nuclei_channel",
114
+ "dead_nuclei_channel",
115
+ "effector_fluo_channel",
116
+ "adhesion_channel",
117
+ "fluo_channel_1",
118
+ "fluo_channel_2",
119
+ "area",
120
+ "area_bbox",
121
+ "area_convex",
122
+ "area_filled",
123
+ "major_axis_length",
124
+ "minor_axis_length",
125
+ "eccentricity",
126
+ "equivalent_diameter_area",
127
+ "euler_number",
128
+ "extent",
129
+ "feret_diameter_max",
130
+ "orientation",
131
+ "perimeter",
132
+ "perimeter_crofton",
133
+ "solidity",
134
+ "angular_second_moment",
135
+ "contrast",
136
+ "correlation",
137
+ "sum_of_square_variance",
138
+ "inverse_difference_moment",
139
+ "sum_average",
140
+ "sum_variance",
141
+ "sum_entropy",
142
+ "entropy",
143
+ "difference_variance",
144
+ "difference_entropy",
145
+ "information_measure_of_correlation_1",
146
+ "information_measure_of_correlation_2",
147
+ "maximal_correlation_coefficient",
148
+ "POSITION_X",
149
+ "POSITION_Y",
150
+ ]
151
+ elif self.mode == "channels":
152
+ generic_measurements = [
153
+ "brightfield_channel",
154
+ "live_nuclei_channel",
155
+ "dead_nuclei_channel",
156
+ "effector_fluo_channel",
157
+ "adhesion_channel",
158
+ "fluo_channel_1",
159
+ "fluo_channel_2",
160
+ "None",
161
+ ]
162
+
163
+ if self.mode == "channels":
164
+ all_measurements = []
165
+ exp_ch = self.attr_parent.exp_channels
166
+ for c in exp_ch:
167
+ all_measurements.append(c)
168
+
169
+ self.channel_items = np.unique(generic_measurements + list(all_measurements))
170
+ self.channel_items = np.insert(self.channel_items, 0, "--")
171
+
172
+ self.add_col_btn = QPushButton("Add channel")
173
+ self.add_col_btn.clicked.connect(self.add_channel)
174
+ self.add_col_btn.setStyleSheet(self.button_add)
175
+ self.add_col_btn.setIcon(icon(MDI6.plus, color="black"))
176
+
177
+ def add_channel(self):
178
+
179
+ self.channel_cbs.append(QSearchableComboBox())
180
+ self.channel_labels.append(QLabel())
181
+ self.channel_cbs[-1].addItems(self.channel_items)
182
+ self.channel_cbs[-1].currentIndexChanged.connect(self.check_valid_channels)
183
+ self.channel_labels[-1].setText(f"channel {len(self.channel_cbs)-1}: ")
184
+
185
+ self.normalization_mode_btns.append(QPushButton(""))
186
+ self.normalization_mode.append(True)
187
+ self.normalization_clip_btns.append(QPushButton(""))
188
+ self.clip_option.append(False)
189
+
190
+ self.normalization_mode_btns[-1].setIcon(
191
+ icon(MDI6.percent_circle, color="#1565c0")
192
+ )
193
+ self.normalization_mode_btns[-1].setIconSize(QSize(20, 20))
194
+ self.normalization_mode_btns[-1].setStyleSheet(self.button_select_all)
195
+ self.normalization_mode_btns[-1].setToolTip(
196
+ "Switch to absolute normalization values."
197
+ )
198
+ self.normalization_mode_btns[-1].clicked.connect(
199
+ partial(self.switch_normalization_mode, len(self.channel_cbs) - 1)
200
+ )
201
+
202
+ self.normalization_clip_btns[-1].setIcon(icon(MDI6.content_cut, color="black"))
203
+ self.normalization_clip_btns[-1].setIconSize(QSize(20, 20))
204
+ self.normalization_clip_btns[-1].setStyleSheet(self.button_select_all)
205
+ self.normalization_clip_btns[-1].clicked.connect(
206
+ partial(self.switch_clipping_mode, len(self.channel_cbs) - 1)
207
+ )
208
+ self.normalization_clip_btns[-1].setToolTip("clip")
209
+
210
+ self.normalization_min_value_lbl.append(QLabel("Min %: "))
211
+ self.normalization_min_value_le.append(QLineEdit("0.1"))
212
+ self.normalization_max_value_lbl.append(QLabel("Max %: "))
213
+ self.normalization_max_value_le.append(QLineEdit("99.99"))
214
+
215
+ ch_layout = QHBoxLayout()
216
+ ch_layout.addWidget(self.channel_labels[-1], 30)
217
+ ch_layout.addWidget(self.channel_cbs[-1], 70)
218
+ self.channels_vb.addLayout(ch_layout)
219
+
220
+ channel_norm_options_layout = QHBoxLayout()
221
+ channel_norm_options_layout.addWidget(QLabel(""), 30)
222
+ ch_norm_sublayout = QHBoxLayout()
223
+ ch_norm_sublayout.addWidget(self.normalization_min_value_lbl[-1])
224
+ ch_norm_sublayout.addWidget(self.normalization_min_value_le[-1])
225
+ ch_norm_sublayout.addWidget(self.normalization_max_value_lbl[-1])
226
+ ch_norm_sublayout.addWidget(self.normalization_max_value_le[-1])
227
+ ch_norm_sublayout.addWidget(self.normalization_clip_btns[-1])
228
+ ch_norm_sublayout.addWidget(self.normalization_mode_btns[-1])
229
+ channel_norm_options_layout.addLayout(ch_norm_sublayout, 70)
230
+
231
+ self.channels_vb.addLayout(channel_norm_options_layout)
232
+
233
+ def add_to_layout(self):
234
+
235
+ self.channels_vb = QVBoxLayout()
236
+ self.channel_option_layouts = []
237
+ for i in range(len(self.channel_cbs)):
238
+
239
+ ch_layout = QHBoxLayout()
240
+ self.channel_labels[i].setText(f"channel {i}: ")
241
+ ch_layout.addWidget(self.channel_labels[i], 30)
242
+ self.channel_cbs[i].addItems(self.channel_items)
243
+ self.channel_cbs[i].currentIndexChanged.connect(self.check_valid_channels)
244
+ ch_layout.addWidget(self.channel_cbs[i], 70)
245
+ self.channels_vb.addLayout(ch_layout)
246
+
247
+ channel_norm_options_layout = QHBoxLayout()
248
+ # channel_norm_options_layout.setContentsMargins(130,0,0,0)
249
+ channel_norm_options_layout.addWidget(QLabel(""), 30)
250
+ ch_norm_sublayout = QHBoxLayout()
251
+ ch_norm_sublayout.addWidget(self.normalization_min_value_lbl[i])
252
+ ch_norm_sublayout.addWidget(self.normalization_min_value_le[i])
253
+ ch_norm_sublayout.addWidget(self.normalization_max_value_lbl[i])
254
+ ch_norm_sublayout.addWidget(self.normalization_max_value_le[i])
255
+ ch_norm_sublayout.addWidget(self.normalization_clip_btns[i])
256
+ ch_norm_sublayout.addWidget(self.normalization_mode_btns[i])
257
+ channel_norm_options_layout.addLayout(ch_norm_sublayout, 70)
258
+ self.channels_vb.addLayout(channel_norm_options_layout)
259
+
260
+ self.addLayout(self.channels_vb)
261
+
262
+ add_hbox = QHBoxLayout()
263
+ add_hbox.addWidget(QLabel(""), 66)
264
+ add_hbox.addWidget(self.add_col_btn, 33, alignment=Qt.AlignRight)
265
+ self.addLayout(add_hbox)
266
+
267
+ def switch_normalization_mode(self, index):
268
+ """
269
+ Use absolute or percentile values for the normalization of each individual channel.
270
+
271
+ """
272
+
273
+ currentNormMode = self.normalization_mode[index]
274
+ self.normalization_mode[index] = not currentNormMode
275
+
276
+ if self.normalization_mode[index]:
277
+ self.normalization_mode_btns[index].setIcon(
278
+ icon(MDI6.percent_circle, color="#1565c0")
279
+ )
280
+ self.normalization_mode_btns[index].setIconSize(QSize(20, 20))
281
+ self.normalization_mode_btns[index].setStyleSheet(self.button_select_all)
282
+ self.normalization_mode_btns[index].setToolTip(
283
+ "Switch to absolute normalization values."
284
+ )
285
+ self.normalization_min_value_lbl[index].setText("Min %: ")
286
+ self.normalization_max_value_lbl[index].setText("Max %: ")
287
+ self.normalization_min_value_le[index].setText("0.1")
288
+ self.normalization_max_value_le[index].setText("99.99")
289
+
290
+ else:
291
+ self.normalization_mode_btns[index].setIcon(
292
+ icon(MDI6.percent_circle_outline, color="black")
293
+ )
294
+ self.normalization_mode_btns[index].setIconSize(QSize(20, 20))
295
+ self.normalization_mode_btns[index].setStyleSheet(self.button_select_all)
296
+ self.normalization_mode_btns[index].setToolTip(
297
+ "Switch to percentile normalization values."
298
+ )
299
+ self.normalization_min_value_lbl[index].setText("Min: ")
300
+ self.normalization_min_value_le[index].setText("0")
301
+ self.normalization_max_value_lbl[index].setText("Max: ")
302
+ self.normalization_max_value_le[index].setText("1000")
303
+
304
+ def switch_clipping_mode(self, index):
305
+
306
+ currentClipMode = self.clip_option[index]
307
+ self.clip_option[index] = not currentClipMode
308
+
309
+ if self.clip_option[index]:
310
+ self.normalization_clip_btns[index].setIcon(
311
+ icon(MDI6.content_cut, color="#1565c0")
312
+ )
313
+ self.normalization_clip_btns[index].setIconSize(QSize(20, 20))
314
+ self.normalization_clip_btns[index].setStyleSheet(self.button_select_all)
315
+
316
+ else:
317
+ self.normalization_clip_btns[index].setIcon(
318
+ icon(MDI6.content_cut, color="black")
319
+ )
320
+ self.normalization_clip_btns[index].setIconSize(QSize(20, 20))
321
+ self.normalization_clip_btns[index].setStyleSheet(self.button_select_all)
322
+
323
+ def check_valid_channels(self):
324
+
325
+ if hasattr(self.parent_window, "submit_btn"):
326
+ if np.all([cb.currentText() == "--" for cb in self.channel_cbs]):
327
+ self.parent_window.submit_btn.setEnabled(False)
328
+
329
+ if hasattr(self.parent_window, "spatial_calib_le") and hasattr(
330
+ self.parent_window, "submit_btn"
331
+ ):
332
+ if self.parent_window.spatial_calib_le.text() != "--":
333
+ self.parent_window.submit_btn.setEnabled(True)
334
+ elif hasattr(self.parent_window, "submit_btn"):
335
+ self.parent_window.submit_btn.setEnabled(True)