celldetective 1.3.3.post1__py3-none-any.whl → 1.3.4__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 (35) hide show
  1. celldetective/__main__.py +30 -4
  2. celldetective/_version.py +1 -1
  3. celldetective/extra_properties.py +21 -0
  4. celldetective/filters.py +15 -2
  5. celldetective/gui/InitWindow.py +28 -34
  6. celldetective/gui/analyze_block.py +3 -498
  7. celldetective/gui/classifier_widget.py +1 -1
  8. celldetective/gui/control_panel.py +98 -27
  9. celldetective/gui/generic_signal_plot.py +35 -18
  10. celldetective/gui/gui_utils.py +143 -2
  11. celldetective/gui/layouts.py +7 -6
  12. celldetective/gui/measurement_options.py +3 -11
  13. celldetective/gui/plot_measurements.py +5 -13
  14. celldetective/gui/plot_signals_ui.py +30 -30
  15. celldetective/gui/process_block.py +61 -103
  16. celldetective/gui/signal_annotator.py +50 -32
  17. celldetective/gui/signal_annotator2.py +7 -4
  18. celldetective/gui/styles.py +13 -0
  19. celldetective/gui/survival_ui.py +8 -21
  20. celldetective/gui/tableUI.py +1 -2
  21. celldetective/gui/thresholds_gui.py +0 -6
  22. celldetective/gui/viewers.py +1 -5
  23. celldetective/io.py +31 -4
  24. celldetective/measure.py +8 -5
  25. celldetective/neighborhood.py +0 -2
  26. celldetective/scripts/measure_cells.py +21 -9
  27. celldetective/signals.py +77 -66
  28. celldetective/tracking.py +19 -13
  29. {celldetective-1.3.3.post1.dist-info → celldetective-1.3.4.dist-info}/METADATA +2 -1
  30. {celldetective-1.3.3.post1.dist-info → celldetective-1.3.4.dist-info}/RECORD +35 -35
  31. tests/test_qt.py +5 -3
  32. {celldetective-1.3.3.post1.dist-info → celldetective-1.3.4.dist-info}/LICENSE +0 -0
  33. {celldetective-1.3.3.post1.dist-info → celldetective-1.3.4.dist-info}/WHEEL +0 -0
  34. {celldetective-1.3.3.post1.dist-info → celldetective-1.3.4.dist-info}/entry_points.txt +0 -0
  35. {celldetective-1.3.3.post1.dist-info → celldetective-1.3.4.dist-info}/top_level.txt +0 -0
@@ -47,8 +47,8 @@ class ConfigSignalPlot(QWidget, Styles):
47
47
  self.ax2=None
48
48
  self.auto_close = False
49
49
 
50
- self.well_option = self.parent_window.parent_window.well_list.currentIndex()
51
- self.position_option = self.parent_window.parent_window.position_list.currentIndex()
50
+ self.well_option = self.parent_window.parent_window.well_list.getSelectedIndices()
51
+ self.position_option = self.parent_window.parent_window.position_list.getSelectedIndices()
52
52
  self.interpret_pos_location()
53
53
 
54
54
  self.screen_height = self.parent_window.parent_window.parent_window.screen_height
@@ -67,15 +67,11 @@ class ConfigSignalPlot(QWidget, Styles):
67
67
 
68
68
  """
69
69
 
70
- if self.well_option==len(self.wells):
71
- self.well_indices = np.arange(len(self.wells))
72
- else:
73
- self.well_indices = np.array([self.well_option],dtype=int)
74
-
75
- if self.position_option==0:
70
+ self.well_indices = self.parent_window.parent_window.well_list.getSelectedIndices()
71
+ self.position_indices = self.parent_window.parent_window.position_list.getSelectedIndices()
72
+ if not self.parent_window.parent_window.position_list.isAnySelected():
76
73
  self.position_indices = None
77
- else:
78
- self.position_indices = np.array([self.position_option],dtype=int)
74
+
79
75
 
80
76
  def populate_widget(self):
81
77
 
@@ -188,11 +184,21 @@ class ConfigSignalPlot(QWidget, Styles):
188
184
  if 't0' in self.all_columns:
189
185
  time_columns.append('t0')
190
186
 
187
+ self.class_columns = np.unique(class_columns)
188
+ self.time_columns = np.unique(time_columns)
189
+ thresh = 18
190
+ self.class_truncated = [w[:thresh - 3]+'...' if len(w)>thresh else w for w in self.class_columns]
191
+ self.time_truncated = [w[:thresh - 3]+'...' if len(w)>thresh else w for w in self.time_columns]
192
+
191
193
  self.cbs[2].clear()
192
- self.cbs[2].addItems(np.unique(self.cb_options[2]+time_columns))
194
+ self.cbs[2].addItems(self.time_truncated)
195
+ for i in range(len(self.time_columns)):
196
+ self.cbs[2].setItemData(i, self.time_columns[i], Qt.ToolTipRole)
193
197
 
194
198
  self.cbs[1].clear()
195
- self.cbs[1].addItems(np.unique(self.cb_options[1]+class_columns))
199
+ self.cbs[1].addItems(self.class_truncated)
200
+ for i in range(len(self.class_columns)):
201
+ self.cbs[1].setItemData(i, self.class_columns[i], Qt.ToolTipRole)
196
202
 
197
203
  def ask_for_feature(self):
198
204
 
@@ -273,7 +279,8 @@ class ConfigSignalPlot(QWidget, Styles):
273
279
 
274
280
  # read instructions from combobox options
275
281
  self.load_available_tables()
276
- class_col = self.cbs[1].currentText()
282
+ class_col = self.class_columns[self.cbs[1].currentIndex()]
283
+ print(f"{class_col=}")
277
284
 
278
285
  if self.df is not None:
279
286
 
@@ -300,18 +307,10 @@ class ConfigSignalPlot(QWidget, Styles):
300
307
 
301
308
  """
302
309
 
303
- self.well_option = self.parent_window.parent_window.well_list.currentIndex()
304
- if self.well_option==len(self.wells):
305
- wo = '*'
306
- else:
307
- wo = self.well_option
308
- self.position_option = self.parent_window.parent_window.position_list.currentIndex()
309
- if self.position_option==0:
310
- po = '*'
311
- else:
312
- po = self.position_option - 1
310
+ self.well_option = self.parent_window.parent_window.well_list.getSelectedIndices()
311
+ self.position_option = self.parent_window.parent_window.position_list.getSelectedIndices()
313
312
 
314
- self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=wo, position_option=po, population=self.cbs[0].currentText(), return_pos_info=True)
313
+ self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=self.well_option, position_option=self.position_option, population=self.cbs[0].currentText(), return_pos_info=True)
315
314
 
316
315
  if self.df is None:
317
316
 
@@ -337,8 +336,8 @@ class ConfigSignalPlot(QWidget, Styles):
337
336
 
338
337
  # Per position signal
339
338
  max_time = int(self.df.FRAME.max()) + 1
340
- class_col = self.cbs[1].currentText()
341
- time_col = self.cbs[2].currentText()
339
+ class_col = self.class_columns[self.cbs[1].currentIndex()]
340
+ time_col = self.time_columns[self.cbs[2].currentIndex()]
342
341
  if self.abs_time_checkbox.isChecked():
343
342
  time_col = self.frame_slider.value()
344
343
 
@@ -375,6 +374,9 @@ class ConfigSignalPlot(QWidget, Styles):
375
374
  if isinstance(cclass,int):
376
375
  cclass = [cclass]
377
376
 
377
+ class_col = self.class_columns[self.cbs[1].currentIndex()]
378
+ time_col = self.time_columns[self.cbs[2].currentIndex()]
379
+
378
380
  n_cells = len(well_group.groupby(['position','TRACK_ID']))
379
381
  depth = int(2*max_time + 3)
380
382
  matrix = np.zeros((n_cells, depth))
@@ -382,14 +384,14 @@ class ConfigSignalPlot(QWidget, Styles):
382
384
  mapping = np.arange(-max_time-1, max_time+2)
383
385
  cid=0
384
386
  for block,movie_group in well_group.groupby('position'):
385
- for tid,track_group in movie_group.loc[movie_group[self.cbs[1].currentText()].isin(cclass)].groupby('TRACK_ID'):
387
+ for tid,track_group in movie_group.loc[movie_group[class_col].isin(cclass)].groupby('TRACK_ID'):
386
388
  try:
387
389
  timeline = track_group['FRAME'].to_numpy().astype(int)
388
390
  feature = track_group[feature_selected].to_numpy()
389
391
  if self.checkBox_feature.isChecked():
390
392
  second_feature=track_group[self.second_feature_selected].to_numpy()
391
393
  if self.cbs[2].currentText().startswith('t') and not self.abs_time_checkbox.isChecked():
392
- t0 = math.floor(track_group[self.cbs[2].currentText()].to_numpy()[0])
394
+ t0 = math.floor(track_group[time_col].to_numpy()[0])
393
395
  timeline -= t0
394
396
  elif self.cbs[2].currentText()=='first detection' and not self.abs_time_checkbox.isChecked():
395
397
 
@@ -401,7 +403,6 @@ class ConfigSignalPlot(QWidget, Styles):
401
403
 
402
404
  first_detection = timeline[feat==feat][0]
403
405
  timeline -= first_detection
404
- print(first_detection, timeline)
405
406
 
406
407
  elif self.abs_time_checkbox.isChecked():
407
408
  timeline -= int(self.frame_slider.value())
@@ -410,7 +411,6 @@ class ConfigSignalPlot(QWidget, Styles):
410
411
  matrix[cid,loc_t] = feature
411
412
  if second_feature:
412
413
  matrix[cid,loc_t+1]=second_feature
413
- print(timeline, loc_t)
414
414
 
415
415
  cid+=1
416
416
  except:
@@ -186,11 +186,8 @@ class ProcessPanel(QFrame, Styles):
186
186
  measure_layout = QHBoxLayout()
187
187
 
188
188
  self.measure_action = QCheckBox("MEASURE")
189
- self.measure_action.setStyleSheet("""
190
- font-size: 10px;
191
- padding-left: 10px;
192
- padding-top: 5px;
193
- """)
189
+ self.measure_action.setStyleSheet(self.menu_check_style)
190
+
194
191
  self.measure_action.setIcon(icon(MDI6.eyedropper,color="black"))
195
192
  self.measure_action.setIconSize(QSize(20, 20))
196
193
  self.measure_action.setToolTip("Measure.")
@@ -229,11 +226,7 @@ class ProcessPanel(QFrame, Styles):
229
226
  signal_layout = QVBoxLayout()
230
227
  signal_hlayout = QHBoxLayout()
231
228
  self.signal_analysis_action = QCheckBox("DETECT EVENTS")
232
- self.signal_analysis_action.setStyleSheet("""
233
- font-size: 10px;
234
- padding-left: 10px;
235
- padding-top: 5px;
236
- """)
229
+ self.signal_analysis_action.setStyleSheet(self.menu_check_style)
237
230
  self.signal_analysis_action.setIcon(icon(MDI6.chart_bell_curve_cumulative,color="black"))
238
231
  self.signal_analysis_action.setIconSize(QSize(20, 20))
239
232
  self.signal_analysis_action.setToolTip("Detect events in single-cell signals.")
@@ -295,14 +288,10 @@ class ProcessPanel(QFrame, Styles):
295
288
  grid_track = QHBoxLayout()
296
289
 
297
290
  self.track_action = QCheckBox("TRACK")
291
+ self.track_action.setStyleSheet(self.menu_check_style)
298
292
  self.track_action.setIcon(icon(MDI6.chart_timeline_variant,color="black"))
299
293
  self.track_action.setIconSize(QSize(20, 20))
300
294
  self.track_action.setToolTip("Track the target cells using bTrack.")
301
- self.track_action.setStyleSheet("""
302
- font-size: 10px;
303
- padding-left: 10px;
304
- padding-top: 5px;
305
- """)
306
295
  grid_track.addWidget(self.track_action, 75)
307
296
 
308
297
  self.delete_tracks_btn = QPushButton()
@@ -372,10 +361,7 @@ class ProcessPanel(QFrame, Styles):
372
361
  grid_segment.setSpacing(0)
373
362
 
374
363
  self.segment_action = QCheckBox("SEGMENT")
375
- self.segment_action.setStyleSheet("""
376
- font-size: 10px;
377
- padding-left: 10px;
378
- """)
364
+ self.segment_action.setStyleSheet(self.menu_check_style)
379
365
  self.segment_action.setIcon(icon(MDI6.bacteria, color='black'))
380
366
  self.segment_action.setToolTip(f"Segment the {self.mode} cells on the images.")
381
367
  self.segment_action.toggled.connect(self.enable_segmentation_model_list)
@@ -698,11 +684,24 @@ class ProcessPanel(QFrame, Styles):
698
684
 
699
685
  def process_population(self):
700
686
 
701
- if self.parent_window.well_list.currentText()=="*":
702
- self.well_index = np.linspace(0,len(self.wells)-1,len(self.wells),dtype=int)
703
- else:
704
- self.well_index = [self.parent_window.well_list.currentIndex()]
705
- print(f"Processing well {self.parent_window.well_list.currentText()}...")
687
+ # if self.parent_window.well_list.currentText().startswith('Multiple'):
688
+ # self.well_index = np.linspace(0,len(self.wells)-1,len(self.wells),dtype=int)
689
+ # else:
690
+
691
+ self.well_index = self.parent_window.well_list.getSelectedIndices()
692
+ if len(self.well_index)==0:
693
+ msgBox = QMessageBox()
694
+ msgBox.setIcon(QMessageBox.Warning)
695
+ msgBox.setText("Please select at least one well first...")
696
+ msgBox.setWindowTitle("Warning")
697
+ msgBox.setStandardButtons(QMessageBox.Ok)
698
+ returnValue = msgBox.exec()
699
+ if returnValue == QMessageBox.Ok:
700
+ return None
701
+ else:
702
+ return None
703
+
704
+ print(f"Processing {self.parent_window.well_list.currentText()}...")
706
705
 
707
706
  # self.freeze()
708
707
  # QApplication.setOverrideCursor(Qt.WaitCursor)
@@ -735,10 +734,10 @@ class ProcessPanel(QFrame, Styles):
735
734
 
736
735
  loop_iter=0
737
736
 
738
- if self.parent_window.position_list.currentText()=="*":
737
+ if self.parent_window.position_list.isMultipleSelection():
739
738
  msgBox = QMessageBox()
740
739
  msgBox.setIcon(QMessageBox.Question)
741
- msgBox.setText("If you continue, all positions will be processed.\nDo you want to proceed?")
740
+ msgBox.setText("If you continue, several positions will be processed.\nDo you want to proceed?")
742
741
  msgBox.setWindowTitle("Info")
743
742
  msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
744
743
  returnValue = msgBox.exec()
@@ -767,12 +766,8 @@ class ProcessPanel(QFrame, Styles):
767
766
  for w_idx in self.well_index:
768
767
 
769
768
  pos = self.parent_window.positions[w_idx]
770
- if self.parent_window.position_list.currentText()=="*":
771
- pos_indices = np.linspace(0,len(pos)-1,len(pos),dtype=int)
772
- print("Processing all positions...")
773
- else:
774
- pos_indices = natsorted([pos.index(self.parent_window.position_list.currentText())])
775
- print(f"Processing position {self.parent_window.position_list.currentText()}...")
769
+ pos_indices = self.parent_window.position_list.getSelectedIndices()
770
+ #print(f"Processing position {self.parent_window.position_list.currentText()}...")
776
771
 
777
772
  well = self.parent_window.wells[w_idx]
778
773
 
@@ -788,7 +783,7 @@ class ProcessPanel(QFrame, Styles):
788
783
 
789
784
  if self.segment_action.isChecked():
790
785
 
791
- if len(glob(os.sep.join([self.pos, f'labels_{self.mode}','*.tif'])))>0 and self.parent_window.position_list.currentText()!="*":
786
+ if len(glob(os.sep.join([self.pos, f'labels_{self.mode}','*.tif'])))>0 and not self.parent_window.position_list.isMultipleSelection():
792
787
  msgBox = QMessageBox()
793
788
  msgBox.setIcon(QMessageBox.Question)
794
789
  msgBox.setText("Labels have already been produced for this position. Do you want to segment again?")
@@ -815,7 +810,7 @@ class ProcessPanel(QFrame, Styles):
815
810
  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)
816
811
 
817
812
  if self.track_action.isChecked():
818
- if os.path.exists(os.sep.join([self.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])) and self.parent_window.position_list.currentText()!="*":
813
+ if os.path.exists(os.sep.join([self.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])) and not self.parent_window.position_list.isMultipleSelection():
819
814
  msgBox = QMessageBox()
820
815
  msgBox.setIcon(QMessageBox.Question)
821
816
  msgBox.setText("A trajectory set already exists. Previously annotated data for\nthis position will be lost. Do you want to proceed?")
@@ -831,15 +826,12 @@ class ProcessPanel(QFrame, Styles):
831
826
 
832
827
  table = os.sep.join([self.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])
833
828
  if self.signal_analysis_action.isChecked() and os.path.exists(table):
834
- print('table exists')
835
829
  table = pd.read_csv(table)
836
830
  cols = list(table.columns)
837
- print(table, cols)
838
831
  if 'class_color' in cols:
839
- print(cols, 'class_color in cols')
840
832
  colors = list(table['class_color'].to_numpy())
841
833
  if 'tab:orange' in colors or 'tab:cyan' in colors:
842
- if self.parent_window.position_list.currentText()!="*":
834
+ if not self.parent_window.position_list.isMultipleSelection():
843
835
  msgBox = QMessageBox()
844
836
  msgBox.setIcon(QMessageBox.Question)
845
837
  msgBox.setText("The signals of the cells in the position appear to have been annotated... Do you want to proceed?")
@@ -871,7 +863,7 @@ class ProcessPanel(QFrame, Styles):
871
863
  plot_mode = 'plot_track_signals'
872
864
  if 'TRACK_ID' not in list(self.df.columns):
873
865
  plot_mode = 'static'
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)
866
+ self.tab_ui = TableUI(self.df, f"{self.parent_window.well_list.currentText()}; Position {self.parent_window.position_list.currentText()}", population=self.mode, plot_mode=plot_mode, save_inplace_option=True)
875
867
  self.tab_ui.show()
876
868
  else:
877
869
  print('Table could not be loaded...')
@@ -891,18 +883,10 @@ class ProcessPanel(QFrame, Styles):
891
883
 
892
884
  """
893
885
 
894
- self.well_option = self.parent_window.well_list.currentIndex()
895
- if self.well_option==len(self.wells):
896
- wo = '*'
897
- else:
898
- wo = self.well_option
899
- self.position_option = self.parent_window.position_list.currentIndex()
900
- if self.position_option==0:
901
- po = '*'
902
- else:
903
- po = self.position_option - 1
886
+ self.well_option = self.parent_window.well_list.getSelectedIndices()
887
+ self.position_option = self.parent_window.position_list.getSelectedIndices()
904
888
 
905
- self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=wo, position_option=po, population=self.mode, return_pos_info=True)
889
+ self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=self.well_option, position_option=self.position_option, population=self.mode, return_pos_info=True)
906
890
  if self.df is None:
907
891
  print('No table could be found...')
908
892
 
@@ -1027,11 +1011,8 @@ class NeighPanel(QFrame, Styles):
1027
1011
  # Button to compute the neighborhoods
1028
1012
  neigh_option_hbox = QHBoxLayout()
1029
1013
  self.neigh_action = QCheckBox('NEIGHBORHOODS')
1030
- self.neigh_action.setStyleSheet("""
1031
- font-size: 10px;
1032
- padding-left: 10px;
1033
- padding-top: 5px;
1034
- """)
1014
+ self.neigh_action.setStyleSheet(self.menu_check_style)
1015
+
1035
1016
  #self.neigh_action.setIcon(icon(MDI6.eyedropper, color="black"))
1036
1017
  #self.neigh_action.setIconSize(QSize(20, 20))
1037
1018
  self.neigh_action.setToolTip(
@@ -1131,11 +1112,8 @@ class NeighPanel(QFrame, Styles):
1131
1112
 
1132
1113
  rel_layout = QHBoxLayout()
1133
1114
  self.measure_pairs_action = QCheckBox("MEASURE PAIRS")
1134
- self.measure_pairs_action.setStyleSheet("""
1135
- font-size: 10px;
1136
- padding-left: 10px;
1137
- padding-top: 5px;
1138
- """)
1115
+ self.measure_pairs_action.setStyleSheet(self.menu_check_style)
1116
+
1139
1117
  self.measure_pairs_action.setIcon(icon(MDI6.eyedropper, color="black"))
1140
1118
  self.measure_pairs_action.setIconSize(QSize(20, 20))
1141
1119
  self.measure_pairs_action.setToolTip("Measure the relative quantities defined for the cell pairs, for all neighborhoods.")
@@ -1154,10 +1132,7 @@ class NeighPanel(QFrame, Styles):
1154
1132
  signal_layout = QVBoxLayout()
1155
1133
  signal_hlayout = QHBoxLayout()
1156
1134
  self.signal_analysis_action = QCheckBox("DETECT PAIR EVENTS")
1157
- self.signal_analysis_action.setStyleSheet("""
1158
- font-size: 10px;
1159
- padding-left: 10px;
1160
- padding-top: 5px;""")
1135
+ self.signal_analysis_action.setStyleSheet(self.menu_check_style)
1161
1136
 
1162
1137
  self.signal_analysis_action.setIcon(icon(MDI6.chart_bell_curve_cumulative, color="black"))
1163
1138
  self.signal_analysis_action.setIconSize(QSize(20, 20))
@@ -1282,18 +1257,10 @@ class NeighPanel(QFrame, Styles):
1282
1257
 
1283
1258
  """
1284
1259
 
1285
- self.well_option = self.parent_window.well_list.currentIndex()
1286
- if self.well_option==len(self.wells):
1287
- wo = '*'
1288
- else:
1289
- wo = self.well_option
1290
- self.position_option = self.parent_window.position_list.currentIndex()
1291
- if self.position_option==0:
1292
- po = '*'
1293
- else:
1294
- po = self.position_option - 1
1260
+ self.well_option = self.parent_window.well_list.getSelectedIndices()
1261
+ self.position_option = self.parent_window.position_list.getSelectedIndices()
1295
1262
 
1296
- self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=wo, position_option=po, population="pairs", return_pos_info=True)
1263
+ self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=self.well_option, position_option=self.position_option, population="pairs", return_pos_info=True)
1297
1264
  if self.df is None:
1298
1265
  print('No table could be found...')
1299
1266
 
@@ -1305,7 +1272,7 @@ class NeighPanel(QFrame, Styles):
1305
1272
 
1306
1273
  if self.df is not None:
1307
1274
  plot_mode = 'static'
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)
1275
+ self.tab_ui = TableUI(self.df, f"{self.parent_window.well_list.currentText()}; Position {self.parent_window.position_list.currentText()}", population='pairs', plot_mode=plot_mode, save_inplace_option=True)
1309
1276
  self.tab_ui.show()
1310
1277
  else:
1311
1278
  print('Table could not be loaded...')
@@ -1382,18 +1349,18 @@ class NeighPanel(QFrame, Styles):
1382
1349
 
1383
1350
  def process_neighborhood(self):
1384
1351
 
1385
- if self.parent_window.well_list.currentText()=="*":
1386
- self.well_index = np.linspace(0,len(self.wells)-1,len(self.wells),dtype=int)
1387
- else:
1388
- self.well_index = [self.parent_window.well_list.currentIndex()]
1389
- print(f"Processing well {self.parent_window.well_list.currentText()}...")
1352
+ # if self.parent_window.well_list.currentText().startswith('Multiple'):
1353
+ # self.well_index = np.linspace(0,len(self.wells)-1,len(self.wells),dtype=int)
1354
+ # else:
1355
+ self.well_index = self.parent_window.well_list.getSelectedIndices()
1356
+ print(f"Processing well {self.parent_window.well_list.currentText()}...")
1390
1357
 
1391
1358
  # self.freeze()
1392
1359
  # QApplication.setOverrideCursor(Qt.WaitCursor)
1393
1360
 
1394
1361
  loop_iter=0
1395
1362
 
1396
- if self.parent_window.position_list.currentText()=="*":
1363
+ if self.parent_window.position_list.isMultipleSelection():
1397
1364
  msgBox = QMessageBox()
1398
1365
  msgBox.setIcon(QMessageBox.Question)
1399
1366
  msgBox.setText("If you continue, all positions will be processed.\nDo you want to proceed?")
@@ -1406,12 +1373,7 @@ class NeighPanel(QFrame, Styles):
1406
1373
  for w_idx in self.well_index:
1407
1374
 
1408
1375
  pos = self.parent_window.positions[w_idx]
1409
- if self.parent_window.position_list.currentText()=="*":
1410
- pos_indices = np.linspace(0,len(pos)-1,len(pos),dtype=int)
1411
- print("Processing all positions...")
1412
- else:
1413
- pos_indices = natsorted([pos.index(self.parent_window.position_list.currentText())])
1414
- print(f"Processing position {self.parent_window.position_list.currentText()}...")
1376
+ pos_indices = self.parent_window.position_list.getSelectedIndices()
1415
1377
 
1416
1378
  well = self.parent_window.wells[w_idx]
1417
1379
 
@@ -1618,8 +1580,8 @@ class PreprocessingPanel(QFrame, Styles):
1618
1580
  if returnValue == QMessageBox.Cancel:
1619
1581
  return None
1620
1582
  elif returnValue == QMessageBox.Yes:
1621
- self.parent_window.well_list.setCurrentIndex(self.parent_window.well_list.count()-1)
1622
- self.parent_window.position_list.setCurrentIndex(0)
1583
+ self.parent_window.well_list.selectAll()
1584
+ self.parent_window.position_list.selectAll()
1623
1585
  elif returnValue == QMessageBox.No:
1624
1586
  msgBox2 = QMessageBox()
1625
1587
  msgBox2.setIcon(QMessageBox.Question)
@@ -1634,16 +1596,11 @@ class PreprocessingPanel(QFrame, Styles):
1634
1596
 
1635
1597
  print('Proceed with correction...')
1636
1598
 
1637
- if self.parent_window.well_list.currentText()=='*':
1638
- well_option = "*"
1639
- else:
1640
- well_option = self.parent_window.well_list.currentIndex()
1641
-
1642
- if self.parent_window.position_list.currentText()=='*':
1643
- pos_option = "*"
1644
- else:
1645
- pos_option = self.parent_window.position_list.currentIndex()-1
1646
-
1599
+ # if self.parent_window.well_list.currentText()=='*':
1600
+ # well_option = "*"
1601
+ # else:
1602
+ well_option = self.parent_window.well_list.getSelectedIndices()
1603
+ position_option = self.parent_window.position_list.getSelectedIndices()
1647
1604
 
1648
1605
  for k,correction_protocol in enumerate(self.protocol_layout.protocols):
1649
1606
 
@@ -1658,7 +1615,7 @@ class PreprocessingPanel(QFrame, Styles):
1658
1615
  print(f'Model-free correction; {movie_prefix=} {export_prefix=}')
1659
1616
  correct_background_model_free(self.exp_dir,
1660
1617
  well_option=well_option,
1661
- position_option=pos_option,
1618
+ position_option=position_option,
1662
1619
  export = True,
1663
1620
  return_stacks=False,
1664
1621
  show_progress_per_well = True,
@@ -1672,7 +1629,7 @@ class PreprocessingPanel(QFrame, Styles):
1672
1629
  print(f'Fit correction; {movie_prefix=} {export_prefix=} {correction_protocol=}')
1673
1630
  correct_background_model(self.exp_dir,
1674
1631
  well_option=well_option,
1675
- position_option=pos_option,
1632
+ position_option=position_option,
1676
1633
  export= True,
1677
1634
  return_stacks=False,
1678
1635
  show_progress_per_well = True,
@@ -1685,7 +1642,7 @@ class PreprocessingPanel(QFrame, Styles):
1685
1642
  print(f'Offset correction; {movie_prefix=} {export_prefix=} {correction_protocol=}')
1686
1643
  correct_channel_offset(self.exp_dir,
1687
1644
  well_option=well_option,
1688
- position_option=pos_option,
1645
+ position_option=position_option,
1689
1646
  export= True,
1690
1647
  return_stacks=False,
1691
1648
  show_progress_per_well = True,
@@ -1703,6 +1660,7 @@ class PreprocessingPanel(QFrame, Styles):
1703
1660
  Load the first frame of the first movie found in the experiment folder as a sample.
1704
1661
  """
1705
1662
 
1663
+ print(f"{self.parent_window.pos}")
1706
1664
  movies = glob(self.parent_window.pos + os.sep.join(['movie', f"{self.parent_window.movie_prefix}*.tif"]))
1707
1665
 
1708
1666
  if len(movies) == 0:
@@ -50,6 +50,7 @@ class SignalAnnotator(QMainWindow, Styles):
50
50
  self.soft_path = get_software_location()
51
51
  self.recently_modified = False
52
52
  self.selection = []
53
+
53
54
  if self.mode == "targets":
54
55
  self.instructions_path = self.exp_dir + os.sep.join(['configs', 'signal_annotator_config_targets.json'])
55
56
  self.trajectories_path = self.pos + os.sep.join(['output','tables','trajectories_targets.csv'])
@@ -505,6 +506,11 @@ class SignalAnnotator(QMainWindow, Styles):
505
506
  self.df_tracks['class_color'] = [color_from_class(i) for i in self.df_tracks[self.class_name].to_numpy()]
506
507
 
507
508
  self.extract_scatter_from_trajectories()
509
+ if len(self.selection)>0:
510
+ self.select_single_cell(self.selection[0][0], self.selection[0][1])
511
+
512
+ self.fcanvas.canvas.draw()
513
+
508
514
 
509
515
  def contrast_slider_action(self):
510
516
 
@@ -527,14 +533,14 @@ class SignalAnnotator(QMainWindow, Styles):
527
533
  try:
528
534
  self.selection.pop(0)
529
535
  except Exception as e:
530
- print(e)
536
+ print(f"L 536 {e=}")
531
537
 
532
538
  try:
533
539
  for k, (t, idx) in enumerate(zip(self.loc_t, self.loc_idx)):
534
540
  self.colors[t][idx, 0] = self.previous_color[k][0]
535
541
  self.colors[t][idx, 1] = self.previous_color[k][1]
536
542
  except Exception as e:
537
- print(f'{e=}')
543
+ print(f'L 543 {e=}')
538
544
 
539
545
  def hide_annotation_buttons(self):
540
546
 
@@ -589,7 +595,7 @@ class SignalAnnotator(QMainWindow, Styles):
589
595
  self.line_dt.set_xdata([t0, t0])
590
596
  self.cell_fcanvas.canvas.draw_idle()
591
597
  except Exception as e:
592
- print(e)
598
+ print(f"L 598 {e=}")
593
599
  t0 = -1
594
600
  cclass = 2
595
601
  elif self.no_event_btn.isChecked():
@@ -611,9 +617,14 @@ class SignalAnnotator(QMainWindow, Styles):
611
617
  status[:] = 2
612
618
  if cclass > 2:
613
619
  status[:] = 42
620
+
614
621
  status_color = [color_from_status(s, recently_modified=True) for s in status]
615
622
  class_color = [color_from_class(cclass, recently_modified=True) for i in range(len(status))]
616
623
 
624
+
625
+ # self.df_tracks['status_color'] = [color_from_status(i) for i in self.df_tracks[self.status_name].to_numpy()]
626
+ # self.df_tracks['class_color'] = [color_from_class(i) for i in self.df_tracks[self.class_name].to_numpy()]
627
+
617
628
  self.df_tracks.loc[indices, self.status_name] = status
618
629
  self.df_tracks.loc[indices, 'status_color'] = status_color
619
630
  self.df_tracks.loc[indices, 'class_color'] = class_color
@@ -984,13 +995,9 @@ class SignalAnnotator(QMainWindow, Styles):
984
995
  self.stack[np.where(self.stack > 0.)] = np.log(self.stack[np.where(self.stack > 0.)])
985
996
 
986
997
  def closeEvent(self, event):
998
+
987
999
  try:
988
1000
  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
1001
  del self.stack
995
1002
  gc.collect()
996
1003
  except:
@@ -1068,6 +1075,8 @@ class SignalAnnotator(QMainWindow, Styles):
1068
1075
 
1069
1076
  def on_scatter_pick(self, event):
1070
1077
 
1078
+ self.event = event
1079
+
1071
1080
  self.correct_btn.disconnect()
1072
1081
  self.correct_btn.clicked.connect(self.show_annotation_buttons)
1073
1082
 
@@ -1082,36 +1091,41 @@ class SignalAnnotator(QMainWindow, Styles):
1082
1091
  ind = [ind[np.argmin(dist)]]
1083
1092
 
1084
1093
  if len(ind) > 0 and (len(self.selection) == 0):
1085
- ind = ind[0]
1086
- self.selection.append(ind)
1087
- self.correct_btn.setEnabled(True)
1088
- self.cancel_btn.setEnabled(True)
1089
- self.del_shortcut.setEnabled(True)
1090
- self.no_event_shortcut.setEnabled(True)
1091
-
1092
- self.track_of_interest = self.tracks[self.framedata][ind]
1093
- print(f'You selected cell #{self.track_of_interest}...')
1094
- self.give_cell_information()
1095
- self.plot_signals()
1096
-
1097
- self.loc_t = []
1098
- self.loc_idx = []
1099
- for t in range(len(self.tracks)):
1100
- indices = np.where(self.tracks[t] == self.track_of_interest)[0]
1101
- if len(indices) > 0:
1102
- self.loc_t.append(t)
1103
- self.loc_idx.append(indices[0])
1104
-
1105
- self.previous_color = []
1106
- for t, idx in zip(self.loc_t, self.loc_idx):
1107
- self.previous_color.append(self.colors[t][idx].copy())
1108
- self.colors[t][idx] = 'lime'
1094
+
1095
+ self.selection.append([ind[0],self.framedata])
1096
+ self.select_single_cell(ind[0], self.framedata)
1109
1097
 
1110
1098
  elif len(ind) > 0 and len(self.selection) == 1:
1111
1099
  self.cancel_btn.click()
1112
1100
  else:
1113
1101
  pass
1114
1102
 
1103
+ def select_single_cell(self, index, timepoint):
1104
+
1105
+ self.correct_btn.setEnabled(True)
1106
+ self.cancel_btn.setEnabled(True)
1107
+ self.del_shortcut.setEnabled(True)
1108
+ self.no_event_shortcut.setEnabled(True)
1109
+
1110
+ self.track_of_interest = self.tracks[timepoint][index]
1111
+ print(f'You selected cell #{self.track_of_interest}...')
1112
+ self.give_cell_information()
1113
+ self.plot_signals()
1114
+
1115
+ self.loc_t = []
1116
+ self.loc_idx = []
1117
+ for t in range(len(self.tracks)):
1118
+ indices = np.where(self.tracks[t] == self.track_of_interest)[0]
1119
+ if len(indices) > 0:
1120
+ self.loc_t.append(t)
1121
+ self.loc_idx.append(indices[0])
1122
+
1123
+ self.previous_color = []
1124
+ for t, idx in zip(self.loc_t, self.loc_idx):
1125
+ self.previous_color.append(self.colors[t][idx].copy())
1126
+ self.colors[t][idx] = 'lime'
1127
+
1128
+
1115
1129
  def shortcut_suppr(self):
1116
1130
  self.correct_btn.click()
1117
1131
  self.suppr_btn.click()
@@ -1158,6 +1172,7 @@ class SignalAnnotator(QMainWindow, Styles):
1158
1172
  if len(min_values) > 0:
1159
1173
  self.cell_ax.set_ylim(smallest_value - pad_small, largest_value + pad_large)
1160
1174
  except Exception as e:
1175
+ print(f"L1170 {e=}")
1161
1176
  pass
1162
1177
 
1163
1178
  def draw_frame(self, framedata):
@@ -1220,6 +1235,7 @@ class SignalAnnotator(QMainWindow, Styles):
1220
1235
  self.df_tracks = self.df_tracks.drop(self.df_tracks[self.df_tracks[self.class_name] > 2].index)
1221
1236
  self.df_tracks.to_csv(self.trajectories_path, index=False)
1222
1237
  print('Table successfully exported...')
1238
+ self.compute_status_and_colors(0)
1223
1239
  self.extract_scatter_from_trajectories()
1224
1240
 
1225
1241
  # self.give_cell_information()
@@ -2424,6 +2440,8 @@ class MeasureAnnotator(SignalAnnotator):
2424
2440
  except Exception as e:
2425
2441
  print("cancel_selection: ",f'{e=}')
2426
2442
 
2443
+ self.event = None
2444
+
2427
2445
  def locate_stack(self):
2428
2446
 
2429
2447
  """
@@ -2213,10 +2213,13 @@ class SignalAnnotator2(QMainWindow,Styles):
2213
2213
  self.index = ind[0]
2214
2214
  elif len(ind)>1:
2215
2215
  # More than one point in vicinity
2216
- datax,datay = [positions[self.framedata][i,0] for i in ind],[positions[self.framedata][i,1] for i in ind]
2217
- msx, msy = event.mouseevent.xdata, event.mouseevent.ydata
2218
- dist = np.sqrt((np.array(datax)-msx)**2+(np.array(datay)-msy)**2)
2219
- self.index = ind[np.argmin(dist)]
2216
+ try:
2217
+ datax,datay = [positions[self.framedata][i,0] for i in ind],[positions[self.framedata][i,1] for i in ind]
2218
+ msx, msy = event.mouseevent.xdata, event.mouseevent.ydata
2219
+ dist = np.sqrt((np.array(datax)-msx)**2+(np.array(datay)-msy)**2)
2220
+ self.index = ind[np.argmin(dist)]
2221
+ except Exception as e:
2222
+ print(f"{e=}")
2220
2223
  else:
2221
2224
  self.index = None
2222
2225