celldetective 1.3.1__py3-none-any.whl → 1.3.3.post1__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 (34) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/events.py +2 -0
  3. celldetective/gui/classifier_widget.py +51 -3
  4. celldetective/gui/control_panel.py +9 -3
  5. celldetective/gui/generic_signal_plot.py +161 -2
  6. celldetective/gui/gui_utils.py +90 -1
  7. celldetective/gui/measurement_options.py +35 -32
  8. celldetective/gui/plot_signals_ui.py +8 -3
  9. celldetective/gui/process_block.py +36 -114
  10. celldetective/gui/retrain_segmentation_model_options.py +3 -1
  11. celldetective/gui/signal_annotator.py +53 -26
  12. celldetective/gui/signal_annotator2.py +17 -30
  13. celldetective/gui/survival_ui.py +7 -3
  14. celldetective/gui/tableUI.py +300 -183
  15. celldetective/gui/thresholds_gui.py +195 -199
  16. celldetective/gui/viewers.py +267 -13
  17. celldetective/io.py +110 -10
  18. celldetective/measure.py +128 -88
  19. celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +79 -0
  20. celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
  21. celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +37 -0
  22. celldetective/models/segmentation_effectors/test-transfer/config_input.json +39 -0
  23. celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
  24. celldetective/neighborhood.py +154 -69
  25. celldetective/relative_measurements.py +128 -4
  26. celldetective/scripts/measure_cells.py +3 -3
  27. celldetective/signals.py +207 -213
  28. celldetective/utils.py +16 -0
  29. {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/METADATA +11 -10
  30. {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/RECORD +34 -29
  31. {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/WHEEL +1 -1
  32. {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/LICENSE +0 -0
  33. {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/entry_points.txt +0 -0
  34. {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/top_level.txt +0 -0
@@ -291,6 +291,7 @@ class ProcessPanel(QFrame, Styles):
291
291
  self.signal_models_list.addItems(signal_models)
292
292
 
293
293
  def generate_tracking_options(self):
294
+
294
295
  grid_track = QHBoxLayout()
295
296
 
296
297
  self.track_action = QCheckBox("TRACK")
@@ -303,16 +304,6 @@ class ProcessPanel(QFrame, Styles):
303
304
  padding-top: 5px;
304
305
  """)
305
306
  grid_track.addWidget(self.track_action, 75)
306
- #self.to_disable.append(self.track_action_tc)
307
-
308
- # self.show_track_table_btn = QPushButton()
309
- # self.show_track_table_btn.setIcon(icon(MDI6.table,color="black"))
310
- # self.show_track_table_btn.setIconSize(QSize(20, 20))
311
- # self.show_track_table_btn.setToolTip("Show trajectories table.")
312
- # self.show_track_table_btn.setStyleSheet(self.button_select_all)
313
- # #self.show_track_table_btn.clicked.connect(self.display_trajectory_table)
314
- # self.show_track_table_btn.setEnabled(False)
315
- # grid_track.addWidget(self.show_track_table_btn, 6) #4,3,1,1, alignment=Qt.AlignLeft
316
307
 
317
308
  self.delete_tracks_btn = QPushButton()
318
309
  self.delete_tracks_btn.setIcon(icon(MDI6.trash_can,color="black"))
@@ -640,25 +631,6 @@ class ProcessPanel(QFrame, Styles):
640
631
 
641
632
  self.seg_model_list.insertSeparator(len(self.models_truncated))
642
633
 
643
-
644
- #if ("live_nuclei_channel" in self.exp_channels)*("dead_nuclei_channel" in self.exp_channels):
645
- # print("both channels found")
646
- # index = self.tc_seg_model_list.findText("MCF7_Hoescht_PI_w_primary_NK", Qt.MatchFixedString)
647
- # if index >= 0:
648
- # self.tc_seg_model_list.setCurrentIndex(index)
649
- # elif ("live_nuclei_channel" in self.exp_channels)*("dead_nuclei_channel" not in self.exp_channels):
650
- # index = self.tc_seg_model_list.findText("MCF7_Hoescht_w_primary_NK", Qt.MatchFixedString)
651
- # if index >= 0:
652
- # self.tc_seg_model_list.setCurrentIndex(index)
653
- # elif ("live_nuclei_channel" not in self.exp_channels)*("dead_nuclei_channel" in self.exp_channels):
654
- # index = self.tc_seg_model_list.findText("MCF7_PI_w_primary_NK", Qt.MatchFixedString)
655
- # if index >= 0:
656
- # self.tc_seg_model_list.setCurrentIndex(index)
657
- # elif ("live_nuclei_channel" not in self.exp_channels)*("dead_nuclei_channel" not in self.exp_channels)*("adhesion_channel" in self.exp_channels):
658
- # index = self.tc_seg_model_list.findText("RICM", Qt.MatchFixedString)
659
- # if index >= 0:
660
- # self.tc_seg_model_list.setCurrentIndex(index)
661
-
662
634
  def tick_all_actions(self):
663
635
  self.switch_all_ticks_option()
664
636
  if self.all_ticked:
@@ -884,28 +856,7 @@ class ProcessPanel(QFrame, Styles):
884
856
  if self.segment_action.isChecked():
885
857
  self.segment_action.setChecked(False)
886
858
 
887
- # QApplication.restoreOverrideCursor()
888
- # self.unfreeze()
889
-
890
- # def view_current_stack_with_scale_bar(self):
891
-
892
- # self.parent_window.locate_image()
893
- # if self.parent_window.current_stack is not None:
894
- # self.viewer = CellSizeViewer(
895
- # initial_diameter = float(self.diameter_le.text().replace(',', '.')),
896
- # parent_le = self.diameter_le,
897
- # stack_path=self.parent_window.current_stack,
898
- # window_title=f'Position {self.parent_window.position_list.currentText()}',
899
- # frame_slider = True,
900
- # contrast_slider = True,
901
- # channel_cb = True,
902
- # channel_names = self.parent_window.exp_channels,
903
- # n_channels = self.parent_window.nbr_channels,
904
- # PxToUm = 1,
905
- # )
906
- # self.viewer.show()
907
-
908
-
859
+ self.cellpose_calibrated = False
909
860
 
910
861
  def open_napari_tracking(self):
911
862
  print(f'View the tracks before post-processing for position {self.parent_window.pos} in napari...')
@@ -920,7 +871,7 @@ class ProcessPanel(QFrame, Styles):
920
871
  plot_mode = 'plot_track_signals'
921
872
  if 'TRACK_ID' not in list(self.df.columns):
922
873
  plot_mode = 'static'
923
- 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)
874
+ 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, save_inplace_option=True)
924
875
  self.tab_ui.show()
925
876
  else:
926
877
  print('Table could not be loaded...')
@@ -933,24 +884,6 @@ class ProcessPanel(QFrame, Styles):
933
884
  if returnValue == QMessageBox.Ok:
934
885
  return None
935
886
 
936
- # def interpret_pos_location(self):
937
-
938
- # """
939
- # Read the well/position selection from the control panel to decide which data to load
940
- # Set position_indices to None if all positions must be taken
941
-
942
- # """
943
-
944
- # if self.well_option==len(self.wells):
945
- # self.well_indices = np.arange(len(self.wells))
946
- # else:
947
- # self.well_indices = np.array([self.well_option],dtype=int)
948
-
949
- # if self.position_option==0:
950
- # self.position_indices = None
951
- # else:
952
- # self.position_indices = np.array([self.position_option],dtype=int)
953
-
954
887
  def load_available_tables(self):
955
888
 
956
889
  """
@@ -1016,6 +949,7 @@ class ProcessPanel(QFrame, Styles):
1016
949
  self.process_population()
1017
950
 
1018
951
 
952
+
1019
953
  class NeighPanel(QFrame, Styles):
1020
954
  def __init__(self, parent_window):
1021
955
 
@@ -1207,28 +1141,19 @@ class NeighPanel(QFrame, Styles):
1207
1141
  self.measure_pairs_action.setToolTip("Measure the relative quantities defined for the cell pairs, for all neighborhoods.")
1208
1142
  rel_layout.addWidget(self.measure_pairs_action, 90)
1209
1143
 
1210
- # self.visu_btn = QPushButton()
1211
- # self.visu_btn.setIcon(icon(MDI6.eye_check_outline,color="black"))
1212
- # self.visu_btn.setIconSize(QSize(20, 20))
1213
- # self.visu_btn.clicked.connect(self.check_measurements2)
1214
- # self.visu_btn.setToolTip("Open measurement annotator for two populations.")
1215
- # self.visu_btn.setStyleSheet(self.button_select_all)
1216
- # self.grid_contents.addWidget(self.visu_btn, 1,1,1,1,alignment=Qt.AlignRight)
1217
- # rel_layout.addWidget(self.visu_btn, 6)
1218
-
1219
- # self.config_rel_annotator_btn = QPushButton()
1220
- # self.config_rel_annotator_btn.setIcon(icon(MDI6.cog_outline, color="black"))
1221
- # self.config_rel_annotator_btn.setIconSize(QSize(20, 20))
1222
- # self.config_rel_annotator_btn.setToolTip("Configure the animation of the annotation tool.")
1223
- # self.config_rel_annotator_btn.setStyleSheet(self.button_select_all)
1224
- # self.config_rel_annotator_btn.clicked.connect(self.open_signal_annotator_configuration_ui)
1225
- # # self.grid_contents.addWidget(self.config_rel_annotator_btn, 1,2,1,1, alignment=Qt.AlignRight)
1226
- # rel_layout.addWidget(self.config_rel_annotator_btn, 6)
1144
+ self.classify_pairs_btn = QPushButton()
1145
+ self.classify_pairs_btn.setIcon(icon(MDI6.scatter_plot, color="black"))
1146
+ self.classify_pairs_btn.setIconSize(QSize(20, 20))
1147
+ self.classify_pairs_btn.setToolTip("Classify data.")
1148
+ self.classify_pairs_btn.setStyleSheet(self.button_select_all)
1149
+ self.classify_pairs_btn.clicked.connect(self.open_classifier_ui_pairs)
1150
+ rel_layout.addWidget(self.classify_pairs_btn, 5) #4,2,1,1, alignment=Qt.AlignRight
1151
+
1227
1152
  self.grid_contents.addLayout(rel_layout, 6, 0, 1, 4)
1228
1153
 
1229
1154
  signal_layout = QVBoxLayout()
1230
1155
  signal_hlayout = QHBoxLayout()
1231
- self.signal_analysis_action = QCheckBox("PAIR SIGNAL ANALYSIS")
1156
+ self.signal_analysis_action = QCheckBox("DETECT PAIR EVENTS")
1232
1157
  self.signal_analysis_action.setStyleSheet("""
1233
1158
  font-size: 10px;
1234
1159
  padding-left: 10px;
@@ -1304,6 +1229,26 @@ class NeighPanel(QFrame, Styles):
1304
1229
  self.neigh_action.setChecked(True)
1305
1230
  self.neigh_action.setChecked(False)
1306
1231
 
1232
+ def open_classifier_ui_pairs(self):
1233
+
1234
+ self.mode = "pairs"
1235
+ self.load_available_tables()
1236
+ if self.df is None:
1237
+
1238
+ msgBox = QMessageBox()
1239
+ msgBox.setIcon(QMessageBox.Warning)
1240
+ msgBox.setText("No table was found...")
1241
+ msgBox.setWindowTitle("Warning")
1242
+ msgBox.setStandardButtons(QMessageBox.Ok)
1243
+ returnValue = msgBox.exec()
1244
+ if returnValue == QMessageBox.Ok:
1245
+ return None
1246
+ else:
1247
+ return None
1248
+ else:
1249
+ self.ClassifierWidget = ClassifierWidget(self)
1250
+ self.ClassifierWidget.show()
1251
+
1307
1252
 
1308
1253
  def help_neighborhood(self):
1309
1254
 
@@ -1360,7 +1305,7 @@ class NeighPanel(QFrame, Styles):
1360
1305
 
1361
1306
  if self.df is not None:
1362
1307
  plot_mode = 'static'
1363
- self.tab_ui = TableUI(self.df, f"Well {self.parent_window.well_list.currentText()}; Position {self.parent_window.position_list.currentText()}", population='pairs', plot_mode=plot_mode)
1308
+ self.tab_ui = TableUI(self.df, f"Well {self.parent_window.well_list.currentText()}; Position {self.parent_window.position_list.currentText()}", population='pairs', plot_mode=plot_mode, save_inplace_option=True)
1364
1309
  self.tab_ui.show()
1365
1310
  else:
1366
1311
  print('Table could not be loaded...')
@@ -1512,35 +1457,12 @@ class NeighPanel(QFrame, Styles):
1512
1457
  rel_measure_at_position(self.pos)
1513
1458
 
1514
1459
  if self.signal_analysis_action.isChecked():
1515
-
1516
- # df_targets = get_position_pickle(self.pos, population='targets')
1517
- # df_effectors = get_position_pickle(self.pos, population='effectors')
1518
- # self.dataframes = {
1519
- # 'targets': df_targets,
1520
- # 'effectors': df_effectors,
1521
- # }
1522
-
1523
- # df_pairs = get_position_table(self.pos, population='pairs')
1524
-
1525
- # # Need to identify expected reference / neighbor tables
1526
- # model_path = locate_signal_model(self.pair_signal_models_list.currentText(), pairs=True)
1527
- # print(f'Looking for model in {model_path}...')
1528
- # complete_path = model_path
1529
- # complete_path = rf"{complete_path}"
1530
- # model_config_path = os.sep.join([complete_path, 'config_input.json'])
1531
- # model_config_path = rf"{model_config_path}"
1532
- # f = open(model_config_path)
1533
- # model_config_path = json.load(f)
1534
-
1535
- # reference_population = model_config_path['reference_population']
1536
- # neighbor_population = model_config_path['neighbor_population']
1537
-
1538
- # analyze_pair_signals(df_pairs, self.dataframes[reference_population], self.dataframes[neighbor_population], model=self.pair_signal_models_list.currentText())
1460
+
1539
1461
  analyze_pair_signals_at_position(self.pos, self.pair_signal_models_list.currentText(), use_gpu=self.parent_window.parent_window.use_gpu)
1462
+
1540
1463
  self.parent_window.update_position_options()
1541
1464
  print('Done.')
1542
1465
 
1543
-
1544
1466
  def check_signals2(self):
1545
1467
 
1546
1468
  test = self.parent_window.locate_selected_position()
@@ -616,4 +616,6 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
616
616
 
617
617
  train_segmentation_model(model_folder+"training_instructions.json", use_gpu=self.parent_window.parent_window.parent_window.use_gpu)
618
618
 
619
- # self.parent.refresh_signal_models()
619
+ self.parent_window.init_seg_model_list()
620
+ idx = self.parent_window.seg_model_list.findText(model_name)
621
+ self.parent_window.seg_model_list.setCurrentIndex(idx)
@@ -35,7 +35,10 @@ class SignalAnnotator(QMainWindow, Styles):
35
35
  def __init__(self, parent_window=None):
36
36
 
37
37
  super().__init__()
38
+
38
39
  center_window(self)
40
+ self.proceed = True
41
+ self.setAttribute(Qt.WA_DeleteOnClose)
39
42
 
40
43
  self.parent_window = parent_window
41
44
  self.setWindowTitle("Signal annotator")
@@ -65,18 +68,19 @@ class SignalAnnotator(QMainWindow, Styles):
65
68
  self.status_name = 'status'
66
69
 
67
70
  self.locate_stack()
68
- self.load_annotator_config()
69
- self.locate_tracks()
70
- self.prepare_stack()
71
+ if not self.proceed:
72
+ self.close()
73
+ else:
74
+ self.load_annotator_config()
75
+ self.locate_tracks()
76
+ self.prepare_stack()
71
77
 
72
- self.generate_signal_choices()
73
- self.frame_lbl = QLabel('frame: ')
74
- self.looped_animation()
75
- self.create_cell_signal_canvas()
78
+ self.generate_signal_choices()
79
+ self.frame_lbl = QLabel('frame: ')
80
+ self.looped_animation()
81
+ self.create_cell_signal_canvas()
76
82
 
77
- self.populate_widget()
78
-
79
- self.setAttribute(Qt.WA_DeleteOnClose)
83
+ self.populate_widget()
80
84
 
81
85
  def populate_widget(self):
82
86
 
@@ -645,11 +649,14 @@ class SignalAnnotator(QMainWindow, Styles):
645
649
  if len(movies) == 0:
646
650
  msgBox = QMessageBox()
647
651
  msgBox.setIcon(QMessageBox.Warning)
648
- msgBox.setText("No movies are detected in the experiment folder. Cannot load an image to test Haralick.")
652
+ msgBox.setText("No movie is detected in the experiment folder.\nPlease check the stack prefix...")
649
653
  msgBox.setWindowTitle("Warning")
650
654
  msgBox.setStandardButtons(QMessageBox.Ok)
651
655
  returnValue = msgBox.exec()
652
- if returnValue == QMessageBox.Yes:
656
+ if returnValue == QMessageBox.Ok:
657
+ self.proceed = False
658
+ self.close()
659
+ else:
653
660
  self.close()
654
661
  else:
655
662
  self.stack_path = movies[0]
@@ -977,15 +984,17 @@ class SignalAnnotator(QMainWindow, Styles):
977
984
  self.stack[np.where(self.stack > 0.)] = np.log(self.stack[np.where(self.stack > 0.)])
978
985
 
979
986
  def closeEvent(self, event):
980
-
981
- self.stop()
982
- # result = QMessageBox.question(self,
983
- # "Confirm Exit...",
984
- # "Are you sure you want to exit ?",
985
- # QMessageBox.Yes| QMessageBox.No,
986
- # )
987
- del self.stack
988
- gc.collect()
987
+ try:
988
+ self.stop()
989
+ # result = QMessageBox.question(self,
990
+ # "Confirm Exit...",
991
+ # "Are you sure you want to exit ?",
992
+ # QMessageBox.Yes| QMessageBox.No,
993
+ # )
994
+ del self.stack
995
+ gc.collect()
996
+ except:
997
+ pass
989
998
 
990
999
  def looped_animation(self):
991
1000
 
@@ -1118,6 +1127,7 @@ class SignalAnnotator(QMainWindow, Styles):
1118
1127
  try:
1119
1128
  min_values = []
1120
1129
  max_values = []
1130
+ feats = []
1121
1131
  for i in range(len(self.signal_choice_cb)):
1122
1132
  signal = self.signal_choice_cb[i].currentText()
1123
1133
  if signal == '--':
@@ -1127,9 +1137,26 @@ class SignalAnnotator(QMainWindow, Styles):
1127
1137
  minn = np.nanpercentile(self.df_tracks.loc[:, signal].to_numpy().flatten(), 1)
1128
1138
  min_values.append(minn)
1129
1139
  max_values.append(maxx)
1140
+ feats.append(signal)
1141
+
1142
+ smallest_value = np.amin(min_values)
1143
+ feat_smallest_value = feats[np.argmin(min_values)]
1144
+ min_feat = self.df_tracks[feat_smallest_value].min()
1145
+ max_feat = self.df_tracks[feat_smallest_value].max()
1146
+ pad_small = (max_feat - min_feat) * 0.05
1147
+ if pad_small==0:
1148
+ pad_small = 0.05
1149
+
1150
+ largest_value = np.amax(max_values)
1151
+ feat_largest_value = feats[np.argmax(max_values)]
1152
+ min_feat = self.df_tracks[feat_largest_value].min()
1153
+ max_feat = self.df_tracks[feat_largest_value].max()
1154
+ pad_large = (max_feat - min_feat) * 0.05
1155
+ if pad_large==0:
1156
+ pad_large = 0.05
1130
1157
 
1131
1158
  if len(min_values) > 0:
1132
- self.cell_ax.set_ylim(np.amin(min_values), np.amax(max_values))
1159
+ self.cell_ax.set_ylim(smallest_value - pad_small, largest_value + pad_large)
1133
1160
  except Exception as e:
1134
1161
  pass
1135
1162
 
@@ -2305,8 +2332,8 @@ class MeasureAnnotator(SignalAnnotator):
2305
2332
  else:
2306
2333
  self.current_stack = self.current_stack[0]
2307
2334
  if self.log_option:
2308
- self.current_stack[np.where(self.current_stack > 0.)] = np.log(
2309
- self.current_stack[np.where(self.current_stack > 0.)])
2335
+ self.current_stack[np.where((self.current_stack > 0.)&(self.current_stack==self.current_stack))] = np.log(
2336
+ self.current_stack[np.where((self.current_stack > 0.)&(self.current_stack==self.current_stack))])
2310
2337
 
2311
2338
  def changed_channel(self):
2312
2339
 
@@ -2422,11 +2449,11 @@ class MeasureAnnotator(SignalAnnotator):
2422
2449
  if len(movies) == 0:
2423
2450
  msgBox = QMessageBox()
2424
2451
  msgBox.setIcon(QMessageBox.Warning)
2425
- msgBox.setText("No movies are detected in the experiment folder. Cannot load an image to test Haralick.")
2452
+ msgBox.setText("No movie is detected in the experiment folder.\nPlease check the stack prefix...")
2426
2453
  msgBox.setWindowTitle("Warning")
2427
2454
  msgBox.setStandardButtons(QMessageBox.Ok)
2428
2455
  returnValue = msgBox.exec()
2429
- if returnValue == QMessageBox.Yes:
2456
+ if returnValue == QMessageBox.Ok:
2430
2457
  self.close()
2431
2458
  else:
2432
2459
  self.stack_path = movies[0]
@@ -140,17 +140,22 @@ class SignalAnnotator2(QMainWindow,Styles):
140
140
 
141
141
  self.button_widget = QWidget()
142
142
  main_layout = QHBoxLayout()
143
+ main_layout.setSpacing(30)
144
+
143
145
  self.button_widget.setLayout(main_layout)
144
146
 
145
147
  main_layout.setContentsMargins(30,30,30,30)
146
148
  self.left_panel = QVBoxLayout()
147
- self.left_panel.setContentsMargins(30,30,30,30)
148
- self.left_panel.setSpacing(10)
149
+ self.left_panel.setContentsMargins(5,5,5,5)
150
+ self.left_panel.setSpacing(5)
149
151
 
150
152
  self.right_panel = QVBoxLayout()
153
+ self.right_panel.setSpacing(0)
154
+ self.right_panel.setContentsMargins(5,5,5,5)
151
155
 
152
156
  #NEIGHBORHOOD
153
157
  neigh_hbox = QHBoxLayout()
158
+ neigh_hbox.setContentsMargins(0,0,0,0)
154
159
  neigh_hbox.addWidget(QLabel('neighborhood: '), 25)
155
160
  self.neighborhood_choice_cb = QComboBox()
156
161
  self.neighborhood_choice_cb.addItems(self.neighborhood_cols)
@@ -193,17 +198,17 @@ class SignalAnnotator2(QMainWindow,Styles):
193
198
  self.relative_del_class_btn.clicked.connect(self.del_relative_event_class)
194
199
  subclass_hbox.addWidget(self.relative_del_class_btn, 5)
195
200
  class_hbox.addLayout(subclass_hbox, 75)
196
-
197
-
198
201
  self.left_panel.addLayout(class_hbox)
199
202
 
200
203
  self.cell_events_hbox = QHBoxLayout()
204
+ self.cell_events_hbox.setContentsMargins(0,0,0,0)
201
205
  self.cell_events_hbox.addWidget(QLabel('reference event: '), 25)
202
206
  self.reference_event_choice_cb = QComboBox()
203
207
  self.cell_events_hbox.addWidget(self.reference_event_choice_cb, 75)
204
208
 
205
209
  #if 'self' not in self.neighborhood_choice_cb.currentText():
206
210
  self.neigh_cell_events_hbox = QHBoxLayout()
211
+ self.neigh_cell_events_hbox.setContentsMargins(0,0,0,0)
207
212
  self.neigh_lab=QLabel('neighbor event: ')
208
213
  self.neigh_cell_events_hbox.addWidget(self.neigh_lab, 25)
209
214
  self.neighbor_event_choice_cb = QComboBox()
@@ -214,7 +219,7 @@ class SignalAnnotator2(QMainWindow,Styles):
214
219
 
215
220
  # Text information about selected cells
216
221
  self.cell_info_hbox = QHBoxLayout()
217
- self.cell_info_hbox.setContentsMargins(30,30,30,30)
222
+ self.cell_info_hbox.setContentsMargins(30,0,30,0)
218
223
 
219
224
  reference_layout = QVBoxLayout()
220
225
  reference_layout.addWidget(self.reference_cell_info)
@@ -232,7 +237,7 @@ class SignalAnnotator2(QMainWindow,Styles):
232
237
 
233
238
  # Annotation buttons
234
239
  options_hbox = QHBoxLayout()
235
- options_hbox.setContentsMargins(150, 30, 50, 0)
240
+ options_hbox.setContentsMargins(150, 0, 50, 0)
236
241
  self.event_btn = QRadioButton('event')
237
242
  self.event_btn.setStyleSheet(self.button_style_sheet_2)
238
243
  self.event_btn.toggled.connect(self.enable_time_of_interest)
@@ -256,7 +261,7 @@ class SignalAnnotator2(QMainWindow,Styles):
256
261
  self.left_panel.addLayout(options_hbox)
257
262
 
258
263
  time_option_hbox = QHBoxLayout()
259
- time_option_hbox.setContentsMargins(100, 30, 100, 30)
264
+ time_option_hbox.setContentsMargins(100, 0, 100, 0)
260
265
  self.time_of_interest_label = QLabel('time of interest: ')
261
266
  time_option_hbox.addWidget(self.time_of_interest_label, 30)
262
267
  self.time_of_interest_le = QLineEdit()
@@ -264,6 +269,7 @@ class SignalAnnotator2(QMainWindow,Styles):
264
269
  self.left_panel.addLayout(time_option_hbox)
265
270
 
266
271
  main_action_hbox = QHBoxLayout()
272
+ main_action_hbox.setContentsMargins(100, 0, 100, 0)
267
273
  self.correct_btn = QPushButton('correct')
268
274
  self.correct_btn.setIcon(icon(MDI6.redo_variant, color="white"))
269
275
  self.correct_btn.setIconSize(QSize(20, 20))
@@ -320,7 +326,7 @@ class SignalAnnotator2(QMainWindow,Styles):
320
326
  self.left_panel.addLayout(plot_buttons_hbox)
321
327
 
322
328
  signal_choice_grid = QVBoxLayout()
323
- signal_choice_grid.setContentsMargins(30,0,30,50)
329
+ signal_choice_grid.setContentsMargins(30,0,70,5)
324
330
 
325
331
  header_layout = QHBoxLayout()
326
332
  header_layout.addWidget(QLabel('reference'), 23, alignment=Qt.AlignCenter)
@@ -332,32 +338,14 @@ class SignalAnnotator2(QMainWindow,Styles):
332
338
  for i in range(self.n_signals):
333
339
 
334
340
  h_layout = QHBoxLayout()
341
+ if i==(self.n_signals-1):
342
+ h_layout.setContentsMargins(0,0,0,0)
335
343
  h_layout.addWidget(self.reference_pop_option_buttons[i], 23, alignment=Qt.AlignCenter)
336
344
  h_layout.addWidget(self.neighbor_pop_option_buttons[i], 23, alignment=Qt.AlignCenter)
337
345
  h_layout.addWidget(self.relative_pop_option_buttons[i], 23, alignment=Qt.AlignCenter)
338
346
  h_layout.addWidget(self.signal_choices[i], 30)
339
347
  signal_choice_grid.addLayout(h_layout)
340
348
 
341
- # signal_choice_vbox = QVBoxLayout()
342
- # signal_choice_vbox.setContentsMargins(30,0,30,50)
343
- # for i in range(len(self.signal_choices)):
344
- # signal_choice_grid.addWidget(self.signal_choices[i],i+1,3)
345
- # # hlayout = QHBoxLayout()
346
- # #
347
- # # #hlayout.addLayout(self.signal_labels[i], 20)
348
- # # #hlayout.addLayout(self.signal_choices[i], 75)
349
- # # # if i==0:
350
- # # # hlayout.addWidget(self.signal_choices[i], 75,alignment=Qt.AlignBottom)
351
- # # # else:
352
- # # hlayout.addWidget(self.signal_choices[i], 75)
353
- # # #hlayout.addWidget(self.log_btns[i], 5)
354
- # # signal_choice_vbox.addLayout(hlayout)
355
-
356
- # self.log_btns[i].setIcon(icon(MDI6.math_log,color="black"))
357
- # self.log_btns[i].setStyleSheet(self.parent.parent.parent.button_select_all)
358
- # self.log_btns[i].clicked.connect(lambda ch, i=i: self.switch_to_log(i))
359
- #signal_choice_hbox.addLayout(signal_choice_vbox,alignment=Qt.AlignCenter)
360
-
361
349
  self.left_panel.addLayout(signal_choice_grid)
362
350
 
363
351
  btn_hbox = QHBoxLayout()
@@ -377,7 +365,6 @@ class SignalAnnotator2(QMainWindow,Styles):
377
365
  # Animation
378
366
  animation_buttons_box = QHBoxLayout()
379
367
 
380
-
381
368
  animation_buttons_box.addWidget(self.frame_lbl, 20, alignment=Qt.AlignLeft)
382
369
 
383
370
  self.first_frame_btn = QPushButton()
@@ -1817,7 +1804,7 @@ class SignalAnnotator2(QMainWindow,Styles):
1817
1804
  else:
1818
1805
  self.stack = self.stack[0]
1819
1806
  if self.log_option:
1820
- self.stack[np.where(self.stack>0.)] = np.log(self.stack[np.where(self.stack>0.)])
1807
+ self.stack[np.where((self.stack>0.)&(self.stack==self.stack))] = np.log(self.stack[np.where((self.stack>0.))&(self.stack==self.stack)])
1821
1808
 
1822
1809
  print(f'Load stack of shape: {self.stack.shape}.')
1823
1810
 
@@ -126,13 +126,15 @@ class ConfigSurvival(QWidget, Styles):
126
126
  main_layout.addLayout(select_layout)
127
127
 
128
128
  time_cut_layout = QHBoxLayout()
129
- time_cut_layout.addWidget(QLabel('cut observation time [min]: '), 33)
129
+ cut_time_lbl = QLabel('cut obs.\ntime [min]: ')
130
+ cut_time_lbl.setToolTip('Filter out later events from\nthe analysis (in absolute time).')
131
+ time_cut_layout.addWidget(cut_time_lbl, 33)
130
132
  self.query_time_cut = QLineEdit()
131
133
  self.query_time_cut.setValidator(self.float_validator)
132
134
  time_cut_layout.addWidget(self.query_time_cut, 66)
133
135
  main_layout.addLayout(time_cut_layout)
134
136
 
135
- self.cbs[0].setCurrentIndex(0)
137
+ self.set_classes_and_times()
136
138
  self.cbs[1].setCurrentText('t_firstdetection')
137
139
 
138
140
  time_calib_layout = QHBoxLayout()
@@ -174,6 +176,8 @@ class ConfigSurvival(QWidget, Styles):
174
176
  print('no column starts with t')
175
177
  self.auto_close = True
176
178
  return None
179
+ if 't0' in self.all_columns:
180
+ time_columns.append('t0')
177
181
 
178
182
  self.cbs[1].clear()
179
183
  self.cbs[1].addItems(np.unique(self.cb_options[1]+time_columns))
@@ -268,7 +272,7 @@ class ConfigSurvival(QWidget, Styles):
268
272
 
269
273
  cut_observation_time = None
270
274
  try:
271
- cut_observation_time = float(self.query_time_cut.text()) / self.FrameToMin
275
+ cut_observation_time = float(self.query_time_cut.text().replace(',','.')) / self.FrameToMin
272
276
  if not 0<cut_observation_time<=(self.df['FRAME'].max()):
273
277
  print('Invalid cut time (larger than movie length)... Not applied.')
274
278
  cut_observation_time = None