celldetective 1.0.2.post1__py3-none-any.whl → 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. celldetective/__main__.py +2 -2
  2. celldetective/events.py +2 -44
  3. celldetective/filters.py +4 -5
  4. celldetective/gui/__init__.py +1 -1
  5. celldetective/gui/analyze_block.py +37 -10
  6. celldetective/gui/btrack_options.py +24 -23
  7. celldetective/gui/classifier_widget.py +62 -19
  8. celldetective/gui/configure_new_exp.py +32 -35
  9. celldetective/gui/control_panel.py +115 -81
  10. celldetective/gui/gui_utils.py +674 -396
  11. celldetective/gui/json_readers.py +7 -6
  12. celldetective/gui/layouts.py +755 -0
  13. celldetective/gui/measurement_options.py +168 -487
  14. celldetective/gui/neighborhood_options.py +322 -270
  15. celldetective/gui/plot_measurements.py +1114 -0
  16. celldetective/gui/plot_signals_ui.py +20 -20
  17. celldetective/gui/process_block.py +449 -169
  18. celldetective/gui/retrain_segmentation_model_options.py +27 -26
  19. celldetective/gui/retrain_signal_model_options.py +25 -24
  20. celldetective/gui/seg_model_loader.py +31 -27
  21. celldetective/gui/signal_annotator.py +2326 -2295
  22. celldetective/gui/signal_annotator_options.py +18 -16
  23. celldetective/gui/styles.py +16 -1
  24. celldetective/gui/survival_ui.py +61 -39
  25. celldetective/gui/tableUI.py +60 -23
  26. celldetective/gui/thresholds_gui.py +68 -66
  27. celldetective/gui/viewers.py +596 -0
  28. celldetective/io.py +234 -23
  29. celldetective/measure.py +37 -32
  30. celldetective/neighborhood.py +495 -27
  31. celldetective/preprocessing.py +683 -0
  32. celldetective/scripts/analyze_signals.py +7 -0
  33. celldetective/scripts/measure_cells.py +12 -0
  34. celldetective/scripts/segment_cells.py +5 -0
  35. celldetective/scripts/track_cells.py +11 -0
  36. celldetective/signals.py +221 -98
  37. celldetective/tracking.py +0 -1
  38. celldetective/utils.py +178 -36
  39. celldetective-1.1.0.dist-info/METADATA +305 -0
  40. celldetective-1.1.0.dist-info/RECORD +80 -0
  41. {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/top_level.txt +1 -0
  42. tests/__init__.py +0 -0
  43. tests/test_events.py +28 -0
  44. tests/test_filters.py +24 -0
  45. tests/test_io.py +70 -0
  46. tests/test_measure.py +141 -0
  47. tests/test_neighborhood.py +70 -0
  48. tests/test_segmentation.py +93 -0
  49. tests/test_signals.py +135 -0
  50. tests/test_tracking.py +164 -0
  51. tests/test_utils.py +71 -0
  52. celldetective-1.0.2.post1.dist-info/METADATA +0 -221
  53. celldetective-1.0.2.post1.dist-info/RECORD +0 -66
  54. {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/LICENSE +0 -0
  55. {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/WHEEL +0 -0
  56. {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,1114 @@
1
+ from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox, QScrollArea, QButtonGroup, QComboBox, QFrame, \
2
+ QCheckBox, QFileDialog, QGridLayout, QTextEdit, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton, \
3
+ QRadioButton, QSpacerItem, QSizePolicy
4
+ from PyQt5.QtCore import Qt, QSize, QRect
5
+ from PyQt5.QtGui import QIcon, QDoubleValidator
6
+ from sklearn.preprocessing import MinMaxScaler
7
+
8
+ from celldetective.gui import Styles
9
+ from celldetective.gui.gui_utils import center_window, FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, \
10
+ GeometryChoice, OperationChoice
11
+ from superqt import QLabeledSlider
12
+ from superqt.fonticon import icon
13
+ from fonticon_mdi6 import MDI6
14
+ from celldetective.utils import extract_experiment_channels, get_software_location, _extract_labels_from_config
15
+ from celldetective.io import interpret_tracking_configuration, load_frames, auto_load_number_of_frames, \
16
+ load_experiment_tables, get_experiment_antibodies, get_experiment_cell_types, get_experiment_concentrations, \
17
+ get_positions_in_well, get_experiment_wells
18
+ from celldetective.measure import compute_haralick_features, contour_of_instance_segmentation
19
+ from celldetective.signals import columnwise_mean, mean_signal
20
+ import numpy as np
21
+ from tifffile import imread
22
+ import json
23
+ from shutil import copyfile
24
+ import os
25
+ import matplotlib.pyplot as plt
26
+
27
+ plt.rcParams['svg.fonttype'] = 'none'
28
+ from mpl_toolkits.axes_grid1 import make_axes_locatable
29
+ from glob import glob
30
+ from natsort import natsorted
31
+ from tifffile import imread
32
+ from pathlib import Path, PurePath
33
+ import gc
34
+ import pandas as pd
35
+ from tqdm import tqdm
36
+ from lifelines import KaplanMeierFitter
37
+ from matplotlib.cm import viridis, tab10
38
+ import math
39
+ import seaborn as sns
40
+
41
+
42
+ class ConfigMeasurementsPlot(QWidget,Styles):
43
+ """
44
+ UI to set survival instructions.
45
+
46
+ """
47
+
48
+ def __init__(self, parent_window=None):
49
+
50
+ super().__init__()
51
+ self.parent_window = parent_window
52
+ self.setWindowTitle("Configure signal plot")
53
+ self.setWindowIcon(QIcon(os.sep.join(['celldetective', 'icons', 'mexican-hat.png'])))
54
+ self.exp_dir = self.parent_window.exp_dir
55
+ self.soft_path = get_software_location()
56
+ self.exp_config = self.exp_dir + "config.ini"
57
+ self.wells = np.array(self.parent_window.parent_window.wells, dtype=str)
58
+ self.well_labels = _extract_labels_from_config(self.exp_config, len(self.wells))
59
+ self.FrameToMin = self.parent_window.parent_window.FrameToMin
60
+ self.float_validator = QDoubleValidator()
61
+ self.target_class = [0, 1]
62
+ self.show_ci = True
63
+ self.show_cell_lines = False
64
+ self.ax2 = None
65
+ self.auto_close = False
66
+ self.palette = sns.cubehelix_palette()
67
+ # sns.color_palette("cubehelix", as_cmap=True)
68
+ # sns.color_palette("ch:s=-.2,r=.6", as_cmap=True)
69
+
70
+ print('Parent wells: ', self.wells)
71
+
72
+ self.well_option = self.parent_window.parent_window.well_list.currentIndex()
73
+ self.position_option = self.parent_window.parent_window.position_list.currentIndex()
74
+ self.interpret_pos_location()
75
+ # self.load_available_tables()
76
+ # self.config_path = self.exp_dir + self.config_name
77
+
78
+ self.screen_height = self.parent_window.parent_window.parent_window.screen_height
79
+ center_window(self)
80
+ # self.setMinimumHeight(int(0.8*self.screen_height))
81
+ # self.setMaximumHeight(int(0.8*self.screen_height))
82
+ self.populate_widget()
83
+ # self.load_previous_measurement_instructions()
84
+ if self.auto_close:
85
+ self.close()
86
+ self.hue_colors = {0: self.palette[0], 1: self.palette[3]}
87
+
88
+ def interpret_pos_location(self):
89
+
90
+ """
91
+ Read the well/position selection from the control panel to decide which data to load
92
+ Set position_indices to None if all positions must be taken
93
+
94
+ """
95
+
96
+ if self.well_option == len(self.wells):
97
+ self.well_indices = np.arange(len(self.wells))
98
+ else:
99
+ self.well_indices = np.array([self.well_option], dtype=int)
100
+
101
+ if self.position_option == 0:
102
+ self.position_indices = None
103
+ else:
104
+ self.position_indices = np.array([self.position_option], dtype=int)
105
+
106
+ def populate_widget(self):
107
+
108
+ """
109
+ Create the multibox design.
110
+
111
+ """
112
+
113
+ # Create button widget and layout
114
+ main_layout = QVBoxLayout()
115
+ self.setLayout(main_layout)
116
+ main_layout.setContentsMargins(30, 30, 30, 30)
117
+ panel_title = QLabel('Options')
118
+ panel_title.setStyleSheet("""
119
+ font-weight: bold;
120
+ padding: 0px;
121
+ """)
122
+ main_layout.addWidget(panel_title, alignment=Qt.AlignCenter)
123
+
124
+ labels = [QLabel('population: '), QLabel('class: '), QLabel('group: ')] # , QLabel('time of\ninterest: ')]
125
+ self.cb_options = [['targets', 'effectors'], ['class'], ['group']] # , ['t0','first detection']]
126
+ self.cbs = [QComboBox() for i in range(len(labels))]
127
+ self.cbs[0].currentIndexChanged.connect(self.set_classes_and_times)
128
+
129
+ choice_layout = QVBoxLayout()
130
+ choice_layout.setContentsMargins(20, 20, 20, 20)
131
+ for i in range(len(labels)):
132
+ hbox = QHBoxLayout()
133
+ hbox.addWidget(labels[i], 33)
134
+ hbox.addWidget(self.cbs[i], 66)
135
+ self.cbs[i].addItems(self.cb_options[i])
136
+ if i == 1:
137
+ vbox = QVBoxLayout()
138
+ self.check_class = QCheckBox("plot by class")
139
+ vbox.addWidget(self.check_class)
140
+ vbox.addLayout(hbox)
141
+ choice_layout.addLayout(vbox)
142
+ if i == 2:
143
+ vbox = QVBoxLayout()
144
+ self.check_group = QCheckBox("plot by group")
145
+ vbox.addWidget(self.check_group)
146
+ vbox.addLayout(hbox)
147
+ choice_layout.addLayout(vbox)
148
+ else:
149
+ choice_layout.addLayout(hbox)
150
+
151
+ self.cbs[0].setCurrentIndex(1)
152
+ self.cbs[0].setCurrentIndex(0)
153
+ self.cbs[1].setEnabled(False)
154
+ self.check_class.toggled.connect(self.class_enable)
155
+ self.cbs[2].setEnabled(False)
156
+ self.check_group.toggled.connect(self.group_enable)
157
+
158
+ # self.abs_time_checkbox = QCheckBox('absolute time')
159
+ # self.frame_slider = QLabeledSlider()
160
+ # self.frame_slider.setSingleStep(1)
161
+ # self.frame_slider.setOrientation(1)
162
+ # self.frame_slider.setRange(0,self.parent.parent.len_movie)
163
+ # self.frame_slider.setValue(0)
164
+ # self.frame_slider.setEnabled(False)
165
+ # slider_hbox = QHBoxLayout()
166
+ # slider_hbox.addWidget(self.abs_time_checkbox, 33)
167
+ # slider_hbox.addWidget(self.frame_slider, 66)
168
+ # choice_layout.addLayout(slider_hbox)
169
+ main_layout.addLayout(choice_layout)
170
+
171
+ # self.abs_time_checkbox.stateChanged.connect(self.switch_ref_time_mode)
172
+
173
+ # time_calib_layout = QHBoxLayout()
174
+ # time_calib_layout.setContentsMargins(20,20,20,20)
175
+ # time_calib_layout.addWidget(QLabel('time calibration\n(frame to min)'), 33)
176
+ # self.time_calibration_le = QLineEdit(str(self.FrameToMin).replace('.',','))
177
+ # self.time_calibration_le.setValidator(self.float_validator)
178
+ # time_calib_layout.addWidget(self.time_calibration_le, 66)
179
+ # #time_calib_layout.addWidget(QLabel(' min'))
180
+ # main_layout.addLayout(time_calib_layout)
181
+
182
+ self.submit_btn = QPushButton('Submit')
183
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
184
+ self.submit_btn.clicked.connect(self.process_signal)
185
+ main_layout.addWidget(self.submit_btn)
186
+
187
+ # self.populate_left_panel()
188
+ # grid.addLayout(self.left_side, 0, 0, 1, 1)
189
+
190
+ # self.setCentralWidget(self.scroll_area)
191
+ # self.show()
192
+ def class_enable(self):
193
+ if self.check_class.isChecked():
194
+ self.cbs[1].setEnabled(True)
195
+ self.check_group.setChecked(False)
196
+ else:
197
+ self.cbs[1].setEnabled(False)
198
+
199
+ def group_enable(self):
200
+ if self.check_group.isChecked():
201
+ self.cbs[2].setEnabled(True)
202
+ self.check_class.setChecked(False)
203
+ else:
204
+ self.cbs[2].setEnabled(False)
205
+
206
+ def set_classes_and_times(self):
207
+ ext = 'csv'
208
+ # Look for all classes and times
209
+ tables = glob(self.exp_dir + os.sep.join(['W*', '*', 'output', 'tables', f'trajectories_*{ext}']))
210
+ self.all_columns = []
211
+ for tab in tables:
212
+ cols = pd.read_csv(tab, nrows=1).columns.tolist()
213
+ self.all_columns.extend(cols)
214
+ self.all_columns = np.unique(self.all_columns)
215
+ class_idx = np.array([s.startswith('class_') for s in self.all_columns])
216
+ group_idx = np.array([s.startswith('group_') for s in self.all_columns])
217
+
218
+ # time_idx = np.array([s.startswith('t_') for s in self.all_columns])
219
+
220
+ try:
221
+ class_columns = list(self.all_columns[class_idx])
222
+ group_columns = list(self.all_columns[group_idx])
223
+ # time_columns = list(self.all_columns[time_idx])
224
+ except:
225
+ print('columns not found')
226
+ self.auto_close = True
227
+ return None
228
+
229
+ # self.cbs[2].clear()
230
+ # self.cbs[2].addItems(np.unique(self.cb_options[2]+time_columns))
231
+
232
+ self.cbs[1].clear()
233
+ self.cbs[1].addItems(np.unique(self.cb_options[1] + class_columns))
234
+ self.cbs[2].clear()
235
+ self.cbs[2].addItems(np.unique(group_columns))
236
+
237
+ def ask_for_feature(self):
238
+
239
+ cols = np.array(list(self.df.columns))
240
+ is_number = np.vectorize(lambda x: np.issubdtype(x, np.number))
241
+ feats = cols[is_number(self.df.dtypes)]
242
+
243
+ self.feature_choice_widget = QWidget()
244
+ self.feature_choice_widget.setWindowTitle("Select numeric feature")
245
+ layout = QVBoxLayout()
246
+ self.feature_choice_widget.setLayout(layout)
247
+ self.feature_cb = QComboBox()
248
+ self.feature_cb.addItems(feats)
249
+ hbox = QHBoxLayout()
250
+ hbox.addWidget(QLabel('feature: '), 33)
251
+ hbox.addWidget(self.feature_cb, 66)
252
+ layout.addLayout(hbox)
253
+
254
+ self.set_feature_btn = QPushButton('set')
255
+ self.set_feature_btn.clicked.connect(self.compute_signals)
256
+ layout.addWidget(self.set_feature_btn)
257
+ self.feature_choice_widget.show()
258
+ center_window(self.feature_choice_widget)
259
+
260
+ def ask_for_features(self):
261
+
262
+ cols = np.array(list(self.df.columns))
263
+ is_number = np.vectorize(lambda x: np.issubdtype(x, np.number))
264
+ feats = cols[is_number(self.df.dtypes)]
265
+
266
+ self.feature_choice_widget = QWidget()
267
+ self.feature_choice_widget.setWindowTitle("Select numeric feature")
268
+ layout = QVBoxLayout()
269
+ self.feature_choice_widget.setLayout(layout)
270
+ self.feature_cb = QComboBox()
271
+ self.feature_cb.addItems(feats)
272
+ hbox = QHBoxLayout()
273
+ hbox.addWidget(QLabel('feature: '), 33)
274
+ hbox.addWidget(self.feature_cb, 66)
275
+ # hbox.addWidget((QLabel('Plot two features')))
276
+ layout.addLayout(hbox)
277
+ self.checkBox_feature = QCheckBox(self.feature_choice_widget)
278
+ self.checkBox_feature.setGeometry(QRect(170, 120, 81, 20))
279
+ # self.checkBox_feature.setText('Plot two features')
280
+ # #hbox.addWidget(self.checkBox_feature)
281
+ # layout.addWidget(self.checkBox_feature, alignment=Qt.AlignCenter)
282
+ # self.checkBox_feature.stateChanged.connect(self.enable_second_feature)
283
+
284
+ # layout = QVBoxLayout()
285
+ # self.feature_two_choice_widget.setLayout(layout)
286
+ # self.feature_two_cb = QComboBox()
287
+ # self.feature_two_cb.addItems(feats)
288
+ # self.feature_two_cb.setEnabled(False)
289
+ # hbox_two = QHBoxLayout()
290
+ # hbox_two.addWidget(QLabel('feature two: '), 33)
291
+ # hbox_two.addWidget(self.feature_two_cb, 66)
292
+ # layout.addLayout(hbox_two)
293
+
294
+ self.set_feature_btn = QPushButton('set')
295
+ self.set_feature_btn.clicked.connect(self.compute_signals)
296
+ layout.addWidget(self.set_feature_btn)
297
+ self.feature_choice_widget.show()
298
+ center_window(self.feature_choice_widget)
299
+
300
+ def enable_second_feature(self):
301
+ if self.checkBox_feature.isChecked():
302
+ self.feature_two_cb.setEnabled(True)
303
+ else:
304
+ self.feature_two_cb.setEnabled(False)
305
+
306
+ def compute_signals(self):
307
+
308
+ if self.df is not None:
309
+ self.feature_selected = self.feature_cb.currentText()
310
+ self.feature_choice_widget.close()
311
+ self.compute_signal_functions()
312
+ # prepare survival
313
+
314
+ # plot survival
315
+ self.survivalWidget = QWidget()
316
+ self.scroll = QScrollArea()
317
+ self.survivalWidget.setMinimumHeight(int(0.8 * self.screen_height))
318
+ self.survivalWidget.setWindowTitle('signals')
319
+ self.plotvbox = QVBoxLayout(self.survivalWidget)
320
+ self.plotvbox.setContentsMargins(30, 30, 30, 30)
321
+ self.survival_title = QLabel('Signal function')
322
+ self.survival_title.setStyleSheet("""
323
+ font-weight: bold;
324
+ padding: 0px;
325
+ """)
326
+ self.plotvbox.addWidget(self.survival_title, alignment=Qt.AlignCenter)
327
+
328
+ plot_buttons_hbox = QHBoxLayout()
329
+ plot_buttons_hbox.addWidget(QLabel(''), 80, alignment=Qt.AlignLeft)
330
+
331
+ self.legend_btn = QPushButton('')
332
+ self.legend_btn.setIcon(icon(MDI6.text_box, color="black"))
333
+ self.legend_btn.setStyleSheet(self.button_select_all)
334
+ self.legend_btn.setToolTip('Show or hide the legend')
335
+ self.legend_visible = True
336
+ self.legend_btn.clicked.connect(self.show_hide_legend)
337
+ plot_buttons_hbox.addWidget(self.legend_btn, 5, alignment=Qt.AlignRight)
338
+
339
+ self.log_btn = QPushButton('')
340
+ self.log_btn.setIcon(icon(MDI6.math_log, color="black"))
341
+ self.log_btn.setStyleSheet(self.button_select_all)
342
+ self.log_btn.clicked.connect(self.switch_to_log)
343
+ self.log_btn.setToolTip('Enable or disable log scale')
344
+ plot_buttons_hbox.addWidget(self.log_btn, 5, alignment=Qt.AlignRight)
345
+
346
+ # self.ci_btn = QPushButton('')
347
+ # self.ci_btn.setIcon(icon(MDI6.arrow_expand_horizontal,color="blue"))
348
+ # self.ci_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
349
+ # self.ci_btn.clicked.connect(self.switch_ci)
350
+ # self.ci_btn.setToolTip('Show or hide confidence intervals.')
351
+ # plot_buttons_hbox.addWidget(self.ci_btn, 5, alignment=Qt.AlignRight)
352
+
353
+ self.cell_lines_btn = QPushButton('')
354
+ self.cell_lines_btn.setIcon(icon(MDI6.view_headline, color="black"))
355
+ self.cell_lines_btn.setStyleSheet(self.button_select_all)
356
+ self.cell_lines_btn.clicked.connect(self.switch_cell_lines)
357
+ self.cell_lines_btn.setToolTip('Show or hide individual cell signals.')
358
+ plot_buttons_hbox.addWidget(self.cell_lines_btn, 5, alignment=Qt.AlignRight)
359
+
360
+ self.fig, self.ax = plt.subplots(1, 1, figsize=(10, 10)) # ,figsize=(10,10)
361
+ # self.setMinimumHeight(100)
362
+ self.survival_window = FigureCanvas(self.fig, title="Survival")
363
+ self.survival_window.setContentsMargins(0, 0, 0, 0)
364
+ if self.df is not None:
365
+ self.initialize_axis()
366
+ plt.tight_layout()
367
+
368
+ self.fig.set_facecolor('none') # or 'None'
369
+ self.fig.canvas.setStyleSheet("background-color: transparent;")
370
+ self.survival_window.canvas.draw()
371
+
372
+ # self.survival_window.layout.addWidget(QLabel('WHAAAAATTT???'))
373
+
374
+ self.plot_options = [QRadioButton() for i in range(3)]
375
+ self.radio_labels = ['well', 'pos', 'both']
376
+ radio_hbox = QHBoxLayout()
377
+ radio_hbox.setContentsMargins(30,30,30,30)
378
+ self.plot_btn_group = QButtonGroup()
379
+ for i in range(3):
380
+ self.plot_options[i].setText(self.radio_labels[i])
381
+ #self.plot_options[i].toggled.connect(self.plot_survivals)
382
+ self.plot_btn_group.addButton(self.plot_options[i])
383
+ radio_hbox.addWidget(self.plot_options[i], 33, alignment=Qt.AlignCenter)
384
+ self.plot_btn_group.buttonClicked[int].connect(self.plot_survivals)
385
+
386
+ if self.position_indices is not None:
387
+ if len(self.well_indices) > 1 and len(self.position_indices) == 1:
388
+ self.plot_btn_group.buttons()[0].click()
389
+ for i in [1, 2]:
390
+ self.plot_options[i].setEnabled(False)
391
+ elif len(self.well_indices) > 1:
392
+ self.plot_btn_group.buttons()[0].click()
393
+ elif len(self.well_indices) == 1 and len(self.position_indices) == 1:
394
+ self.plot_btn_group.buttons()[1].click()
395
+ for i in [0, 2]:
396
+ self.plot_options[i].setEnabled(False)
397
+ # else:
398
+ # if len(self.well_indices)>1:
399
+ # self.plot_btn_group.buttons()[0].click()
400
+ # elif len(self.well_indices)==1:
401
+ # self.plot_btn_group.buttons()[2].click()
402
+
403
+ # elif len(self.well_indices)>1:
404
+ # self.plot_btn_group.buttons()[0].click()
405
+ # else:
406
+ # self.plot_btn_group.buttons()[1].click()
407
+
408
+ if self.position_indices is not None:
409
+ for i in [0, 2]:
410
+ self.plot_options[i].setEnabled(False)
411
+
412
+ # self.plot_options[0].setChecked(True)
413
+ # self.plotvbox.addLayout(radio_hbox)
414
+ self.plotvbox.addLayout(plot_buttons_hbox)
415
+ self.plotvbox.addWidget(self.survival_window)
416
+ # self.class_selection_lbl = QLabel('Select class')
417
+ # self.class_selection_lbl.setStyleSheet("""
418
+ # font-weight: bold;
419
+ # padding: 0px;
420
+ # """)
421
+ # self.plotvbox.addWidget(self.class_selection_lbl, alignment=Qt.AlignCenter)
422
+ # class_selection_hbox = QHBoxLayout()
423
+ # class_selection_hbox.setContentsMargins(30,30,30,30)
424
+ # self.all_btn = QRadioButton('*')
425
+ # self.all_btn.setChecked(True)
426
+ # self.event_btn = QRadioButton('event')
427
+ # self.no_event_btn = QRadioButton('no event')
428
+ # self.class_btn_group = QButtonGroup()
429
+ # for btn in [self.all_btn, self.event_btn, self.no_event_btn]:
430
+ # self.class_btn_group.addButton(btn)
431
+ #
432
+ # self.class_btn_group.buttonClicked[int].connect(self.set_class_to_plot)
433
+ #
434
+ # class_selection_hbox.addWidget(self.all_btn, 33, alignment=Qt.AlignLeft)
435
+ # class_selection_hbox.addWidget(self.event_btn, 33, alignment=Qt.AlignCenter)
436
+ # class_selection_hbox.addWidget(self.no_event_btn, 33, alignment=Qt.AlignRight)
437
+ # self.plotvbox.addLayout(class_selection_hbox)
438
+
439
+ self.select_pos_label = QLabel('Select positions')
440
+ self.select_pos_label.setStyleSheet("""
441
+ font-weight: bold;
442
+ padding: 0px;
443
+ """)
444
+ self.plotvbox.addWidget(self.select_pos_label, alignment=Qt.AlignCenter)
445
+ #
446
+ # self.select_option = [QRadioButton() for i in range(2)]
447
+ # self.select_label = ['name', 'spatial']
448
+ # select_hbox = QHBoxLayout()
449
+ # select_hbox.setContentsMargins(30,30,30,30)
450
+ # self.select_btn_group = QButtonGroup()
451
+ # for i in range(2):
452
+ # self.select_option[i].setText(self.select_label[i])
453
+ # #self.select_option[i].toggled.connect(self.switch_selection_mode)
454
+ # self.select_btn_group.addButton(self.select_option[i])
455
+ # select_hbox.addWidget(self.select_option[i],33, alignment=Qt.AlignCenter)
456
+ # self.select_btn_group.buttonClicked[int].connect(self.switch_selection_mode)
457
+ # self.plotvbox.addLayout(select_hbox)
458
+
459
+ self.look_for_metadata()
460
+ if self.metadata_found:
461
+ self.fig_scatter, self.ax_scatter = plt.subplots(1, 1, figsize=(4, 3))
462
+ self.position_scatter = FigureCanvas(self.fig_scatter)
463
+ self.load_coordinates()
464
+ self.plot_spatial_location()
465
+ # self.plot_positions()
466
+ self.ax_scatter.spines['top'].set_visible(False)
467
+ self.ax_scatter.spines['right'].set_visible(False)
468
+ self.ax_scatter.set_aspect('equal')
469
+ self.ax_scatter.set_xticks([])
470
+ self.ax_scatter.set_yticks([])
471
+ plt.tight_layout()
472
+
473
+ self.fig_scatter.set_facecolor('none') # or 'None'
474
+ self.fig_scatter.canvas.setStyleSheet("background-color: transparent;")
475
+ self.plotvbox.addWidget(self.position_scatter)
476
+
477
+ self.generate_pos_selection_widget()
478
+
479
+ # if self.df is not None and len(self.ks_estimators_per_position)>0:
480
+ # self.plot_survivals()
481
+ # self.select_btn_group.buttons()[0].click()
482
+ self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
483
+ self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
484
+ # self.scroll.setWidgetResizable(True)
485
+ self.scroll.setWidget(self.survivalWidget)
486
+
487
+ self.scroll.setMinimumHeight(int(0.8 * self.screen_height))
488
+ self.survivalWidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
489
+ self.scroll.show()
490
+
491
+ def process_signal(self):
492
+
493
+ print('you clicked!!')
494
+ # self.FrameToMin = float(self.time_calibration_le.text().replace(',','.'))
495
+ print(self.FrameToMin, 'set')
496
+
497
+ # read instructions from combobox options
498
+ self.load_available_tables()
499
+ if self.df is not None:
500
+ self.ask_for_features()
501
+ else:
502
+ return None
503
+
504
+ def generate_pos_selection_widget(self):
505
+
506
+ self.well_names = self.df['well_name'].unique()
507
+ self.pos_names = self.df_pos_info[
508
+ 'pos_name'].unique() # pd.DataFrame(self.ks_estimators_per_position)['position_name'].unique()
509
+ print(f'POSITION NAMES: ', self.pos_names)
510
+ self.usable_well_labels = []
511
+ for name in self.well_names:
512
+ for lbl in self.well_labels:
513
+ if name + ':' in lbl:
514
+ self.usable_well_labels.append(lbl)
515
+
516
+ self.line_choice_widget = QWidget()
517
+ self.line_check_vbox = QVBoxLayout()
518
+ self.line_choice_widget.setLayout(self.line_check_vbox)
519
+ if len(self.well_indices) > 1:
520
+ self.well_display_options = [QCheckBox(self.usable_well_labels[i]) for i in
521
+ range(len(self.usable_well_labels))]
522
+ for i in range(len(self.well_names)):
523
+ self.line_check_vbox.addWidget(self.well_display_options[i], alignment=Qt.AlignLeft)
524
+ self.well_display_options[i].setChecked(True)
525
+ self.well_display_options[i].toggled.connect(self.select_survival_lines)
526
+ else:
527
+ self.pos_display_options = [QCheckBox(self.pos_names[i]) for i in range(len(self.pos_names))]
528
+ for i in range(len(self.pos_names)):
529
+ self.line_check_vbox.addWidget(self.pos_display_options[i], alignment=Qt.AlignLeft)
530
+ self.pos_display_options[i].setChecked(True)
531
+ self.pos_display_options[i].toggled.connect(self.select_survival_lines)
532
+
533
+ self.plotvbox.addWidget(self.line_choice_widget, alignment=Qt.AlignCenter)
534
+
535
+ def load_available_tables(self):
536
+
537
+ """
538
+ Load the tables of the selected wells/positions from the control Panel for the population of interest
539
+
540
+ """
541
+
542
+ self.well_option = self.parent_window.parent_window.well_list.currentIndex()
543
+ if self.well_option == len(self.wells):
544
+ wo = '*'
545
+ else:
546
+ wo = self.well_option
547
+ self.position_option = self.parent_window.parent_window.position_list.currentIndex()
548
+ if self.position_option == 0:
549
+ po = '*'
550
+ else:
551
+ po = self.position_option - 1
552
+
553
+ self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=wo, position_option=po,
554
+ population=self.cbs[0].currentText(), return_pos_info=True)
555
+
556
+ if self.df is None:
557
+
558
+ print('No table could be found...')
559
+ msgBox = QMessageBox()
560
+ msgBox.setIcon(QMessageBox.Warning)
561
+ msgBox.setText("No table could be found to compute survival...")
562
+ msgBox.setWindowTitle("Warning")
563
+ msgBox.setStandardButtons(QMessageBox.Ok)
564
+ returnValue = msgBox.exec()
565
+ if returnValue == QMessageBox.Ok:
566
+ self.close()
567
+ return None
568
+ else:
569
+ self.close()
570
+ return None
571
+ else:
572
+ self.df_well_info = self.df_pos_info.loc[:,
573
+ ['well_path', 'well_index', 'well_name', 'well_number', 'well_alias']].drop_duplicates()
574
+
575
+ def compute_signal_functions(self):
576
+ wells = get_experiment_wells(self.exp_dir)
577
+ antibodies = get_experiment_antibodies(self.exp_dir)
578
+ cell_types = get_experiment_cell_types(self.exp_dir)
579
+ concentrations = get_experiment_concentrations(self.exp_dir)
580
+ well_name = [' '.join([s1, '+', s2, s3, 'pM']) for s1, s2, s3 in zip(cell_types, antibodies, concentrations)]
581
+
582
+ data = []
583
+ full_data = []
584
+ print(self.well_option)
585
+ if self.well_option > 1:
586
+ self.plot_mode = 'wells'
587
+ for z, well in enumerate(wells): # loop over wells
588
+ # print(well)
589
+ if z not in self.well_indices:
590
+ pass
591
+ else:
592
+ positions = get_positions_in_well(well)
593
+ for ind, pos in enumerate(positions): # loop over positions
594
+ if self.position_indices is not None:
595
+ if ind + 1 in self.position_indices:
596
+ print(f'Processing position {pos}...')
597
+ tab_tc = pos + os.sep.join(
598
+ ['output', 'tables', f'trajectories_{self.cbs[0].currentText()}.csv'])
599
+ if not os.path.exists(tab_tc):
600
+ return None
601
+ else:
602
+ data = pd.read_csv(tab_tc)
603
+ data['well_name'] = well_name[z]
604
+ full_data.append(data)
605
+
606
+ else:
607
+ print(f'Processing position {pos}...')
608
+ tab_tc = pos + os.sep.join(
609
+ ['output', 'tables', f'trajectories_{self.cbs[0].currentText()}.csv'])
610
+ if not os.path.exists(tab_tc):
611
+ return None
612
+ else:
613
+ data = pd.read_csv(tab_tc)
614
+ data['well_name'] = well_name[z]
615
+ full_data.append(data)
616
+ else:
617
+ self.plot_mode = 'positions'
618
+ positions = get_positions_in_well(wells[self.well_indices[0]])
619
+ for ind, pos in enumerate(positions): # loop over positions
620
+ pos_name = pos.split(os.sep)[-2]
621
+ if self.position_indices is not None:
622
+ if ind + 1 in self.position_indices:
623
+ print(f'Processing position {pos}...')
624
+ tab_tc = pos + os.sep.join(
625
+ ['output', 'tables', f'trajectories_{self.cbs[0].currentText()}.csv'])
626
+ if not os.path.exists(tab_tc):
627
+ return None
628
+ else:
629
+ data = pd.read_csv(tab_tc)
630
+ data['position'] = pos_name
631
+ full_data.append(data)
632
+ else:
633
+ print(f'Processing position {pos}...')
634
+ tab_tc = pos + os.sep.join(['output', 'tables', f'trajectories_{self.cbs[0].currentText()}.csv'])
635
+ if not os.path.exists(tab_tc):
636
+ return None
637
+ else:
638
+ data = pd.read_csv(tab_tc)
639
+ data['position'] = pos_name
640
+ full_data.append(data)
641
+ self.plot_data = pd.concat(full_data, ignore_index=True)
642
+
643
+ def generate_synchronized_matrix(self, well_group, feature_selected, cclass, max_time):
644
+
645
+ if isinstance(cclass, int):
646
+ cclass = [cclass]
647
+
648
+ n_cells = len(well_group.groupby(['position', 'TRACK_ID']))
649
+ depth = int(2 * max_time + 3)
650
+ matrix = np.zeros((n_cells, depth))
651
+ matrix[:, :] = np.nan
652
+ mapping = np.arange(-max_time - 1, max_time + 2)
653
+ cid = 0
654
+ for block, movie_group in well_group.groupby('position'):
655
+ for tid, track_group in movie_group.loc[movie_group[self.cbs[1].currentText()].isin(cclass)].groupby(
656
+ 'TRACK_ID'):
657
+ try:
658
+ timeline = track_group['FRAME'].to_numpy().astype(int)
659
+ feature = track_group[feature_selected].to_numpy()
660
+ if self.checkBox_feature.isChecked():
661
+ second_feature = track_group[self.second_feature_selected].to_numpy()
662
+ if self.cbs[2].currentText().startswith('t') and not self.abs_time_checkbox.isChecked():
663
+ t0 = math.floor(track_group[self.cbs[2].currentText()].to_numpy()[0])
664
+ timeline -= t0
665
+ elif self.cbs[2].currentText() == 'first detection' and not self.abs_time_checkbox.isChecked():
666
+
667
+ if 'area' in list(track_group.columns):
668
+ print('area in list')
669
+ feat = track_group['area'].values
670
+ else:
671
+ feat = feature
672
+
673
+ first_detection = timeline[feat == feat][0]
674
+ timeline -= first_detection
675
+ print(first_detection, timeline)
676
+
677
+ elif self.abs_time_checkbox.isChecked():
678
+ timeline -= int(self.frame_slider.value())
679
+
680
+ loc_t = [np.where(mapping == t)[0][0] for t in timeline]
681
+ matrix[cid, loc_t] = feature
682
+ if second_feature:
683
+ matrix[cid, loc_t + 1] = second_feature
684
+ print(timeline, loc_t)
685
+
686
+ cid += 1
687
+ except:
688
+ pass
689
+ return matrix
690
+
691
+ def col_mean(self, matrix):
692
+
693
+ mean_line = np.zeros(matrix.shape[1])
694
+ mean_line[:] = np.nan
695
+ std_line = np.copy(mean_line)
696
+
697
+ for k in range(matrix.shape[1]):
698
+ values = matrix[:, k]
699
+ # values = values[values!=0]
700
+ if len(values[values == values]) > 2:
701
+ mean_line[k] = np.nanmean(values)
702
+ std_line[k] = np.nanstd(values)
703
+
704
+ return mean_line, std_line
705
+
706
+ def initialize_axis(self):
707
+ plt.rcParams['svg.fonttype'] = 'none'
708
+ plt.rcParams["font.family"] = "sans-serif"
709
+ SMALL_SIZE = 5
710
+ MEDIUM_SIZE = 6
711
+ BIGGER_SIZE = 7
712
+
713
+ plt.rc('font', size=SMALL_SIZE) # controls default text sizes
714
+ plt.rc('axes', titlesize=SMALL_SIZE) # fontsize of the axes title
715
+ plt.rc('axes', labelsize=MEDIUM_SIZE) # fontsize of the x and y labels
716
+ plt.rc('xtick', labelsize=SMALL_SIZE) # fontsize of the tick labels
717
+ plt.rc('ytick', labelsize=SMALL_SIZE) # fontsize of the tick labels
718
+ plt.rc('legend', fontsize=SMALL_SIZE) # legend fontsize
719
+ plt.rc('figure', titlesize=BIGGER_SIZE) # fontsize of the figure title
720
+
721
+ fig, ax = plt.subplots(1, 1, figsize=(10, 5))
722
+ if self.well_option > 1:
723
+ if self.check_class.isChecked():
724
+ sns.boxenplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='well_name',
725
+ hue=self.cbs[1].currentText(),
726
+ dodge=True, palette=self.palette)
727
+ if self.show_cell_lines:
728
+ sns.stripplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='well_name',
729
+ hue=self.cbs[1].currentText(),
730
+ dodge=True, alpha=0.3, linewidth=0.5, size=3, palette=self.palette)
731
+ elif self.check_group.isChecked():
732
+ sns.boxenplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='well_name',
733
+ hue=self.cbs[2].currentText(),
734
+ dodge=True, palette=self.hue_colors)
735
+ if self.show_cell_lines:
736
+ sns.stripplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='well_name',
737
+ hue=self.cbs[2].currentText(),
738
+ dodge=True, alpha=0.3, linewidth=0.5, size=3, palette=self.hue_colors)
739
+ else:
740
+ sns.boxenplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='well_name',
741
+ color=self.palette[0],
742
+ dodge=True)
743
+ if self.show_cell_lines:
744
+ sns.stripplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='well_name',
745
+ color=self.palette[0],
746
+ dodge=True, alpha=0.3, linewidth=0.5, size=3)
747
+ else:
748
+ if self.check_class.isChecked():
749
+ sns.boxenplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='position',
750
+ hue=self.cbs[1].currentText(),
751
+ dodge=True, palette=self.palette)
752
+ if self.show_cell_lines:
753
+ sns.stripplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='position',
754
+ hue=self.cbs[1].currentText(),
755
+ dodge=True, alpha=0.3, linewidth=0.5, size=3, palette=self.palette)
756
+ elif self.check_group.isChecked():
757
+ sns.boxenplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='position',
758
+ hue=self.cbs[2].currentText(),
759
+ dodge=True, palette=self.hue_colors)
760
+ if self.show_cell_lines:
761
+ sns.stripplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='position',
762
+ hue=self.cbs[2].currentText(),
763
+ dodge=True, alpha=0.3, linewidth=0.5, size=3, palette=self.hue_colors)
764
+ else:
765
+ sns.boxenplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='position',
766
+ color=self.palette[0],
767
+ dodge=True)
768
+ if self.show_cell_lines:
769
+ sns.stripplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='position',
770
+ color=self.palette[0],
771
+ dodge=True, alpha=0.3, linewidth=0.5, size=3)
772
+ self.ax.spines['top'].set_visible(False)
773
+ self.ax.spines['right'].set_visible(False)
774
+ self.ax.set_ylabel(self.feature_selected)
775
+ # ax.set_ylim(0.95,1.3)
776
+ plt.tight_layout()
777
+
778
+ def plot_survivals(self, id):
779
+ self.ax.clear()
780
+ plt.rcParams['svg.fonttype'] = 'none'
781
+ plt.rcParams["font.family"] = "sans-serif"
782
+ SMALL_SIZE = 5
783
+ MEDIUM_SIZE = 6
784
+ BIGGER_SIZE = 7
785
+
786
+ plt.rc('font', size=SMALL_SIZE) # controls default text sizes
787
+ plt.rc('axes', titlesize=SMALL_SIZE) # fontsize of the axes title
788
+ plt.rc('axes', labelsize=MEDIUM_SIZE) # fontsize of the x and y labels
789
+ plt.rc('xtick', labelsize=SMALL_SIZE) # fontsize of the tick labels
790
+ plt.rc('ytick', labelsize=SMALL_SIZE) # fontsize of the tick labels
791
+ plt.rc('legend', fontsize=SMALL_SIZE) # legend fontsize
792
+ plt.rc('figure', titlesize=BIGGER_SIZE) # fontsize of the figure title
793
+
794
+ fig, ax = plt.subplots(1, 1, figsize=(10, 5))
795
+ if self.well_option > 1:
796
+ if self.check_class.isChecked():
797
+ sns.boxenplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='well_name',
798
+ hue=self.cbs[1].currentText(),
799
+ dodge=True, palette=self.palette)
800
+ if self.show_cell_lines:
801
+ sns.stripplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='well_name',
802
+ hue=self.cbs[1].currentText(),
803
+ dodge=True, alpha=0.3, linewidth=0.5, size=3, palette=self.palette)
804
+ elif self.check_group.isChecked():
805
+ sns.boxenplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='well_name',
806
+ hue=self.cbs[2].currentText(),
807
+ dodge=True, palette=self.hue_colors)
808
+ if self.show_cell_lines:
809
+ sns.stripplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='well_name',
810
+ hue=self.cbs[2].currentText(),
811
+ dodge=True, alpha=0.3, linewidth=0.5, size=3, palette=self.hue_colors)
812
+ else:
813
+ sns.boxenplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='well_name',
814
+ color=self.palette[0],
815
+ dodge=True)
816
+ if self.show_cell_lines:
817
+ sns.stripplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='well_name',
818
+ color=self.palette[0],
819
+ dodge=True, alpha=0.3, linewidth=0.5, size=3)
820
+ else:
821
+ if self.check_class.isChecked():
822
+ sns.boxenplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='position',
823
+ hue=self.cbs[1].currentText(),
824
+ dodge=True, palette=self.palette)
825
+ if self.show_cell_lines:
826
+ sns.stripplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='position',
827
+ hue=self.cbs[1].currentText(),
828
+ dodge=True, alpha=0.3, linewidth=0.5, size=3, palette=self.palette)
829
+ elif self.check_group.isChecked():
830
+ sns.boxenplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='position',
831
+ hue=self.cbs[2].currentText(),
832
+ dodge=True, palette=self.hue_colors)
833
+ if self.show_cell_lines:
834
+ sns.stripplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='position',
835
+ hue=self.cbs[2].currentText(),
836
+ dodge=True, alpha=0.3, linewidth=0.5, size=3, palette=self.hue_colors)
837
+ else:
838
+ sns.boxenplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='position',
839
+ color=self.palette[0],
840
+ dodge=True)
841
+ if self.show_cell_lines:
842
+ sns.stripplot(ax=self.ax, data=self.plot_data, y=self.feature_selected, x='position',
843
+ color=self.palette[0],
844
+ dodge=True, alpha=0.3, linewidth=0.5, size=3)
845
+ ax.spines['top'].set_visible(False)
846
+ ax.spines['right'].set_visible(False)
847
+ ax.set_ylabel(self.feature_selected)
848
+ plt.tight_layout()
849
+ self.survival_window.setMinimumHeight(int(0.5 * self.screen_height))
850
+ self.survival_window.setMinimumWidth(int(0.8 * self.survivalWidget.width()))
851
+ self.survival_window.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
852
+
853
+ self.survival_window.canvas.draw()
854
+
855
+ def plot_line(self, line, color, label, mean_signal, ci_option=True, cell_lines_option=False, alpha_ci=0.5,
856
+ alpha_cell_lines=0.5, std_signal=None, matrix=None):
857
+ try:
858
+ if 'second' in str(mean_signal):
859
+ self.ax2.plot(line['timeline'] * self.FrameToMin, line[mean_signal], color=color, label=label)
860
+ else:
861
+ self.ax.plot(line['timeline'] * self.FrameToMin, line[mean_signal], color=color, label=label)
862
+ if ci_option and std_signal is not None:
863
+ if 'second' in str(mean_signal):
864
+ self.ax2.fill_between(line['timeline'] * self.FrameToMin,
865
+ [a - b for a, b in zip(line[mean_signal], line[std_signal])],
866
+ [a + b for a, b in zip(line[mean_signal], line[std_signal])],
867
+ color=color,
868
+ alpha=alpha_ci,
869
+ )
870
+ else:
871
+ self.ax.fill_between(line['timeline'] * self.FrameToMin,
872
+ [a - b for a, b in zip(line[mean_signal], line[std_signal])],
873
+ [a + b for a, b in zip(line[mean_signal], line[std_signal])],
874
+ color=color,
875
+ alpha=alpha_ci,
876
+ )
877
+ if cell_lines_option and matrix is not None:
878
+ print(mean_signal)
879
+ mat = line[matrix]
880
+ if 'second' in str(mean_signal):
881
+ for i in range(mat.shape[0]):
882
+ self.ax2.plot(line['timeline'] * self.FrameToMin, mat[i, :], color=color,
883
+ alpha=alpha_cell_lines)
884
+ else:
885
+ for i in range(mat.shape[0]):
886
+ self.ax.plot(line['timeline'] * self.FrameToMin, mat[i, :], color=color, alpha=alpha_cell_lines)
887
+
888
+ except Exception as e:
889
+ print(f'Exception {e}')
890
+
891
+ def switch_to_log(self):
892
+
893
+ """
894
+ Switch threshold histogram to log scale. Auto adjust.
895
+ """
896
+
897
+ if self.ax.get_yscale() == 'linear':
898
+ self.ax.set_yscale('log')
899
+ # self.ax.set_ylim(0.01,1.05)
900
+ else:
901
+ self.ax.set_yscale('linear')
902
+ # self.ax.set_ylim(0.01,1.05)
903
+
904
+ # self.ax.autoscale()
905
+ self.survival_window.canvas.draw_idle()
906
+
907
+ def show_hide_legend(self):
908
+ if self.legend_visible:
909
+ self.ax.legend().set_visible(False)
910
+ self.legend_visible = False
911
+ self.legend_btn.setIcon(icon(MDI6.text_box_outline, color="black"))
912
+ else:
913
+ self.ax.legend().set_visible(True)
914
+ self.legend_visible = True
915
+ self.legend_btn.setIcon(icon(MDI6.text_box, color="black"))
916
+
917
+ self.survival_window.canvas.draw_idle()
918
+
919
+ def look_for_metadata(self):
920
+
921
+ self.metadata_found = False
922
+ self.metafiles = glob(self.exp_dir + os.sep.join([f'W*', '*', 'movie', '*metadata.txt'])) \
923
+ + glob(self.exp_dir + os.sep.join([f'W*', '*', '*metadata.txt'])) \
924
+ + glob(self.exp_dir + os.sep.join([f'W*', '*metadata.txt'])) \
925
+ + glob(self.exp_dir + '*metadata.txt')
926
+ print(f'Found {len(self.metafiles)} metadata files...')
927
+ if len(self.metafiles) > 0:
928
+ self.metadata_found = True
929
+
930
+ def switch_selection_mode(self, id):
931
+ print(f'button {id} was clicked')
932
+ for i in range(2):
933
+ if self.select_option[i].isChecked():
934
+ self.selection_mode = self.select_label[i]
935
+ if self.selection_mode == 'name':
936
+ if len(self.metafiles) > 0:
937
+ self.position_scatter.hide()
938
+ self.line_choice_widget.show()
939
+ else:
940
+ if len(self.metafiles) > 0:
941
+ self.position_scatter.show()
942
+ self.line_choice_widget.hide()
943
+
944
+ def load_coordinates(self):
945
+
946
+ """
947
+ Read metadata and try to extract position coordinates
948
+ """
949
+
950
+ self.no_meta = False
951
+ try:
952
+ with open(self.metafiles[0], 'r') as f:
953
+ data = json.load(f)
954
+ positions = data['Summary']['InitialPositionList']
955
+ except Exception as e:
956
+ print(f'Trouble loading metadata: error {e}...')
957
+ return None
958
+
959
+ for k in range(len(positions)):
960
+ pos_label = positions[k]['Label']
961
+ try:
962
+ coords = positions[k]['DeviceCoordinatesUm']['XYStage']
963
+ except:
964
+ try:
965
+ coords = positions[k]['DeviceCoordinatesUm']['PIXYStage']
966
+ except:
967
+ self.no_meta = True
968
+
969
+ if not self.no_meta:
970
+ files = self.df_pos_info['stack_path'].values
971
+ pos_loc = [pos_label in f for f in files]
972
+ self.df_pos_info.loc[pos_loc, 'x'] = coords[0]
973
+ self.df_pos_info.loc[pos_loc, 'y'] = coords[1]
974
+ self.df_pos_info.loc[pos_loc, 'metadata_tag'] = pos_label
975
+
976
+ def update_annot(self, ind):
977
+
978
+ pos = self.sc.get_offsets()[ind["ind"][0]]
979
+ self.annot.xy = pos
980
+ text = self.scat_labels[ind["ind"][0]]
981
+ self.annot.set_text(text)
982
+ self.annot.get_bbox_patch().set_facecolor('k')
983
+ self.annot.get_bbox_patch().set_alpha(0.4)
984
+
985
+ def hover(self, event):
986
+ vis = self.annot.get_visible()
987
+ if event.inaxes == self.ax_scatter:
988
+ cont, ind = self.sc.contains(event)
989
+ if cont:
990
+ self.update_annot(ind)
991
+ self.annot.set_visible(True)
992
+ self.fig_scatter.canvas.draw_idle()
993
+ else:
994
+ if vis:
995
+ self.annot.set_visible(False)
996
+ self.fig_scatter.canvas.draw_idle()
997
+
998
+ def unselect_position(self, event):
999
+ print('unselecting position')
1000
+ self.survival_window.canvas.clear()
1001
+ ind = event.ind # index of selected position
1002
+ well_idx = self.df_pos_info.iloc[ind]['well_index'].values[0]
1003
+ selectedPos = self.df_pos_info.iloc[ind]['pos_path'].values[0]
1004
+ currentSelState = self.df_pos_info.iloc[ind]['select'].values[0]
1005
+ if self.plot_options[0].isChecked() or self.plot_options[2].isChecked():
1006
+ self.df_pos_info.loc[self.df_pos_info['well_index'] == well_idx, 'select'] = not currentSelState
1007
+ self.df_well_info.loc[self.df_well_info['well_index'] == well_idx, 'select'] = not currentSelState
1008
+ if len(self.well_indices) > 1:
1009
+ self.well_display_options[well_idx].setChecked(not currentSelState)
1010
+ else:
1011
+ for p in self.pos_display_options:
1012
+ p.setChecked(not currentSelState)
1013
+ else:
1014
+ self.df_pos_info.loc[self.df_pos_info['pos_path'] == selectedPos, 'select'] = not currentSelState
1015
+ if len(self.well_indices) <= 1:
1016
+ self.pos_display_options[ind[0]].setChecked(not currentSelState)
1017
+
1018
+ self.sc.set_color(self.select_color(self.df_pos_info["select"].values))
1019
+ self.position_scatter.canvas.draw_idle()
1020
+ self.plot_survivals(0)
1021
+
1022
+ def select_survival_lines(self):
1023
+ if self.plot_mode == 'wells':
1024
+ selected_wells = []
1025
+ for i in range(len(self.well_display_options)):
1026
+ if self.well_display_options[i].isChecked():
1027
+ selected_wells.append(i)
1028
+ self.df_well_info.loc[self.df_well_info['well_index'] == i, 'select'] = self.well_display_options[
1029
+ i].isChecked()
1030
+ self.df_pos_info.loc[self.df_pos_info['well_index'] == i, 'select'] = self.well_display_options[
1031
+ i].isChecked()
1032
+ if len(selected_wells) == 0:
1033
+ print('No wells selected')
1034
+ self.ax.clear()
1035
+ else:
1036
+ self.ax.clear()
1037
+ self.well_indices = selected_wells
1038
+ self.compute_signal_functions()
1039
+ self.plot_survivals(0)
1040
+ else:
1041
+ selected_pos = []
1042
+ for i in range(len(self.pos_display_options)):
1043
+ if self.pos_display_options[i].isChecked():
1044
+ selected_pos.append(i + 1)
1045
+ self.df_pos_info.loc[self.df_pos_info['pos_index'] == i, 'select'] = self.pos_display_options[
1046
+ i].isChecked()
1047
+ if len(selected_pos) == 0:
1048
+ self.ax.clear()
1049
+ else:
1050
+ self.position_indices = selected_pos
1051
+ self.ax.clear()
1052
+ self.compute_signal_functions()
1053
+ self.plot_survivals(0)
1054
+
1055
+ if len(self.metafiles) > 0:
1056
+ self.sc.set_color(self.select_color(self.df_pos_info["select"].values))
1057
+ self.position_scatter.canvas.draw_idle()
1058
+
1059
+ def select_color(self, selection):
1060
+ colors = [tab10(0) if s else tab10(0.1) for s in selection]
1061
+ return colors
1062
+
1063
+ def plot_spatial_location(self):
1064
+
1065
+ try:
1066
+ self.sc = self.ax_scatter.scatter(self.df_pos_info["x"].values, self.df_pos_info["y"].values, picker=True,
1067
+ pickradius=1, color=self.select_color(self.df_pos_info["select"].values))
1068
+ self.scat_labels = self.df_pos_info['metadata_tag'].values
1069
+ self.ax_scatter.invert_xaxis()
1070
+ self.annot = self.ax_scatter.annotate("", xy=(0, 0), xytext=(10, 10), textcoords="offset points",
1071
+ bbox=dict(boxstyle="round", fc="w"),
1072
+ arrowprops=dict(arrowstyle="->"))
1073
+ self.annot.set_visible(False)
1074
+ self.fig_scatter.canvas.mpl_connect("motion_notify_event", self.hover)
1075
+ self.fig_scatter.canvas.mpl_connect("pick_event", self.unselect_position)
1076
+ except Exception as e:
1077
+ pass
1078
+
1079
+ def switch_ref_time_mode(self):
1080
+ if self.abs_time_checkbox.isChecked():
1081
+ self.frame_slider.setEnabled(True)
1082
+ self.cbs[-1].setEnabled(False)
1083
+ else:
1084
+ self.frame_slider.setEnabled(False)
1085
+ self.cbs[-1].setEnabled(True)
1086
+
1087
+ def switch_ci(self):
1088
+
1089
+ if self.show_ci:
1090
+ self.ci_btn.setIcon(icon(MDI6.arrow_expand_horizontal, color="black"))
1091
+ else:
1092
+ self.ci_btn.setIcon(icon(MDI6.arrow_expand_horizontal, color="blue"))
1093
+ self.show_ci = not self.show_ci
1094
+ self.plot_survivals(0)
1095
+
1096
+ def switch_cell_lines(self):
1097
+
1098
+ if self.show_cell_lines:
1099
+ self.cell_lines_btn.setIcon(icon(MDI6.view_headline, color="black"))
1100
+ else:
1101
+ self.cell_lines_btn.setIcon(icon(MDI6.view_headline, color="blue"))
1102
+ self.show_cell_lines = not self.show_cell_lines
1103
+ self.plot_survivals(0)
1104
+
1105
+ def set_class_to_plot(self):
1106
+
1107
+ if self.all_btn.isChecked():
1108
+ self.target_class = [0, 1]
1109
+ elif self.event_btn.isChecked():
1110
+ self.target_class = [0]
1111
+ else:
1112
+ self.target_class = [1]
1113
+
1114
+ self.plot_survivals(0)