celldetective 1.3.9.post5__py3-none-any.whl → 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. celldetective/__init__.py +0 -3
  2. celldetective/_version.py +1 -1
  3. celldetective/events.py +2 -4
  4. celldetective/extra_properties.py +132 -0
  5. celldetective/gui/InitWindow.py +33 -45
  6. celldetective/gui/__init__.py +1 -0
  7. celldetective/gui/about.py +19 -15
  8. celldetective/gui/analyze_block.py +34 -19
  9. celldetective/gui/base_components.py +23 -0
  10. celldetective/gui/btrack_options.py +26 -34
  11. celldetective/gui/classifier_widget.py +68 -81
  12. celldetective/gui/configure_new_exp.py +113 -17
  13. celldetective/gui/control_panel.py +68 -141
  14. celldetective/gui/generic_signal_plot.py +9 -12
  15. celldetective/gui/gui_utils.py +49 -21
  16. celldetective/gui/json_readers.py +5 -4
  17. celldetective/gui/layouts.py +246 -22
  18. celldetective/gui/measurement_options.py +32 -17
  19. celldetective/gui/neighborhood_options.py +10 -13
  20. celldetective/gui/plot_measurements.py +21 -17
  21. celldetective/gui/plot_signals_ui.py +125 -72
  22. celldetective/gui/process_block.py +180 -123
  23. celldetective/gui/processes/compute_neighborhood.py +594 -0
  24. celldetective/gui/processes/measure_cells.py +5 -0
  25. celldetective/gui/processes/segment_cells.py +27 -6
  26. celldetective/gui/processes/track_cells.py +6 -0
  27. celldetective/gui/retrain_segmentation_model_options.py +12 -20
  28. celldetective/gui/retrain_signal_model_options.py +57 -56
  29. celldetective/gui/seg_model_loader.py +21 -62
  30. celldetective/gui/signal_annotator.py +129 -70
  31. celldetective/gui/signal_annotator2.py +431 -635
  32. celldetective/gui/signal_annotator_options.py +8 -11
  33. celldetective/gui/survival_ui.py +49 -95
  34. celldetective/gui/tableUI.py +28 -25
  35. celldetective/gui/thresholds_gui.py +617 -1221
  36. celldetective/gui/viewers.py +106 -39
  37. celldetective/gui/workers.py +9 -3
  38. celldetective/io.py +57 -20
  39. celldetective/measure.py +63 -27
  40. celldetective/neighborhood.py +342 -268
  41. celldetective/preprocessing.py +25 -17
  42. celldetective/relative_measurements.py +50 -29
  43. celldetective/scripts/analyze_signals.py +4 -1
  44. celldetective/scripts/measure_relative.py +4 -1
  45. celldetective/scripts/segment_cells.py +0 -6
  46. celldetective/scripts/track_cells.py +3 -1
  47. celldetective/scripts/train_segmentation_model.py +7 -4
  48. celldetective/signals.py +29 -14
  49. celldetective/tracking.py +7 -2
  50. celldetective/utils.py +36 -8
  51. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/METADATA +24 -16
  52. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/RECORD +57 -55
  53. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/WHEEL +1 -1
  54. tests/test_qt.py +21 -21
  55. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/entry_points.txt +0 -0
  56. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info/licenses}/LICENSE +0 -0
  57. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,7 @@
2
2
  Copright © 2023 Laboratoire Adhesion et Inflammation, Authored by Remy Torro.
3
3
  """
4
4
 
5
- from PyQt5.QtWidgets import QMainWindow, QComboBox, QLabel, QRadioButton, QLineEdit, QApplication, QPushButton, QScrollArea, QWidget, QVBoxLayout, QHBoxLayout
5
+ from PyQt5.QtWidgets import QComboBox, QLabel, QRadioButton, QLineEdit, QApplication, QPushButton, QScrollArea, QVBoxLayout, QHBoxLayout
6
6
  from PyQt5.QtCore import Qt, QSize
7
7
  from celldetective.gui.gui_utils import center_window, QHSeperationLine
8
8
  from superqt import QLabeledDoubleSlider, QLabeledSlider
@@ -12,9 +12,9 @@ import numpy as np
12
12
  from superqt.fonticon import icon
13
13
  from fonticon_mdi6 import MDI6
14
14
  import os
15
- from celldetective.gui import Styles
15
+ from celldetective.gui import CelldetectiveWidget, CelldetectiveMainWindow
16
16
 
17
- class ConfigSignalAnnotator(QMainWindow, Styles):
17
+ class ConfigSignalAnnotator(CelldetectiveMainWindow):
18
18
 
19
19
  """
20
20
  UI to set normalization and animation parameters for the annotator tool.
@@ -30,11 +30,8 @@ class ConfigSignalAnnotator(QMainWindow, Styles):
30
30
  self.exp_dir = self.parent_window.exp_dir
31
31
  self.soft_path = get_software_location()
32
32
 
33
- if self.mode=="targets":
34
- self.instructions_path = self.parent_window.exp_dir + "configs/signal_annotator_config_targets.json"
35
- elif self.mode=="effectors":
36
- self.instructions_path = self.parent_window.exp_dir + "configs/signal_annotator_config_effectors.json"
37
- elif self.mode == "pairs":
33
+ self.instructions_path = self.parent_window.exp_dir + f"configs/signal_annotator_config_{self.mode}.json"
34
+ if self.mode == "pairs":
38
35
  self.instructions_path = self.parent_window.exp_dir + "configs/signal_annotator_config_neighborhood.json"
39
36
 
40
37
  exp_config = self.exp_dir +"config.ini"
@@ -61,7 +58,7 @@ class ConfigSignalAnnotator(QMainWindow, Styles):
61
58
  """
62
59
 
63
60
  self.scroll_area = QScrollArea(self)
64
- self.button_widget = QWidget()
61
+ self.button_widget = CelldetectiveWidget()
65
62
 
66
63
  self.main_layout = QVBoxLayout()
67
64
  self.main_layout.setContentsMargins(30,30,30,30)
@@ -151,7 +148,7 @@ class ConfigSignalAnnotator(QMainWindow, Styles):
151
148
  self.fraction_slider.setSingleStep(0.05)
152
149
  self.fraction_slider.setTickInterval(0.05)
153
150
  self.fraction_slider.setSingleStep(1)
154
- self.fraction_slider.setOrientation(1)
151
+ self.fraction_slider.setOrientation(Qt.Horizontal)
155
152
  self.fraction_slider.setRange(0.1,1)
156
153
  self.fraction_slider.setValue(0.25)
157
154
 
@@ -166,7 +163,7 @@ class ConfigSignalAnnotator(QMainWindow, Styles):
166
163
  self.interval_slider.setSingleStep(1)
167
164
  self.interval_slider.setTickInterval(1)
168
165
  self.interval_slider.setSingleStep(1)
169
- self.interval_slider.setOrientation(1)
166
+ self.interval_slider.setOrientation(Qt.Horizontal)
170
167
  self.interval_slider.setRange(1,1000)
171
168
  self.interval_slider.setValue(1)
172
169
  hbox_interval.addWidget(self.interval_slider, 80)
@@ -1,7 +1,7 @@
1
- from PyQt5.QtWidgets import QMessageBox, QComboBox, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
1
+ from PyQt5.QtWidgets import QMessageBox, QComboBox, QLineEdit, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
2
2
  from PyQt5.QtCore import Qt
3
3
  from PyQt5.QtGui import QDoubleValidator
4
- from celldetective.gui.gui_utils import center_window
4
+ from celldetective.gui.gui_utils import center_window, generic_message
5
5
  from superqt import QColormapComboBox
6
6
  from celldetective.gui.generic_signal_plot import SurvivalPlotWidget
7
7
  from celldetective.utils import get_software_location, _extract_labels_from_config, extract_cols_from_table_list
@@ -11,14 +11,14 @@ import os
11
11
  import matplotlib.pyplot as plt
12
12
  plt.rcParams['svg.fonttype'] = 'none'
13
13
  from glob import glob
14
- from celldetective.gui import Styles
14
+ from celldetective.gui import Styles, CelldetectiveWidget
15
15
  from matplotlib import colormaps
16
16
  from celldetective.events import compute_survival
17
17
  from celldetective.relative_measurements import expand_pair_table
18
18
  import matplotlib.cm
19
19
  from celldetective.neighborhood import extract_neighborhood_in_pair_table
20
20
 
21
- class ConfigSurvival(QWidget, Styles):
21
+ class ConfigSurvival(CelldetectiveWidget):
22
22
 
23
23
  """
24
24
  UI to set survival instructions.
@@ -30,7 +30,6 @@ class ConfigSurvival(QWidget, Styles):
30
30
  super().__init__()
31
31
  self.parent_window = parent_window
32
32
  self.setWindowTitle("Configure survival")
33
- self.setWindowIcon(self.celldetective_icon)
34
33
 
35
34
  self.exp_dir = self.parent_window.exp_dir
36
35
  self.soft_path = get_software_location()
@@ -53,8 +52,6 @@ class ConfigSurvival(QWidget, Styles):
53
52
  self.populate_widget()
54
53
  if self.auto_close:
55
54
  self.close()
56
-
57
- self.setAttribute(Qt.WA_DeleteOnClose)
58
55
 
59
56
  def interpret_pos_location(self):
60
57
 
@@ -91,51 +88,27 @@ class ConfigSurvival(QWidget, Styles):
91
88
 
92
89
 
93
90
  pops = []
94
- for population in ['effectors','targets','pairs']:
91
+ self.cols_per_pop = {}
92
+ for population in self.parent_window.parent_window.populations:
95
93
  tables = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_{population}.csv']))
96
94
  if len(tables)>0:
97
95
  pops.append(population)
98
-
99
- tables_targets = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_targets.csv']))
100
- self.cols_targets = extract_cols_from_table_list(tables_targets)
101
- tables_effectors = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_effectors.csv']))
102
- self.cols_effectors = extract_cols_from_table_list(tables_effectors)
103
-
104
- # Smart reading of existing neighborhoods (without loading tables in memory)
105
- if 'pairs' in pops and not 'targets' in pops:
106
- # must be effector-effector
107
- effector_neighs = [c[16:] for c in self.cols_effectors if c.startswith('inclusive_count_neighborhood')]
108
- if len(effector_neighs)>0:
109
- pops.pop(pops.index('pairs'))
110
- pops.append('effectors-effectors')
111
- elif 'pairs' in pops and not 'effectors' in pops:
112
- # must be target-target
113
- target_neighs = [c for c in self.cols_targets if c.startswith('inclusive_count_neighborhood')]
114
- if len(target_neighs)>0:
115
- pops.pop(pops.index('pairs'))
116
- pops.append('targets-targets')
117
- elif 'pairs' in pops:
118
- # either effector-target or target-effector
119
- target_neighs_cross = [c for c in self.cols_targets if c.startswith('inclusive_count_neighborhood') and '_2_' in c]
120
- if len(target_neighs_cross)>0:
121
- pops.append('targets-effectors')
122
- effector_neighs_cross = [c for c in self.cols_effectors if c.startswith('inclusive_count_neighborhood') and '_2_' in c]
123
- if len(effector_neighs_cross)>0:
124
- pops.append('effectors-targets')
125
- target_neighs = [c for c in self.cols_targets if c.startswith('inclusive_count_neighborhood') and 'self' in c]
126
- if len(target_neighs)>0:
127
- pops.append('targets-targets')
128
- effector_neighs = [c for c in self.cols_effectors if c.startswith('inclusive_count_neighborhood') and 'self' in c]
129
- if len(effector_neighs)>0:
130
- pops.append('effectors-effectors')
131
- pops.pop(pops.index('pairs'))
132
- else:
133
- pass
134
-
96
+ cols = extract_cols_from_table_list(tables)
97
+
98
+ # check for neighbor pairs
99
+ neigh_cols = [c for c in cols if c.startswith('inclusive_count_neighborhood')]
100
+ neigh_pairs = [c.split('_(')[-1].split(')_')[0].split('-') for c in neigh_cols]
101
+ neigh_pairs = ['-'.join(c) for c in neigh_pairs]
102
+ for k in range(len(neigh_pairs)):
103
+ if "_self_" in neigh_pairs[k]:
104
+ neigh_pairs[k] = '-'.join([population, population])
105
+ pops.extend(neigh_pairs)
106
+
107
+ self.cols_per_pop.update({population: cols})
135
108
 
136
109
  labels = [QLabel('population: '), QLabel('time of\nreference: '), QLabel('time of\ninterest: '), QLabel('cmap: ')] #QLabel('class: '),
137
110
  self.cb_options = [pops, ['0'], [], []] #['class'],
138
- self.cbs = [QComboBox() for i in range(len(labels))]
111
+ self.cbs = [QComboBox() for _ in range(len(labels))]
139
112
 
140
113
  self.cbs[-1] = QColormapComboBox()
141
114
  self.cbs[0].currentIndexChanged.connect(self.set_classes_and_times)
@@ -155,12 +128,8 @@ class ConfigSurvival(QWidget, Styles):
155
128
  if hasattr(matplotlib.cm, str(cm).lower()):
156
129
  try:
157
130
  self.cbs[-1].addColormap(cm.lower())
158
- except:
131
+ except Exception as _:
159
132
  pass
160
- #try:
161
- # self.cbs[-1].addColormap(cm)
162
- # except:
163
- # pass
164
133
 
165
134
  main_layout.addLayout(choice_layout)
166
135
 
@@ -213,18 +182,16 @@ class ConfigSurvival(QWidget, Styles):
213
182
 
214
183
  self.population = 'pairs'
215
184
  tables_pairs = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_pairs.csv']))
185
+ if not tables_pairs:
186
+ print('No pair table found... please compute the pair measurements...')
187
+ return None
216
188
  self.cols_pairs = extract_cols_from_table_list(tables_pairs)
217
189
 
218
190
  self.population_reference = pop_split[0]
219
191
  self.population_neigh = pop_split[1]
220
- if self.population_reference=='targets':
221
- cols_ref = self.cols_targets
222
- else:
223
- cols_ref = self.cols_effectors
224
- if self.population_neigh=='targets':
225
- cols_neigh = self.cols_targets
226
- else:
227
- cols_neigh = self.cols_effectors
192
+
193
+ cols_ref = self.cols_per_pop[self.population_reference]
194
+ cols_neigh = self.cols_per_pop[self.population_neigh]
228
195
 
229
196
  time_cols_ref = np.array([s.startswith('t_') or s=='t0' for s in cols_ref])
230
197
  if len(time_cols_ref)>0:
@@ -237,9 +204,11 @@ class ConfigSurvival(QWidget, Styles):
237
204
  time_cols_neigh = ['neighbor_'+t for t in time_cols_neigh]
238
205
 
239
206
  if self.population_reference!=self.population_neigh:
240
- self.neighborhood_keys = [c[16:] for c in cols_ref if c.startswith('inclusive_count_neighborhood') and '_2_' in c]
207
+ self.neighborhood_keys = [c[16:] for c in cols_ref if c.startswith('inclusive_count_neighborhood') and str(self.population_neigh) in c]
241
208
  else:
242
- self.neighborhood_keys = [c[16:] for c in cols_ref if c.startswith('inclusive_count_neighborhood') and 'self' in c]
209
+ self.neighborhood_keys = [c[16:] for c in cols_ref if c.startswith('inclusive_count_neighborhood') and str(self.population_neigh) not in c]
210
+
211
+ print(f"{self.neighborhood_keys=}")
243
212
 
244
213
  time_idx = np.array([s.startswith('t_') or s.startswith('t0') for s in self.cols_pairs])
245
214
  time_cols_pairs = list(self.cols_pairs[time_idx])
@@ -247,10 +216,7 @@ class ConfigSurvival(QWidget, Styles):
247
216
  time_columns = time_cols_ref + time_cols_neigh + time_cols_pairs
248
217
 
249
218
  else:
250
- if self.population=='targets':
251
- self.all_columns = self.cols_targets
252
- else:
253
- self.all_columns = self.cols_effectors
219
+ self.all_columns = self.cols_per_pop[self.population]
254
220
  time_idx = np.array([s.startswith('t_') or s=='t0' for s in self.all_columns])
255
221
 
256
222
  try:
@@ -274,6 +240,8 @@ class ConfigSurvival(QWidget, Styles):
274
240
  self.time_of_interest = self.cbs[2].currentText()
275
241
  if self.time_of_interest=="t0":
276
242
  self.class_of_interest = "class"
243
+ elif self.time_of_interest.startswith('t0'):
244
+ self.class_of_interest = self.time_of_interest.replace('t0_','class_')
277
245
  else:
278
246
  self.class_of_interest = self.time_of_interest.replace('t_','class_')
279
247
 
@@ -290,29 +258,19 @@ class ConfigSurvival(QWidget, Styles):
290
258
  print(e, ' The query is misunderstood and will not be applied...')
291
259
 
292
260
  self.interpret_pos_location()
261
+
293
262
  if self.class_of_interest in list(self.df.columns) and self.cbs[2].currentText() in list(self.df.columns):
294
263
  self.compute_survival_functions()
295
264
  else:
296
- msgBox = QMessageBox()
297
- msgBox.setIcon(QMessageBox.Warning)
298
- msgBox.setText("The class and/or event time of interest is not found in the dataframe...")
299
- msgBox.setWindowTitle("Warning")
300
- msgBox.setStandardButtons(QMessageBox.Ok)
301
- returnValue = msgBox.exec()
302
- if returnValue == QMessageBox.Ok:
303
- return None
265
+ generic_message("The class and/or event time of interest is not found in the dataframe...")
266
+ return None
267
+
304
268
  if 'survival_fit' in list(self.df_pos_info.columns):
305
269
  self.plot_window = SurvivalPlotWidget(parent_window=self, df=self.df, df_pos_info = self.df_pos_info, df_well_info = self.df_well_info, title='plot survivals')
306
270
  self.plot_window.show()
307
271
  else:
308
- msgBox = QMessageBox()
309
- msgBox.setIcon(QMessageBox.Warning)
310
- msgBox.setText("No survival function was successfully computed...\nCheck your parameter choice.")
311
- msgBox.setWindowTitle("Warning")
312
- msgBox.setStandardButtons(QMessageBox.Ok)
313
- returnValue = msgBox.exec()
314
- if returnValue == QMessageBox.Ok:
315
- return None
272
+ generic_message("No survival function was successfully computed...\nCheck your parameter choice.")
273
+ return None
316
274
 
317
275
  def load_available_tables_local(self):
318
276
 
@@ -327,32 +285,26 @@ class ConfigSurvival(QWidget, Styles):
327
285
  self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=self.well_option, position_option=self.position_option, population=self.population, return_pos_info=True)
328
286
 
329
287
  if self.df is None:
330
- msgBox = QMessageBox()
331
- msgBox.setIcon(QMessageBox.Warning)
332
- msgBox.setText("No table could be found.. Abort.")
333
- msgBox.setWindowTitle("Warning")
334
- msgBox.setStandardButtons(QMessageBox.Ok)
335
- returnValue = msgBox.exec()
336
- if returnValue == QMessageBox.Ok:
337
- return None
288
+ generic_message("No table could be found.. Abort.")
289
+ return None
338
290
  else:
339
291
  self.df_well_info = self.df_pos_info.loc[:,['well_path', 'well_index', 'well_name', 'well_number', 'well_alias']].drop_duplicates()
340
- #print(f"{self.df_well_info=}")
341
292
 
342
293
  if self.population=='pairs':
343
294
  self.df = expand_pair_table(self.df)
344
295
  self.df = extract_neighborhood_in_pair_table(self.df, reference_population=self.population_reference, neighbor_population=self.population_neigh, neighborhood_key=self.neighborhood_keys[0], contact_only=True)
345
296
 
346
-
347
297
  def compute_survival_functions(self):
348
298
 
349
299
  cut_observation_time = None
350
300
  try:
351
- cut_observation_time = float(self.query_time_cut.text().replace(',','.')) / self.FrameToMin
352
- if not 0<cut_observation_time<=(self.df['FRAME'].max()):
353
- print('Invalid cut time (larger than movie length)... Not applied.')
354
- cut_observation_time = None
301
+ if self.query_time_cut.text()!='':
302
+ cut_observation_time = float(self.query_time_cut.text().replace(',','.')) / self.FrameToMin
303
+ if not 0<cut_observation_time<=(self.df['FRAME'].max()):
304
+ print('Invalid cut time (larger than movie length)... Not applied.')
305
+ cut_observation_time = None
355
306
  except Exception as e:
307
+ print(f"{e=}")
356
308
  pass
357
309
 
358
310
  pairs = False
@@ -361,7 +313,9 @@ class ConfigSurvival(QWidget, Styles):
361
313
 
362
314
  # Per position survival
363
315
  for block,movie_group in self.df.groupby(['well','position']):
316
+ print(f"{block=}")
364
317
  ks = compute_survival(movie_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin, cut_observation_time=cut_observation_time, pairs=pairs)
318
+ print(f"{ks=}")
365
319
  if ks is not None:
366
320
  self.df_pos_info.loc[self.df_pos_info['pos_path']==block[1],'survival_fit'] = ks
367
321
 
@@ -1,4 +1,4 @@
1
- from PyQt5.QtWidgets import QRadioButton, QButtonGroup, QMainWindow, QTableView, QAction, QMenu,QFileDialog, QLineEdit, QHBoxLayout, QWidget, QPushButton, QVBoxLayout, QComboBox, QLabel, QCheckBox, QMessageBox
1
+ from PyQt5.QtWidgets import QRadioButton, QButtonGroup, QTableView, QAction, QMenu,QFileDialog, QLineEdit, QHBoxLayout, QPushButton, QVBoxLayout, QComboBox, QLabel, QCheckBox, QMessageBox
2
2
  from PyQt5.QtCore import Qt
3
3
  from PyQt5.QtGui import QBrush, QColor, QDoubleValidator
4
4
  import pandas as pd
@@ -12,7 +12,7 @@ import numpy as np
12
12
  import seaborn as sns
13
13
  import matplotlib.cm as mcm
14
14
  import os
15
- from celldetective.gui import Styles
15
+ from celldetective.gui import CelldetectiveWidget, CelldetectiveMainWindow
16
16
  from superqt import QColormapComboBox, QLabeledSlider, QSearchableComboBox
17
17
  from superqt.fonticon import icon
18
18
  from fonticon_mdi6 import MDI6
@@ -22,12 +22,13 @@ from matplotlib import colormaps
22
22
  import matplotlib.cm
23
23
 
24
24
 
25
- class QueryWidget(QWidget):
25
+ class QueryWidget(CelldetectiveWidget):
26
26
 
27
27
  def __init__(self, parent_window):
28
28
 
29
29
  super().__init__()
30
30
  self.parent_window = parent_window
31
+
31
32
  self.setWindowTitle("Filter table")
32
33
  # Create the QComboBox and add some items
33
34
 
@@ -39,7 +40,6 @@ class QueryWidget(QWidget):
39
40
  self.submit_btn = QPushButton('submit')
40
41
  self.submit_btn.clicked.connect(self.filter_table)
41
42
  layout.addWidget(self.submit_btn, 30)
42
- self.setAttribute(Qt.WA_DeleteOnClose)
43
43
  center_window(self)
44
44
 
45
45
  def filter_table(self):
@@ -54,7 +54,7 @@ class QueryWidget(QWidget):
54
54
  return None
55
55
 
56
56
 
57
- class MergeOneHotWidget(QWidget, Styles):
57
+ class MergeOneHotWidget(CelldetectiveWidget):
58
58
 
59
59
  def __init__(self, parent_window, selected_columns=None):
60
60
 
@@ -139,7 +139,7 @@ class MergeOneHotWidget(QWidget, Styles):
139
139
  self.submit_btn.setEnabled(True)
140
140
 
141
141
 
142
- class DifferentiateColWidget(QWidget, Styles):
142
+ class DifferentiateColWidget(CelldetectiveWidget):
143
143
 
144
144
  def __init__(self, parent_window, column=None):
145
145
 
@@ -217,7 +217,7 @@ class DifferentiateColWidget(QWidget, Styles):
217
217
 
218
218
 
219
219
 
220
- class OperationOnColsWidget(QWidget, Styles):
220
+ class OperationOnColsWidget(CelldetectiveWidget):
221
221
 
222
222
  def __init__(self, parent_window, column1=None, column2=None, operation='divide'):
223
223
 
@@ -394,7 +394,7 @@ class LogColWidget(GenericOpColWidget):
394
394
  self.parent_window.data['log10('+self.measurements_cb.currentText()+')'] = safe_log(self.parent_window.data[self.measurements_cb.currentText()].values)
395
395
 
396
396
 
397
- class RenameColWidget(QWidget):
397
+ class RenameColWidget(CelldetectiveWidget):
398
398
 
399
399
  def __init__(self, parent_window, column=None):
400
400
 
@@ -429,11 +429,11 @@ class RenameColWidget(QWidget):
429
429
  self.parent_window.table_view.setModel(self.parent_window.model)
430
430
  self.close()
431
431
 
432
- class PivotTableUI(QWidget):
432
+ class PivotTableUI(CelldetectiveWidget):
433
433
 
434
434
  def __init__(self, data, title="", mode=None, *args, **kwargs):
435
435
 
436
- QWidget.__init__(self, *args, **kwargs)
436
+ CelldetectiveWidget.__init__(self, *args, **kwargs)
437
437
 
438
438
  self.data = data
439
439
  self.title = title
@@ -535,11 +535,11 @@ class PivotTableUI(QWidget):
535
535
  """
536
536
  self.information_label.setText(html_caption)
537
537
 
538
- class TableUI(QMainWindow, Styles):
538
+ class TableUI(CelldetectiveMainWindow):
539
539
 
540
540
  def __init__(self, data, title, population='targets',plot_mode="plot_track_signals", save_inplace_option=False, collapse_tracks_option=True, *args, **kwargs):
541
541
 
542
- QMainWindow.__init__(self, *args, **kwargs)
542
+ CelldetectiveMainWindow.__init__(self, *args, **kwargs)
543
543
 
544
544
  self.setWindowTitle(title)
545
545
  self.setGeometry(100,100,1000,400)
@@ -574,7 +574,6 @@ class TableUI(QMainWindow, Styles):
574
574
  self.model = PandasModel(data)
575
575
  self.table_view.setModel(self.model)
576
576
  self.table_view.resizeColumnsToContents()
577
- self.setAttribute(Qt.WA_DeleteOnClose)
578
577
 
579
578
  def resizeEvent(self, event):
580
579
 
@@ -696,7 +695,7 @@ class TableUI(QMainWindow, Styles):
696
695
 
697
696
  def collapse_pairs_in_neigh(self):
698
697
 
699
- self.selectNeighWidget = QWidget()
698
+ self.selectNeighWidget = CelldetectiveWidget()
700
699
  self.selectNeighWidget.setMinimumWidth(480)
701
700
  self.selectNeighWidget.setWindowTitle('Set neighborhood of interest')
702
701
 
@@ -757,14 +756,20 @@ class TableUI(QMainWindow, Styles):
757
756
  ref_pop = self.reference_pop_cb.currentText()
758
757
  neighborhood = self.neigh_cb.currentText()
759
758
  status_neigh = 'status_'+neighborhood
759
+
760
760
  if 'self' in neighborhood:
761
761
  neighbor_pop = ref_pop
762
- elif ref_pop=='targets':
763
- neighbor_pop = 'effectors'
764
- elif ref_pop=='effectors':
765
- neighbor_pop = "targets"
766
762
 
767
- data = extract_neighborhood_in_pair_table(self.data, neighborhood_key=neighborhood, contact_only=self.contact_only_check.isChecked())
763
+ neigh_col = neighborhood.replace('status_','')
764
+ if '_(' in neigh_col and ')_' in neigh_col:
765
+ neighbor_pop = neigh_col.split('_(')[-1].split(')_')[0].split('-')[-1]
766
+ else:
767
+ if ref_pop=='targets':
768
+ neighbor_pop = 'effectors'
769
+ if ref_pop=='effectors':
770
+ neighbor_pop = "targets"
771
+
772
+ data = extract_neighborhood_in_pair_table(self.data, neighborhood_key=neighborhood, contact_only=self.contact_only_check.isChecked(), reference_population=ref_pop)
768
773
 
769
774
  if self.groupby_pair_rb.isChecked():
770
775
  self.groupby_cols = ['position', 'REFERENCE_ID', 'NEIGHBOR_ID']
@@ -774,6 +779,7 @@ class TableUI(QMainWindow, Styles):
774
779
  self.current_data = data
775
780
  skip_projection = False
776
781
  if 'reference_tracked' in list(self.current_data.columns):
782
+ print(f"{self.current_data['reference_tracked']=} {(self.current_data['reference_tracked']==False)=} {np.all(self.current_data['reference_tracked']==False)=}")
777
783
  if np.all(self.current_data['reference_tracked'].astype(bool)==False):
778
784
  # reference not tracked
779
785
  if self.groupby_reference_rb.isChecked():
@@ -1030,14 +1036,11 @@ class TableUI(QMainWindow, Styles):
1030
1036
 
1031
1037
  x = self.table_view.selectedIndexes()
1032
1038
  col_idx = np.unique(np.array([l.column() for l in x]))
1039
+ selected_cols = None
1033
1040
  if isinstance(col_idx, (list, np.ndarray)):
1034
1041
  cols = np.array(list(self.data.columns))
1035
1042
  if len(col_idx)>0:
1036
1043
  selected_col = str(cols[col_idx[0]])
1037
- else:
1038
- selected_col = None
1039
- else:
1040
- selected_col = None
1041
1044
 
1042
1045
  self.mergewidget = MergeOneHotWidget(self, selected_columns=selected_cols)
1043
1046
  self.mergewidget.show()
@@ -1091,7 +1094,7 @@ class TableUI(QMainWindow, Styles):
1091
1094
 
1092
1095
  self.current_data = self.data
1093
1096
 
1094
- self.projectionWidget = QWidget()
1097
+ self.projectionWidget = CelldetectiveWidget()
1095
1098
  self.projectionWidget.setMinimumWidth(500)
1096
1099
  self.projectionWidget.setWindowTitle('Set projection mode')
1097
1100
 
@@ -1191,7 +1194,7 @@ class TableUI(QMainWindow, Styles):
1191
1194
 
1192
1195
  def set_1D_plot_params(self):
1193
1196
 
1194
- self.plot1Dparams = QWidget()
1197
+ self.plot1Dparams = CelldetectiveWidget()
1195
1198
  self.plot1Dparams.setWindowTitle('Set 1D plot parameters')
1196
1199
 
1197
1200
  layout = QVBoxLayout()