celldetective 1.0.2.post1__py3-none-any.whl → 1.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- celldetective/__main__.py +7 -21
- celldetective/events.py +2 -44
- celldetective/extra_properties.py +62 -52
- celldetective/filters.py +4 -5
- celldetective/gui/__init__.py +1 -1
- celldetective/gui/analyze_block.py +37 -10
- celldetective/gui/btrack_options.py +24 -23
- celldetective/gui/classifier_widget.py +62 -19
- celldetective/gui/configure_new_exp.py +32 -35
- celldetective/gui/control_panel.py +120 -81
- celldetective/gui/gui_utils.py +674 -396
- celldetective/gui/json_readers.py +7 -6
- celldetective/gui/layouts.py +756 -0
- celldetective/gui/measurement_options.py +98 -513
- celldetective/gui/neighborhood_options.py +322 -270
- celldetective/gui/plot_measurements.py +1114 -0
- celldetective/gui/plot_signals_ui.py +21 -20
- celldetective/gui/process_block.py +449 -169
- celldetective/gui/retrain_segmentation_model_options.py +27 -26
- celldetective/gui/retrain_signal_model_options.py +25 -24
- celldetective/gui/seg_model_loader.py +31 -27
- celldetective/gui/signal_annotator.py +2326 -2295
- celldetective/gui/signal_annotator_options.py +18 -16
- celldetective/gui/styles.py +16 -1
- celldetective/gui/survival_ui.py +67 -39
- celldetective/gui/tableUI.py +337 -48
- celldetective/gui/thresholds_gui.py +75 -71
- celldetective/gui/viewers.py +743 -0
- celldetective/io.py +247 -27
- celldetective/measure.py +43 -263
- celldetective/models/segmentation_effectors/primNK_cfse/config_input.json +29 -0
- celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer +0 -0
- celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json +37 -0
- celldetective/neighborhood.py +498 -27
- celldetective/preprocessing.py +1023 -0
- celldetective/scripts/analyze_signals.py +7 -0
- celldetective/scripts/measure_cells.py +12 -0
- celldetective/scripts/segment_cells.py +20 -4
- celldetective/scripts/track_cells.py +11 -0
- celldetective/scripts/train_segmentation_model.py +35 -34
- celldetective/segmentation.py +14 -9
- celldetective/signals.py +234 -329
- celldetective/tracking.py +2 -2
- celldetective/utils.py +602 -49
- celldetective-1.1.1.dist-info/METADATA +305 -0
- celldetective-1.1.1.dist-info/RECORD +84 -0
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.1.dist-info}/top_level.txt +1 -0
- tests/__init__.py +0 -0
- tests/test_events.py +28 -0
- tests/test_filters.py +24 -0
- tests/test_io.py +70 -0
- tests/test_measure.py +141 -0
- tests/test_neighborhood.py +70 -0
- tests/test_preprocessing.py +37 -0
- tests/test_segmentation.py +93 -0
- tests/test_signals.py +135 -0
- tests/test_tracking.py +164 -0
- tests/test_utils.py +118 -0
- celldetective-1.0.2.post1.dist-info/METADATA +0 -221
- celldetective-1.0.2.post1.dist-info/RECORD +0 -66
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.1.dist-info}/LICENSE +0 -0
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.1.dist-info}/WHEEL +0 -0
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from PyQt5.QtWidgets import QWidget, QLineEdit, QMessageBox, QHBoxLayout, QVBoxLayout, QPushButton, QLabel, QComboBox, \
|
|
2
|
-
QCheckBox
|
|
2
|
+
QCheckBox, QRadioButton, QButtonGroup
|
|
3
3
|
from celldetective.gui.gui_utils import FigureCanvas, center_window, color_from_class
|
|
4
4
|
import numpy as np
|
|
5
5
|
import matplotlib.pyplot as plt
|
|
@@ -10,25 +10,27 @@ from PyQt5.QtCore import Qt, QSize
|
|
|
10
10
|
import os
|
|
11
11
|
from sklearn.metrics import r2_score
|
|
12
12
|
from scipy.optimize import curve_fit
|
|
13
|
+
from celldetective.gui import Styles
|
|
14
|
+
from math import ceil
|
|
13
15
|
|
|
14
16
|
def step_function(t, t_shift, dt):
|
|
15
17
|
return 1/(1+np.exp(-(t-t_shift)/dt))
|
|
16
18
|
|
|
17
|
-
class ClassifierWidget(QWidget):
|
|
19
|
+
class ClassifierWidget(QWidget, Styles):
|
|
18
20
|
|
|
19
|
-
def __init__(self,
|
|
21
|
+
def __init__(self, parent_window):
|
|
20
22
|
|
|
21
23
|
super().__init__()
|
|
22
24
|
|
|
23
|
-
self.
|
|
24
|
-
self.screen_height = self.
|
|
25
|
-
self.screen_width = self.
|
|
25
|
+
self.parent_window = parent_window
|
|
26
|
+
self.screen_height = self.parent_window.parent_window.parent_window.screen_height
|
|
27
|
+
self.screen_width = self.parent_window.parent_window.parent_window.screen_width
|
|
26
28
|
self.currentAlpha = 1.0
|
|
27
29
|
|
|
28
30
|
self.setWindowTitle("Custom classification")
|
|
29
31
|
|
|
30
|
-
self.mode = self.
|
|
31
|
-
self.df = self.
|
|
32
|
+
self.mode = self.parent_window.mode
|
|
33
|
+
self.df = self.parent_window.df
|
|
32
34
|
|
|
33
35
|
is_number = np.vectorize(lambda x: np.issubdtype(x, np.number))
|
|
34
36
|
is_number_test = is_number(self.df.dtypes)
|
|
@@ -53,7 +55,7 @@ class ClassifierWidget(QWidget):
|
|
|
53
55
|
fig_btn_hbox = QHBoxLayout()
|
|
54
56
|
fig_btn_hbox.addWidget(QLabel(''), 95)
|
|
55
57
|
self.project_times_btn = QPushButton('')
|
|
56
|
-
self.project_times_btn.setStyleSheet(self.
|
|
58
|
+
self.project_times_btn.setStyleSheet(self.parent_window.parent_window.parent_window.button_select_all)
|
|
57
59
|
self.project_times_btn.setIcon(icon(MDI6.math_integral,color="black"))
|
|
58
60
|
self.project_times_btn.setToolTip("Project measurements at all times.")
|
|
59
61
|
self.project_times_btn.setIconSize(QSize(20, 20))
|
|
@@ -111,7 +113,7 @@ class ClassifierWidget(QWidget):
|
|
|
111
113
|
self.features_cb[i].setCurrentIndex(i)
|
|
112
114
|
|
|
113
115
|
self.log_btns[i].setIcon(icon(MDI6.math_log,color="black"))
|
|
114
|
-
self.log_btns[i].setStyleSheet(self.
|
|
116
|
+
self.log_btns[i].setStyleSheet(self.button_select_all)
|
|
115
117
|
self.log_btns[i].clicked.connect(lambda ch, i=i: self.switch_to_log(i))
|
|
116
118
|
|
|
117
119
|
hbox_classify = QHBoxLayout()
|
|
@@ -124,19 +126,47 @@ class ClassifierWidget(QWidget):
|
|
|
124
126
|
hbox_classify.addWidget(self.submit_query_btn, 20)
|
|
125
127
|
layout.addLayout(hbox_classify)
|
|
126
128
|
|
|
127
|
-
self.time_corr = QCheckBox('Time correlated
|
|
129
|
+
self.time_corr = QCheckBox('Time correlated')
|
|
130
|
+
self.time_corr.toggled.connect(self.activate_time_corr_options)
|
|
128
131
|
if "TRACK_ID" in self.df.columns:
|
|
129
132
|
self.time_corr.setEnabled(True)
|
|
130
133
|
else:
|
|
131
134
|
self.time_corr.setEnabled(False)
|
|
132
135
|
layout.addWidget(self.time_corr,alignment=Qt.AlignCenter)
|
|
136
|
+
|
|
137
|
+
self.irreversible_event_btn = QRadioButton('irreversible event')
|
|
138
|
+
self.unique_state_btn = QRadioButton('unique state')
|
|
139
|
+
self.time_corr_options = [self.irreversible_event_btn, self.unique_state_btn]
|
|
140
|
+
time_corr_btn_group = QButtonGroup()
|
|
141
|
+
self.unique_state_btn.click()
|
|
142
|
+
for btn in self.time_corr_options:
|
|
143
|
+
time_corr_btn_group.addButton(btn)
|
|
144
|
+
btn.setEnabled(False)
|
|
145
|
+
|
|
146
|
+
time_corr_layout = QHBoxLayout()
|
|
147
|
+
time_corr_layout.addWidget(self.unique_state_btn, 50, alignment=Qt.AlignCenter)
|
|
148
|
+
time_corr_layout.addWidget(self.irreversible_event_btn, 50,alignment=Qt.AlignCenter)
|
|
149
|
+
layout.addLayout(time_corr_layout)
|
|
150
|
+
|
|
151
|
+
layout.addWidget(QLabel())
|
|
152
|
+
|
|
133
153
|
self.submit_btn = QPushButton('apply')
|
|
154
|
+
self.submit_btn.setStyleSheet(self.button_style_sheet)
|
|
134
155
|
self.submit_btn.clicked.connect(self.submit_classification)
|
|
135
156
|
layout.addWidget(self.submit_btn, 30)
|
|
136
157
|
|
|
137
158
|
self.frame_slider.valueChanged.connect(self.set_frame)
|
|
138
159
|
self.alpha_slider.valueChanged.connect(self.set_transparency)
|
|
139
160
|
|
|
161
|
+
def activate_time_corr_options(self):
|
|
162
|
+
|
|
163
|
+
if self.time_corr.isChecked():
|
|
164
|
+
for btn in self.time_corr_options:
|
|
165
|
+
btn.setEnabled(True)
|
|
166
|
+
else:
|
|
167
|
+
for btn in self.time_corr_options:
|
|
168
|
+
btn.setEnabled(False)
|
|
169
|
+
|
|
140
170
|
def init_class(self):
|
|
141
171
|
|
|
142
172
|
self.class_name = 'custom'
|
|
@@ -277,14 +307,27 @@ class ClassifierWidget(QWidget):
|
|
|
277
307
|
for tid,track in self.df.groupby(['position','TRACK_ID']):
|
|
278
308
|
indices = track[self.class_name_user].index
|
|
279
309
|
status_values = track[stat_col].to_numpy()
|
|
280
|
-
if
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
310
|
+
if self.irreversible_event_btn.isChecked():
|
|
311
|
+
if np.all([s==0 for s in status_values]):
|
|
312
|
+
self.df.loc[indices, self.class_name_user] = 1
|
|
313
|
+
elif np.all([s==1 for s in status_values]):
|
|
314
|
+
self.df.loc[indices, self.class_name_user] = 2
|
|
315
|
+
self.df.loc[indices, self.class_name_user.replace('class','status')] = 2
|
|
316
|
+
else:
|
|
317
|
+
self.df.loc[indices, self.class_name_user] = 2
|
|
318
|
+
elif self.unique_state_btn.isChecked():
|
|
319
|
+
frames = track['FRAME'].to_numpy()
|
|
320
|
+
t_first = track['t_firstdetection'].to_numpy()[0]
|
|
321
|
+
median_status = np.nanmedian(status_values[frames>=t_first])
|
|
322
|
+
c = ceil(median_status)
|
|
323
|
+
if c==0:
|
|
324
|
+
self.df.loc[indices, self.class_name_user] = 1
|
|
325
|
+
self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
|
|
326
|
+
elif c==1:
|
|
327
|
+
self.df.loc[indices, self.class_name_user] = 2
|
|
328
|
+
self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
|
|
329
|
+
if self.irreversible_event_btn.isChecked():
|
|
330
|
+
self.estimate_time()
|
|
288
331
|
else:
|
|
289
332
|
self.group_name_user = 'group_' + self.name_le.text()
|
|
290
333
|
print(f'User defined characteristic group name: {self.group_name_user}.')
|
|
@@ -12,23 +12,20 @@ from shutil import copyfile
|
|
|
12
12
|
import time
|
|
13
13
|
from functools import partial
|
|
14
14
|
import numpy as np
|
|
15
|
+
from celldetective.gui import Styles
|
|
15
16
|
|
|
16
|
-
class ConfigNewExperiment(QMainWindow):
|
|
17
|
+
class ConfigNewExperiment(QMainWindow, Styles):
|
|
17
18
|
|
|
18
|
-
def __init__(self,
|
|
19
|
+
def __init__(self, parent_window=None):
|
|
19
20
|
|
|
20
21
|
super().__init__()
|
|
21
|
-
self.
|
|
22
|
+
self.parent_window = parent_window
|
|
22
23
|
self.setWindowTitle("New experiment")
|
|
23
24
|
center_window(self)
|
|
24
25
|
self.setFixedWidth(500)
|
|
25
|
-
self.setMaximumHeight(int(0.8*self.
|
|
26
|
+
self.setMaximumHeight(int(0.8*self.parent_window.screen_height))
|
|
26
27
|
self.onlyFloat = QDoubleValidator()
|
|
27
28
|
|
|
28
|
-
self.Styles = Styles()
|
|
29
|
-
self.button_style = self.Styles.button_add
|
|
30
|
-
self.button_style_sheet = self.Styles.button_style_sheet
|
|
31
|
-
|
|
32
29
|
self.newExpFolder = str(QFileDialog.getExistingDirectory(self, 'Select directory'))
|
|
33
30
|
self.populate_widget()
|
|
34
31
|
|
|
@@ -37,25 +34,25 @@ class ConfigNewExperiment(QMainWindow):
|
|
|
37
34
|
# Create button widget and layout
|
|
38
35
|
self.scroll_area = QScrollArea(self)
|
|
39
36
|
button_widget = QWidget()
|
|
40
|
-
grid = QGridLayout()
|
|
41
|
-
button_widget.setLayout(grid)
|
|
37
|
+
self.grid = QGridLayout()
|
|
38
|
+
button_widget.setLayout(self.grid)
|
|
42
39
|
|
|
43
|
-
grid.setContentsMargins(30,30,30,30)
|
|
44
|
-
grid.addWidget(QLabel("Folder:"), 0, 0, 1, 3)
|
|
40
|
+
self.grid.setContentsMargins(30,30,30,30)
|
|
41
|
+
self.grid.addWidget(QLabel("Folder:"), 0, 0, 1, 3)
|
|
45
42
|
self.supFolder = QLineEdit()
|
|
46
43
|
self.supFolder.setAlignment(Qt.AlignLeft)
|
|
47
44
|
self.supFolder.setEnabled(True)
|
|
48
45
|
self.supFolder.setText(self.newExpFolder)
|
|
49
|
-
grid.addWidget(self.supFolder, 1, 0, 1, 1)
|
|
46
|
+
self.grid.addWidget(self.supFolder, 1, 0, 1, 1)
|
|
50
47
|
|
|
51
48
|
self.browse_button = QPushButton("Browse...")
|
|
52
49
|
self.browse_button.clicked.connect(self.browse_experiment_folder)
|
|
53
50
|
self.browse_button.setIcon(icon(MDI6.folder, color="white"))
|
|
54
|
-
self.browse_button.setStyleSheet(self.
|
|
51
|
+
self.browse_button.setStyleSheet(self.button_style_sheet)
|
|
55
52
|
#self.browse_button.setIcon(QIcon_from_svg(abs_path+f"/icons/browse.svg", color='white'))
|
|
56
|
-
grid.addWidget(self.browse_button, 1, 1, 1, 1)
|
|
53
|
+
self.grid.addWidget(self.browse_button, 1, 1, 1, 1)
|
|
57
54
|
|
|
58
|
-
grid.addWidget(QLabel("Experiment name:"), 2, 0, 1, 3)
|
|
55
|
+
self.grid.addWidget(QLabel("Experiment name:"), 2, 0, 1, 3)
|
|
59
56
|
|
|
60
57
|
self.expName = QLineEdit()
|
|
61
58
|
self.expName.setPlaceholderText('folder_name_for_the_experiment')
|
|
@@ -63,20 +60,20 @@ class ConfigNewExperiment(QMainWindow):
|
|
|
63
60
|
self.expName.setEnabled(True)
|
|
64
61
|
self.expName.setFixedWidth(400)
|
|
65
62
|
self.expName.setText("Untitled_Experiment")
|
|
66
|
-
grid.addWidget(self.expName, 3, 0, 1, 3)
|
|
63
|
+
self.grid.addWidget(self.expName, 3, 0, 1, 3)
|
|
67
64
|
|
|
68
65
|
self.generate_movie_settings()
|
|
69
|
-
grid.addLayout(self.ms_grid,29,0,1,3)
|
|
66
|
+
self.grid.addLayout(self.ms_grid,29,0,1,3)
|
|
70
67
|
|
|
71
68
|
self.generate_channel_params_box()
|
|
72
|
-
grid.addLayout(self.channel_grid,30,0,1,3)
|
|
69
|
+
self.grid.addLayout(self.channel_grid,30,0,1,3)
|
|
73
70
|
|
|
74
71
|
self.validate_button = QPushButton("Submit")
|
|
75
72
|
self.validate_button.clicked.connect(self.create_config)
|
|
76
|
-
self.validate_button.setStyleSheet(self.
|
|
73
|
+
self.validate_button.setStyleSheet(self.button_style_sheet)
|
|
77
74
|
#self.validate_button.setIcon(QIcon_from_svg(abs_path+f"/icons/process.svg", color='white'))
|
|
78
75
|
|
|
79
|
-
grid.addWidget(self.validate_button, 31, 0, 1, 3, alignment = Qt.AlignBottom)
|
|
76
|
+
self.grid.addWidget(self.validate_button, 31, 0, 1, 3, alignment = Qt.AlignBottom)
|
|
80
77
|
button_widget.adjustSize()
|
|
81
78
|
|
|
82
79
|
self.scroll_area.setAlignment(Qt.AlignCenter)
|
|
@@ -233,9 +230,9 @@ class ConfigNewExperiment(QMainWindow):
|
|
|
233
230
|
|
|
234
231
|
# Add channel button
|
|
235
232
|
self.addChannelBtn = QPushButton('Add channel')
|
|
236
|
-
self.addChannelBtn.setIcon(icon(MDI6.plus,color="
|
|
233
|
+
self.addChannelBtn.setIcon(icon(MDI6.plus,color="white"))
|
|
237
234
|
self.addChannelBtn.setIconSize(QSize(25, 25))
|
|
238
|
-
self.addChannelBtn.setStyleSheet(self.
|
|
235
|
+
self.addChannelBtn.setStyleSheet(self.button_style_sheet)
|
|
239
236
|
self.addChannelBtn.clicked.connect(self.add_custom_channel)
|
|
240
237
|
self.channel_grid.addWidget(self.addChannelBtn, 1000, 0, 1, 1)
|
|
241
238
|
|
|
@@ -427,14 +424,14 @@ class ConfigNewExperiment(QMainWindow):
|
|
|
427
424
|
with open('config.ini', 'w') as configfile:
|
|
428
425
|
config.write(configfile)
|
|
429
426
|
|
|
430
|
-
self.
|
|
427
|
+
self.parent_window.set_experiment_path(self.directory)
|
|
431
428
|
print(f'New experiment successfully configured in folder {self.directory}...')
|
|
432
429
|
self.close()
|
|
433
430
|
|
|
434
|
-
class SetupConditionLabels(QWidget):
|
|
435
|
-
def __init__(self,
|
|
431
|
+
class SetupConditionLabels(QWidget, Styles):
|
|
432
|
+
def __init__(self, parent_window, n_wells):
|
|
436
433
|
super().__init__()
|
|
437
|
-
self.
|
|
434
|
+
self.parent_window = parent_window
|
|
438
435
|
self.n_wells = n_wells
|
|
439
436
|
self.setWindowTitle("Well conditions")
|
|
440
437
|
self.layout = QVBoxLayout()
|
|
@@ -477,12 +474,12 @@ class SetupConditionLabels(QWidget):
|
|
|
477
474
|
btn_hbox = QHBoxLayout()
|
|
478
475
|
btn_hbox.setContentsMargins(0,20,0,0)
|
|
479
476
|
self.skip_btn = QPushButton('Skip')
|
|
480
|
-
self.skip_btn.setStyleSheet(self.
|
|
477
|
+
self.skip_btn.setStyleSheet(self.button_style_sheet_2)
|
|
481
478
|
self.skip_btn.clicked.connect(self.set_default_values)
|
|
482
479
|
btn_hbox.addWidget(self.skip_btn)
|
|
483
480
|
|
|
484
481
|
self.submit_btn = QPushButton('Submit')
|
|
485
|
-
self.submit_btn.setStyleSheet(self.
|
|
482
|
+
self.submit_btn.setStyleSheet(self.button_style_sheet)
|
|
486
483
|
self.submit_btn.clicked.connect(self.set_user_values)
|
|
487
484
|
btn_hbox.addWidget(self.submit_btn)
|
|
488
485
|
self.layout.addLayout(btn_hbox)
|
|
@@ -495,7 +492,7 @@ class SetupConditionLabels(QWidget):
|
|
|
495
492
|
self.concentrations_cbs[i].setText(str(i))
|
|
496
493
|
self.pharmaceutical_agents_cbs[i].setText(str(i))
|
|
497
494
|
self.set_attributes()
|
|
498
|
-
self.
|
|
495
|
+
self.parent_window.create_config_file()
|
|
499
496
|
self.close()
|
|
500
497
|
|
|
501
498
|
def set_user_values(self):
|
|
@@ -509,22 +506,22 @@ class SetupConditionLabels(QWidget):
|
|
|
509
506
|
if self.pharmaceutical_agents_cbs[i].text()=='':
|
|
510
507
|
self.pharmaceutical_agents_cbs[i].setText(str(i))
|
|
511
508
|
self.set_attributes()
|
|
512
|
-
self.
|
|
509
|
+
self.parent_window.create_config_file()
|
|
513
510
|
self.close()
|
|
514
511
|
|
|
515
512
|
def set_attributes(self):
|
|
516
513
|
|
|
517
514
|
cell_type_text = [c.text() for c in self.cell_type_cbs]
|
|
518
|
-
self.
|
|
515
|
+
self.parent_window.cell_types = ','.join(cell_type_text)
|
|
519
516
|
|
|
520
517
|
antibodies_text = [c.text() for c in self.antibodies_cbs]
|
|
521
|
-
self.
|
|
518
|
+
self.parent_window.antibodies = ','.join(antibodies_text)
|
|
522
519
|
|
|
523
520
|
concentrations_text = [c.text() for c in self.concentrations_cbs]
|
|
524
|
-
self.
|
|
521
|
+
self.parent_window.concentrations = ','.join(concentrations_text)
|
|
525
522
|
|
|
526
523
|
pharamaceutical_text = [c.text() for c in self.pharmaceutical_agents_cbs]
|
|
527
|
-
self.
|
|
524
|
+
self.parent_window.pharmaceutical_agents = ','.join(pharamaceutical_text)
|
|
528
525
|
|
|
529
526
|
|
|
530
527
|
|
|
@@ -4,7 +4,7 @@ from PyQt5.QtCore import Qt, QSize
|
|
|
4
4
|
from PyQt5.QtGui import QIcon
|
|
5
5
|
from celldetective.gui.gui_utils import center_window, QHSeperationLine
|
|
6
6
|
from celldetective.utils import _extract_labels_from_config, ConfigSectionMap, extract_experiment_channels
|
|
7
|
-
from celldetective.gui import ConfigEditor, ProcessPanel, AnalysisPanel, NeighPanel
|
|
7
|
+
from celldetective.gui import ConfigEditor, ProcessPanel, PreprocessingPanel, AnalysisPanel, NeighPanel
|
|
8
8
|
from natsort import natsorted
|
|
9
9
|
from glob import glob
|
|
10
10
|
import os
|
|
@@ -13,18 +13,22 @@ from superqt.fonticon import icon
|
|
|
13
13
|
from fonticon_mdi6 import MDI6
|
|
14
14
|
import gc
|
|
15
15
|
import subprocess
|
|
16
|
+
from celldetective.gui.viewers import StackVisualizer
|
|
17
|
+
from celldetective.utils import extract_experiment_channels
|
|
18
|
+
from celldetective.gui import Styles
|
|
16
19
|
|
|
17
|
-
class ControlPanel(QMainWindow):
|
|
20
|
+
class ControlPanel(QMainWindow, Styles):
|
|
18
21
|
|
|
19
|
-
def __init__(self,
|
|
22
|
+
def __init__(self, parent_window=None, exp_dir=""):
|
|
20
23
|
|
|
21
24
|
super().__init__()
|
|
25
|
+
|
|
22
26
|
self.exp_dir = exp_dir
|
|
23
27
|
if not self.exp_dir.endswith(os.sep):
|
|
24
28
|
self.exp_dir = self.exp_dir+os.sep
|
|
25
29
|
self.setWindowTitle("celldetective")
|
|
26
30
|
self.setWindowIcon(QIcon(os.sep.join(['celldetective','icons','logo.png'])))
|
|
27
|
-
self.
|
|
31
|
+
self.parent_window = parent_window
|
|
28
32
|
center_window(self)
|
|
29
33
|
|
|
30
34
|
self.init_wells_and_positions()
|
|
@@ -33,24 +37,25 @@ class ControlPanel(QMainWindow):
|
|
|
33
37
|
self.w = QWidget()
|
|
34
38
|
self.grid = QGridLayout(self.w)
|
|
35
39
|
self.grid.setSpacing(5)
|
|
36
|
-
self.grid.setContentsMargins(
|
|
40
|
+
self.grid.setContentsMargins(10,10,10,10) #left top right bottom
|
|
37
41
|
|
|
38
42
|
self.to_disable = []
|
|
39
43
|
self.generate_header()
|
|
40
44
|
self.ProcessEffectors = ProcessPanel(self,'effectors')
|
|
41
45
|
self.ProcessTargets = ProcessPanel(self,'targets')
|
|
42
46
|
self.NeighPanel = NeighPanel(self)
|
|
47
|
+
self.PreprocessingPanel = PreprocessingPanel(self)
|
|
43
48
|
|
|
44
49
|
ProcessFrame = QFrame()
|
|
45
50
|
grid_process = QVBoxLayout(ProcessFrame)
|
|
46
|
-
grid_process.setContentsMargins(
|
|
51
|
+
grid_process.setContentsMargins(15,30,15,15)
|
|
47
52
|
|
|
48
53
|
AnalyzeFrame = QFrame()
|
|
49
54
|
grid_analyze = QVBoxLayout(AnalyzeFrame)
|
|
50
|
-
grid_analyze.setContentsMargins(
|
|
55
|
+
grid_analyze.setContentsMargins(15,30,15,15)
|
|
51
56
|
self.SurvivalBlock = AnalysisPanel(self,title='Survival')
|
|
52
57
|
|
|
53
|
-
|
|
58
|
+
grid_process.addWidget(self.PreprocessingPanel)
|
|
54
59
|
grid_process.addWidget(self.ProcessEffectors)
|
|
55
60
|
grid_process.addWidget(self.ProcessTargets)
|
|
56
61
|
grid_process.addWidget(self.NeighPanel)
|
|
@@ -61,7 +66,7 @@ class ControlPanel(QMainWindow):
|
|
|
61
66
|
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
|
62
67
|
self.scroll.setWidgetResizable(True)
|
|
63
68
|
desktop = QDesktopWidget()
|
|
64
|
-
self.scroll.setMinimumHeight(
|
|
69
|
+
self.scroll.setMinimumHeight(550)
|
|
65
70
|
#self.scroll.setMinimumHeight(int(0.4*screen_height))
|
|
66
71
|
|
|
67
72
|
tabWidget = QTabWidget()
|
|
@@ -70,7 +75,7 @@ class ControlPanel(QMainWindow):
|
|
|
70
75
|
|
|
71
76
|
tab_index_analyze = tabWidget.addTab(AnalyzeFrame, "Analyze")
|
|
72
77
|
tabWidget.setTabIcon(tab_index_analyze, icon(MDI6.poll, color='black'))
|
|
73
|
-
tabWidget.setStyleSheet(self.
|
|
78
|
+
tabWidget.setStyleSheet(self.qtab_style)
|
|
74
79
|
|
|
75
80
|
self.grid.addWidget(tabWidget, 7,0,1,3, alignment=Qt.AlignTop)
|
|
76
81
|
self.grid.setSpacing(5)
|
|
@@ -84,7 +89,7 @@ class ControlPanel(QMainWindow):
|
|
|
84
89
|
self.initial_width = self.size().width()
|
|
85
90
|
self.screen_height = desktop.screenGeometry().height()
|
|
86
91
|
self.screen_width = desktop.screenGeometry().width()
|
|
87
|
-
self.scroll.setMinimumWidth(
|
|
92
|
+
self.scroll.setMinimumWidth(425)
|
|
88
93
|
|
|
89
94
|
def init_wells_and_positions(self):
|
|
90
95
|
|
|
@@ -122,7 +127,7 @@ class ControlPanel(QMainWindow):
|
|
|
122
127
|
self.folder_exp_btn.setIconSize(QSize(20, 20))
|
|
123
128
|
self.folder_exp_btn.setToolTip("Experiment folder")
|
|
124
129
|
self.folder_exp_btn.clicked.connect(self.open_experiment_folder)
|
|
125
|
-
self.folder_exp_btn.setStyleSheet(self.
|
|
130
|
+
self.folder_exp_btn.setStyleSheet(self.button_select_all)
|
|
126
131
|
|
|
127
132
|
|
|
128
133
|
self.edit_config_button = QPushButton()
|
|
@@ -130,7 +135,7 @@ class ControlPanel(QMainWindow):
|
|
|
130
135
|
self.edit_config_button.setIconSize(QSize(20, 20))
|
|
131
136
|
self.edit_config_button.setToolTip("Configuration file")
|
|
132
137
|
self.edit_config_button.clicked.connect(self.open_config_editor)
|
|
133
|
-
self.edit_config_button.setStyleSheet(self.
|
|
138
|
+
self.edit_config_button.setStyleSheet(self.button_select_all)
|
|
134
139
|
|
|
135
140
|
self.exp_options_layout = QHBoxLayout()
|
|
136
141
|
self.exp_options_layout.addWidget(experiment_label, 32, alignment=Qt.AlignRight)
|
|
@@ -139,8 +144,9 @@ class ControlPanel(QMainWindow):
|
|
|
139
144
|
self.exp_options_layout.addWidget(self.edit_config_button, 5, alignment=Qt.AlignRight)
|
|
140
145
|
self.grid.addLayout(self.exp_options_layout, 0,0,1,3)
|
|
141
146
|
|
|
147
|
+
well_layout = QHBoxLayout()
|
|
142
148
|
self.well_list = QComboBox()
|
|
143
|
-
thresh =
|
|
149
|
+
thresh = 32
|
|
144
150
|
self.well_truncated = [w[:thresh - 3]+'...' if len(w)>thresh else w for w in self.well_labels]
|
|
145
151
|
self.well_list.addItems(self.well_truncated) #self.well_labels
|
|
146
152
|
for i in range(len(self.well_labels)):
|
|
@@ -149,6 +155,7 @@ class ControlPanel(QMainWindow):
|
|
|
149
155
|
self.well_list.activated.connect(self.display_positions)
|
|
150
156
|
self.to_disable.append(self.well_list)
|
|
151
157
|
|
|
158
|
+
position_layout = QHBoxLayout()
|
|
152
159
|
self.position_list = QComboBox()
|
|
153
160
|
self.position_list.addItems(["*"])
|
|
154
161
|
self.position_list.addItems(self.positions[0])
|
|
@@ -156,16 +163,73 @@ class ControlPanel(QMainWindow):
|
|
|
156
163
|
self.to_disable.append(self.position_list)
|
|
157
164
|
#self.locate_selected_position()
|
|
158
165
|
|
|
159
|
-
self.
|
|
160
|
-
self.
|
|
161
|
-
|
|
162
|
-
self.
|
|
163
|
-
self.
|
|
166
|
+
self.view_stack_btn = QPushButton()
|
|
167
|
+
self.view_stack_btn.setStyleSheet(self.button_select_all)
|
|
168
|
+
self.view_stack_btn.setIcon(icon(MDI6.image_check, color="black"))
|
|
169
|
+
self.view_stack_btn.setToolTip("View stack.")
|
|
170
|
+
self.view_stack_btn.setIconSize(QSize(20, 20))
|
|
171
|
+
self.view_stack_btn.clicked.connect(self.view_current_stack)
|
|
172
|
+
self.view_stack_btn.setEnabled(False)
|
|
173
|
+
|
|
174
|
+
well_layout.setContentsMargins(0,0,0,0)
|
|
175
|
+
well_lbl = QLabel('Well: ')
|
|
176
|
+
well_lbl.setAlignment(Qt.AlignRight)
|
|
177
|
+
well_layout.addWidget(well_lbl, 32)
|
|
178
|
+
well_layout.addWidget(self.well_list, 68)
|
|
179
|
+
self.grid.addLayout(well_layout, 1, 0, 1, 3)
|
|
180
|
+
|
|
181
|
+
position_layout.setContentsMargins(0,0,0,0)
|
|
182
|
+
pos_lbl = QLabel('Position: ')
|
|
183
|
+
pos_lbl.setAlignment(Qt.AlignRight)
|
|
184
|
+
position_layout.addWidget(pos_lbl, 32)
|
|
185
|
+
|
|
186
|
+
subposition_layout = QHBoxLayout()
|
|
187
|
+
subposition_layout.addWidget(self.position_list, 95)
|
|
188
|
+
subposition_layout.addWidget(self.view_stack_btn, 5)
|
|
189
|
+
position_layout.addLayout(subposition_layout, 68)
|
|
190
|
+
self.grid.addLayout(position_layout, 2, 0, 1, 3)
|
|
164
191
|
|
|
165
192
|
|
|
166
193
|
hsep = QHSeperationLine()
|
|
167
194
|
self.grid.addWidget(hsep, 5, 0, 1, 3)
|
|
168
195
|
|
|
196
|
+
def locate_image(self):
|
|
197
|
+
|
|
198
|
+
"""
|
|
199
|
+
Load the first frame of the first movie found in the experiment folder as a sample.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
movies = glob(self.pos + os.sep.join(['movie', f"{self.movie_prefix}*.tif"]))
|
|
203
|
+
|
|
204
|
+
if len(movies) == 0:
|
|
205
|
+
msgBox = QMessageBox()
|
|
206
|
+
msgBox.setIcon(QMessageBox.Warning)
|
|
207
|
+
msgBox.setText("Please select a position containing a movie...")
|
|
208
|
+
msgBox.setWindowTitle("Warning")
|
|
209
|
+
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
210
|
+
returnValue = msgBox.exec()
|
|
211
|
+
if returnValue == QMessageBox.Ok:
|
|
212
|
+
self.current_stack = None
|
|
213
|
+
return None
|
|
214
|
+
else:
|
|
215
|
+
self.current_stack = movies[0]
|
|
216
|
+
|
|
217
|
+
def view_current_stack(self):
|
|
218
|
+
|
|
219
|
+
self.locate_image()
|
|
220
|
+
if self.current_stack is not None:
|
|
221
|
+
self.viewer = StackVisualizer(
|
|
222
|
+
stack_path=self.current_stack,
|
|
223
|
+
window_title=f'Position {self.position_list.currentText()}',
|
|
224
|
+
frame_slider = True,
|
|
225
|
+
contrast_slider = True,
|
|
226
|
+
channel_cb = True,
|
|
227
|
+
channel_names = self.exp_channels,
|
|
228
|
+
n_channels = self.nbr_channels,
|
|
229
|
+
PxToUm = self.PxToUm,
|
|
230
|
+
)
|
|
231
|
+
self.viewer.show()
|
|
232
|
+
|
|
169
233
|
def open_experiment_folder(self):
|
|
170
234
|
|
|
171
235
|
try:
|
|
@@ -223,79 +287,50 @@ class ControlPanel(QMainWindow):
|
|
|
223
287
|
print("Warning... pharmaceutical agents not found...")
|
|
224
288
|
self.pharmaceutical_agents = [str(s) for s in np.linspace(0,number_of_wells-1,number_of_wells)]
|
|
225
289
|
|
|
226
|
-
|
|
227
290
|
def closeEvent(self, event):
|
|
228
291
|
|
|
229
292
|
"""
|
|
230
293
|
Close child windows if closed.
|
|
231
294
|
"""
|
|
232
295
|
|
|
296
|
+
for process_block in [self.ProcessTargets, self.ProcessEffectors]:
|
|
297
|
+
try:
|
|
298
|
+
if process_block.SegModelLoader:
|
|
299
|
+
process_block.SegModelLoader.close()
|
|
300
|
+
except:
|
|
301
|
+
pass
|
|
302
|
+
try:
|
|
303
|
+
if process_block.ConfigTracking:
|
|
304
|
+
process_block.ConfigTracking.close()
|
|
305
|
+
except:
|
|
306
|
+
pass
|
|
307
|
+
try:
|
|
308
|
+
if process_block.ConfigSignalTrain:
|
|
309
|
+
process_block.ConfigSignalTrain.close()
|
|
310
|
+
except:
|
|
311
|
+
pass
|
|
312
|
+
try:
|
|
313
|
+
if process_block.ConfigMeasurements:
|
|
314
|
+
process_block.ConfigMeasurements.close()
|
|
315
|
+
except:
|
|
316
|
+
pass
|
|
317
|
+
try:
|
|
318
|
+
if process_block.ConfigSignalAnnotator:
|
|
319
|
+
process_block.ConfigSignalAnnotator.close()
|
|
320
|
+
except:
|
|
321
|
+
pass
|
|
322
|
+
try:
|
|
323
|
+
if process_block.tab_ui:
|
|
324
|
+
process_block.tab_ui.close()
|
|
325
|
+
except:
|
|
326
|
+
pass
|
|
327
|
+
|
|
233
328
|
try:
|
|
234
329
|
if self.cfg_editor:
|
|
235
330
|
self.cfg_editor.close()
|
|
236
331
|
except:
|
|
237
332
|
pass
|
|
238
333
|
|
|
239
|
-
try:
|
|
240
|
-
if self.ProcessTargets.SegModelLoader:
|
|
241
|
-
self.ProcessTargets.SegModelLoader.close()
|
|
242
|
-
except:
|
|
243
|
-
pass
|
|
244
|
-
|
|
245
|
-
try:
|
|
246
|
-
if self.ProcessEffectors.SegModelLoader:
|
|
247
|
-
self.ProcessEffectors.SegModelLoader.close()
|
|
248
|
-
except:
|
|
249
|
-
pass
|
|
250
|
-
|
|
251
|
-
try:
|
|
252
|
-
if self.ProcessTargets.ConfigTracking:
|
|
253
|
-
self.ProcessTargets.ConfigTracking.close()
|
|
254
|
-
except:
|
|
255
|
-
pass
|
|
256
|
-
|
|
257
|
-
try:
|
|
258
|
-
if self.ProcessEffectors.ConfigTracking:
|
|
259
|
-
self.ProcessEffectors.ConfigTracking.close()
|
|
260
|
-
except:
|
|
261
|
-
pass
|
|
262
|
-
|
|
263
|
-
try:
|
|
264
|
-
if self.ProcessTargets.ConfigSignalTrain:
|
|
265
|
-
self.ProcessTargets.ConfigSignalTrain.close()
|
|
266
|
-
except:
|
|
267
|
-
pass
|
|
268
|
-
|
|
269
|
-
try:
|
|
270
|
-
if self.ProcessEffectors.ConfigSignalTrain:
|
|
271
|
-
self.ProcessEffectors.ConfigSignalTrain.close()
|
|
272
|
-
except:
|
|
273
|
-
pass
|
|
274
|
-
|
|
275
|
-
try:
|
|
276
|
-
if self.ProcessTargets.ConfigMeasurements:
|
|
277
|
-
self.ProcessTargets.ConfigMeasurements.close()
|
|
278
|
-
except:
|
|
279
|
-
pass
|
|
280
|
-
|
|
281
|
-
try:
|
|
282
|
-
if self.ProcessEffectors.ConfigMeasurements:
|
|
283
|
-
self.ProcessEffectors.ConfigMeasurements.close()
|
|
284
|
-
except:
|
|
285
|
-
pass
|
|
286
|
-
|
|
287
|
-
try:
|
|
288
|
-
if self.ProcessTargets.ConfigSignalAnnotator:
|
|
289
|
-
self.ProcessTargets.ConfigSignalAnnotator.close()
|
|
290
|
-
except:
|
|
291
|
-
pass
|
|
292
|
-
|
|
293
|
-
try:
|
|
294
|
-
if self.ProcessEffectors.ConfigSignalAnnotator:
|
|
295
|
-
self.ProcessEffectors.ConfigSignalAnnotator.close()
|
|
296
|
-
except:
|
|
297
|
-
pass
|
|
298
|
-
|
|
299
334
|
gc.collect()
|
|
300
335
|
|
|
301
336
|
|
|
@@ -316,6 +351,7 @@ class ControlPanel(QMainWindow):
|
|
|
316
351
|
self.position_list.clear()
|
|
317
352
|
self.position_list.addItems(["*"])
|
|
318
353
|
self.position_list.addItems(self.positions[pos_index])
|
|
354
|
+
self.update_position_options()
|
|
319
355
|
|
|
320
356
|
def open_config_editor(self):
|
|
321
357
|
self.cfg_editor = ConfigEditor(self)
|
|
@@ -392,12 +428,15 @@ class ControlPanel(QMainWindow):
|
|
|
392
428
|
#self.ProcessEffectors.signal_analysis_action.setEnabled(False)
|
|
393
429
|
self.ProcessTargets.check_signals_btn.setEnabled(False)
|
|
394
430
|
self.ProcessEffectors.check_signals_btn.setEnabled(False)
|
|
431
|
+
self.view_stack_btn.setEnabled(False)
|
|
395
432
|
elif self.well_list.currentText()=='*':
|
|
396
433
|
self.ProcessTargets.view_tab_btn.setEnabled(True)
|
|
397
|
-
self.ProcessEffectors.view_tab_btn.setEnabled(True)
|
|
434
|
+
self.ProcessEffectors.view_tab_btn.setEnabled(True)
|
|
435
|
+
self.view_stack_btn.setEnabled(False)
|
|
398
436
|
else:
|
|
399
437
|
if not self.well_list.currentText()=="*":
|
|
400
438
|
self.locate_selected_position()
|
|
439
|
+
self.view_stack_btn.setEnabled(True)
|
|
401
440
|
# if os.path.exists(os.sep.join([self.pos,'labels_effectors', os.sep])):
|
|
402
441
|
self.ProcessEffectors.check_seg_btn.setEnabled(True)
|
|
403
442
|
# if os.path.exists(os.sep.join([self.pos,'labels_targets', os.sep])):
|