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
@@ -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