celldetective 1.4.2__py3-none-any.whl → 1.5.0b0__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 (151) 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 +304 -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/measure_cells.py +565 -0
  81. celldetective/processes/segment_cells.py +760 -0
  82. celldetective/processes/track_cells.py +435 -0
  83. celldetective/processes/train_segmentation_model.py +694 -0
  84. celldetective/processes/train_signal_model.py +265 -0
  85. celldetective/processes/unified_process.py +292 -0
  86. celldetective/regionprops/_regionprops.py +358 -317
  87. celldetective/relative_measurements.py +987 -710
  88. celldetective/scripts/measure_cells.py +313 -212
  89. celldetective/scripts/measure_relative.py +90 -46
  90. celldetective/scripts/segment_cells.py +165 -104
  91. celldetective/scripts/segment_cells_thresholds.py +96 -68
  92. celldetective/scripts/track_cells.py +198 -149
  93. celldetective/scripts/train_segmentation_model.py +324 -201
  94. celldetective/scripts/train_signal_model.py +87 -45
  95. celldetective/segmentation.py +844 -749
  96. celldetective/signals.py +3514 -2861
  97. celldetective/tracking.py +30 -15
  98. celldetective/utils/__init__.py +0 -0
  99. celldetective/utils/cellpose_utils/__init__.py +133 -0
  100. celldetective/utils/color_mappings.py +42 -0
  101. celldetective/utils/data_cleaning.py +630 -0
  102. celldetective/utils/data_loaders.py +450 -0
  103. celldetective/utils/dataset_helpers.py +207 -0
  104. celldetective/utils/downloaders.py +197 -0
  105. celldetective/utils/event_detection/__init__.py +8 -0
  106. celldetective/utils/experiment.py +1782 -0
  107. celldetective/utils/image_augmenters.py +308 -0
  108. celldetective/utils/image_cleaning.py +74 -0
  109. celldetective/utils/image_loaders.py +926 -0
  110. celldetective/utils/image_transforms.py +335 -0
  111. celldetective/utils/io.py +62 -0
  112. celldetective/utils/mask_cleaning.py +348 -0
  113. celldetective/utils/mask_transforms.py +5 -0
  114. celldetective/utils/masks.py +184 -0
  115. celldetective/utils/maths.py +351 -0
  116. celldetective/utils/model_getters.py +325 -0
  117. celldetective/utils/model_loaders.py +296 -0
  118. celldetective/utils/normalization.py +380 -0
  119. celldetective/utils/parsing.py +465 -0
  120. celldetective/utils/plots/__init__.py +0 -0
  121. celldetective/utils/plots/regression.py +53 -0
  122. celldetective/utils/resources.py +34 -0
  123. celldetective/utils/stardist_utils/__init__.py +104 -0
  124. celldetective/utils/stats.py +90 -0
  125. celldetective/utils/types.py +21 -0
  126. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/METADATA +1 -1
  127. celldetective-1.5.0b0.dist-info/RECORD +187 -0
  128. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/WHEEL +1 -1
  129. tests/gui/test_new_project.py +129 -117
  130. tests/gui/test_project.py +127 -79
  131. tests/test_filters.py +39 -15
  132. tests/test_notebooks.py +8 -0
  133. tests/test_tracking.py +232 -13
  134. tests/test_utils.py +123 -77
  135. celldetective/gui/base_components.py +0 -23
  136. celldetective/gui/layouts.py +0 -1602
  137. celldetective/gui/processes/compute_neighborhood.py +0 -594
  138. celldetective/gui/processes/measure_cells.py +0 -360
  139. celldetective/gui/processes/segment_cells.py +0 -499
  140. celldetective/gui/processes/track_cells.py +0 -303
  141. celldetective/gui/processes/train_segmentation_model.py +0 -270
  142. celldetective/gui/processes/train_signal_model.py +0 -108
  143. celldetective/gui/table_ops/merge_groups.py +0 -118
  144. celldetective/gui/viewers.py +0 -1354
  145. celldetective/io.py +0 -3663
  146. celldetective/utils.py +0 -3108
  147. celldetective-1.4.2.dist-info/RECORD +0 -123
  148. /celldetective/{gui/processes → processes}/downloader.py +0 -0
  149. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/entry_points.txt +0 -0
  150. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/licenses/LICENSE +0 -0
  151. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/top_level.txt +0 -0
@@ -1,12 +1,48 @@
1
- from PyQt5.QtWidgets import QPushButton, QHBoxLayout, QLabel, QGridLayout, QFrame, \
2
- QTabWidget, QVBoxLayout, QScrollArea, QDesktopWidget
3
- from celldetective.gui import CelldetectiveMainWindow, CelldetectiveWidget
4
-
5
- from PyQt5.QtCore import Qt, QSize
6
- from celldetective.gui.gui_utils import center_window, QHSeperationLine, QCheckableComboBox, generic_message
7
- from celldetective.utils import _extract_labels_from_config, config_section_to_dict, extract_identity_col
8
- from celldetective.gui import ConfigEditor, ProcessPanel, PreprocessingPanel, AnalysisPanel, NeighPanel
9
- from celldetective.io import extract_position_name, get_experiment_wells, get_config, get_spatial_calibration, get_temporal_calibration, get_experiment_concentrations, get_experiment_cell_types, get_experiment_antibodies, get_experiment_pharmaceutical_agents, get_experiment_populations, extract_well_name_and_number
1
+ import time
2
+
3
+ from PyQt5.QtWidgets import (
4
+ QPushButton,
5
+ QHBoxLayout,
6
+ QLabel,
7
+ QGridLayout,
8
+ QFrame,
9
+ QTabWidget,
10
+ QVBoxLayout,
11
+ QScrollArea,
12
+ QDesktopWidget,
13
+ )
14
+ from celldetective.gui.base.components import (
15
+ CelldetectiveMainWindow,
16
+ CelldetectiveWidget,
17
+ QCheckableComboBox,
18
+ QHSeperationLine,
19
+ )
20
+
21
+ from PyQt5.QtCore import Qt, QSize, QThread
22
+ from celldetective.gui.base.components import generic_message
23
+ from celldetective.utils.parsing import (
24
+ config_section_to_dict,
25
+ _extract_labels_from_config,
26
+ )
27
+ from celldetective.gui.process_block import ProcessPanel
28
+ from celldetective.gui.preprocessing_block import PreprocessingPanel
29
+ from celldetective.gui.interactions_block import NeighPanel
30
+ from celldetective.gui.analyze_block import AnalysisPanel
31
+
32
+ from celldetective.utils.experiment import (
33
+ get_experiment_wells,
34
+ extract_well_name_and_number,
35
+ extract_position_name,
36
+ extract_experiment_channels,
37
+ get_spatial_calibration,
38
+ get_temporal_calibration,
39
+ get_experiment_concentrations,
40
+ get_experiment_cell_types,
41
+ get_experiment_antibodies,
42
+ get_experiment_pharmaceutical_agents,
43
+ get_experiment_populations,
44
+ get_config,
45
+ )
10
46
  from natsort import natsorted
11
47
  from glob import glob
12
48
  import os
@@ -15,525 +51,600 @@ from superqt.fonticon import icon
15
51
  from fonticon_mdi6 import MDI6
16
52
  import gc
17
53
  import subprocess
18
- from celldetective.gui.viewers import StackVisualizer
19
- from celldetective.utils import extract_experiment_channels
20
- import pandas as pd
54
+
55
+ import logging
56
+
57
+ logger = logging.getLogger(__name__)
58
+
59
+
60
+ class BackgroundLoader(QThread):
61
+ def run(self):
62
+ logger.info("Loading background packages...")
63
+ try:
64
+ from celldetective.gui.viewers.base_viewer import StackVisualizer
65
+ self.StackVisualizer = StackVisualizer
66
+ except Exception:
67
+ logger.error("Background packages not loaded...")
68
+ logger.info("Background packages loaded...")
21
69
 
22
70
 
23
71
  class ControlPanel(CelldetectiveMainWindow):
24
72
 
25
- def __init__(self, parent_window=None, exp_dir=""):
26
-
27
- super().__init__()
28
-
29
- self.exp_dir = exp_dir
30
- if not self.exp_dir.endswith(os.sep):
31
- self.exp_dir = self.exp_dir+os.sep
32
- self.setWindowTitle("celldetective")
33
- self.parent_window = parent_window
34
-
35
- self.init_wells_and_positions()
36
- self.load_configuration()
37
-
38
- self.w = CelldetectiveWidget()
39
- self.grid = QGridLayout(self.w)
40
- self.grid.setSpacing(5)
41
- self.grid.setContentsMargins(10, 10, 10, 10) # left top right bottom
42
-
43
- self.to_disable = []
44
- self.generate_header()
45
- self.ProcessPopulations = [ProcessPanel(self, pop) for pop in self.populations]
46
-
47
- self.NeighPanel = NeighPanel(self)
48
- self.PreprocessingPanel = PreprocessingPanel(self)
49
-
50
- ProcessFrame = QFrame()
51
- grid_process = QVBoxLayout(ProcessFrame)
52
- grid_process.setContentsMargins(15,30,15,15)
53
-
54
- AnalyzeFrame = QFrame()
55
- grid_analyze = QVBoxLayout(AnalyzeFrame)
56
- grid_analyze.setContentsMargins(15,30,15,15)
57
- self.SurvivalBlock = AnalysisPanel(self,title='Survival')
58
-
59
- grid_process.addWidget(self.PreprocessingPanel)
60
- for panel in self.ProcessPopulations:
61
- grid_process.addWidget(panel)
62
- grid_process.addWidget(self.NeighPanel)
63
-
64
- grid_analyze.addWidget(self.SurvivalBlock)
65
-
66
- self.scroll=QScrollArea()
67
- self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
68
- self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
69
- self.scroll.setWidgetResizable(True)
70
- desktop = QDesktopWidget()
71
- self.scroll.setMinimumHeight(550)
72
- #self.scroll.setMinimumHeight(int(0.4*screen_height))
73
-
74
- tabWidget = QTabWidget()
75
- tab_index_process = tabWidget.addTab(ProcessFrame, "Process")
76
- tabWidget.setTabIcon(tab_index_process, icon(MDI6.cog_outline, color='black'))
77
-
78
- tab_index_analyze = tabWidget.addTab(AnalyzeFrame, "Analyze")
79
- tabWidget.setTabIcon(tab_index_analyze, icon(MDI6.poll, color='black'))
80
- tabWidget.setStyleSheet(self.qtab_style)
81
-
82
- self.grid.addWidget(tabWidget, 7,0,1,3, alignment=Qt.AlignTop)
83
- self.grid.setSpacing(5)
84
- self.scroll.setWidget(self.w)
85
- self.setCentralWidget(self.scroll)
86
- self.create_config_dir()
87
- self.update_position_options()
88
- #self.setMinimumHeight(int(self.sizeHint().height()))
89
-
90
- self.initial_height = self.size().height()
91
- self.initial_width = self.size().width()
92
- self.screen_height = desktop.screenGeometry().height()
93
- self.screen_width = desktop.screenGeometry().width()
94
- self.scroll.setMinimumWidth(440)
95
-
96
- center_window(self)
97
-
98
-
99
- self.well_list.setCurrentIndex(0)
100
- #self.position_list.setCurrentIndex(0)
101
-
102
- def init_wells_and_positions(self):
103
-
104
- """
105
- Detect the wells in the experiment folder and the associated positions.
106
- """
107
-
108
- self.wells = get_experiment_wells(self.exp_dir) #natsorted(glob(self.exp_dir + "W*" + os.sep))
109
- self.positions = []
110
- for w in self.wells:
111
- well_name, well_nbr = extract_well_name_and_number(w)
112
- positions_path = natsorted(glob(os.sep.join([w,f"{well_nbr}*", os.sep])))
113
- self.positions.append([extract_position_name(pos) for pos in positions_path])
114
-
115
- def generate_header(self):
116
-
117
- """
118
- Show the experiment name, create two QComboBox for respectively the
119
- biological condition (well) and position of interest, access the experiment config.
120
- """
121
-
122
- condition_label = QLabel("condition: ")
123
- position_label = QLabel("position: ")
124
-
125
- name = self.exp_dir.split(os.sep)[-2]
126
- experiment_label = QLabel(f"Experiment:")
127
- experiment_label.setStyleSheet("""
73
+ def __init__(self, parent_window=None, exp_dir=""):
74
+
75
+ super().__init__()
76
+
77
+ self.exp_dir = exp_dir
78
+ if not self.exp_dir.endswith(os.sep):
79
+ self.exp_dir = self.exp_dir + os.sep
80
+ self.setWindowTitle("celldetective")
81
+ self.parent_window = parent_window
82
+
83
+ self.init_wells_and_positions()
84
+ self.load_configuration()
85
+
86
+ self.w = CelldetectiveWidget()
87
+ self.grid = QGridLayout(self.w)
88
+ self.grid.setSpacing(5)
89
+ self.grid.setContentsMargins(10, 10, 10, 10) # left top right bottom
90
+
91
+ self.to_disable = []
92
+ self.generate_header()
93
+ self.ProcessPopulations = [ProcessPanel(self, pop) for pop in self.populations]
94
+
95
+ self.NeighPanel = NeighPanel(self)
96
+ self.PreprocessingPanel = PreprocessingPanel(self)
97
+
98
+ ProcessFrame = QFrame()
99
+ grid_process = QVBoxLayout(ProcessFrame)
100
+ grid_process.setContentsMargins(15, 30, 15, 15)
101
+
102
+ AnalyzeFrame = QFrame()
103
+ grid_analyze = QVBoxLayout(AnalyzeFrame)
104
+ grid_analyze.setContentsMargins(15, 30, 15, 15)
105
+ self.SurvivalBlock = AnalysisPanel(self, title="Survival")
106
+
107
+ grid_process.addWidget(self.PreprocessingPanel)
108
+ for panel in self.ProcessPopulations:
109
+ grid_process.addWidget(panel)
110
+ grid_process.addWidget(self.NeighPanel)
111
+
112
+ grid_analyze.addWidget(self.SurvivalBlock)
113
+
114
+ self.scroll = QScrollArea()
115
+ self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
116
+ self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
117
+ self.scroll.setWidgetResizable(True)
118
+ desktop = QDesktopWidget()
119
+ self.scroll.setMinimumHeight(550)
120
+ # self.scroll.setMinimumHeight(int(0.4*screen_height))
121
+
122
+ tabWidget = QTabWidget()
123
+ tab_index_process = tabWidget.addTab(ProcessFrame, "Process")
124
+ tabWidget.setTabIcon(tab_index_process, icon(MDI6.cog_outline, color="black"))
125
+
126
+ tab_index_analyze = tabWidget.addTab(AnalyzeFrame, "Analyze")
127
+ tabWidget.setTabIcon(tab_index_analyze, icon(MDI6.poll, color="black"))
128
+ tabWidget.setStyleSheet(self.qtab_style)
129
+
130
+ self.grid.addWidget(tabWidget, 7, 0, 1, 3, alignment=Qt.AlignTop)
131
+ self.grid.setSpacing(5)
132
+ self.scroll.setWidget(self.w)
133
+ self.setCentralWidget(self.scroll)
134
+ self.create_config_dir()
135
+ self.update_position_options()
136
+ # self.setMinimumHeight(int(self.sizeHint().height()))
137
+
138
+ self.initial_height = self.size().height()
139
+ self.initial_width = self.size().width()
140
+ self.screen_height = desktop.screenGeometry().height()
141
+ self.screen_width = desktop.screenGeometry().width()
142
+ self.scroll.setMinimumWidth(440)
143
+
144
+ self.well_list.setCurrentIndex(0)
145
+ # self.position_list.setCurrentIndex(0)
146
+
147
+ t_loaded = time.time()
148
+ logger.info(f"Launch time: {t_loaded - self.parent_window.t_ref} s...")
149
+
150
+ self.bg_loader = BackgroundLoader()
151
+ self.bg_loader.start()
152
+
153
+ def init_wells_and_positions(self):
154
+ """
155
+ Detect the wells in the experiment folder and the associated positions.
156
+ """
157
+
158
+ self.wells = get_experiment_wells(
159
+ self.exp_dir
160
+ ) # natsorted(glob(self.exp_dir + "W*" + os.sep))
161
+ self.positions = []
162
+ self.position_paths = []
163
+ for w in self.wells:
164
+ well_name, well_nbr = extract_well_name_and_number(w)
165
+ positions_path = natsorted(glob(os.sep.join([w, f"{well_nbr}*", os.sep])))
166
+ self.position_paths.append(positions_path)
167
+ self.positions.append(
168
+ [extract_position_name(pos) for pos in positions_path]
169
+ )
170
+
171
+ def generate_header(self):
172
+ """
173
+ Show the experiment name, create two QComboBox for respectively the
174
+ biological condition (well) and position of interest, access the experiment config.
175
+ """
176
+
177
+ condition_label = QLabel("condition: ")
178
+ position_label = QLabel("position: ")
179
+
180
+ name = self.exp_dir.split(os.sep)[-2]
181
+ experiment_label = QLabel(f"Experiment:")
182
+ experiment_label.setStyleSheet("""
128
183
  font-weight: bold;
129
184
  """)
130
185
 
131
- self.folder_exp_btn = QPushButton()
132
- self.folder_exp_btn.setIcon(icon(MDI6.folder,color="black"))
133
- self.folder_exp_btn.setIconSize(QSize(20, 20))
134
- self.folder_exp_btn.setToolTip("Experiment folder")
135
- self.folder_exp_btn.clicked.connect(self.open_experiment_folder)
136
- self.folder_exp_btn.setStyleSheet(self.button_select_all)
137
-
138
-
139
- self.edit_config_button = QPushButton()
140
- self.edit_config_button.setIcon(icon(MDI6.cog_outline,color="black"))
141
- self.edit_config_button.setIconSize(QSize(20, 20))
142
- self.edit_config_button.setToolTip("Configuration file")
143
- self.edit_config_button.clicked.connect(self.open_config_editor)
144
- self.edit_config_button.setStyleSheet(self.button_select_all)
145
-
146
- self.well_list = QCheckableComboBox(obj='well', parent_window=self)
147
- thresh = 32
148
- self.well_truncated = [w[:thresh - 3]+'...' if len(w)>thresh else w for w in self.well_labels]
149
- for i in range(len(self.well_truncated)):
150
- self.well_list.addItem(self.well_truncated[i], tooltip=self.well_labels[i])
151
-
152
- self.position_list = QCheckableComboBox(obj='position', parent_window=self)
153
- self.position_list.addItems(self.positions[0])
154
- self.to_disable.append(self.position_list)
155
- #self.locate_selected_position()
156
-
157
- self.well_list.activated.connect(self.display_positions)
158
-
159
- self.position_list.activated.connect(self.update_position_options)
160
-
161
- self.view_stack_btn = QPushButton()
162
- self.view_stack_btn.setStyleSheet(self.button_select_all)
163
- self.view_stack_btn.setIcon(icon(MDI6.image_check, color="black"))
164
- self.view_stack_btn.setToolTip("View stack.")
165
- self.view_stack_btn.setIconSize(QSize(20, 20))
166
- self.view_stack_btn.clicked.connect(self.view_current_stack)
167
- self.view_stack_btn.setEnabled(False)
168
-
169
- self.select_all_wells_btn = QPushButton()
170
- self.select_all_wells_btn.setIcon(icon(MDI6.select_all,color="black"))
171
- self.select_all_wells_btn.setIconSize(QSize(20, 20))
172
- self.select_all_wells_btn.setToolTip("Select all wells.")
173
- self.select_all_wells_btn.clicked.connect(self.select_all_wells)
174
- self.select_all_wells_btn.setStyleSheet(self.button_select_all)
175
- self.select_all_wells_option = False
176
-
177
-
178
- self.select_all_pos_btn = QPushButton()
179
- self.select_all_pos_btn.setIcon(icon(MDI6.select_all,color="black"))
180
- self.select_all_pos_btn.setIconSize(QSize(20, 20))
181
- self.select_all_pos_btn.setToolTip("Select all positions.")
182
- self.select_all_pos_btn.clicked.connect(self.select_all_positions)
183
- self.select_all_pos_btn.setStyleSheet(self.button_select_all)
184
- self.select_all_pos_option = False
185
-
186
-
187
- well_lbl = QLabel('Well: ')
188
- well_lbl.setAlignment(Qt.AlignRight)
189
-
190
- pos_lbl = QLabel('Position: ')
191
- pos_lbl.setAlignment(Qt.AlignRight)
192
-
193
- hsep = QHSeperationLine()
194
-
195
- ## LAYOUT
196
-
197
- # Header layout
198
- vbox = QVBoxLayout()
199
- self.grid.addLayout(vbox, 0,0,1,3)
200
-
201
- # Experiment row
202
- exp_hbox = QHBoxLayout()
203
- exp_hbox.addWidget(experiment_label, 25, alignment=Qt.AlignRight)
204
- exp_subhbox = QHBoxLayout()
205
- if len(name)>thresh:
206
- name_cut = name[:thresh - 3]+'...'
207
- else:
208
- name_cut = name
209
- exp_name_lbl = QLabel(name_cut)
210
- exp_name_lbl.setToolTip(name)
211
- exp_subhbox.addWidget(exp_name_lbl, 90, alignment=Qt.AlignLeft)
212
- exp_subhbox.addWidget(self.folder_exp_btn, 5, alignment=Qt.AlignRight)
213
- exp_subhbox.addWidget(self.edit_config_button, 5, alignment=Qt.AlignRight)
214
- exp_hbox.addLayout(exp_subhbox, 75)
215
- vbox.addLayout(exp_hbox)
216
-
217
- # Well row
218
- well_hbox = QHBoxLayout()
219
- well_hbox.addWidget(well_lbl, 25, alignment=Qt.AlignVCenter)
220
- well_subhbox = QHBoxLayout()
221
- well_subhbox.addWidget(self.well_list, 95)
222
- well_subhbox.addWidget(self.select_all_wells_btn, 5)
223
- well_hbox.addLayout(well_subhbox, 75)
224
- vbox.addLayout(well_hbox)
225
-
226
- # Position row
227
- position_hbox = QHBoxLayout()
228
- position_hbox.addWidget(pos_lbl, 25, alignment=Qt.AlignVCenter)
229
- pos_subhbox = QHBoxLayout()
230
- pos_subhbox.addWidget(self.position_list, 90)
231
- pos_subhbox.addWidget(self.select_all_pos_btn, 5)
232
- pos_subhbox.addWidget(self.view_stack_btn, 5)
233
- position_hbox.addLayout(pos_subhbox, 75)
234
- vbox.addLayout(position_hbox)
235
-
236
- vbox.addWidget(hsep)
237
-
238
- def select_all_wells(self):
239
-
240
- if not self.select_all_wells_option:
241
- self.well_list.selectAll()
242
- self.select_all_wells_option = True
243
- self.select_all_wells_btn.setIcon(icon(MDI6.select_all,color=self.celldetective_blue))
244
- self.select_all_wells_btn.setIconSize(QSize(20, 20))
245
- self.display_positions()
246
- else:
247
- self.well_list.unselectAll()
248
- self.select_all_wells_option = False
249
- self.select_all_wells_btn.setIcon(icon(MDI6.select_all,color="black"))
250
- self.select_all_wells_btn.setIconSize(QSize(20, 20))
251
- self.display_positions()
252
-
253
- def select_all_positions(self):
254
-
255
- if not self.select_all_pos_option:
256
- self.position_list.selectAll()
257
- self.select_all_pos_option = True
258
- self.select_all_pos_btn.setIcon(icon(MDI6.select_all,color=self.celldetective_blue))
259
- self.select_all_pos_btn.setIconSize(QSize(20, 20))
260
- else:
261
- self.position_list.unselectAll()
262
- self.select_all_pos_option = False
263
- self.select_all_pos_btn.setIcon(icon(MDI6.select_all,color="black"))
264
- self.select_all_pos_btn.setIconSize(QSize(20, 20))
265
-
266
- def locate_image(self):
267
-
268
- """
269
- Load the first frame of the first movie found in the experiment folder as a sample.
270
- """
271
-
272
- movies = glob(self.pos + os.sep.join(['movie', f"{self.movie_prefix}*.tif"]))
273
-
274
- if len(movies) == 0:
275
- generic_message("Please select a position containing a movie...")
276
- self.current_stack = None
277
- return None
278
- else:
279
- self.current_stack = movies[0]
280
-
281
- def view_current_stack(self):
282
-
283
- self.locate_image()
284
- if self.current_stack is not None:
285
- self.viewer = StackVisualizer(
286
- stack_path=self.current_stack,
287
- window_title=f'Position {self.position_list.currentText()}',
288
- frame_slider = True,
289
- contrast_slider = True,
290
- channel_cb = True,
291
- channel_names = self.exp_channels,
292
- n_channels = self.nbr_channels,
293
- PxToUm = self.PxToUm,
294
- )
295
- self.viewer.show()
296
-
297
- def open_experiment_folder(self):
298
-
299
- try:
300
- subprocess.Popen(f'explorer {os.path.realpath(self.exp_dir)}')
301
- except:
302
- try:
303
- os.system('xdg-open "%s"' % self.exp_dir)
304
- except:
305
- return None
306
-
307
- def load_configuration(self):
308
-
309
- '''
310
- This methods load the configuration read in the config.ini file of the experiment.
311
- '''
312
-
313
- print('Reading experiment configuration...')
314
- self.exp_config = get_config(self.exp_dir)
315
-
316
- self.populations = get_experiment_populations(self.exp_dir)
317
- self.PxToUm = get_spatial_calibration(self.exp_dir)
318
- self.FrameToMin = get_temporal_calibration(self.exp_dir)
319
-
320
-
321
- self.len_movie = int(config_section_to_dict(self.exp_config, "MovieSettings")["len_movie"])
322
- self.shape_x = int(config_section_to_dict(self.exp_config, "MovieSettings")["shape_x"])
323
- self.shape_y = int(config_section_to_dict(self.exp_config, "MovieSettings")["shape_y"])
324
- self.movie_prefix = config_section_to_dict(self.exp_config, "MovieSettings")["movie_prefix"]
325
-
326
- # Read channels
327
- self.exp_channels, channel_indices = extract_experiment_channels(self.exp_dir)
328
- self.nbr_channels = len(self.exp_channels)
329
-
330
- number_of_wells = len(self.wells)
331
- self.well_labels = _extract_labels_from_config(self.exp_config,number_of_wells)
332
-
333
- self.concentrations = get_experiment_concentrations(self.exp_dir)
334
- self.cell_types = get_experiment_cell_types(self.exp_dir)
335
- self.antibodies = get_experiment_antibodies(self.exp_dir)
336
- self.pharmaceutical_agents = get_experiment_pharmaceutical_agents(self.exp_dir)
337
-
338
- self.metadata = config_section_to_dict(self.exp_config, "Metadata")
339
- print('Experiment configuration successfully read...')
340
-
341
- def closeEvent(self, event):
342
-
343
- """
344
- Close child windows if closed.
345
- """
346
-
347
- for process_block in self.ProcessPopulations:
348
- try:
349
- if process_block.SegModelLoader:
350
- process_block.SegModelLoader.close()
351
- except:
352
- pass
353
- try:
354
- if process_block.ConfigTracking:
355
- process_block.ConfigTracking.close()
356
- except:
357
- pass
358
- try:
359
- if process_block.ConfigSignalTrain:
360
- process_block.ConfigSignalTrain.close()
361
- except:
362
- pass
363
- try:
364
- if process_block.ConfigMeasurements:
365
- process_block.ConfigMeasurements.close()
366
- except:
367
- pass
368
- try:
369
- if process_block.ConfigSignalAnnotator:
370
- process_block.ConfigSignalAnnotator.close()
371
- except:
372
- pass
373
- try:
374
- if process_block.tab_ui:
375
- process_block.tab_ui.close()
376
- except:
377
- pass
378
-
379
- try:
380
- if self.cfg_editor:
381
- self.cfg_editor.close()
382
- except:
383
- pass
384
-
385
- gc.collect()
386
-
387
-
388
- def display_positions(self):
389
-
390
- """
391
- Show the positions as the well is changed.
392
- """
393
-
394
- if self.well_list.isMultipleSelection():
395
-
396
- self.position_list.clear()
397
- position_linspace = np.linspace(0,len(self.positions[0])-1,len(self.positions[0]),dtype=int)
398
- position_linspace = [str(s) for s in position_linspace]
399
- self.position_list.addItems(position_linspace)
400
- if self.select_all_pos_option:
401
- self.select_all_pos_btn.click()
402
- self.select_all_pos_btn.click()
403
-
404
- elif not self.well_list.isAnySelected():
405
-
406
- self.position_list.unselectAll()
407
- if self.select_all_pos_option:
408
- self.select_all_pos_btn.click()
409
-
410
- else:
411
- pos_index = self.well_list.getSelectedIndices()[0]
412
- self.position_list.clear()
413
- self.position_list.addItems(self.positions[pos_index])
414
- if self.select_all_pos_option:
415
- self.select_all_pos_btn.click()
416
- self.position_list.setCurrentIndex(0)
417
-
418
- self.update_position_options()
419
-
420
- def open_config_editor(self):
421
- self.cfg_editor = ConfigEditor(self)
422
- self.cfg_editor.show()
423
-
424
- def locate_selected_position(self):
425
-
426
- """
427
- Set the current position if the option one well, one positon is selected
428
- Display error messages otherwise.
429
-
430
- """
431
-
432
- if self.well_list.isMultipleSelection():
433
- generic_message("Please select a single well...")
434
- return False
435
- else:
436
- self.well_index = self.well_list.getSelectedIndices() #[self.well_list.currentIndex()]
437
-
438
- for w_idx in self.well_index:
439
-
440
- pos = self.positions[w_idx]
441
- if not self.position_list.isSingleSelection():
442
- generic_message("Please select a single position...")
443
- return False
444
- else:
445
- pos_indices = self.position_list.getSelectedIndices()
446
-
447
- well = self.wells[w_idx]
448
-
449
- for pos_idx in pos_indices:
450
-
451
- self.pos = natsorted(glob(well+f"{os.path.split(well)[-1].replace('W','').replace(os.sep,'')}*{os.sep}"))[pos_idx]
452
- if not os.path.exists(self.pos + 'output'):
453
- os.mkdir(self.pos + 'output')
454
- if not os.path.exists(self.pos + os.sep.join(['output','tables'])):
455
- os.mkdir(self.pos + os.sep.join(['output','tables']))
456
-
457
- return True
458
-
459
- def create_config_dir(self):
460
-
461
- self.config_folder = self.exp_dir+'configs'+os.sep
462
- if not os.path.exists(self.config_folder):
463
- os.mkdir(self.config_folder)
464
-
465
- def update_position_options(self):
466
-
467
- self.pos = self.position_list.currentText()
468
-
469
- if self.position_list.isMultipleSelection() or not self.position_list.isAnySelected():
470
-
471
- for p in self.ProcessPopulations:
472
- p.check_seg_btn.setEnabled(False)
473
- p.check_tracking_result_btn.setEnabled(False)
474
- p.view_tab_btn.setEnabled(True)
475
- p.signal_analysis_action.setEnabled(True)
476
- p.check_seg_btn.setEnabled(False)
477
- p.check_tracking_result_btn.setEnabled(False)
478
- p.check_measurements_btn.setEnabled(False)
479
- p.check_signals_btn.setEnabled(False)
480
- p.delete_tracks_btn.hide()
481
-
482
-
483
- self.NeighPanel.view_tab_btn.setEnabled(True)
484
- self.NeighPanel.check_signals_btn.setEnabled(False)
485
- self.view_stack_btn.setEnabled(False)
486
-
487
- elif self.well_list.isMultipleSelection():
488
-
489
- for p in self.ProcessPopulations:
490
- p.view_tab_btn.setEnabled(True)
491
- p.signal_analysis_action.setEnabled(True)
492
- p.delete_tracks_btn.hide()
493
-
494
-
495
- self.NeighPanel.view_tab_btn.setEnabled(True)
496
- self.view_stack_btn.setEnabled(False)
497
- if hasattr(self,'delete_tracks_btn'):
498
- self.delete_tracks_btn.hide()
499
- else:
500
-
501
- if self.well_list.isAnySelected() and self.position_list.isAnySelected():
502
-
503
- self.locate_selected_position()
504
- self.view_stack_btn.setEnabled(True)
505
- for i,p in enumerate(self.ProcessPopulations):
506
- p.check_seg_btn.setEnabled(True)
507
- if os.path.exists(os.sep.join([self.pos,'output','tables',f'trajectories_{self.populations[i]}.csv'])):
508
- try:
509
- df = pd.read_csv(os.sep.join([self.pos,'output','tables',f'trajectories_{self.populations[i]}.csv']), nrows=1)
510
- except Exception as e:
511
- continue
512
- id_col = extract_identity_col(df)
513
- p.check_measurements_btn.setEnabled(True)
514
-
515
- if id_col=='TRACK_ID':
516
- p.check_signals_btn.setEnabled(True)
517
- p.delete_tracks_btn.show()
518
- p.signal_analysis_action.setEnabled(True)
519
- p.check_tracking_result_btn.setEnabled(True)
520
- else:
521
- p.signal_analysis_action.setEnabled(False)
522
- p.check_tracking_result_btn.setEnabled(False)
523
-
524
- p.view_tab_btn.setEnabled(True)
525
- p.classify_btn.setEnabled(True)
526
- else:
527
- p.check_measurements_btn.setEnabled(False)
528
- p.check_signals_btn.setEnabled(False)
529
- p.view_tab_btn.setEnabled(False)
530
- p.classify_btn.setEnabled(False)
531
- p.delete_tracks_btn.hide()
532
- p.signal_analysis_action.setEnabled(False)
533
-
534
- if os.path.exists(os.sep.join([self.pos,'output','tables','trajectories_pairs.csv'])):
535
- self.NeighPanel.view_tab_btn.setEnabled(True)
536
- self.NeighPanel.check_signals_btn.setEnabled(True)
537
- else:
538
- self.NeighPanel.view_tab_btn.setEnabled(False)
539
- self.NeighPanel.check_signals_btn.setEnabled(False)
186
+ self.folder_exp_btn = QPushButton()
187
+ self.folder_exp_btn.setIcon(icon(MDI6.folder, color="black"))
188
+ self.folder_exp_btn.setIconSize(QSize(20, 20))
189
+ self.folder_exp_btn.setToolTip("Experiment folder")
190
+ self.folder_exp_btn.clicked.connect(self.open_experiment_folder)
191
+ self.folder_exp_btn.setStyleSheet(self.button_select_all)
192
+
193
+ self.edit_config_button = QPushButton()
194
+ self.edit_config_button.setIcon(icon(MDI6.cog_outline, color="black"))
195
+ self.edit_config_button.setIconSize(QSize(20, 20))
196
+ self.edit_config_button.setToolTip("Configuration file")
197
+ self.edit_config_button.clicked.connect(self.open_config_editor)
198
+ self.edit_config_button.setStyleSheet(self.button_select_all)
199
+
200
+ self.well_list = QCheckableComboBox(obj="well", parent_window=self)
201
+ thresh = 32
202
+ self.well_truncated = [
203
+ w[: thresh - 3] + "..." if len(w) > thresh else w for w in self.well_labels
204
+ ]
205
+ for i in range(len(self.well_truncated)):
206
+ self.well_list.addItem(self.well_truncated[i], tooltip=self.well_labels[i])
207
+
208
+ self.position_list = QCheckableComboBox(obj="position", parent_window=self)
209
+ self.position_list.addItems(self.positions[0])
210
+ self.to_disable.append(self.position_list)
211
+ # self.locate_selected_position()
212
+
213
+ self.well_list.activated.connect(self.display_positions)
214
+
215
+ self.position_list.activated.connect(self.update_position_options)
216
+
217
+ self.view_stack_btn = QPushButton()
218
+ self.view_stack_btn.setStyleSheet(self.button_select_all)
219
+ self.view_stack_btn.setIcon(icon(MDI6.image_check, color="black"))
220
+ self.view_stack_btn.setToolTip("View stack.")
221
+ self.view_stack_btn.setIconSize(QSize(20, 20))
222
+ self.view_stack_btn.clicked.connect(self.view_current_stack)
223
+ self.view_stack_btn.setEnabled(False)
224
+
225
+ self.select_all_wells_btn = QPushButton()
226
+ self.select_all_wells_btn.setIcon(icon(MDI6.select_all, color="black"))
227
+ self.select_all_wells_btn.setIconSize(QSize(20, 20))
228
+ self.select_all_wells_btn.setToolTip("Select all wells.")
229
+ self.select_all_wells_btn.clicked.connect(self.select_all_wells)
230
+ self.select_all_wells_btn.setStyleSheet(self.button_select_all)
231
+ self.select_all_wells_option = False
232
+
233
+ self.select_all_pos_btn = QPushButton()
234
+ self.select_all_pos_btn.setIcon(icon(MDI6.select_all, color="black"))
235
+ self.select_all_pos_btn.setIconSize(QSize(20, 20))
236
+ self.select_all_pos_btn.setToolTip("Select all positions.")
237
+ self.select_all_pos_btn.clicked.connect(self.select_all_positions)
238
+ self.select_all_pos_btn.setStyleSheet(self.button_select_all)
239
+ self.select_all_pos_option = False
240
+
241
+ well_lbl = QLabel("Well: ")
242
+ well_lbl.setAlignment(Qt.AlignRight)
243
+
244
+ pos_lbl = QLabel("Position: ")
245
+ pos_lbl.setAlignment(Qt.AlignRight)
246
+
247
+ hsep = QHSeperationLine()
248
+
249
+ ## LAYOUT
250
+
251
+ # Header layout
252
+ vbox = QVBoxLayout()
253
+ self.grid.addLayout(vbox, 0, 0, 1, 3)
254
+
255
+ # Experiment row
256
+ exp_hbox = QHBoxLayout()
257
+ exp_hbox.addWidget(experiment_label, 25, alignment=Qt.AlignRight)
258
+ exp_subhbox = QHBoxLayout()
259
+ if len(name) > thresh:
260
+ name_cut = name[: thresh - 3] + "..."
261
+ else:
262
+ name_cut = name
263
+ exp_name_lbl = QLabel(name_cut)
264
+ exp_name_lbl.setToolTip(name)
265
+ exp_subhbox.addWidget(exp_name_lbl, 90, alignment=Qt.AlignLeft)
266
+ exp_subhbox.addWidget(self.folder_exp_btn, 5, alignment=Qt.AlignRight)
267
+ exp_subhbox.addWidget(self.edit_config_button, 5, alignment=Qt.AlignRight)
268
+ exp_hbox.addLayout(exp_subhbox, 75)
269
+ vbox.addLayout(exp_hbox)
270
+
271
+ # Well row
272
+ well_hbox = QHBoxLayout()
273
+ well_hbox.addWidget(well_lbl, 25, alignment=Qt.AlignVCenter)
274
+ well_subhbox = QHBoxLayout()
275
+ well_subhbox.addWidget(self.well_list, 95)
276
+ well_subhbox.addWidget(self.select_all_wells_btn, 5)
277
+ well_hbox.addLayout(well_subhbox, 75)
278
+ vbox.addLayout(well_hbox)
279
+
280
+ # Position row
281
+ position_hbox = QHBoxLayout()
282
+ position_hbox.addWidget(pos_lbl, 25, alignment=Qt.AlignVCenter)
283
+ pos_subhbox = QHBoxLayout()
284
+ pos_subhbox.addWidget(self.position_list, 90)
285
+ pos_subhbox.addWidget(self.select_all_pos_btn, 5)
286
+ pos_subhbox.addWidget(self.view_stack_btn, 5)
287
+ position_hbox.addLayout(pos_subhbox, 75)
288
+ vbox.addLayout(position_hbox)
289
+
290
+ vbox.addWidget(hsep)
291
+
292
+ def select_all_wells(self):
293
+
294
+ if not self.select_all_wells_option:
295
+ self.well_list.selectAll()
296
+ self.select_all_wells_option = True
297
+ self.select_all_wells_btn.setIcon(
298
+ icon(MDI6.select_all, color=self.celldetective_blue)
299
+ )
300
+ self.select_all_wells_btn.setIconSize(QSize(20, 20))
301
+ self.display_positions()
302
+ else:
303
+ self.well_list.unselectAll()
304
+ self.select_all_wells_option = False
305
+ self.select_all_wells_btn.setIcon(icon(MDI6.select_all, color="black"))
306
+ self.select_all_wells_btn.setIconSize(QSize(20, 20))
307
+ self.display_positions()
308
+
309
+ def select_all_positions(self):
310
+
311
+ if not self.select_all_pos_option:
312
+ self.position_list.selectAll()
313
+ self.select_all_pos_option = True
314
+ self.select_all_pos_btn.setIcon(
315
+ icon(MDI6.select_all, color=self.celldetective_blue)
316
+ )
317
+ self.select_all_pos_btn.setIconSize(QSize(20, 20))
318
+ else:
319
+ self.position_list.unselectAll()
320
+ self.select_all_pos_option = False
321
+ self.select_all_pos_btn.setIcon(icon(MDI6.select_all, color="black"))
322
+ self.select_all_pos_btn.setIconSize(QSize(20, 20))
323
+
324
+ def locate_image(self):
325
+ """
326
+ Load the first frame of the first movie found in the experiment folder as a sample.
327
+ """
328
+
329
+ movies = glob(self.pos + os.sep.join(["movie", f"{self.movie_prefix}*.tif"]))
330
+
331
+ if len(movies) == 0:
332
+ generic_message("Please select a position containing a movie...")
333
+ self.current_stack = None
334
+ return None
335
+ else:
336
+ self.current_stack = movies[0]
337
+
338
+ def view_current_stack(self):
339
+
340
+ if self.bg_loader.isFinished() and hasattr(self.bg_loader, "StackVisualizer"):
341
+ StackVisualizer = self.bg_loader.StackVisualizer
342
+ else:
343
+ from celldetective.gui.viewers.base_viewer import StackVisualizer
344
+
345
+ self.locate_image()
346
+ if self.current_stack is not None:
347
+ self.viewer = StackVisualizer(
348
+ stack_path=self.current_stack,
349
+ window_title=f"Position {self.position_list.currentText()}",
350
+ frame_slider=True,
351
+ contrast_slider=True,
352
+ channel_cb=True,
353
+ channel_names=self.exp_channels,
354
+ n_channels=self.nbr_channels,
355
+ PxToUm=self.PxToUm,
356
+ )
357
+
358
+ # Not working for some reason
359
+ # def post_widget(widget):
360
+ # try:
361
+ # widget.resize(widget.width() + 1, widget.height() + 1)
362
+ # center_window(widget)
363
+ # except Exception as e:
364
+ # traceback.print_exc()
365
+
366
+ self.viewer.show()
367
+
368
+ def open_experiment_folder(self):
369
+
370
+ try:
371
+ subprocess.Popen(f"explorer {os.path.realpath(self.exp_dir)}")
372
+ except:
373
+ try:
374
+ os.system('xdg-open "%s"' % self.exp_dir)
375
+ except:
376
+ return None
377
+
378
+ def load_configuration(self):
379
+ """
380
+ This methods load the configuration read in the config.ini file of the experiment.
381
+ """
382
+
383
+ logger.info("Reading experiment configuration...")
384
+ self.exp_config = get_config(self.exp_dir)
385
+
386
+ self.populations = get_experiment_populations(self.exp_dir)
387
+ self.PxToUm = get_spatial_calibration(self.exp_dir)
388
+ self.FrameToMin = get_temporal_calibration(self.exp_dir)
389
+
390
+ self.len_movie = int(
391
+ config_section_to_dict(self.exp_config, "MovieSettings")["len_movie"]
392
+ )
393
+ self.shape_x = int(
394
+ config_section_to_dict(self.exp_config, "MovieSettings")["shape_x"]
395
+ )
396
+ self.shape_y = int(
397
+ config_section_to_dict(self.exp_config, "MovieSettings")["shape_y"]
398
+ )
399
+ self.movie_prefix = config_section_to_dict(self.exp_config, "MovieSettings")[
400
+ "movie_prefix"
401
+ ]
402
+
403
+ # Read channels
404
+ self.exp_channels, channel_indices = extract_experiment_channels(self.exp_dir)
405
+ self.nbr_channels = len(self.exp_channels)
406
+
407
+ number_of_wells = len(self.wells)
408
+ self.well_labels = _extract_labels_from_config(self.exp_config, number_of_wells)
409
+
410
+ self.concentrations = get_experiment_concentrations(self.exp_dir)
411
+ self.cell_types = get_experiment_cell_types(self.exp_dir)
412
+ self.antibodies = get_experiment_antibodies(self.exp_dir)
413
+ self.pharmaceutical_agents = get_experiment_pharmaceutical_agents(self.exp_dir)
414
+
415
+ self.metadata = config_section_to_dict(self.exp_config, "Metadata")
416
+ logger.info("Experiment configuration successfully read...")
417
+
418
+ def closeEvent(self, event):
419
+ """
420
+ Close child windows if closed.
421
+ """
422
+
423
+ for process_block in self.ProcessPopulations:
424
+ try:
425
+ if process_block.SegModelLoader:
426
+ process_block.SegModelLoader.close()
427
+ except:
428
+ pass
429
+ try:
430
+ if process_block.ConfigTracking:
431
+ process_block.ConfigTracking.close()
432
+ except:
433
+ pass
434
+ try:
435
+ if process_block.ConfigSignalTrain:
436
+ process_block.ConfigSignalTrain.close()
437
+ except:
438
+ pass
439
+ try:
440
+ if process_block.ConfigMeasurements:
441
+ process_block.ConfigMeasurements.close()
442
+ except:
443
+ pass
444
+ try:
445
+ if process_block.ConfigSignalAnnotator:
446
+ process_block.ConfigSignalAnnotator.close()
447
+ except:
448
+ pass
449
+ try:
450
+ if process_block.tab_ui:
451
+ process_block.tab_ui.close()
452
+ except:
453
+ pass
454
+
455
+ try:
456
+ if self.cfg_editor:
457
+ self.cfg_editor.close()
458
+ except:
459
+ pass
460
+
461
+ gc.collect()
462
+
463
+ def display_positions(self):
464
+ """
465
+ Show the positions as the well is changed.
466
+ """
467
+
468
+ if self.well_list.isMultipleSelection():
469
+
470
+ self.position_list.clear()
471
+ position_linspace = np.linspace(
472
+ 0, len(self.positions[0]) - 1, len(self.positions[0]), dtype=int
473
+ )
474
+ position_linspace = [str(s) for s in position_linspace]
475
+ self.position_list.addItems(position_linspace)
476
+ if self.select_all_pos_option:
477
+ self.select_all_pos_btn.click()
478
+ self.select_all_pos_btn.click()
479
+
480
+ elif not self.well_list.isAnySelected():
481
+
482
+ self.position_list.unselectAll()
483
+ if self.select_all_pos_option:
484
+ self.select_all_pos_btn.click()
485
+
486
+ else:
487
+ pos_index = self.well_list.getSelectedIndices()[0]
488
+ self.position_list.clear()
489
+ self.position_list.addItems(self.positions[pos_index])
490
+ if self.select_all_pos_option:
491
+ self.select_all_pos_btn.click()
492
+ self.position_list.setCurrentIndex(0)
493
+
494
+ self.update_position_options()
495
+
496
+ def open_config_editor(self):
497
+ from celldetective.gui.json_readers import ConfigEditor
498
+
499
+ self.cfg_editor = ConfigEditor(self)
500
+ self.cfg_editor.show()
501
+
502
+ def locate_selected_position(self):
503
+ """
504
+ Set the current position if the option one well, one positon is selected
505
+ Display error messages otherwise.
506
+
507
+ """
508
+
509
+ if self.well_list.isMultipleSelection():
510
+ generic_message("Please select a single well...")
511
+ return False
512
+ else:
513
+ self.well_index = (
514
+ self.well_list.getSelectedIndices()
515
+ ) # [self.well_list.currentIndex()]
516
+
517
+ for w_idx in self.well_index:
518
+
519
+ pos = self.positions[w_idx]
520
+ if not self.position_list.isSingleSelection():
521
+ generic_message("Please select a single position...")
522
+ return False
523
+ else:
524
+ pos_indices = self.position_list.getSelectedIndices()
525
+
526
+ well = self.wells[w_idx]
527
+
528
+ for pos_idx in pos_indices:
529
+
530
+ self.pos = self.position_paths[w_idx][pos_idx]
531
+ if not os.path.exists(self.pos + "output"):
532
+ os.mkdir(self.pos + "output")
533
+ if not os.path.exists(self.pos + os.sep.join(["output", "tables"])):
534
+ os.mkdir(self.pos + os.sep.join(["output", "tables"]))
535
+
536
+ return True
537
+
538
+ def create_config_dir(self):
539
+
540
+ self.config_folder = self.exp_dir + "configs" + os.sep
541
+ if not os.path.exists(self.config_folder):
542
+ os.mkdir(self.config_folder)
543
+
544
+ def update_position_options(self):
545
+
546
+ self.pos = self.position_list.currentText()
547
+
548
+ if (
549
+ self.position_list.isMultipleSelection()
550
+ or not self.position_list.isAnySelected()
551
+ ):
552
+
553
+ for p in self.ProcessPopulations:
554
+ p.check_seg_btn.setEnabled(False)
555
+ p.check_tracking_result_btn.setEnabled(False)
556
+ p.view_tab_btn.setEnabled(True)
557
+ p.signal_analysis_action.setEnabled(True)
558
+ p.check_seg_btn.setEnabled(False)
559
+ p.check_tracking_result_btn.setEnabled(False)
560
+ p.check_measurements_btn.setEnabled(False)
561
+ p.check_signals_btn.setEnabled(False)
562
+ p.delete_tracks_btn.hide()
563
+
564
+ self.NeighPanel.view_tab_btn.setEnabled(True)
565
+ self.NeighPanel.check_signals_btn.setEnabled(False)
566
+ self.view_stack_btn.setEnabled(False)
567
+
568
+ elif self.well_list.isMultipleSelection():
569
+
570
+ for p in self.ProcessPopulations:
571
+ p.view_tab_btn.setEnabled(True)
572
+ p.signal_analysis_action.setEnabled(True)
573
+ p.delete_tracks_btn.hide()
574
+
575
+ self.NeighPanel.view_tab_btn.setEnabled(True)
576
+ self.view_stack_btn.setEnabled(False)
577
+ if hasattr(self, "delete_tracks_btn"):
578
+ self.delete_tracks_btn.hide()
579
+ else:
580
+
581
+ if self.well_list.isAnySelected() and self.position_list.isAnySelected():
582
+
583
+ self.locate_selected_position()
584
+ self.view_stack_btn.setEnabled(True)
585
+ for i, p in enumerate(self.ProcessPopulations):
586
+ p.check_seg_btn.setEnabled(True)
587
+ if os.path.exists(
588
+ os.sep.join(
589
+ [
590
+ self.pos,
591
+ "output",
592
+ "tables",
593
+ f"trajectories_{self.populations[i]}.csv",
594
+ ]
595
+ )
596
+ ):
597
+ try:
598
+ import pandas as pd
599
+
600
+ cols = pd.read_csv(
601
+ os.sep.join(
602
+ [
603
+ self.pos,
604
+ "output",
605
+ "tables",
606
+ f"trajectories_{self.populations[i]}.csv",
607
+ ]
608
+ ),
609
+ nrows=0,
610
+ ).columns
611
+ except Exception as e:
612
+ continue
613
+
614
+ if "TRACK_ID" in cols:
615
+ id_col = "TRACK_ID"
616
+ elif "ID" in cols:
617
+ id_col = "ID"
618
+ else:
619
+ id_col = None
620
+ p.check_measurements_btn.setEnabled(True)
621
+
622
+ if id_col == "TRACK_ID":
623
+ p.check_signals_btn.setEnabled(True)
624
+ p.delete_tracks_btn.show()
625
+ p.signal_analysis_action.setEnabled(True)
626
+ p.check_tracking_result_btn.setEnabled(True)
627
+ else:
628
+ p.signal_analysis_action.setEnabled(False)
629
+ p.check_tracking_result_btn.setEnabled(False)
630
+
631
+ p.view_tab_btn.setEnabled(True)
632
+ p.classify_btn.setEnabled(True)
633
+ else:
634
+ p.check_measurements_btn.setEnabled(False)
635
+ p.check_signals_btn.setEnabled(False)
636
+ p.view_tab_btn.setEnabled(False)
637
+ p.classify_btn.setEnabled(False)
638
+ p.delete_tracks_btn.hide()
639
+ p.signal_analysis_action.setEnabled(False)
640
+
641
+ if os.path.exists(
642
+ os.sep.join(
643
+ [self.pos, "output", "tables", "trajectories_pairs.csv"]
644
+ )
645
+ ):
646
+ self.NeighPanel.view_tab_btn.setEnabled(True)
647
+ self.NeighPanel.check_signals_btn.setEnabled(True)
648
+ else:
649
+ self.NeighPanel.view_tab_btn.setEnabled(False)
650
+ self.NeighPanel.check_signals_btn.setEnabled(False)