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
@@ -0,0 +1,395 @@
1
+ import json
2
+ import os
3
+ from glob import glob
4
+
5
+ import numpy as np
6
+ from PyQt5.QtCore import QSize, QTimer, Qt
7
+ from PyQt5.QtGui import QDoubleValidator, QIntValidator
8
+ from PyQt5.QtWidgets import (
9
+ QDialog,
10
+ QFrame,
11
+ QGridLayout,
12
+ QLabel,
13
+ QMessageBox,
14
+ QPushButton,
15
+ QVBoxLayout,
16
+ )
17
+ from fonticon_mdi6 import MDI6
18
+ from superqt.fonticon import icon
19
+
20
+ from celldetective import get_software_location
21
+ from celldetective.gui.base.styles import Styles
22
+ from celldetective.gui.base.utils import center_window
23
+ from celldetective.gui.gui_utils import help_generic
24
+ from celldetective.gui.layouts import (
25
+ BackgroundFitCorrectionLayout,
26
+ BackgroundModelFreeCorrectionLayout,
27
+ ChannelOffsetOptionsLayout,
28
+ ProtocolDesignerLayout,
29
+ )
30
+ from celldetective.utils.experiment import extract_experiment_channels
31
+ from celldetective import get_logger
32
+
33
+ logger = get_logger(__name__)
34
+
35
+
36
+ class PreprocessingPanel(QFrame, Styles):
37
+
38
+ def __init__(self, parent_window):
39
+
40
+ super().__init__()
41
+ self.parent_window = parent_window
42
+ self.exp_channels = self.parent_window.exp_channels
43
+ self.exp_dir = self.parent_window.exp_dir
44
+ self.wells = np.array(self.parent_window.wells, dtype=str)
45
+ exp_config = self.exp_dir + "config.ini"
46
+ self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
47
+ self.channel_names = np.array(self.channel_names)
48
+ self.background_correction = []
49
+ self.onlyFloat = QDoubleValidator()
50
+ self.onlyInt = QIntValidator()
51
+
52
+ self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
53
+ self.grid = QGridLayout(self)
54
+
55
+ self.generate_header()
56
+
57
+ def generate_header(self):
58
+ """
59
+ Read the mode and prepare a collapsable block to process a specific cell population.
60
+
61
+ """
62
+
63
+ panel_title = QLabel(f"PREPROCESSING")
64
+ panel_title.setStyleSheet(
65
+ """
66
+ font-weight: bold;
67
+ padding: 0px;
68
+ """
69
+ )
70
+
71
+ self.grid.addWidget(panel_title, 0, 0, 1, 4, alignment=Qt.AlignCenter)
72
+
73
+ # self.select_all_btn = QPushButton()
74
+ # self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
75
+ # self.select_all_btn.setIconSize(QSize(20, 20))
76
+ # self.all_ticked = False
77
+ # #self.select_all_btn.clicked.connect(self.tick_all_actions)
78
+ # self.select_all_btn.setStyleSheet(self.button_select_all)
79
+ # self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
80
+ # self.to_disable.append(self.all_tc_actions)
81
+
82
+ self.collapse_btn = QPushButton()
83
+ self.collapse_btn.setIcon(icon(MDI6.chevron_down, color="black"))
84
+ self.collapse_btn.setIconSize(QSize(25, 25))
85
+ self.collapse_btn.setStyleSheet(self.button_select_all)
86
+ self.grid.addWidget(self.collapse_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
87
+
88
+ self.populate_contents()
89
+
90
+ self.grid.addWidget(self.ContentsFrame, 1, 0, 1, 4, alignment=Qt.AlignTop)
91
+ self.collapse_btn.clicked.connect(
92
+ lambda: self.ContentsFrame.setHidden(not self.ContentsFrame.isHidden())
93
+ )
94
+ self.collapse_btn.clicked.connect(self.collapse_advanced)
95
+ self.ContentsFrame.hide()
96
+
97
+ def collapse_advanced(self):
98
+
99
+ panels_open = [
100
+ not p.ContentsFrame.isHidden()
101
+ for p in self.parent_window.ProcessPopulations
102
+ ]
103
+ interactions_open = not self.parent_window.NeighPanel.ContentsFrame.isHidden()
104
+ preprocessing_open = (
105
+ not self.parent_window.PreprocessingPanel.ContentsFrame.isHidden()
106
+ )
107
+ is_open = np.array(panels_open + [interactions_open, preprocessing_open])
108
+
109
+ if self.ContentsFrame.isHidden():
110
+ self.collapse_btn.setIcon(icon(MDI6.chevron_down, color="black"))
111
+ self.collapse_btn.setIconSize(QSize(20, 20))
112
+ if len(is_open[is_open]) == 0:
113
+ self.parent_window.scroll.setMinimumHeight(int(550))
114
+ self.parent_window.adjustSize()
115
+ else:
116
+ self.collapse_btn.setIcon(icon(MDI6.chevron_up, color="black"))
117
+ self.collapse_btn.setIconSize(QSize(20, 20))
118
+ self.parent_window.scroll.setMinimumHeight(
119
+ min(int(930), int(0.9 * self.parent_window.screen_height))
120
+ )
121
+
122
+ def safe_center():
123
+ try:
124
+ center_window(self.window())
125
+ except RuntimeError:
126
+ pass
127
+
128
+ try:
129
+ QTimer.singleShot(10, safe_center)
130
+ except:
131
+ pass
132
+
133
+ def populate_contents(self):
134
+
135
+ self.ContentsFrame = QFrame()
136
+ self.grid_contents = QGridLayout(self.ContentsFrame)
137
+
138
+ self.model_free_correction_layout = BackgroundModelFreeCorrectionLayout(self)
139
+ self.fit_correction_layout = BackgroundFitCorrectionLayout(self)
140
+
141
+ self.protocol_layout = ProtocolDesignerLayout(
142
+ parent_window=self,
143
+ tab_layouts=[self.fit_correction_layout, self.model_free_correction_layout],
144
+ tab_names=["Fit", "Model-free"],
145
+ title="BACKGROUND CORRECTION",
146
+ list_title="Corrections to apply:",
147
+ )
148
+
149
+ self.help_background_btn = QPushButton()
150
+ self.help_background_btn.setIcon(icon(MDI6.help_circle, color=self.help_color))
151
+ self.help_background_btn.setIconSize(QSize(20, 20))
152
+ self.help_background_btn.clicked.connect(self.help_background)
153
+ self.help_background_btn.setStyleSheet(self.button_select_all)
154
+ self.help_background_btn.setToolTip("Help.")
155
+
156
+ self.protocol_layout.title_layout.addWidget(
157
+ self.help_background_btn, 5, alignment=Qt.AlignRight
158
+ )
159
+
160
+ self.channel_offset_correction_layout = QVBoxLayout()
161
+
162
+ self.channel_shift_lbl = QLabel("CHANNEL OFFSET CORRECTION")
163
+ self.channel_shift_lbl.setStyleSheet(
164
+ """
165
+ font-weight: bold;
166
+ padding: 0px;
167
+ """
168
+ )
169
+ self.channel_offset_correction_layout.addWidget(
170
+ self.channel_shift_lbl, alignment=Qt.AlignCenter
171
+ )
172
+
173
+ self.channel_offset_options_layout = ChannelOffsetOptionsLayout(self)
174
+ self.channel_offset_correction_layout.addLayout(
175
+ self.channel_offset_options_layout
176
+ )
177
+
178
+ self.protocol_layout.correction_layout.addWidget(QLabel(""))
179
+ self.protocol_layout.correction_layout.addLayout(
180
+ self.channel_offset_correction_layout
181
+ )
182
+
183
+ self.grid_contents.addLayout(self.protocol_layout, 0, 0, 1, 4)
184
+
185
+ self.submit_preprocessing_btn = QPushButton("Submit")
186
+ self.submit_preprocessing_btn.setStyleSheet(self.button_style_sheet)
187
+ self.submit_preprocessing_btn.clicked.connect(self.launch_preprocessing)
188
+
189
+ self.grid_contents.addWidget(self.submit_preprocessing_btn, 1, 0, 1, 4)
190
+
191
+ def add_offset_instructions_to_parent_list(self):
192
+ logger.info("adding instructions")
193
+
194
+ def launch_preprocessing(self):
195
+
196
+ msgBox1 = QMessageBox()
197
+ msgBox1.setIcon(QMessageBox.Question)
198
+ msgBox1.setText(
199
+ "Do you want to apply the preprocessing\nto all wells and positions?"
200
+ )
201
+ msgBox1.setWindowTitle("Selection")
202
+ msgBox1.setStandardButtons(
203
+ QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel
204
+ )
205
+ returnValue = msgBox1.exec()
206
+ if returnValue == QMessageBox.Cancel:
207
+ return None
208
+ elif returnValue == QMessageBox.Yes:
209
+ self.parent_window.well_list.selectAll()
210
+ self.parent_window.position_list.selectAll()
211
+ elif returnValue == QMessageBox.No:
212
+ msgBox2 = QMessageBox()
213
+ msgBox2.setIcon(QMessageBox.Question)
214
+ msgBox2.setText(
215
+ "Do you want to apply the preprocessing\nto the positions selected at the top only?"
216
+ )
217
+ msgBox2.setWindowTitle("Selection")
218
+ msgBox2.setStandardButtons(
219
+ QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel
220
+ )
221
+ returnValue = msgBox2.exec()
222
+ if returnValue == QMessageBox.Cancel:
223
+ return None
224
+ if returnValue == QMessageBox.No:
225
+ return None
226
+
227
+ logger.info("Proceed with correction...")
228
+
229
+ # if self.parent_window.well_list.currentText()=='*':
230
+ # well_option = "*"
231
+ # else:
232
+ well_option = self.parent_window.well_list.getSelectedIndices()
233
+ position_option = self.parent_window.position_list.getSelectedIndices()
234
+
235
+ for k, correction_protocol in enumerate(self.protocol_layout.protocols):
236
+
237
+ movie_prefix = None
238
+ export_prefix = "Corrected"
239
+ if k > 0:
240
+ # switch source stack to cumulate multi-channel preprocessing
241
+ movie_prefix = "Corrected"
242
+ export_prefix = None
243
+
244
+ if correction_protocol["correction_type"] == "model-free":
245
+ print(f"Model-free correction; {movie_prefix=} {export_prefix=}")
246
+ from celldetective.gui.workers import ProgressWindow
247
+ from celldetective.processes.background_correction import (
248
+ BackgroundCorrectionProcess,
249
+ )
250
+
251
+ process_args = {
252
+ "exp_dir": self.exp_dir,
253
+ "well_option": well_option,
254
+ "position_option": position_option,
255
+ "movie_prefix": movie_prefix,
256
+ "export_prefix": export_prefix,
257
+ "export": True,
258
+ "return_stacks": False,
259
+ "activation_protocol": [["gauss", 2], ["std", 4]],
260
+ "correction_type": "model-free", # Explicitly set type
261
+ }
262
+ process_args.update(correction_protocol)
263
+
264
+ self.job = ProgressWindow(
265
+ BackgroundCorrectionProcess,
266
+ parent_window=None,
267
+ title="Model-Free Background Correction",
268
+ position_info=False,
269
+ process_args=process_args,
270
+ )
271
+ result = self.job.exec_()
272
+ if result == QDialog.Rejected:
273
+ logger.info("Background correction cancelled.")
274
+ return None
275
+
276
+ elif correction_protocol["correction_type"] == "fit":
277
+ print(
278
+ f"Fit correction; {movie_prefix=} {export_prefix=} {correction_protocol=}"
279
+ )
280
+ from celldetective.gui.workers import ProgressWindow
281
+ from celldetective.processes.background_correction import (
282
+ BackgroundCorrectionProcess,
283
+ )
284
+
285
+ process_args = {
286
+ "exp_dir": self.exp_dir,
287
+ "well_option": well_option,
288
+ "position_option": position_option,
289
+ "movie_prefix": movie_prefix,
290
+ "export_prefix": export_prefix,
291
+ "export": True,
292
+ "return_stacks": False,
293
+ "activation_protocol": [["gauss", 2], ["std", 4]],
294
+ }
295
+ process_args.update(correction_protocol)
296
+
297
+ self.job = ProgressWindow(
298
+ BackgroundCorrectionProcess,
299
+ parent_window=None,
300
+ title="Fit Background Correction",
301
+ position_info=False,
302
+ process_args=process_args,
303
+ )
304
+ result = self.job.exec_()
305
+ if result == QDialog.Rejected:
306
+ logger.info("Background correction cancelled.")
307
+ return None
308
+ elif correction_protocol["correction_type"] == "offset":
309
+ logger.info(
310
+ f"Offset correction; {movie_prefix=} {export_prefix=} {correction_protocol=}"
311
+ )
312
+ from celldetective.gui.workers import ProgressWindow
313
+ from celldetective.processes.background_correction import (
314
+ BackgroundCorrectionProcess,
315
+ )
316
+
317
+ process_args = {
318
+ "exp_dir": self.exp_dir,
319
+ "well_option": well_option,
320
+ "position_option": position_option,
321
+ "movie_prefix": movie_prefix,
322
+ "export_prefix": export_prefix,
323
+ "export": True,
324
+ "return_stacks": False,
325
+ # Offset specific args if any, otherwise they are in correction_protocol
326
+ }
327
+ process_args.update(correction_protocol)
328
+
329
+ self.job = ProgressWindow(
330
+ BackgroundCorrectionProcess,
331
+ parent_window=None,
332
+ title="Offset Correction",
333
+ position_info=False,
334
+ process_args=process_args,
335
+ )
336
+ result = self.job.exec_()
337
+ if result == QDialog.Rejected:
338
+ logger.info("Correction cancelled.")
339
+ return None
340
+ logger.info("Done.")
341
+
342
+ def locate_image(self):
343
+ """
344
+ Load the first frame of the first movie found in the experiment folder as a sample.
345
+ """
346
+
347
+ logger.info(f"{self.parent_window.pos}")
348
+ movies = glob(
349
+ self.parent_window.pos
350
+ + os.sep.join(["movie", f"{self.parent_window.movie_prefix}*.tif"])
351
+ )
352
+
353
+ if len(movies) == 0:
354
+ msgBox = QMessageBox()
355
+ msgBox.setIcon(QMessageBox.Warning)
356
+ msgBox.setText("Please select a position containing a movie...")
357
+ msgBox.setWindowTitle("Warning")
358
+ msgBox.setStandardButtons(QMessageBox.Ok)
359
+ returnValue = msgBox.exec()
360
+ if returnValue == QMessageBox.Ok:
361
+ self.current_stack = None
362
+ return None
363
+ else:
364
+ self.current_stack = movies[0]
365
+
366
+ def help_background(self):
367
+ """
368
+ Helper to choose a proper cell population structure.
369
+ """
370
+
371
+ dict_path = os.sep.join(
372
+ [
373
+ get_software_location(),
374
+ "celldetective",
375
+ "gui",
376
+ "help",
377
+ "preprocessing.json",
378
+ ]
379
+ )
380
+
381
+ with open(dict_path) as f:
382
+ d = json.load(f)
383
+
384
+ suggestion = help_generic(d)
385
+ if isinstance(suggestion, str):
386
+ logger.info(f"{suggestion=}")
387
+ msgBox = QMessageBox()
388
+ msgBox.setIcon(QMessageBox.Information)
389
+ msgBox.setTextFormat(Qt.RichText)
390
+ msgBox.setText(suggestion)
391
+ msgBox.setWindowTitle("Info")
392
+ msgBox.setStandardButtons(QMessageBox.Ok)
393
+ returnValue = msgBox.exec()
394
+ if returnValue == QMessageBox.Ok:
395
+ return None