celldetective 1.0.2.post1__py3-none-any.whl → 1.1.0__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 (56) hide show
  1. celldetective/__main__.py +2 -2
  2. celldetective/events.py +2 -44
  3. celldetective/filters.py +4 -5
  4. celldetective/gui/__init__.py +1 -1
  5. celldetective/gui/analyze_block.py +37 -10
  6. celldetective/gui/btrack_options.py +24 -23
  7. celldetective/gui/classifier_widget.py +62 -19
  8. celldetective/gui/configure_new_exp.py +32 -35
  9. celldetective/gui/control_panel.py +115 -81
  10. celldetective/gui/gui_utils.py +674 -396
  11. celldetective/gui/json_readers.py +7 -6
  12. celldetective/gui/layouts.py +755 -0
  13. celldetective/gui/measurement_options.py +168 -487
  14. celldetective/gui/neighborhood_options.py +322 -270
  15. celldetective/gui/plot_measurements.py +1114 -0
  16. celldetective/gui/plot_signals_ui.py +20 -20
  17. celldetective/gui/process_block.py +449 -169
  18. celldetective/gui/retrain_segmentation_model_options.py +27 -26
  19. celldetective/gui/retrain_signal_model_options.py +25 -24
  20. celldetective/gui/seg_model_loader.py +31 -27
  21. celldetective/gui/signal_annotator.py +2326 -2295
  22. celldetective/gui/signal_annotator_options.py +18 -16
  23. celldetective/gui/styles.py +16 -1
  24. celldetective/gui/survival_ui.py +61 -39
  25. celldetective/gui/tableUI.py +60 -23
  26. celldetective/gui/thresholds_gui.py +68 -66
  27. celldetective/gui/viewers.py +596 -0
  28. celldetective/io.py +234 -23
  29. celldetective/measure.py +37 -32
  30. celldetective/neighborhood.py +495 -27
  31. celldetective/preprocessing.py +683 -0
  32. celldetective/scripts/analyze_signals.py +7 -0
  33. celldetective/scripts/measure_cells.py +12 -0
  34. celldetective/scripts/segment_cells.py +5 -0
  35. celldetective/scripts/track_cells.py +11 -0
  36. celldetective/signals.py +221 -98
  37. celldetective/tracking.py +0 -1
  38. celldetective/utils.py +178 -36
  39. celldetective-1.1.0.dist-info/METADATA +305 -0
  40. celldetective-1.1.0.dist-info/RECORD +80 -0
  41. {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/top_level.txt +1 -0
  42. tests/__init__.py +0 -0
  43. tests/test_events.py +28 -0
  44. tests/test_filters.py +24 -0
  45. tests/test_io.py +70 -0
  46. tests/test_measure.py +141 -0
  47. tests/test_neighborhood.py +70 -0
  48. tests/test_segmentation.py +93 -0
  49. tests/test_signals.py +135 -0
  50. tests/test_tracking.py +164 -0
  51. tests/test_utils.py +71 -0
  52. celldetective-1.0.2.post1.dist-info/METADATA +0 -221
  53. celldetective-1.0.2.post1.dist-info/RECORD +0 -66
  54. {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/LICENSE +0 -0
  55. {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/WHEEL +0 -0
  56. {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/entry_points.txt +0 -0
@@ -1,23 +1,25 @@
1
- from PyQt5.QtWidgets import QFrame, QGridLayout, QComboBox, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, \
2
- QMessageBox, QWidget, QLineEdit, QScrollArea
1
+ from PyQt5.QtWidgets import QFrame, QGridLayout, QRadioButton, QButtonGroup, QGroupBox, QComboBox,QTabWidget,QSizePolicy,QListWidget, QDialog, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, \
2
+ QMessageBox, QWidget, QLineEdit, QScrollArea, QSpacerItem, QLayout, QSizePolicy
3
3
  from PyQt5.QtCore import Qt, QSize
4
4
  from superqt.fonticon import icon
5
5
  from fonticon_mdi6 import MDI6
6
6
  import gc
7
+ from PyQt5.QtGui import QIcon, QDoubleValidator, QIntValidator
7
8
 
8
9
  from celldetective.gui.signal_annotator import MeasureAnnotator
9
10
  from celldetective.io import get_segmentation_models_list, control_segmentation_napari, get_signal_models_list, control_tracking_btrack, load_experiment_tables
10
- from celldetective.io import locate_segmentation_model
11
+ from celldetective.io import locate_segmentation_model, auto_load_number_of_frames, load_frames
11
12
  from celldetective.gui import SegmentationModelLoader, ClassifierWidget, ConfigNeighborhoods, ConfigSegmentationModelTraining, ConfigTracking, SignalAnnotator, ConfigSignalModelTraining, ConfigMeasurements, ConfigSignalAnnotator, TableUI
12
13
  from celldetective.gui.gui_utils import QHSeperationLine
13
14
  from celldetective.segmentation import segment_at_position, segment_from_threshold_at_position
14
15
  from celldetective.tracking import track_at_position
15
16
  from celldetective.measure import measure_at_position
16
17
  from celldetective.signals import analyze_signals_at_position
18
+ from celldetective.utils import extract_experiment_channels
17
19
  import numpy as np
18
20
  from glob import glob
19
21
  from natsort import natsorted
20
- from superqt import QLabeledDoubleSlider
22
+ from superqt import QLabeledDoubleSlider, QLabeledSlider, QLabeledRangeSlider, QLabeledSlider, QLabeledDoubleRangeSlider
21
23
  import os
22
24
  import pandas as pd
23
25
  from tqdm import tqdm
@@ -25,26 +27,37 @@ from celldetective.gui.gui_utils import center_window
25
27
  from tifffile import imwrite
26
28
  import json
27
29
  import psutil
28
- from celldetective.neighborhood import compute_neighborhood_at_position
29
-
30
- class ProcessPanel(QFrame):
31
- def __init__(self, parent, mode):
30
+ from celldetective.neighborhood import compute_neighborhood_at_position, compute_contact_neighborhood_at_position
31
+ from celldetective.gui.gui_utils import FigureCanvas
32
+ import matplotlib.pyplot as plt
33
+ from celldetective.filters import std_filter, median_filter, gauss_filter
34
+ from stardist import fill_label_holes
35
+ from celldetective.preprocessing import correct_background_model_free, estimate_background_per_condition, correct_background_model
36
+ from celldetective.utils import _estimate_scale_factor, _extract_channel_indices_from_config, _extract_channel_indices, ConfigSectionMap, _extract_nbr_channels_from_config, _get_img_num_per_channel, normalize_per_channel
37
+ from celldetective.gui.gui_utils import ThresholdLineEdit, QuickSliderLayout
38
+ from celldetective.gui.viewers import StackVisualizer, CellSizeViewer, ThresholdedStackVisualizer
39
+ from celldetective.gui.layouts import BackgroundModelFreeCorrectionLayout, ProtocolDesignerLayout, BackgroundFitCorrectionLayout, OperationLayout
40
+ from celldetective.gui import Styles
41
+
42
+ class ProcessPanel(QFrame, Styles):
43
+ def __init__(self, parent_window, mode):
32
44
 
33
45
  super().__init__()
34
- self.parent = parent
46
+ self.parent_window = parent_window
35
47
  self.mode = mode
36
- self.exp_channels = self.parent.exp_channels
37
- self.exp_dir = self.parent.exp_dir
48
+ self.exp_channels = self.parent_window.exp_channels
49
+ self.exp_dir = self.parent_window.exp_dir
50
+ self.exp_config = self.parent_window.exp_config
51
+ self.movie_prefix = self.parent_window.movie_prefix
38
52
  self.threshold_config_targets = None
39
53
  self.threshold_config_effectors = None
40
- self.wells = np.array(self.parent.wells,dtype=str)
54
+ self.wells = np.array(self.parent_window.wells,dtype=str)
41
55
  self.cellpose_calibrated = False
42
56
  self.stardist_calibrated = False
43
57
 
44
-
45
-
46
58
  self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
47
59
  self.grid = QGridLayout(self)
60
+ self.grid.setContentsMargins(5,5,5,5)
48
61
  self.generate_header()
49
62
 
50
63
  def generate_header(self):
@@ -67,14 +80,14 @@ class ProcessPanel(QFrame):
67
80
  self.select_all_btn.setIconSize(QSize(20, 20))
68
81
  self.all_ticked = False
69
82
  self.select_all_btn.clicked.connect(self.tick_all_actions)
70
- self.select_all_btn.setStyleSheet(self.parent.parent.button_select_all)
83
+ self.select_all_btn.setStyleSheet(self.button_select_all)
71
84
  self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
72
85
  #self.to_disable.append(self.all_tc_actions)
73
86
 
74
87
  self.collapse_btn = QPushButton()
75
88
  self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
76
89
  self.collapse_btn.setIconSize(QSize(25, 25))
77
- self.collapse_btn.setStyleSheet(self.parent.parent.button_select_all)
90
+ self.collapse_btn.setStyleSheet(self.button_select_all)
78
91
  self.grid.addWidget(self.collapse_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
79
92
 
80
93
  self.populate_contents()
@@ -88,22 +101,23 @@ class ProcessPanel(QFrame):
88
101
  if self.ContentsFrame.isHidden():
89
102
  self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
90
103
  self.collapse_btn.setIconSize(QSize(20, 20))
91
- self.parent.scroll.setMinimumHeight(int(500))
104
+ self.parent_window.scroll.setMinimumHeight(int(550))
92
105
  #self.parent.w.adjustSize()
93
- self.parent.adjustSize()
106
+ self.parent_window.adjustSize()
94
107
  #self.parent.scroll.adjustSize()
95
108
  else:
96
109
  self.collapse_btn.setIcon(icon(MDI6.chevron_up,color="black"))
97
110
  self.collapse_btn.setIconSize(QSize(20, 20))
98
111
  #self.parent.w.adjustSize()
99
112
  #self.parent.adjustSize()
100
- self.parent.scroll.setMinimumHeight(min(int(880), int(0.8*self.parent.screen_height)))
101
- self.parent.scroll.setMinimumWidth(410)
113
+ self.parent_window.scroll.setMinimumHeight(min(int(880), int(0.8*self.parent_window.screen_height)))
114
+ self.parent_window.scroll.setMinimumWidth(425)
102
115
 
103
116
  #self.parent.scroll.adjustSize()
104
117
 
105
118
  def populate_contents(self):
106
119
  self.ContentsFrame = QFrame()
120
+ self.ContentsFrame.setContentsMargins(5,5,5,5)
107
121
  self.grid_contents = QGridLayout(self.ContentsFrame)
108
122
  self.grid_contents.setContentsMargins(0,0,0,0)
109
123
  self.generate_segmentation_options()
@@ -113,7 +127,7 @@ class ProcessPanel(QFrame):
113
127
 
114
128
  self.grid_contents.addWidget(QHSeperationLine(), 9, 0, 1, 4)
115
129
  self.view_tab_btn = QPushButton("View table")
116
- self.view_tab_btn.setStyleSheet(self.parent.parent.button_style_sheet_2)
130
+ self.view_tab_btn.setStyleSheet(self.button_style_sheet_2)
117
131
  self.view_tab_btn.clicked.connect(self.view_table_ui)
118
132
  self.view_tab_btn.setToolTip('View table')
119
133
  self.view_tab_btn.setIcon(icon(MDI6.table,color="#1565c0"))
@@ -123,7 +137,7 @@ class ProcessPanel(QFrame):
123
137
 
124
138
  self.grid_contents.addWidget(QHSeperationLine(), 9, 0, 1, 4)
125
139
  self.submit_btn = QPushButton("Submit")
126
- self.submit_btn.setStyleSheet(self.parent.parent.button_style_sheet_2)
140
+ self.submit_btn.setStyleSheet(self.button_style_sheet_2)
127
141
  self.submit_btn.clicked.connect(self.process_population)
128
142
  self.grid_contents.addWidget(self.submit_btn, 11, 0, 1, 4)
129
143
 
@@ -147,7 +161,7 @@ class ProcessPanel(QFrame):
147
161
  self.classify_btn.setIcon(icon(MDI6.scatter_plot, color="black"))
148
162
  self.classify_btn.setIconSize(QSize(20, 20))
149
163
  self.classify_btn.setToolTip("Classify data.")
150
- self.classify_btn.setStyleSheet(self.parent.parent.button_select_all)
164
+ self.classify_btn.setStyleSheet(self.button_select_all)
151
165
  self.classify_btn.clicked.connect(self.open_classifier_ui)
152
166
  measure_layout.addWidget(self.classify_btn, 5) #4,2,1,1, alignment=Qt.AlignRight
153
167
 
@@ -155,7 +169,7 @@ class ProcessPanel(QFrame):
155
169
  self.check_measurements_btn.setIcon(icon(MDI6.eye_check_outline,color="black"))
156
170
  self.check_measurements_btn.setIconSize(QSize(20, 20))
157
171
  self.check_measurements_btn.setToolTip("Explore measurements in-situ.")
158
- self.check_measurements_btn.setStyleSheet(self.parent.parent.button_select_all)
172
+ self.check_measurements_btn.setStyleSheet(self.button_select_all)
159
173
  self.check_measurements_btn.clicked.connect(self.check_measurements)
160
174
  measure_layout.addWidget(self.check_measurements_btn, 5)
161
175
 
@@ -164,7 +178,7 @@ class ProcessPanel(QFrame):
164
178
  self.measurements_config_btn.setIcon(icon(MDI6.cog_outline,color="black"))
165
179
  self.measurements_config_btn.setIconSize(QSize(20, 20))
166
180
  self.measurements_config_btn.setToolTip("Configure measurements.")
167
- self.measurements_config_btn.setStyleSheet(self.parent.parent.button_select_all)
181
+ self.measurements_config_btn.setStyleSheet(self.button_select_all)
168
182
  self.measurements_config_btn.clicked.connect(self.open_measurement_configuration_ui)
169
183
  measure_layout.addWidget(self.measurements_config_btn, 5) #4,2,1,1, alignment=Qt.AlignRight
170
184
 
@@ -191,14 +205,14 @@ class ProcessPanel(QFrame):
191
205
  self.check_signals_btn.setIconSize(QSize(20, 20))
192
206
  self.check_signals_btn.clicked.connect(self.check_signals)
193
207
  self.check_signals_btn.setToolTip("Explore signals in-situ.")
194
- self.check_signals_btn.setStyleSheet(self.parent.parent.button_select_all)
208
+ self.check_signals_btn.setStyleSheet(self.button_select_all)
195
209
  signal_hlayout.addWidget(self.check_signals_btn, 6)
196
210
 
197
211
  self.config_signal_annotator_btn = QPushButton()
198
212
  self.config_signal_annotator_btn.setIcon(icon(MDI6.cog_outline,color="black"))
199
213
  self.config_signal_annotator_btn.setIconSize(QSize(20, 20))
200
214
  self.config_signal_annotator_btn.setToolTip("Configure the dynamic visualizer.")
201
- self.config_signal_annotator_btn.setStyleSheet(self.parent.parent.button_select_all)
215
+ self.config_signal_annotator_btn.setStyleSheet(self.button_select_all)
202
216
  self.config_signal_annotator_btn.clicked.connect(self.open_signal_annotator_configuration_ui)
203
217
  signal_hlayout.addWidget(self.config_signal_annotator_btn, 6)
204
218
 
@@ -220,7 +234,7 @@ class ProcessPanel(QFrame):
220
234
  self.train_signal_model_btn.setToolTip("Open a dialog box to create a new target segmentation model.")
221
235
  self.train_signal_model_btn.setIcon(icon(MDI6.redo_variant,color='black'))
222
236
  self.train_signal_model_btn.setIconSize(QSize(20, 20))
223
- self.train_signal_model_btn.setStyleSheet(self.parent.parent.button_style_sheet_3)
237
+ self.train_signal_model_btn.setStyleSheet(self.button_style_sheet_3)
224
238
  model_zoo_layout.addWidget(self.train_signal_model_btn, 5)
225
239
  self.train_signal_model_btn.clicked.connect(self.open_signal_model_config_ui)
226
240
 
@@ -255,7 +269,7 @@ class ProcessPanel(QFrame):
255
269
  # self.show_track_table_btn.setIcon(icon(MDI6.table,color="black"))
256
270
  # self.show_track_table_btn.setIconSize(QSize(20, 20))
257
271
  # self.show_track_table_btn.setToolTip("Show trajectories table.")
258
- # self.show_track_table_btn.setStyleSheet(self.parent.parent.button_select_all)
272
+ # self.show_track_table_btn.setStyleSheet(self.button_select_all)
259
273
  # #self.show_track_table_btn.clicked.connect(self.display_trajectory_table)
260
274
  # self.show_track_table_btn.setEnabled(False)
261
275
  # grid_track.addWidget(self.show_track_table_btn, 6) #4,3,1,1, alignment=Qt.AlignLeft
@@ -264,7 +278,7 @@ class ProcessPanel(QFrame):
264
278
  self.check_tracking_result_btn.setIcon(icon(MDI6.eye_check_outline,color="black"))
265
279
  self.check_tracking_result_btn.setIconSize(QSize(20, 20))
266
280
  self.check_tracking_result_btn.setToolTip("View raw bTrack output in napari.")
267
- self.check_tracking_result_btn.setStyleSheet(self.parent.parent.button_select_all)
281
+ self.check_tracking_result_btn.setStyleSheet(self.button_select_all)
268
282
  self.check_tracking_result_btn.clicked.connect(self.open_napari_tracking)
269
283
  self.check_tracking_result_btn.setEnabled(False)
270
284
  grid_track.addWidget(self.check_tracking_result_btn, 6) #4,3,1,1, alignment=Qt.AlignLeft
@@ -273,7 +287,7 @@ class ProcessPanel(QFrame):
273
287
  self.track_config_btn.setIcon(icon(MDI6.cog_outline,color="black"))
274
288
  self.track_config_btn.setIconSize(QSize(20, 20))
275
289
  self.track_config_btn.setToolTip("Configure tracking.")
276
- self.track_config_btn.setStyleSheet(self.parent.parent.button_select_all)
290
+ self.track_config_btn.setStyleSheet(self.button_select_all)
277
291
  self.track_config_btn.clicked.connect(self.open_tracking_configuration_ui)
278
292
  grid_track.addWidget(self.track_config_btn, 6) #4,2,1,1, alignment=Qt.AlignRight
279
293
 
@@ -301,7 +315,7 @@ class ProcessPanel(QFrame):
301
315
  self.check_seg_btn.setIcon(icon(MDI6.eye_check_outline,color="black"))
302
316
  self.check_seg_btn.setIconSize(QSize(20, 20))
303
317
  self.check_seg_btn.clicked.connect(self.check_segmentation)
304
- self.check_seg_btn.setStyleSheet(self.parent.parent.button_select_all)
318
+ self.check_seg_btn.setStyleSheet(self.button_select_all)
305
319
  self.check_seg_btn.setToolTip("View segmentation output in napari.")
306
320
  #self.check_seg_btn.setEnabled(False)
307
321
  #self.to_disable.append(self.control_target_seg)
@@ -321,7 +335,7 @@ class ProcessPanel(QFrame):
321
335
  self.upload_model_btn = QPushButton("UPLOAD")
322
336
  self.upload_model_btn.setIcon(icon(MDI6.upload,color="black"))
323
337
  self.upload_model_btn.setIconSize(QSize(20, 20))
324
- self.upload_model_btn.setStyleSheet(self.parent.parent.button_style_sheet_3)
338
+ self.upload_model_btn.setStyleSheet(self.button_style_sheet_3)
325
339
  self.upload_model_btn.setToolTip("Upload a new segmentation model\n(Deep learning or threshold-based).")
326
340
  model_zoo_layout.addWidget(self.upload_model_btn, 5)
327
341
  self.upload_model_btn.clicked.connect(self.upload_segmentation_model)
@@ -331,7 +345,7 @@ class ProcessPanel(QFrame):
331
345
  self.train_btn.setToolTip("Train or retrain a segmentation model\non newly annotated data.")
332
346
  self.train_btn.setIcon(icon(MDI6.redo_variant,color='black'))
333
347
  self.train_btn.setIconSize(QSize(20, 20))
334
- self.train_btn.setStyleSheet(self.parent.parent.button_style_sheet_3)
348
+ self.train_btn.setStyleSheet(self.button_style_sheet_3)
335
349
  self.train_btn.clicked.connect(self.open_segmentation_model_config_ui)
336
350
  model_zoo_layout.addWidget(self.train_btn, 5)
337
351
  # self.train_button_tc.clicked.connect(self.train_stardist_model_tc)
@@ -344,7 +358,7 @@ class ProcessPanel(QFrame):
344
358
 
345
359
  def check_segmentation(self):
346
360
 
347
- if not os.path.exists(os.sep.join([self.parent.pos,f'labels_{self.mode}', os.sep])):
361
+ if not os.path.exists(os.sep.join([self.parent_window.pos,f'labels_{self.mode}', os.sep])):
348
362
  msgBox = QMessageBox()
349
363
  msgBox.setIcon(QMessageBox.Question)
350
364
  msgBox.setText("No labels can be found for this position. Do you want to annotate from scratch?")
@@ -354,18 +368,18 @@ class ProcessPanel(QFrame):
354
368
  if returnValue == QMessageBox.No:
355
369
  return None
356
370
  else:
357
- os.mkdir(os.sep.join([self.parent.pos,f'labels_{self.mode}']))
358
- lbl = np.zeros((self.parent.shape_x, self.parent.shape_y), dtype=int)
359
- for i in range(self.parent.len_movie):
360
- imwrite(os.sep.join([self.parent.pos,f'labels_{self.mode}', str(i).zfill(4)+'.tif']), lbl)
371
+ os.mkdir(os.sep.join([self.parent_window.pos,f'labels_{self.mode}']))
372
+ lbl = np.zeros((self.parent_window.shape_x, self.parent_window.shape_y), dtype=int)
373
+ for i in range(self.parent_window.len_movie):
374
+ imwrite(os.sep.join([self.parent_window.pos,f'labels_{self.mode}', str(i).zfill(4)+'.tif']), lbl)
361
375
 
362
376
  #self.freeze()
363
377
  #QApplication.setOverrideCursor(Qt.WaitCursor)
364
- test = self.parent.locate_selected_position()
378
+ test = self.parent_window.locate_selected_position()
365
379
  if test:
366
380
  print('Memory use: ', dict(psutil.virtual_memory()._asdict()))
367
381
  try:
368
- control_segmentation_napari(self.parent.pos, prefix=self.parent.movie_prefix, population=self.mode,flush_memory=True)
382
+ control_segmentation_napari(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode,flush_memory=True)
369
383
  except Exception as e:
370
384
  msgBox = QMessageBox()
371
385
  msgBox.setIcon(QMessageBox.Warning)
@@ -377,14 +391,14 @@ class ProcessPanel(QFrame):
377
391
 
378
392
  def check_signals(self):
379
393
 
380
- test = self.parent.locate_selected_position()
394
+ test = self.parent_window.locate_selected_position()
381
395
  if test:
382
396
  self.SignalAnnotator = SignalAnnotator(self)
383
397
  self.SignalAnnotator.show()
384
398
 
385
399
  def check_measurements(self):
386
400
 
387
- test = self.parent.locate_selected_position()
401
+ test = self.parent_window.locate_selected_position()
388
402
  if test:
389
403
  self.MeasureAnnotator = MeasureAnnotator(self)
390
404
  self.MeasureAnnotator.show()
@@ -507,11 +521,11 @@ class ProcessPanel(QFrame):
507
521
 
508
522
  def process_population(self):
509
523
 
510
- if self.parent.well_list.currentText()=="*":
524
+ if self.parent_window.well_list.currentText()=="*":
511
525
  self.well_index = np.linspace(0,len(self.wells)-1,len(self.wells),dtype=int)
512
526
  else:
513
- self.well_index = [self.parent.well_list.currentIndex()]
514
- print(f"Processing well {self.parent.well_list.currentText()}...")
527
+ self.well_index = [self.parent_window.well_list.currentIndex()]
528
+ print(f"Processing well {self.parent_window.well_list.currentText()}...")
515
529
 
516
530
  # self.freeze()
517
531
  # QApplication.setOverrideCursor(Qt.WaitCursor)
@@ -523,7 +537,7 @@ class ProcessPanel(QFrame):
523
537
 
524
538
  loop_iter=0
525
539
 
526
- if self.parent.position_list.currentText()=="*":
540
+ if self.parent_window.position_list.currentText()=="*":
527
541
  msgBox = QMessageBox()
528
542
  msgBox.setIcon(QMessageBox.Question)
529
543
  msgBox.setText("If you continue, all positions will be processed.\nDo you want to proceed?")
@@ -546,7 +560,15 @@ class ProcessPanel(QFrame):
546
560
 
547
561
  layout = QVBoxLayout()
548
562
  self.diamWidget.setLayout(layout)
549
- self.diameter_le = QLineEdit('40')
563
+
564
+ self.view_diameter_btn = QPushButton()
565
+ self.view_diameter_btn.setStyleSheet(self.button_select_all)
566
+ self.view_diameter_btn.setIcon(icon(MDI6.image_check, color="black"))
567
+ self.view_diameter_btn.setToolTip("View stack.")
568
+ self.view_diameter_btn.setIconSize(QSize(20, 20))
569
+ self.view_diameter_btn.clicked.connect(self.view_current_stack_with_scale_bar)
570
+
571
+ self.diameter_le = ThresholdLineEdit(init_value=40, connected_buttons=[self.view_diameter_btn],placeholder='cell diameter in pixels', value_type='float')
550
572
 
551
573
  self.cellpose_channel_cb = [QComboBox() for i in range(2)]
552
574
  self.cellpose_channel_template = ['brightfield_channel', 'live_nuclei_channel']
@@ -563,12 +585,21 @@ class ProcessPanel(QFrame):
563
585
  else:
564
586
  self.cellpose_channel_cb[k].addItems(list(self.exp_channels))
565
587
  idx = self.cellpose_channel_cb[k].findText(self.cellpose_channel_template[k])
566
- self.cellpose_channel_cb[k].setCurrentIndex(idx)
588
+ if idx>0:
589
+ self.cellpose_channel_cb[k].setCurrentIndex(idx)
590
+ else:
591
+ self.cellpose_channel_cb[k].setCurrentIndex(0)
592
+
593
+ if k==1:
594
+ idx = self.cellpose_channel_cb[k].findText('None')
595
+ self.cellpose_channel_cb[k].setCurrentIndex(idx)
596
+
567
597
  layout.addLayout(hbox_channel)
568
598
 
569
599
  hbox = QHBoxLayout()
570
600
  hbox.addWidget(QLabel('diameter [px]: '), 33)
571
- hbox.addWidget(self.diameter_le, 66)
601
+ hbox.addWidget(self.diameter_le, 61)
602
+ hbox.addWidget(self.view_diameter_btn)
572
603
  layout.addLayout(hbox)
573
604
 
574
605
  self.flow_slider = QLabeledDoubleSlider()
@@ -625,7 +656,11 @@ class ProcessPanel(QFrame):
625
656
  else:
626
657
  self.stardist_channel_cb[k].addItems(list(self.exp_channels))
627
658
  idx = self.stardist_channel_cb[k].findText(self.stardist_channel_template[k])
628
- self.stardist_channel_cb[k].setCurrentIndex(idx)
659
+ if idx>0:
660
+ self.stardist_channel_cb[k].setCurrentIndex(idx)
661
+ else:
662
+ self.stardist_channel_cb[k].setCurrentIndex(0)
663
+
629
664
  layout.addLayout(hbox_channel)
630
665
 
631
666
  self.set_stardist_scale_btn = QPushButton('set')
@@ -639,15 +674,15 @@ class ProcessPanel(QFrame):
639
674
 
640
675
  for w_idx in self.well_index:
641
676
 
642
- pos = self.parent.positions[w_idx]
643
- if self.parent.position_list.currentText()=="*":
677
+ pos = self.parent_window.positions[w_idx]
678
+ if self.parent_window.position_list.currentText()=="*":
644
679
  pos_indices = np.linspace(0,len(pos)-1,len(pos),dtype=int)
645
680
  print("Processing all positions...")
646
681
  else:
647
- pos_indices = natsorted([pos.index(self.parent.position_list.currentText())])
648
- print(f"Processing position {self.parent.position_list.currentText()}...")
682
+ pos_indices = natsorted([pos.index(self.parent_window.position_list.currentText())])
683
+ print(f"Processing position {self.parent_window.position_list.currentText()}...")
649
684
 
650
- well = self.parent.wells[w_idx]
685
+ well = self.parent_window.wells[w_idx]
651
686
 
652
687
  for pos_idx in pos_indices:
653
688
 
@@ -661,7 +696,7 @@ class ProcessPanel(QFrame):
661
696
 
662
697
  if self.segment_action.isChecked():
663
698
 
664
- if len(glob(os.sep.join([self.pos, f'labels_{self.mode}','*.tif'])))>0 and self.parent.position_list.currentText()!="*":
699
+ if len(glob(os.sep.join([self.pos, f'labels_{self.mode}','*.tif'])))>0 and self.parent_window.position_list.currentText()!="*":
665
700
  msgBox = QMessageBox()
666
701
  msgBox.setIcon(QMessageBox.Question)
667
702
  msgBox.setText("Labels have already been produced for this position. Do you want to segment again?")
@@ -683,12 +718,12 @@ class ProcessPanel(QFrame):
683
718
  return None
684
719
  else:
685
720
  print(f"Segmentation from threshold config: {self.threshold_config}")
686
- segment_from_threshold_at_position(self.pos, self.mode, self.threshold_config, threads=self.parent.parent.n_threads)
721
+ segment_from_threshold_at_position(self.pos, self.mode, self.threshold_config, threads=self.parent_window.parent_window.n_threads)
687
722
  else:
688
- segment_at_position(self.pos, self.mode, self.model_name, stack_prefix=self.parent.movie_prefix, use_gpu=self.parent.parent.use_gpu, threads=self.parent.parent.n_threads)
723
+ segment_at_position(self.pos, self.mode, self.model_name, stack_prefix=self.parent_window.movie_prefix, use_gpu=self.parent_window.parent_window.use_gpu, threads=self.parent_window.parent_window.n_threads)
689
724
 
690
725
  if self.track_action.isChecked():
691
- if os.path.exists(os.sep.join([self.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])) and self.parent.position_list.currentText()!="*":
726
+ if os.path.exists(os.sep.join([self.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])) and self.parent_window.position_list.currentText()!="*":
692
727
  msgBox = QMessageBox()
693
728
  msgBox.setIcon(QMessageBox.Question)
694
729
  msgBox.setText("A trajectory set already exists. Previously annotated data for\nthis position will be lost. Do you want to proceed?")
@@ -697,10 +732,10 @@ class ProcessPanel(QFrame):
697
732
  returnValue = msgBox.exec()
698
733
  if returnValue == QMessageBox.No:
699
734
  return None
700
- track_at_position(self.pos, self.mode, threads=self.parent.parent.n_threads)
735
+ track_at_position(self.pos, self.mode, threads=self.parent_window.parent_window.n_threads)
701
736
 
702
737
  if self.measure_action.isChecked():
703
- measure_at_position(self.pos, self.mode, threads=self.parent.parent.n_threads)
738
+ measure_at_position(self.pos, self.mode, threads=self.parent_window.parent_window.n_threads)
704
739
 
705
740
  table = os.sep.join([self.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])
706
741
  if self.signal_analysis_action.isChecked() and os.path.exists(table):
@@ -712,7 +747,7 @@ class ProcessPanel(QFrame):
712
747
  print(cols, 'class_color in cols')
713
748
  colors = list(table['class_color'].to_numpy())
714
749
  if 'tab:orange' in colors or 'tab:cyan' in colors:
715
- if self.parent.position_list.currentText()!="*":
750
+ if self.parent_window.position_list.currentText()!="*":
716
751
  msgBox = QMessageBox()
717
752
  msgBox.setIcon(QMessageBox.Question)
718
753
  msgBox.setText("The signals of the cells in the position appear to have been annotated... Do you want to proceed?")
@@ -725,15 +760,35 @@ class ProcessPanel(QFrame):
725
760
 
726
761
 
727
762
  # self.stack = None
728
- self.parent.update_position_options()
763
+ self.parent_window.update_position_options()
729
764
  if self.segment_action.isChecked():
730
765
  self.segment_action.setChecked(False)
731
766
 
732
767
  # QApplication.restoreOverrideCursor()
733
768
  # self.unfreeze()
734
769
 
770
+ def view_current_stack_with_scale_bar(self):
771
+
772
+ self.parent_window.locate_image()
773
+ if self.parent_window.current_stack is not None:
774
+ self.viewer = CellSizeViewer(
775
+ initial_diameter = float(self.diameter_le.text().replace(',','.')),
776
+ parent_le = self.diameter_le,
777
+ stack_path=self.parent_window.current_stack,
778
+ window_title=f'Position {self.parent_window.position_list.currentText()}',
779
+ frame_slider = True,
780
+ contrast_slider = True,
781
+ channel_cb = True,
782
+ channel_names = self.parent_window.exp_channels,
783
+ n_channels = self.parent_window.nbr_channels,
784
+ PxToUm = 1,
785
+ )
786
+ self.viewer.show()
787
+
788
+
789
+
735
790
  def open_napari_tracking(self):
736
- control_tracking_btrack(self.parent.pos, prefix=self.parent.movie_prefix, population=self.mode, threads=self.parent.parent.n_threads)
791
+ control_tracking_btrack(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode, threads=self.parent_window.parent_window.n_threads)
737
792
 
738
793
  def view_table_ui(self):
739
794
 
@@ -744,7 +799,7 @@ class ProcessPanel(QFrame):
744
799
  plot_mode = 'plot_track_signals'
745
800
  if 'TRACK_ID' not in list(self.df.columns):
746
801
  plot_mode = 'static'
747
- self.tab_ui = TableUI(self.df, f"Well {self.parent.well_list.currentText()}; Position {self.parent.position_list.currentText()}", population=self.mode, plot_mode=plot_mode)
802
+ self.tab_ui = TableUI(self.df, f"Well {self.parent_window.well_list.currentText()}; Position {self.parent_window.position_list.currentText()}", population=self.mode, plot_mode=plot_mode)
748
803
  self.tab_ui.show()
749
804
  else:
750
805
  print('Table could not be loaded...')
@@ -782,12 +837,12 @@ class ProcessPanel(QFrame):
782
837
 
783
838
  """
784
839
 
785
- self.well_option = self.parent.well_list.currentIndex()
840
+ self.well_option = self.parent_window.well_list.currentIndex()
786
841
  if self.well_option==len(self.wells):
787
842
  wo = '*'
788
843
  else:
789
844
  wo = self.well_option
790
- self.position_option = self.parent.position_list.currentIndex()
845
+ self.position_option = self.parent_window.position_list.currentIndex()
791
846
  if self.position_option==0:
792
847
  po = '*'
793
848
  else:
@@ -799,9 +854,9 @@ class ProcessPanel(QFrame):
799
854
 
800
855
  def set_cellpose_scale(self):
801
856
 
802
- scale = self.parent.PxToUm * float(self.diameter_le.text()) / 30.0
857
+ scale = self.parent_window.PxToUm * float(self.diameter_le.get_threshold()) / 30.0
803
858
  if self.model_name=="CP_nuclei":
804
- scale = self.parent.PxToUm * float(self.diameter_le.text()) / 17.0
859
+ scale = self.parent_window.PxToUm * float(self.diameter_le.get_threshold()) / 17.0
805
860
  flow_thresh = self.flow_slider.value()
806
861
  cellprob_thresh = self.cellprob_slider.value()
807
862
  model_complete_path = locate_segmentation_model(self.model_name)
@@ -848,14 +903,15 @@ class ProcessPanel(QFrame):
848
903
  self.process_population()
849
904
 
850
905
 
851
- class NeighPanel(QFrame):
852
- def __init__(self, parent):
906
+ class NeighPanel(QFrame, Styles):
907
+ def __init__(self, parent_window):
853
908
 
854
909
  super().__init__()
855
- self.parent = parent
856
- self.exp_channels = self.parent.exp_channels
857
- self.exp_dir = self.parent.exp_dir
858
- self.wells = np.array(self.parent.wells,dtype=str)
910
+ self.parent_window = parent_window
911
+ self.exp_channels = self.parent_window.exp_channels
912
+ self.exp_dir = self.parent_window.exp_dir
913
+ self.wells = np.array(self.parent_window.wells,dtype=str)
914
+ self.protocols = []
859
915
 
860
916
  self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
861
917
  self.grid = QGridLayout(self)
@@ -869,26 +925,21 @@ class NeighPanel(QFrame):
869
925
  """
870
926
 
871
927
  panel_title = QLabel(f"NEIGHBORHOOD")
872
- panel_title.setStyleSheet("""
873
- font-weight: bold;
874
- padding: 0px;
875
- """)
928
+ panel_title.setStyleSheet(self.block_title)
876
929
 
877
930
  self.grid.addWidget(panel_title, 0, 0, 1, 4, alignment=Qt.AlignCenter)
878
931
 
879
- self.select_all_btn = QPushButton()
880
- self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
881
- self.select_all_btn.setIconSize(QSize(20, 20))
882
- self.all_ticked = False
883
- #self.select_all_btn.clicked.connect(self.tick_all_actions)
884
- self.select_all_btn.setStyleSheet(self.parent.parent.button_select_all)
885
- self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
886
- #self.to_disable.append(self.all_tc_actions)
932
+ # self.select_all_btn = QPushButton()
933
+ # self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
934
+ # self.select_all_btn.setIconSize(QSize(20, 20))
935
+ # self.all_ticked = False
936
+ # self.select_all_btn.setStyleSheet(self.button_select_all)
937
+ # self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
887
938
 
888
939
  self.collapse_btn = QPushButton()
889
940
  self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
890
941
  self.collapse_btn.setIconSize(QSize(25, 25))
891
- self.collapse_btn.setStyleSheet(self.parent.parent.button_select_all)
942
+ self.collapse_btn.setStyleSheet(self.button_select_all)
892
943
  self.grid.addWidget(self.collapse_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
893
944
 
894
945
  self.populate_contents()
@@ -899,22 +950,28 @@ class NeighPanel(QFrame):
899
950
  self.ContentsFrame.hide()
900
951
 
901
952
  def collapse_advanced(self):
953
+
902
954
  if self.ContentsFrame.isHidden():
903
955
  self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
904
956
  self.collapse_btn.setIconSize(QSize(20, 20))
905
- self.parent.w.adjustSize()
906
- self.parent.adjustSize()
957
+ self.parent_window.scroll.setMinimumHeight(int(550))
958
+ #self.parent.w.adjustSize()
959
+ self.parent_window.adjustSize()
907
960
  else:
908
961
  self.collapse_btn.setIcon(icon(MDI6.chevron_up,color="black"))
909
962
  self.collapse_btn.setIconSize(QSize(20, 20))
910
- self.parent.w.adjustSize()
911
- self.parent.adjustSize()
963
+ #self.parent.w.adjustSize()
964
+ #self.parent.adjustSize()
965
+ self.parent_window.scroll.setMinimumHeight(min(int(750), int(0.8*self.parent_window.screen_height)))
966
+ self.parent_window.scroll.setMinimumWidth(425)
967
+
912
968
 
913
969
  def populate_contents(self):
914
970
 
915
971
  self.ContentsFrame = QFrame()
916
972
  self.grid_contents = QGridLayout(self.ContentsFrame)
917
973
  self.grid_contents.setContentsMargins(0,0,0,0)
974
+ self.grid_contents.setSpacing(0)
918
975
 
919
976
 
920
977
  # DISTANCE NEIGHBORHOOD
@@ -922,78 +979,108 @@ class NeighPanel(QFrame):
922
979
  dist_neigh_hbox.setContentsMargins(0,0,0,0)
923
980
  dist_neigh_hbox.setSpacing(0)
924
981
 
925
- self.dist_neigh_action = QCheckBox("DISTANCE CUT")
926
- self.dist_neigh_action.setStyleSheet("""
927
- font-size: 10px;
928
- padding-left: 10px;
929
- """)
930
- self.dist_neigh_action.setIcon(icon(MDI6.circle_expand, color='black'))
931
- self.dist_neigh_action.setToolTip("Match cells for which the center of mass is within a threshold distance of each other.")
982
+ self.dist_neigh_action = QLabel("ISOTROPIC DISTANCE THRESHOLD")
983
+ self.dist_neigh_action.setStyleSheet(self.action_lbl_style_sheet)
984
+ #self.dist_neigh_action.setIcon(icon(MDI6.circle_expand, color='black'))
985
+ self.dist_neigh_action.setToolTip("Define an isotropic neighborhood between the center of mass\nof the cells, within a threshold distance.")
932
986
  #self.segment_action.toggled.connect(self.enable_segmentation_model_list)
933
987
  #self.to_disable.append(self.segment_action)
934
- dist_neigh_hbox.addWidget(self.dist_neigh_action, 95)
935
988
 
936
989
  self.config_distance_neigh_btn = QPushButton()
937
- self.config_distance_neigh_btn.setIcon(icon(MDI6.cog_outline,color="black"))
990
+ self.config_distance_neigh_btn.setIcon(icon(MDI6.plus,color="black"))
938
991
  self.config_distance_neigh_btn.setIconSize(QSize(20, 20))
939
- self.config_distance_neigh_btn.setToolTip("Configure distance cut neighbourhood computation.")
940
- self.config_distance_neigh_btn.setStyleSheet(self.parent.parent.button_select_all)
941
- self.config_distance_neigh_btn.clicked.connect(self.open_config_neighborhood)
992
+ self.config_distance_neigh_btn.setToolTip("Configure.")
993
+ self.config_distance_neigh_btn.setStyleSheet(self.button_select_all)
994
+ self.config_distance_neigh_btn.clicked.connect(self.open_config_distance_threshold_neighborhood)
942
995
  dist_neigh_hbox.addWidget(self.config_distance_neigh_btn,5)
996
+ dist_neigh_hbox.addWidget(self.dist_neigh_action, 95)
943
997
 
944
998
  self.grid_contents.addLayout(dist_neigh_hbox, 0,0,1,4)
945
999
 
946
- # MASK INTERSECTION NEIGHBORHOOD
947
- # mask_neigh_hbox = QHBoxLayout()
948
- # mask_neigh_hbox.setContentsMargins(0,0,0,0)
949
- # mask_neigh_hbox.setSpacing(0)
950
-
951
- # self.mask_neigh_action = QCheckBox("MASK INTERSECTION")
952
- # self.mask_neigh_action.setStyleSheet("""
953
- # font-size: 10px;
954
- # padding-left: 10px;
955
- # """)
956
- # self.mask_neigh_action.setIcon(icon(MDI6.domino_mask, color='black'))
957
- # self.mask_neigh_action.setToolTip("Match cells that are co-localizing.")
958
- # #self.segment_action.toggled.connect(self.enable_segmentation_model_list)
959
- # #self.to_disable.append(self.segment_action)
960
- # mask_neigh_hbox.addWidget(self.mask_neigh_action, 95)
1000
+ # DISTANCE NEIGHBORHOOD
1001
+ contact_neighborhood_layout = QHBoxLayout()
1002
+ contact_neighborhood_layout.setContentsMargins(0,0,0,0)
1003
+ contact_neighborhood_layout.setSpacing(0)
1004
+
1005
+ self.contact_neigh_action = QLabel("MASK CONTACT")
1006
+ self.contact_neigh_action.setToolTip("Identify touching cell masks, within a threshold edge distance.")
1007
+ self.contact_neigh_action.setStyleSheet(self.action_lbl_style_sheet)
1008
+ #self.contact_neigh_action.setIcon(icon(MDI6.transition_masked, color='black'))
961
1009
 
962
- # self.config_mask_neigh_btn = QPushButton()
963
- # self.config_mask_neigh_btn.setIcon(icon(MDI6.cog_outline,color="black"))
964
- # self.config_mask_neigh_btn.setIconSize(QSize(20, 20))
965
- # self.config_mask_neigh_btn.setToolTip("Configure mask intersection computation.")
966
- # self.config_mask_neigh_btn.setStyleSheet(self.parent.parent.button_select_all)
967
- # #self.config_distance_neigh_btn.clicked.connect(self.open_signal_annotator_configuration_ui)
968
- # mask_neigh_hbox.addWidget(self.config_mask_neigh_btn,5)
1010
+ self.config_contact_neigh_btn = QPushButton()
1011
+ self.config_contact_neigh_btn.setIcon(icon(MDI6.plus,color="black"))
1012
+ self.config_contact_neigh_btn.setIconSize(QSize(20, 20))
1013
+ self.config_contact_neigh_btn.setToolTip("Configure.")
1014
+ self.config_contact_neigh_btn.setStyleSheet(self.button_select_all)
1015
+ self.config_contact_neigh_btn.clicked.connect(self.open_config_contact_neighborhood)
1016
+ contact_neighborhood_layout.addWidget(self.config_contact_neigh_btn,5)
1017
+ contact_neighborhood_layout.addWidget(self.contact_neigh_action, 95)
969
1018
 
970
- # self.grid_contents.addLayout(mask_neigh_hbox, 1,0,1,4)
1019
+ self.grid_contents.addLayout(contact_neighborhood_layout, 1,0,1,4)
971
1020
 
972
1021
  self.grid_contents.addWidget(QHSeperationLine(), 2, 0, 1, 4)
1022
+
1023
+ self.delete_protocol_btn = QPushButton('')
1024
+ self.delete_protocol_btn.setStyleSheet(self.button_select_all)
1025
+ self.delete_protocol_btn.setIcon(icon(MDI6.trash_can, color="black"))
1026
+ self.delete_protocol_btn.setToolTip("Remove a neighborhood computation.")
1027
+ self.delete_protocol_btn.setIconSize(QSize(20, 20))
1028
+ self.delete_protocol_btn.clicked.connect(self.remove_protocol_from_list)
1029
+
1030
+ self.protocol_list_lbl = QLabel('Neighborhoods to compute: ')
1031
+ self.protocol_list = QListWidget()
1032
+ self.protocol_list.setToolTip("Neighborhoods to compute sequentially.")
1033
+
1034
+ list_header_layout = QHBoxLayout()
1035
+ list_header_layout.addWidget(self.protocol_list_lbl)
1036
+ list_header_layout.addWidget(self.delete_protocol_btn, alignment=Qt.AlignRight)
1037
+ self.grid_contents.addLayout(list_header_layout, 3, 0, 1, 4)
1038
+ self.grid_contents.addWidget(self.protocol_list, 4, 0, 1, 4)
1039
+
973
1040
  self.submit_btn = QPushButton("Submit")
974
- self.submit_btn.setStyleSheet(self.parent.parent.button_style_sheet_2)
1041
+ self.submit_btn.setStyleSheet(self.button_style_sheet_2)
1042
+ self.submit_btn.setToolTip("Compute the neighborhoods of the selected positions.")
975
1043
  self.submit_btn.clicked.connect(self.process_neighborhood)
976
- self.grid_contents.addWidget(self.submit_btn, 3, 0, 1, 4)
1044
+ self.grid_contents.addWidget(self.submit_btn, 5, 0, 1, 4)
1045
+
1046
+ def remove_protocol_from_list(self):
1047
+
1048
+ current_item = self.protocol_list.currentRow()
1049
+ if current_item > -1:
1050
+ del self.protocols[current_item]
1051
+ self.protocol_list.takeItem(current_item)
1052
+
1053
+ def open_config_distance_threshold_neighborhood(self):
1054
+
1055
+ self.ConfigNeigh = ConfigNeighborhoods(parent_window=self,
1056
+ neighborhood_type='distance_threshold',
1057
+ neighborhood_parameter_name='threshold distance',
1058
+ )
1059
+ self.ConfigNeigh.show()
977
1060
 
978
- def open_config_neighborhood(self):
1061
+ def open_config_contact_neighborhood(self):
979
1062
 
980
- self.ConfigNeigh = ConfigNeighborhoods(self)
1063
+ self.ConfigNeigh = ConfigNeighborhoods(parent_window=self,
1064
+ neighborhood_type='mask_contact',
1065
+ neighborhood_parameter_name='tolerance contact distance',
1066
+ )
981
1067
  self.ConfigNeigh.show()
982
1068
 
1069
+
983
1070
  def process_neighborhood(self):
984
1071
 
985
- if self.parent.well_list.currentText()=="*":
1072
+ if self.parent_window.well_list.currentText()=="*":
986
1073
  self.well_index = np.linspace(0,len(self.wells)-1,len(self.wells),dtype=int)
987
1074
  else:
988
- self.well_index = [self.parent.well_list.currentIndex()]
989
- print(f"Processing well {self.parent.well_list.currentText()}...")
1075
+ self.well_index = [self.parent_window.well_list.currentIndex()]
1076
+ print(f"Processing well {self.parent_window.well_list.currentText()}...")
990
1077
 
991
1078
  # self.freeze()
992
1079
  # QApplication.setOverrideCursor(Qt.WaitCursor)
993
1080
 
994
1081
  loop_iter=0
995
1082
 
996
- if self.parent.position_list.currentText()=="*":
1083
+ if self.parent_window.position_list.currentText()=="*":
997
1084
  msgBox = QMessageBox()
998
1085
  msgBox.setIcon(QMessageBox.Question)
999
1086
  msgBox.setText("If you continue, all positions will be processed.\nDo you want to proceed?")
@@ -1005,15 +1092,15 @@ class NeighPanel(QFrame):
1005
1092
 
1006
1093
  for w_idx in self.well_index:
1007
1094
 
1008
- pos = self.parent.positions[w_idx]
1009
- if self.parent.position_list.currentText()=="*":
1095
+ pos = self.parent_window.positions[w_idx]
1096
+ if self.parent_window.position_list.currentText()=="*":
1010
1097
  pos_indices = np.linspace(0,len(pos)-1,len(pos),dtype=int)
1011
1098
  print("Processing all positions...")
1012
1099
  else:
1013
- pos_indices = natsorted([pos.index(self.parent.position_list.currentText())])
1014
- print(f"Processing position {self.parent.position_list.currentText()}...")
1100
+ pos_indices = natsorted([pos.index(self.parent_window.position_list.currentText())])
1101
+ print(f"Processing position {self.parent_window.position_list.currentText()}...")
1015
1102
 
1016
- well = self.parent.wells[w_idx]
1103
+ well = self.parent_window.wells[w_idx]
1017
1104
 
1018
1105
  for pos_idx in pos_indices:
1019
1106
 
@@ -1025,31 +1112,224 @@ class NeighPanel(QFrame):
1025
1112
  if not os.path.exists(self.pos + os.sep.join(['output','tables'])+os.sep):
1026
1113
  os.mkdir(self.pos + os.sep.join(['output','tables'])+os.sep)
1027
1114
 
1028
- if self.dist_neigh_action.isChecked():
1029
-
1030
- config = self.exp_dir + os.sep.join(["configs","neighborhood_instructions.json"])
1031
-
1032
- if not os.path.exists(config):
1033
- print('config could not be found', config)
1034
- msgBox = QMessageBox()
1035
- msgBox.setIcon(QMessageBox.Warning)
1036
- msgBox.setText("Please define a neighborhood first.")
1037
- msgBox.setWindowTitle("Info")
1038
- msgBox.setStandardButtons(QMessageBox.Ok)
1039
- returnValue = msgBox.exec()
1040
- return None
1041
-
1042
- with open(config, 'r') as f:
1043
- config = json.load(f)
1044
-
1045
- compute_neighborhood_at_position(self.pos,
1046
- config['distance'],
1047
- population=config['population'],
1048
- theta_dist=None,
1049
- img_shape=(self.parent.shape_x,self.parent.shape_y),
1050
- return_tables=False,
1051
- clear_neigh=config['clear_neigh'],
1052
- event_time_col=config['event_time_col'],
1053
- neighborhood_kwargs=config['neighborhood_kwargs'],
1054
- )
1055
- print('Done.')
1115
+ for protocol in self.protocols:
1116
+
1117
+ if protocol['neighborhood_type']=='distance_threshold':
1118
+
1119
+ compute_neighborhood_at_position(self.pos,
1120
+ protocol['distance'],
1121
+ population=protocol['population'],
1122
+ theta_dist=None,
1123
+ img_shape=(self.parent_window.shape_x,self.parent_window.shape_y),
1124
+ return_tables=False,
1125
+ clear_neigh=protocol['clear_neigh'],
1126
+ event_time_col=protocol['event_time_col'],
1127
+ neighborhood_kwargs=protocol['neighborhood_kwargs'],
1128
+ )
1129
+
1130
+ elif protocol['neighborhood_type']=='mask_contact':
1131
+
1132
+ compute_contact_neighborhood_at_position(self.pos,
1133
+ protocol['distance'],
1134
+ population=protocol['population'],
1135
+ theta_dist=None,
1136
+ img_shape=(self.parent_window.shape_x,self.parent_window.shape_y),
1137
+ return_tables=False,
1138
+ clear_neigh=protocol['clear_neigh'],
1139
+ event_time_col=protocol['event_time_col'],
1140
+ neighborhood_kwargs=protocol['neighborhood_kwargs'],
1141
+ )
1142
+ print('Done.')
1143
+
1144
+
1145
+ class PreprocessingPanel(QFrame, Styles):
1146
+
1147
+ def __init__(self, parent_window):
1148
+
1149
+ super().__init__()
1150
+ self.parent_window = parent_window
1151
+ self.exp_channels = self.parent_window.exp_channels
1152
+ self.exp_dir = self.parent_window.exp_dir
1153
+ self.wells = np.array(self.parent_window.wells,dtype=str)
1154
+ exp_config = self.exp_dir + "config.ini"
1155
+ self.channel_names, self.channels = extract_experiment_channels(exp_config)
1156
+ self.channel_names = np.array(self.channel_names)
1157
+ self.background_correction = []
1158
+ self.onlyFloat = QDoubleValidator()
1159
+ self.onlyInt = QIntValidator()
1160
+
1161
+ self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
1162
+ self.grid = QGridLayout(self)
1163
+
1164
+ self.generate_header()
1165
+
1166
+ def generate_header(self):
1167
+
1168
+ """
1169
+ Read the mode and prepare a collapsable block to process a specific cell population.
1170
+
1171
+ """
1172
+
1173
+ panel_title = QLabel(f"PREPROCESSING")
1174
+ panel_title.setStyleSheet("""
1175
+ font-weight: bold;
1176
+ padding: 0px;
1177
+ """)
1178
+
1179
+ self.grid.addWidget(panel_title, 0, 0, 1, 4, alignment=Qt.AlignCenter)
1180
+
1181
+ self.select_all_btn = QPushButton()
1182
+ self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
1183
+ self.select_all_btn.setIconSize(QSize(20, 20))
1184
+ self.all_ticked = False
1185
+ #self.select_all_btn.clicked.connect(self.tick_all_actions)
1186
+ self.select_all_btn.setStyleSheet(self.button_select_all)
1187
+ self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
1188
+ #self.to_disable.append(self.all_tc_actions)
1189
+
1190
+ self.collapse_btn = QPushButton()
1191
+ self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
1192
+ self.collapse_btn.setIconSize(QSize(25, 25))
1193
+ self.collapse_btn.setStyleSheet(self.button_select_all)
1194
+ self.grid.addWidget(self.collapse_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
1195
+
1196
+ self.populate_contents()
1197
+
1198
+ self.grid.addWidget(self.ContentsFrame, 1, 0, 1, 4, alignment=Qt.AlignTop)
1199
+ self.collapse_btn.clicked.connect(lambda: self.ContentsFrame.setHidden(not self.ContentsFrame.isHidden()))
1200
+ self.collapse_btn.clicked.connect(self.collapse_advanced)
1201
+ self.ContentsFrame.hide()
1202
+
1203
+ def collapse_advanced(self):
1204
+
1205
+ if self.ContentsFrame.isHidden():
1206
+ self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
1207
+ self.collapse_btn.setIconSize(QSize(20, 20))
1208
+ self.parent_window.scroll.setMinimumHeight(int(550))
1209
+ #self.parent.w.adjustSize()
1210
+ self.parent_window.adjustSize()
1211
+ else:
1212
+ self.collapse_btn.setIcon(icon(MDI6.chevron_up,color="black"))
1213
+ self.collapse_btn.setIconSize(QSize(20, 20))
1214
+ #self.parent.w.adjustSize()
1215
+ #self.parent.adjustSize()
1216
+ self.parent_window.scroll.setMinimumHeight(min(int(880), int(0.8*self.parent_window.screen_height)))
1217
+ self.parent_window.scroll.setMinimumWidth(425)
1218
+
1219
+ def populate_contents(self):
1220
+
1221
+ self.ContentsFrame = QFrame()
1222
+ self.grid_contents = QGridLayout(self.ContentsFrame)
1223
+
1224
+ self.model_free_correction_layout = BackgroundModelFreeCorrectionLayout(self)
1225
+ self.fit_correction_layout = BackgroundFitCorrectionLayout(self)
1226
+
1227
+ self.protocol_layout = ProtocolDesignerLayout(parent_window=self,
1228
+ tab_layouts=[self.fit_correction_layout, self.model_free_correction_layout],
1229
+ tab_names=['Fit', 'Model-free'],
1230
+ title='BACKGROUND CORRECTION',
1231
+ list_title='Corrections to apply:')
1232
+ self.grid_contents.addLayout(self.protocol_layout,0,0,1,4)
1233
+ self.submit_preprocessing_btn = QPushButton("Submit")
1234
+ self.submit_preprocessing_btn.setStyleSheet(self.button_style_sheet_2)
1235
+ self.submit_preprocessing_btn.clicked.connect(self.launch_preprocessing)
1236
+ self.grid_contents.addWidget(self.submit_preprocessing_btn, 1,0,1,4)
1237
+
1238
+
1239
+ def launch_preprocessing(self):
1240
+
1241
+ msgBox1 = QMessageBox()
1242
+ msgBox1.setIcon(QMessageBox.Question)
1243
+ msgBox1.setText("Do you want to apply the preprocessing\nto all wells and positions?")
1244
+ msgBox1.setWindowTitle("Selection")
1245
+ msgBox1.setStandardButtons(QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
1246
+ returnValue = msgBox1.exec()
1247
+ if returnValue == QMessageBox.Cancel:
1248
+ return None
1249
+ elif returnValue == QMessageBox.Yes:
1250
+ self.parent_window.well_list.setCurrentIndex(self.parent_window.well_list.count()-1)
1251
+ self.parent_window.position_list.setCurrentIndex(0)
1252
+ elif returnValue == QMessageBox.No:
1253
+ msgBox2 = QMessageBox()
1254
+ msgBox2.setIcon(QMessageBox.Question)
1255
+ msgBox2.setText("Do you want to apply the preprocessing\nto the positions selected at the top only?")
1256
+ msgBox2.setWindowTitle("Selection")
1257
+ msgBox2.setStandardButtons(QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
1258
+ returnValue = msgBox2.exec()
1259
+ if returnValue == QMessageBox.Cancel:
1260
+ return None
1261
+ if returnValue == QMessageBox.No:
1262
+ return None
1263
+
1264
+ print('Proceed with correction...')
1265
+
1266
+ if self.parent_window.well_list.currentText()=='*':
1267
+ well_option = "*"
1268
+ else:
1269
+ well_option = self.parent_window.well_list.currentIndex()
1270
+
1271
+ if self.parent_window.position_list.currentText()=='*':
1272
+ pos_option = "*"
1273
+ else:
1274
+ pos_option = self.parent_window.position_list.currentIndex()-1
1275
+
1276
+
1277
+ for k,correction_protocol in enumerate(self.protocol_layout.protocols):
1278
+
1279
+ movie_prefix = None
1280
+ export_prefix = 'Corrected'
1281
+ if k>0:
1282
+ # switch source stack to cumulate multi-channel preprocessing
1283
+ movie_prefix = 'Corrected'
1284
+ export_prefix = None
1285
+
1286
+ if correction_protocol['correction_type']=='model-free':
1287
+
1288
+ correct_background_model_free(self.exp_dir,
1289
+ well_option=well_option,
1290
+ position_option=pos_option,
1291
+ export= True,
1292
+ return_stacks=False,
1293
+ show_progress_per_well = True,
1294
+ show_progress_per_pos = True,
1295
+ movie_prefix = movie_prefix,
1296
+ export_prefix = export_prefix,
1297
+ **correction_protocol,
1298
+ )
1299
+
1300
+ elif correction_protocol['correction_type']=='fit':
1301
+
1302
+ correct_background_model(self.exp_dir,
1303
+ well_option=well_option,
1304
+ position_option=pos_option,
1305
+ export= True,
1306
+ return_stacks=False,
1307
+ show_progress_per_well = True,
1308
+ show_progress_per_pos = True,
1309
+ movie_prefix = movie_prefix,
1310
+ export_prefix = export_prefix,
1311
+ **correction_protocol,
1312
+ )
1313
+ print('Done.')
1314
+
1315
+
1316
+ def locate_image(self):
1317
+
1318
+ """
1319
+ Load the first frame of the first movie found in the experiment folder as a sample.
1320
+ """
1321
+
1322
+ movies = glob(self.parent_window.pos + os.sep.join(['movie', f"{self.parent_window.movie_prefix}*.tif"]))
1323
+
1324
+ if len(movies) == 0:
1325
+ msgBox = QMessageBox()
1326
+ msgBox.setIcon(QMessageBox.Warning)
1327
+ msgBox.setText("Please select a position containing a movie...")
1328
+ msgBox.setWindowTitle("Warning")
1329
+ msgBox.setStandardButtons(QMessageBox.Ok)
1330
+ returnValue = msgBox.exec()
1331
+ if returnValue == QMessageBox.Ok:
1332
+ self.current_stack = None
1333
+ return None
1334
+ else:
1335
+ self.current_stack = movies[0]