celldetective 1.4.0__py3-none-any.whl → 1.4.1__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 (78) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/exceptions.py +11 -0
  3. celldetective/filters.py +7 -1
  4. celldetective/gui/InitWindow.py +4 -1
  5. celldetective/gui/__init__.py +2 -9
  6. celldetective/gui/about.py +2 -2
  7. celldetective/gui/base_annotator.py +786 -0
  8. celldetective/gui/classifier_widget.py +18 -13
  9. celldetective/gui/configure_new_exp.py +51 -30
  10. celldetective/gui/control_panel.py +10 -7
  11. celldetective/gui/{signal_annotator.py → event_annotator.py} +473 -1437
  12. celldetective/gui/generic_signal_plot.py +2 -1
  13. celldetective/gui/gui_utils.py +5 -2
  14. celldetective/gui/help/neighborhood.json +2 -2
  15. celldetective/gui/layouts.py +21 -11
  16. celldetective/gui/{signal_annotator2.py → pair_event_annotator.py} +3 -1
  17. celldetective/gui/process_block.py +129 -91
  18. celldetective/gui/processes/downloader.py +37 -34
  19. celldetective/gui/processes/measure_cells.py +14 -8
  20. celldetective/gui/processes/segment_cells.py +21 -6
  21. celldetective/gui/processes/track_cells.py +12 -13
  22. celldetective/gui/settings/__init__.py +7 -0
  23. celldetective/gui/settings/_settings_base.py +70 -0
  24. celldetective/gui/{retrain_signal_model_options.py → settings/_settings_event_model_training.py} +35 -91
  25. celldetective/gui/{measurement_options.py → settings/_settings_measurements.py} +28 -81
  26. celldetective/gui/{neighborhood_options.py → settings/_settings_neighborhood.py} +1 -1
  27. celldetective/gui/settings/_settings_segmentation.py +49 -0
  28. celldetective/gui/{retrain_segmentation_model_options.py → settings/_settings_segmentation_model_training.py} +33 -79
  29. celldetective/gui/{signal_annotator_options.py → settings/_settings_signal_annotator.py} +73 -95
  30. celldetective/gui/{btrack_options.py → settings/_settings_tracking.py} +64 -87
  31. celldetective/gui/styles.py +2 -1
  32. celldetective/gui/survival_ui.py +1 -1
  33. celldetective/gui/tableUI.py +25 -0
  34. celldetective/gui/table_ops/__init__.py +0 -0
  35. celldetective/gui/table_ops/merge_groups.py +118 -0
  36. celldetective/gui/viewers.py +3 -5
  37. celldetective/gui/workers.py +0 -2
  38. celldetective/io.py +98 -55
  39. celldetective/links/zenodo.json +145 -144
  40. celldetective/measure.py +31 -26
  41. celldetective/preprocessing.py +34 -21
  42. celldetective/regionprops/_regionprops.py +16 -5
  43. celldetective/scripts/measure_cells.py +5 -5
  44. celldetective/scripts/measure_relative.py +16 -11
  45. celldetective/scripts/segment_cells.py +4 -4
  46. celldetective/scripts/segment_cells_thresholds.py +3 -3
  47. celldetective/scripts/track_cells.py +7 -7
  48. celldetective/scripts/train_segmentation_model.py +10 -1
  49. celldetective/tracking.py +10 -4
  50. celldetective/utils.py +59 -58
  51. {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/METADATA +1 -1
  52. celldetective-1.4.1.dist-info/RECORD +123 -0
  53. tests/gui/__init__.py +0 -0
  54. tests/gui/test_new_project.py +228 -0
  55. tests/{test_qt.py → gui/test_project.py} +22 -26
  56. tests/test_preprocessing.py +2 -2
  57. celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -79
  58. celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
  59. celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -37
  60. celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -39
  61. celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
  62. celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
  63. celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
  64. celldetective/models/signal_detection/NucCond/config_input.json +0 -1
  65. celldetective/models/signal_detection/NucCond/log_classifier.csv +0 -126
  66. celldetective/models/signal_detection/NucCond/log_regressor.csv +0 -282
  67. celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
  68. celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
  69. celldetective/models/signal_detection/NucCond/scores.npy +0 -0
  70. celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
  71. celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
  72. celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
  73. celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
  74. celldetective-1.4.0.dist-info/RECORD +0 -131
  75. {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/WHEEL +0 -0
  76. {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/entry_points.txt +0 -0
  77. {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/licenses/LICENSE +0 -0
  78. {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/top_level.txt +0 -0
@@ -504,7 +504,8 @@ class GenericSignalPlotWidget(CelldetectiveWidget):
504
504
  # Spines
505
505
  self.ax.spines['top'].set_visible(False)
506
506
  self.ax.spines['right'].set_visible(False)
507
-
507
+ self.ax.grid(which='major', color='black', linestyle='-', linewidth=0.8, alpha=0.2)
508
+ self.ax.grid(which='minor', color='lightgray', linestyle='--', linewidth=0.5, alpha=0.1)
508
509
  # Lims
509
510
  safe_df = self.df.dropna(subset=self.feature_selected)
510
511
  values = safe_df[self.feature_selected].values
@@ -653,6 +653,7 @@ class FilterChoice(CelldetectiveWidget):
653
653
  'log_filter': {'blob_size': 30},
654
654
  'tophat_filter': {'size': 4, 'connectivity': 4},
655
655
  'otsu_filter': None,
656
+ 'multiotsu_filter': {'classes': 3},
656
657
  'local_filter': {'block_size': 73, 'method': 'mean', 'offset': 0},
657
658
  'niblack_filter': {'window_size': 15, 'k': 0.2},
658
659
  # 'sauvola_filter': {'window_size': 15, 'k': 0.2}
@@ -1210,7 +1211,7 @@ def color_from_state(state, recently_modified=False):
1210
1211
  """
1211
1212
 
1212
1213
  unique_values = np.unique(state)
1213
- color_map={}
1214
+ color_map = {}
1214
1215
  for value in unique_values:
1215
1216
 
1216
1217
  if np.isnan(value):
@@ -1220,8 +1221,10 @@ def color_from_state(state, recently_modified=False):
1220
1221
  color_map[value] = 'tab:blue'
1221
1222
  elif value==1:
1222
1223
  color_map[value] = 'tab:red'
1224
+ elif value==99:
1225
+ color_map[value] = 'k'
1223
1226
  else:
1224
- color_map[value] = plt.cm.tab10(value)
1227
+ color_map[value] = plt.cm.tab20(value/20.0)
1225
1228
 
1226
1229
  return color_map
1227
1230
 
@@ -3,13 +3,13 @@
3
3
  "yes": {
4
4
  "Do you have the complete shape information for both populations?": {
5
5
  "yes": "You can calculate a mask-contact neighborhood. Define the reference and neighbor populations. Adjust the tolerance parameter to control how sensitive the analysis is to contact between reference and neighbor cells.",
6
- "no": "You can use an isotropic distance threshold. Define the reference and neighbor populations. Set a radius \( r > (R_{\text{ref}} + 0.5 \times R_{\text{neigh}}) \), where \( R_{\text{ref}} \) is the average radius of reference cells and \( R_{\text{neigh}} \) is the average radius of neighbor cells."
6
+ "no": "You can use an isotropic distance threshold. Define the reference and neighbor populations. Set a radius ( r > (R_{\text{ref}} + 0.5 \times R_{\text{neigh}}) ), where ( R_{\text{ref}} ) is the average radius of reference cells and ( R_{\text{neigh}} ) is the average radius of neighbor cells."
7
7
  }
8
8
  },
9
9
  "no": {
10
10
  "Do you have the complete shape information for the population of interest?": {
11
11
  "yes": "You can calculate a mask-contact neighborhood. Use the same population as both the reference and neighbor. Adjust the tolerance parameter to control sensitivity to cell-cell contact.",
12
- "no": "You can use an isotropic distance threshold. Use the same population as both the reference and neighbor. Set a radius \( r > 1.5 \times R \), where \( R \) is the average cell radius."
12
+ "no": "You can use an isotropic distance threshold. Use the same population as both the reference and neighbor. Set a radius ( r > 1.5 \times R ), where ( R ) is the average cell radius."
13
13
  }
14
14
  }
15
15
  }
@@ -882,7 +882,7 @@ class BackgroundFitCorrectionLayout(QGridLayout, Styles):
882
882
  else:
883
883
  clip = False
884
884
 
885
- corrected_stack = correct_background_model(self.attr_parent.exp_dir,
885
+ corrected_stack = correct_background_model(self.attr_parent.exp_dir,
886
886
  well_option=self.attr_parent.well_list.getSelectedIndices(), #+1 ??
887
887
  position_option=self.attr_parent.position_list.getSelectedIndices(), #+1??
888
888
  target_channel=self.channels_cb.currentText(),
@@ -897,14 +897,18 @@ class BackgroundFitCorrectionLayout(QGridLayout, Styles):
897
897
  show_progress_per_pos = False,
898
898
  )
899
899
 
900
- self.viewer = StackVisualizer(
901
- stack=corrected_stack[0],
902
- window_title='Corrected channel',
903
- target_channel=self.channels_cb.currentIndex(),
904
- frame_slider = True,
905
- contrast_slider = True
906
- )
907
- self.viewer.show()
900
+ if corrected_stack:
901
+ self.viewer = StackVisualizer(
902
+ stack=corrected_stack[0],
903
+ window_title='Corrected channel',
904
+ target_channel=self.channels_cb.currentIndex(),
905
+ frame_slider = True,
906
+ contrast_slider = True
907
+ )
908
+ self.viewer.show()
909
+ else:
910
+ print("Corrected stack could not be generated... No stack available...")
911
+
908
912
 
909
913
 
910
914
  class LocalCorrectionLayout(BackgroundFitCorrectionLayout):
@@ -1330,6 +1334,8 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
1330
1334
  for c in self.coef_widgets:
1331
1335
  c.setEnabled(False)
1332
1336
 
1337
+ self.interpolate_check = QCheckBox("interpolate NaNs")
1338
+
1333
1339
  def add_to_layout(self):
1334
1340
 
1335
1341
  channel_layout = QHBoxLayout()
@@ -1394,10 +1400,12 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
1394
1400
  self.operation_layout = OperationLayout()
1395
1401
  self.addLayout(self.operation_layout, 9, 0, 1, 3)
1396
1402
 
1403
+ self.addWidget(self.interpolate_check, 10, 0, 1, 1)
1404
+
1397
1405
  correction_layout = QHBoxLayout()
1398
1406
  correction_layout.addWidget(self.add_correction_btn, 95)
1399
1407
  correction_layout.addWidget(self.corrected_stack_viewer_btn, 5)
1400
- self.addLayout(correction_layout, 10, 0, 1, 3)
1408
+ self.addLayout(correction_layout, 11, 0, 1, 3)
1401
1409
 
1402
1410
  # verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
1403
1411
  # self.addItem(verticalSpacer, 5, 0, 1, 3)
@@ -1456,7 +1464,8 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
1456
1464
  "opt_coef_nbr": opt_coef_nbr,
1457
1465
  "operation": operation,
1458
1466
  "clip": clip,
1459
- "offset": offset
1467
+ "offset": offset,
1468
+ "fix_nan": self.interpolate_check.isChecked(),
1460
1469
  }
1461
1470
 
1462
1471
  def set_target_channel(self):
@@ -1532,6 +1541,7 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
1532
1541
  clip = clip,
1533
1542
  export= False,
1534
1543
  return_stacks=True,
1544
+ fix_nan=self.interpolate_check.isChecked(),
1535
1545
  show_progress_per_well = True,
1536
1546
  show_progress_per_pos = False,
1537
1547
  )
@@ -26,7 +26,7 @@ from sklearn.preprocessing import MinMaxScaler
26
26
  from functools import partial
27
27
  from pandas.api.types import is_numeric_dtype
28
28
 
29
- class SignalAnnotator2(CelldetectiveMainWindow):
29
+ class PairEventAnnotator(CelldetectiveMainWindow):
30
30
 
31
31
  """
32
32
  UI to set tracking parameters for bTrack.
@@ -167,8 +167,10 @@ class SignalAnnotator2(CelldetectiveMainWindow):
167
167
  self.left_panel.addLayout(neigh_hbox)
168
168
 
169
169
  self.reference_cell_info = QLabel('')
170
+ self.reference_cell_info.setStyleSheet("color: #1f77b4;")
170
171
  self.pair_info = QLabel('')
171
172
  self.neighbor_cell_info= QLabel('')
173
+ self.neighbor_cell_info.setStyleSheet("color: #d62728;")
172
174
 
173
175
  class_hbox = QHBoxLayout()
174
176
  class_hbox.addWidget(QLabel('interaction event: '), 25)
@@ -7,18 +7,20 @@ import gc
7
7
  from PyQt5.QtGui import QDoubleValidator, QIntValidator
8
8
 
9
9
  from celldetective.gui.processes.compute_neighborhood import NeighborhoodProcess
10
- from celldetective.gui.signal_annotator import MeasureAnnotator
11
- from celldetective.gui.signal_annotator2 import SignalAnnotator2
10
+ from celldetective.gui.event_annotator import MeasureAnnotator
12
11
  from celldetective.io import get_segmentation_models_list, control_segmentation_napari, get_signal_models_list, \
13
12
  control_tracks, load_experiment_tables, get_pair_signal_models_list
14
13
  from celldetective.io import locate_segmentation_model, extract_position_name, fix_missing_labels, locate_signal_model
15
- from celldetective.gui import SegmentationModelLoader, ClassifierWidget, ConfigNeighborhoods, \
16
- ConfigSegmentationModelTraining, ConfigTracking, SignalAnnotator, ConfigSignalModelTraining, ConfigMeasurements, \
17
- ConfigSignalAnnotator, TableUI, CelldetectiveWidget
14
+ from celldetective.gui import SegmentationModelLoader, ClassifierWidget, \
15
+ EventAnnotator, TableUI, CelldetectiveWidget, PairEventAnnotator
16
+
17
+ from celldetective.gui.settings import SettingsSegmentation, SettingsMeasurements, SettingsTracking, \
18
+ SettingsSignalAnnotator, SettingsNeighborhood, SettingsSegmentationModelTraining, SettingsEventDetectionModelTraining
19
+
18
20
  from celldetective.gui.gui_utils import QHSeperationLine
19
21
  from celldetective.relative_measurements import rel_measure_at_position
20
22
  from celldetective.signals import analyze_signals_at_position, analyze_pair_signals_at_position
21
- from celldetective.utils import extract_experiment_channels
23
+ from celldetective.utils import extract_experiment_channels, remove_file_if_exists
22
24
  import numpy as np
23
25
  from glob import glob
24
26
  from natsort import natsorted
@@ -81,13 +83,13 @@ class ProcessPanel(QFrame, Styles):
81
83
  title_hbox = QHBoxLayout()
82
84
  self.grid.addWidget(panel_title, 0, 0, 1, 4, alignment=Qt.AlignCenter)
83
85
 
84
- self.help_pop_btn = QPushButton()
85
- self.help_pop_btn.setIcon(icon(MDI6.help_circle, color=self.help_color))
86
- self.help_pop_btn.setIconSize(QSize(20, 20))
87
- self.help_pop_btn.clicked.connect(self.help_population)
88
- self.help_pop_btn.setStyleSheet(self.button_select_all)
89
- self.help_pop_btn.setToolTip("Help.")
90
- self.grid.addWidget(self.help_pop_btn, 0, 0, 1, 3, alignment=Qt.AlignRight)
86
+ # self.help_pop_btn = QPushButton()
87
+ # self.help_pop_btn.setIcon(icon(MDI6.help_circle, color=self.help_color))
88
+ # self.help_pop_btn.setIconSize(QSize(20, 20))
89
+ # self.help_pop_btn.clicked.connect(self.help_population)
90
+ # self.help_pop_btn.setStyleSheet(self.button_select_all)
91
+ # self.help_pop_btn.setToolTip("Help.")
92
+ # self.grid.addWidget(self.help_pop_btn, 0, 0, 1, 3, alignment=Qt.AlignRight)
91
93
 
92
94
  # self.select_all_btn = QPushButton()
93
95
  # self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
@@ -106,7 +108,7 @@ class ProcessPanel(QFrame, Styles):
106
108
 
107
109
  title_hbox.addWidget(QLabel(), 5) #self.select_all_btn
108
110
  title_hbox.addWidget(QLabel(), 85, alignment=Qt.AlignCenter)
109
- title_hbox.addWidget(self.help_pop_btn, 5)
111
+ # title_hbox.addWidget(self.help_pop_btn, 5)
110
112
  title_hbox.addWidget(self.collapse_btn, 5)
111
113
 
112
114
  self.grid.addLayout(title_hbox, 0, 0, 1, 4)
@@ -136,29 +138,29 @@ class ProcessPanel(QFrame, Styles):
136
138
  self.parent_window.scroll.setMinimumHeight(min(int(930), int(0.9*self.parent_window.screen_height)))
137
139
 
138
140
 
139
- def help_population(self):
141
+ # def help_population(self):
140
142
 
141
- """
142
- Helper to choose a proper cell population structure.
143
- """
143
+ # """
144
+ # Helper to choose a proper cell population structure.
145
+ # """
144
146
 
145
- dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','cell-populations.json'])
147
+ # dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','cell-populations.json'])
146
148
 
147
- with open(dict_path) as f:
148
- d = json.load(f)
149
+ # with open(dict_path) as f:
150
+ # d = json.load(f)
149
151
 
150
- suggestion = help_generic(d)
151
- if isinstance(suggestion, str):
152
- print(f"{suggestion=}")
153
- msgBox = QMessageBox()
154
- msgBox.setIcon(QMessageBox.Information)
155
- msgBox.setTextFormat(Qt.RichText)
156
- msgBox.setText(suggestion)
157
- msgBox.setWindowTitle("Info")
158
- msgBox.setStandardButtons(QMessageBox.Ok)
159
- returnValue = msgBox.exec()
160
- if returnValue == QMessageBox.Ok:
161
- return None
152
+ # suggestion = help_generic(d)
153
+ # if isinstance(suggestion, str):
154
+ # print(f"{suggestion=}")
155
+ # msgBox = QMessageBox()
156
+ # msgBox.setIcon(QMessageBox.Information)
157
+ # msgBox.setTextFormat(Qt.RichText)
158
+ # msgBox.setText(suggestion)
159
+ # msgBox.setWindowTitle("Info")
160
+ # msgBox.setStandardButtons(QMessageBox.Ok)
161
+ # returnValue = msgBox.exec()
162
+ # if returnValue == QMessageBox.Ok:
163
+ # return None
162
164
 
163
165
  def populate_contents(self):
164
166
  self.ContentsFrame = QFrame()
@@ -284,9 +286,16 @@ class ProcessPanel(QFrame, Styles):
284
286
  self.grid_contents.addLayout(signal_layout,6,0,1,4)
285
287
 
286
288
  def refresh_signal_models(self):
287
- signal_models = get_signal_models_list()
289
+ self.signal_models = get_signal_models_list()
288
290
  self.signal_models_list.clear()
289
- self.signal_models_list.addItems(signal_models)
291
+
292
+ thresh = 35
293
+ models_truncated = [m[:thresh - 3]+'...' if len(m)>thresh else m for m in self.signal_models]
294
+
295
+ self.signal_models_list.addItems(models_truncated)
296
+ for i in range(len(self.signal_models)):
297
+ self.signal_models_list.setItemData(i, self.signal_models[i], Qt.ToolTipRole)
298
+
290
299
 
291
300
  def generate_tracking_options(self):
292
301
 
@@ -347,14 +356,10 @@ class ProcessPanel(QFrame, Styles):
347
356
  if returnValue == QMessageBox.No:
348
357
  return None
349
358
  elif returnValue == QMessageBox.Yes:
350
- if os.path.exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])):
351
- os.remove(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.csv']))
352
- if os.path.exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.pkl'])):
353
- os.remove(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.pkl']))
354
- if os.path.exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'napari_{self.mode[:-1]}_trajectories.npy'])):
355
- os.remove(os.sep.join([self.parent_window.pos, 'output', 'tables', f'napari_{self.mode[:-1]}_trajectories.npy']))
356
- if os.path.exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_pairs.csv'])):
357
- os.remove(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_pairs.csv']))
359
+ remove_file_if_exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.csv']))
360
+ remove_file_if_exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.pkl']))
361
+ remove_file_if_exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'napari_{self.mode[:-1]}_trajectories.npy']))
362
+ remove_file_if_exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_pairs.csv']))
358
363
  self.parent_window.update_position_options()
359
364
  else:
360
365
  return None
@@ -373,13 +378,21 @@ class ProcessPanel(QFrame, Styles):
373
378
  #self.to_disable.append(self.segment_action)
374
379
  grid_segment.addWidget(self.segment_action, 90)
375
380
 
376
- self.flip_segment_btn = QPushButton()
377
- self.flip_segment_btn.setIcon(icon(MDI6.camera_flip_outline,color="black"))
378
- self.flip_segment_btn.setIconSize(QSize(20, 20))
379
- self.flip_segment_btn.clicked.connect(self.flip_segmentation)
380
- self.flip_segment_btn.setStyleSheet(self.button_select_all)
381
- self.flip_segment_btn.setToolTip("Flip the order of the frames for segmentation.")
382
- grid_segment.addWidget(self.flip_segment_btn, 5)
381
+ # self.flip_segment_btn = QPushButton()
382
+ # self.flip_segment_btn.setIcon(icon(MDI6.camera_flip_outline,color="black"))
383
+ # self.flip_segment_btn.setIconSize(QSize(20, 20))
384
+ # self.flip_segment_btn.clicked.connect(self.flip_segmentation)
385
+ # self.flip_segment_btn.setStyleSheet(self.button_select_all)
386
+ # self.flip_segment_btn.setToolTip("Flip the order of the frames for segmentation.")
387
+ # grid_segment.addWidget(self.flip_segment_btn, 5)
388
+
389
+ self.segmentation_config_btn = QPushButton()
390
+ self.segmentation_config_btn.setIcon(icon(MDI6.cog_outline,color="black"))
391
+ self.segmentation_config_btn.setIconSize(QSize(20, 20))
392
+ self.segmentation_config_btn.setToolTip("Configure segmentation.")
393
+ self.segmentation_config_btn.setStyleSheet(self.button_select_all)
394
+ self.segmentation_config_btn.clicked.connect(self.open_segmentation_configuration_ui)
395
+ grid_segment.addWidget(self.segmentation_config_btn, 5)
383
396
 
384
397
 
385
398
  self.check_seg_btn = QPushButton()
@@ -572,6 +585,14 @@ class ProcessPanel(QFrame, Styles):
572
585
  print(f"Loading images and labels into napari...")
573
586
  try:
574
587
  control_segmentation_napari(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode,flush_memory=True)
588
+ except FileNotFoundError as e:
589
+ msgBox = QMessageBox()
590
+ msgBox.setIcon(QMessageBox.Warning)
591
+ msgBox.setText(str(e))
592
+ msgBox.setWindowTitle("Warning")
593
+ msgBox.setStandardButtons(QMessageBox.Ok)
594
+ _ = msgBox.exec()
595
+ return
575
596
  except Exception as e:
576
597
  print(f'Task unsuccessful... Exception {e}...')
577
598
  msgBox = QMessageBox()
@@ -604,15 +625,15 @@ class ProcessPanel(QFrame, Styles):
604
625
 
605
626
  test = self.parent_window.locate_selected_position()
606
627
  if test:
607
- self.SignalAnnotator = SignalAnnotator(self)
608
- self.SignalAnnotator.show()
628
+ self.event_annotator = EventAnnotator(self)
629
+ self.event_annotator.show()
609
630
 
610
631
  def check_measurements(self):
611
632
 
612
633
  test = self.parent_window.locate_selected_position()
613
634
  if test:
614
- self.MeasureAnnotator = MeasureAnnotator(self)
615
- self.MeasureAnnotator.show()
635
+ self.measure_annotator = MeasureAnnotator(self)
636
+ self.measure_annotator.show()
616
637
 
617
638
  def enable_segmentation_model_list(self):
618
639
  if self.segment_action.isChecked():
@@ -630,22 +651,22 @@ class ProcessPanel(QFrame, Styles):
630
651
 
631
652
  self.seg_model_list.clear()
632
653
  self.seg_models_specific = get_segmentation_models_list(mode=self.mode, return_path=False)
633
- self.seg_models = self.seg_models_specific.copy() #get_segmentation_models_list(mode=self.mode, return_path=False)
634
- thresh = 40
635
- self.models_truncated = [m[:thresh - 3]+'...' if len(m)>thresh else m for m in self.seg_models]
636
- #self.seg_model_list.addItems(models_truncated)
654
+ self.seg_models = self.seg_models_specific.copy()
655
+ self.n_specific_seg_models = len(self.seg_models)
637
656
 
638
657
  self.seg_models_generic = get_segmentation_models_list(mode="generic", return_path=False)
639
658
  self.seg_models.append('Threshold')
640
659
  self.seg_models.extend(self.seg_models_generic)
641
660
 
642
- #self.seg_models_generic.insert(0,'Threshold')
643
- self.seg_model_list.addItems(self.seg_models)
661
+ thresh = 35
662
+ self.models_truncated = [m[:thresh - 3]+'...' if len(m)>thresh else m for m in self.seg_models]
663
+
664
+ self.seg_model_list.addItems(self.models_truncated)
644
665
 
645
666
  for i in range(len(self.seg_models)):
646
667
  self.seg_model_list.setItemData(i, self.seg_models[i], Qt.ToolTipRole)
647
668
 
648
- self.seg_model_list.insertSeparator(len(self.models_truncated))
669
+ self.seg_model_list.insertSeparator(self.n_specific_seg_models)
649
670
 
650
671
  # def tick_all_actions(self):
651
672
  # self.switch_all_ticks_option()
@@ -671,23 +692,28 @@ class ProcessPanel(QFrame, Styles):
671
692
 
672
693
  def open_tracking_configuration_ui(self):
673
694
  print('Set the tracking parameters...')
674
- self.ConfigTracking = ConfigTracking(self)
675
- self.ConfigTracking.show()
695
+ self.settings_tracking = SettingsTracking(self)
696
+ self.settings_tracking.show()
676
697
 
677
698
  def open_signal_model_config_ui(self):
678
699
  print('Set the training parameters for new signal models...')
679
- self.ConfigSignalTrain = ConfigSignalModelTraining(self)
680
- self.ConfigSignalTrain.show()
700
+ self.settings_event_detection_training = SettingsEventDetectionModelTraining(self)
701
+ self.settings_event_detection_training.show()
681
702
 
682
703
  def open_segmentation_model_config_ui(self):
683
704
  print('Set the training parameters for a new segmentation model...')
684
- self.ConfigSegmentationTrain = ConfigSegmentationModelTraining(self)
685
- self.ConfigSegmentationTrain.show()
705
+ self.settings_segmentation_training = SettingsSegmentationModelTraining(self)
706
+ self.settings_segmentation_training.show()
686
707
 
687
708
  def open_measurement_configuration_ui(self):
688
709
  print('Set the measurements to be performed...')
689
- self.ConfigMeasurements = ConfigMeasurements(self)
690
- self.ConfigMeasurements.show()
710
+ self.settings_measurements = SettingsMeasurements(self)
711
+ self.settings_measurements.show()
712
+
713
+ def open_segmentation_configuration_ui(self):
714
+ print('Set the segmentation settings to be performed...')
715
+ self.settings_segmentation = SettingsSegmentation(self)
716
+ self.settings_segmentation.show()
691
717
 
692
718
  def open_classifier_ui(self):
693
719
 
@@ -709,8 +735,8 @@ class ProcessPanel(QFrame, Styles):
709
735
  self.ClassifierWidget.show()
710
736
 
711
737
  def open_signal_annotator_configuration_ui(self):
712
- self.ConfigSignalAnnotator = ConfigSignalAnnotator(self)
713
- self.ConfigSignalAnnotator.show()
738
+ self.settings_signal_annotator = SettingsSignalAnnotator(self)
739
+ self.settings_signal_annotator.show()
714
740
 
715
741
  def reset_generalist_setup(self, index):
716
742
  self.cellpose_calibrated = False
@@ -766,10 +792,11 @@ class ProcessPanel(QFrame, Styles):
766
792
  #tabs += [pos+os.sep.join(['output', 'tables', f'trajectories_pairs.csv']) for pos in self.df_pos_info['pos_path'].unique()]
767
793
  tabs += [pos+os.sep.join(['output', 'tables', f'napari_{self.mode}_trajectories.npy']) for pos in self.df_pos_info['pos_path'].unique()]
768
794
  for t in tabs:
769
- if os.path.exists(t.replace('.csv','.pkl')):
770
- os.remove(t.replace('.csv','.pkl'))
771
- os.remove(t)
772
-
795
+ remove_file_if_exists(t.replace('.csv','.pkl'))
796
+ try:
797
+ os.remove(t)
798
+ except:
799
+ pass
773
800
  loop_iter=0
774
801
 
775
802
  if self.parent_window.position_list.isMultipleSelection():
@@ -782,7 +809,7 @@ class ProcessPanel(QFrame, Styles):
782
809
  if returnValue == QMessageBox.No:
783
810
  return None
784
811
 
785
- if self.seg_model_list.currentIndex() > len(self.models_truncated):
812
+ if self.seg_model_list.currentIndex() > self.n_specific_seg_models:
786
813
  self.model_name = self.seg_models[self.seg_model_list.currentIndex()-1]
787
814
  else:
788
815
  self.model_name = self.seg_models[self.seg_model_list.currentIndex()]
@@ -806,7 +833,8 @@ class ProcessPanel(QFrame, Styles):
806
833
  return None
807
834
 
808
835
  if self.signal_analysis_action.isChecked() and not self.signalChannelsSet:
809
- self.signalChannelWidget = SignalModelParamsWidget(self, model_name = self.signal_models_list.currentText())
836
+ self.signal_model_name = self.signal_models[self.signal_models_list.currentIndex()]
837
+ self.signalChannelWidget = SignalModelParamsWidget(self, model_name = self.signal_model_name)
810
838
  self.signalChannelWidget.show()
811
839
  return None
812
840
 
@@ -856,7 +884,7 @@ class ProcessPanel(QFrame, Styles):
856
884
  return None
857
885
  else:
858
886
  print(f"Segmentation from threshold config: {self.threshold_config}")
859
- process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "threshold_instructions": self.threshold_config, "use_gpu": self.use_gpu, 'flip': self.flipSeg}
887
+ process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "threshold_instructions": self.threshold_config, "use_gpu": self.use_gpu}
860
888
  self.job = ProgressWindow(SegmentCellThresholdProcess, parent_window=self, title="Segment", process_args = process_args)
861
889
  result = self.job.exec_()
862
890
  if result == QDialog.Accepted:
@@ -871,7 +899,7 @@ class ProcessPanel(QFrame, Styles):
871
899
  # process = {"output_dir": self.output_dir, "file": self.model_name}
872
900
  # self.download_model_job = ProgressWindow(DownloadProcess, parent_window=self, title="Download", process_args = args)
873
901
 
874
- process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "model_name": self.model_name, "use_gpu": self.use_gpu, 'flip': self.flipSeg}
902
+ process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "model_name": self.model_name, "use_gpu": self.use_gpu}
875
903
  self.job = ProgressWindow(SegmentCellDLProcess, parent_window=self, title="Segment", process_args = process_args)
876
904
  result = self.job.exec_()
877
905
  if result == QDialog.Accepted:
@@ -926,7 +954,8 @@ class ProcessPanel(QFrame, Styles):
926
954
  returnValue = msgBox.exec()
927
955
  if returnValue == QMessageBox.No:
928
956
  return None
929
- analyze_signals_at_position(self.pos, self.signal_models_list.currentText(), self.mode)
957
+ self.signal_model_name = self.signal_models[self.signal_models_list.currentIndex()]
958
+ analyze_signals_at_position(self.pos, self.signal_model_name, self.mode)
930
959
 
931
960
 
932
961
  # self.stack = None
@@ -940,7 +969,16 @@ class ProcessPanel(QFrame, Styles):
940
969
 
941
970
  def open_napari_tracking(self):
942
971
  print(f'View the tracks before post-processing for position {self.parent_window.pos} in napari...')
943
- control_tracks(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode, threads=self.parent_window.parent_window.n_threads)
972
+ try:
973
+ control_tracks(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode, threads=self.parent_window.parent_window.n_threads)
974
+ except FileNotFoundError as e:
975
+ msgBox = QMessageBox()
976
+ msgBox.setIcon(QMessageBox.Warning)
977
+ msgBox.setText(str(e))
978
+ msgBox.setWindowTitle("Warning")
979
+ msgBox.setStandardButtons(QMessageBox.Ok)
980
+ _ = msgBox.exec()
981
+ return
944
982
 
945
983
  def view_table_ui(self):
946
984
 
@@ -1045,8 +1083,8 @@ class ProcessPanel(QFrame, Styles):
1045
1083
  self.process_population()
1046
1084
 
1047
1085
  def set_selected_signals_for_event_detection(self):
1048
-
1049
- model_complete_path = locate_signal_model(self.signal_models_list.currentText())
1086
+ self.signal_model_name = self.signal_models[self.signal_models_list.currentIndex()]
1087
+ model_complete_path = locate_signal_model(self.signal_model_name)
1050
1088
  input_config_path = model_complete_path+"config_input.json"
1051
1089
  new_channels = [self.signalChannelWidget.channel_cbs[i].currentText() for i in range(len(self.signalChannelWidget.channel_cbs))]
1052
1090
  with open(input_config_path) as config_file:
@@ -1441,12 +1479,12 @@ class NeighPanel(QFrame, Styles):
1441
1479
 
1442
1480
  def open_signal_annotator_configuration_ui(self):
1443
1481
  self.mode = 'pairs'
1444
- self.ConfigSignalAnnotator = ConfigSignalAnnotator(self)
1445
- self.ConfigSignalAnnotator.show()
1482
+ self.config_signal_annotator = SettingsSignalAnnotator(self)
1483
+ self.config_signal_annotator.show()
1446
1484
 
1447
1485
  def open_signal_model_config_ui(self):
1448
- self.ConfigSignalTrain = ConfigSignalModelTraining(self, signal_mode='pairs')
1449
- self.ConfigSignalTrain.show()
1486
+ self.settings_pair_event_detection_training = SettingsEventDetectionModelTraining(self, signal_mode='pairs')
1487
+ self.settings_pair_event_detection_training.show()
1450
1488
 
1451
1489
  def remove_protocol_from_list(self):
1452
1490
 
@@ -1457,7 +1495,7 @@ class NeighPanel(QFrame, Styles):
1457
1495
 
1458
1496
  def open_config_distance_threshold_neighborhood(self):
1459
1497
 
1460
- self.ConfigNeigh = ConfigNeighborhoods(parent_window=self,
1498
+ self.ConfigNeigh = SettingsNeighborhood(parent_window=self,
1461
1499
  neighborhood_type='distance_threshold',
1462
1500
  neighborhood_parameter_name='threshold distance',
1463
1501
  )
@@ -1465,7 +1503,7 @@ class NeighPanel(QFrame, Styles):
1465
1503
 
1466
1504
  def open_config_contact_neighborhood(self):
1467
1505
 
1468
- self.ConfigNeigh = ConfigNeighborhoods(parent_window=self,
1506
+ self.ConfigNeigh = SettingsNeighborhood(parent_window=self,
1469
1507
  neighborhood_type='mask_contact',
1470
1508
  neighborhood_parameter_name='tolerance contact distance',
1471
1509
  )
@@ -1548,8 +1586,8 @@ class NeighPanel(QFrame, Styles):
1548
1586
 
1549
1587
  test = self.parent_window.locate_selected_position()
1550
1588
  if test:
1551
- self.SignalAnnotator2 = SignalAnnotator2(self)
1552
- self.SignalAnnotator2.show()
1589
+ self.pair_event_annotator = PairEventAnnotator(self)
1590
+ self.pair_event_annotator.show()
1553
1591
 
1554
1592
 
1555
1593
  class PreprocessingPanel(QFrame, Styles):
@@ -43,41 +43,44 @@ class DownloadProcess(Process):
43
43
  self.t0 = time.time()
44
44
 
45
45
  def download_url_to_file(self, url, dst):
46
-
47
- file_size = None
48
- ssl._create_default_https_context = ssl._create_unverified_context
49
- u = urlopen(url)
50
- meta = u.info()
51
- if hasattr(meta, 'getheaders'):
52
- content_length = meta.getheaders("Content-Length")
53
- else:
54
- content_length = meta.get_all("Content-Length")
55
- if content_length is not None and len(content_length) > 0:
56
- file_size = int(content_length[0])
57
- # We deliberately save it in a temp file and move it after
58
- dst = os.path.expanduser(dst)
59
- dst_dir = os.path.dirname(dst)
60
- f = tempfile.NamedTemporaryFile(delete=False, dir=dst_dir)
61
-
62
46
  try:
63
- with tqdm(total=file_size, disable=not self.progress,
64
- unit='B', unit_scale=True, unit_divisor=1024) as pbar:
65
- while True:
66
- buffer = u.read(8192) #8192
67
- if len(buffer) == 0:
68
- break
69
- f.write(buffer)
70
- pbar.update(len(buffer))
71
- self.sum_done+=len(buffer) / file_size * 100
72
- mean_exec_per_step = (time.time() - self.t0) / (self.sum_done*file_size / 100 + 1)
73
- pred_time = (file_size - (self.sum_done*file_size / 100 + 1)) * mean_exec_per_step
74
- self.queue.put([self.sum_done, pred_time])
75
- f.close()
76
- shutil.move(f.name, dst)
77
- finally:
78
- f.close()
79
- if os.path.exists(f.name):
80
- os.remove(f.name)
47
+ file_size = None
48
+ ssl._create_default_https_context = ssl._create_unverified_context
49
+ u = urlopen(url)
50
+ meta = u.info()
51
+ if hasattr(meta, 'getheaders'):
52
+ content_length = meta.getheaders("Content-Length")
53
+ else:
54
+ content_length = meta.get_all("Content-Length")
55
+ if content_length is not None and len(content_length) > 0:
56
+ file_size = int(content_length[0])
57
+ # We deliberately save it in a temp file and move it after
58
+ dst = os.path.expanduser(dst)
59
+ dst_dir = os.path.dirname(dst)
60
+ f = tempfile.NamedTemporaryFile(delete=False, dir=dst_dir)
61
+
62
+ try:
63
+ with tqdm(total=file_size, disable=not self.progress,
64
+ unit='B', unit_scale=True, unit_divisor=1024) as pbar:
65
+ while True:
66
+ buffer = u.read(8192) #8192
67
+ if len(buffer) == 0:
68
+ break
69
+ f.write(buffer)
70
+ pbar.update(len(buffer))
71
+ self.sum_done+=len(buffer) / file_size * 100
72
+ mean_exec_per_step = (time.time() - self.t0) / (self.sum_done*file_size / 100 + 1)
73
+ pred_time = (file_size - (self.sum_done*file_size / 100 + 1)) * mean_exec_per_step
74
+ self.queue.put([self.sum_done, pred_time])
75
+ f.close()
76
+ shutil.move(f.name, dst)
77
+ finally:
78
+ f.close()
79
+ if os.path.exists(f.name):
80
+ os.remove(f.name)
81
+ except Exception as e:
82
+ print("No internet connection: ", e)
83
+ return None
81
84
 
82
85
  def run(self):
83
86