celldetective 1.4.0__py3-none-any.whl → 1.4.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/_version.py +1 -1
- celldetective/exceptions.py +11 -0
- celldetective/filters.py +7 -1
- celldetective/gui/InitWindow.py +4 -1
- celldetective/gui/__init__.py +2 -9
- celldetective/gui/about.py +2 -2
- celldetective/gui/base_annotator.py +786 -0
- celldetective/gui/classifier_widget.py +18 -13
- celldetective/gui/configure_new_exp.py +51 -30
- celldetective/gui/control_panel.py +10 -7
- celldetective/gui/{signal_annotator.py → event_annotator.py} +473 -1437
- celldetective/gui/generic_signal_plot.py +2 -1
- celldetective/gui/gui_utils.py +5 -2
- celldetective/gui/help/neighborhood.json +2 -2
- celldetective/gui/layouts.py +21 -11
- celldetective/gui/{signal_annotator2.py → pair_event_annotator.py} +3 -1
- celldetective/gui/process_block.py +129 -91
- celldetective/gui/processes/downloader.py +37 -34
- celldetective/gui/processes/measure_cells.py +14 -8
- celldetective/gui/processes/segment_cells.py +21 -6
- celldetective/gui/processes/track_cells.py +12 -13
- celldetective/gui/settings/__init__.py +7 -0
- celldetective/gui/settings/_settings_base.py +70 -0
- celldetective/gui/{retrain_signal_model_options.py → settings/_settings_event_model_training.py} +35 -91
- celldetective/gui/{measurement_options.py → settings/_settings_measurements.py} +28 -81
- celldetective/gui/{neighborhood_options.py → settings/_settings_neighborhood.py} +1 -1
- celldetective/gui/settings/_settings_segmentation.py +49 -0
- celldetective/gui/{retrain_segmentation_model_options.py → settings/_settings_segmentation_model_training.py} +33 -79
- celldetective/gui/{signal_annotator_options.py → settings/_settings_signal_annotator.py} +73 -95
- celldetective/gui/{btrack_options.py → settings/_settings_tracking.py} +64 -87
- celldetective/gui/styles.py +2 -1
- celldetective/gui/survival_ui.py +1 -1
- celldetective/gui/tableUI.py +25 -0
- celldetective/gui/table_ops/__init__.py +0 -0
- celldetective/gui/table_ops/merge_groups.py +118 -0
- celldetective/gui/viewers.py +3 -5
- celldetective/gui/workers.py +0 -2
- celldetective/io.py +98 -55
- celldetective/links/zenodo.json +145 -144
- celldetective/measure.py +31 -26
- celldetective/preprocessing.py +34 -21
- celldetective/regionprops/_regionprops.py +16 -5
- celldetective/scripts/measure_cells.py +5 -5
- celldetective/scripts/measure_relative.py +16 -11
- celldetective/scripts/segment_cells.py +4 -4
- celldetective/scripts/segment_cells_thresholds.py +3 -3
- celldetective/scripts/track_cells.py +7 -7
- celldetective/scripts/train_segmentation_model.py +10 -1
- celldetective/tracking.py +10 -4
- celldetective/utils.py +59 -58
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/METADATA +1 -1
- celldetective-1.4.1.dist-info/RECORD +123 -0
- tests/gui/__init__.py +0 -0
- tests/gui/test_new_project.py +228 -0
- tests/{test_qt.py → gui/test_project.py} +22 -26
- tests/test_preprocessing.py +2 -2
- celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -79
- celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
- celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -37
- celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -39
- celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
- celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
- celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
- celldetective/models/signal_detection/NucCond/config_input.json +0 -1
- celldetective/models/signal_detection/NucCond/log_classifier.csv +0 -126
- celldetective/models/signal_detection/NucCond/log_regressor.csv +0 -282
- celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
- celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
- celldetective/models/signal_detection/NucCond/scores.npy +0 -0
- celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
- celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
- celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
- celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
- celldetective-1.4.0.dist-info/RECORD +0 -131
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/WHEEL +0 -0
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/licenses/LICENSE +0 -0
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Copyright © 2023 Laboratoire Adhesion et Inflammation, Authored by Remy Torro.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from PyQt5.QtWidgets import QComboBox, QLabel, QRadioButton, QLineEdit, QApplication, QPushButton, QScrollArea, QVBoxLayout, QHBoxLayout
|
|
@@ -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
|
|
15
|
+
from celldetective.gui.settings._settings_base import CelldetectiveSettingsPanel
|
|
16
16
|
|
|
17
|
-
class
|
|
17
|
+
class SettingsSignalAnnotator(CelldetectiveSettingsPanel):
|
|
18
18
|
|
|
19
19
|
"""
|
|
20
20
|
UI to set normalization and animation parameters for the annotator tool.
|
|
@@ -23,65 +23,94 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
|
|
|
23
23
|
|
|
24
24
|
def __init__(self, parent_window=None):
|
|
25
25
|
|
|
26
|
-
super().__init__()
|
|
27
26
|
self.parent_window = parent_window
|
|
28
|
-
self.setWindowTitle("Configure signal annotator")
|
|
29
27
|
self.mode = self.parent_window.mode
|
|
30
28
|
self.exp_dir = self.parent_window.exp_dir
|
|
31
|
-
self.soft_path = get_software_location()
|
|
32
29
|
|
|
33
30
|
self.instructions_path = self.parent_window.exp_dir + f"configs/signal_annotator_config_{self.mode}.json"
|
|
34
31
|
if self.mode == "pairs":
|
|
35
32
|
self.instructions_path = self.parent_window.exp_dir + "configs/signal_annotator_config_neighborhood.json"
|
|
36
33
|
|
|
37
|
-
exp_config = self.exp_dir +"config.ini"
|
|
38
|
-
#self.config_path = self.exp_dir + self.config_name
|
|
39
34
|
self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
|
|
40
35
|
self.channel_names = np.array(self.channel_names)
|
|
41
36
|
self.channels = np.array(self.channels)
|
|
42
37
|
self.log_option = False
|
|
38
|
+
|
|
39
|
+
super().__init__(title="Configure signal annotator")
|
|
43
40
|
|
|
44
|
-
self.
|
|
45
|
-
|
|
41
|
+
self._add_to_layout()
|
|
42
|
+
self._load_previous_instructions()
|
|
43
|
+
|
|
44
|
+
self._adjustSize()
|
|
45
|
+
self.resize(int(self.width()), int(self._screen_height * 0.55))
|
|
46
|
+
|
|
47
|
+
def _add_to_layout(self):
|
|
48
|
+
|
|
49
|
+
sub_layout = QVBoxLayout()
|
|
50
|
+
sub_layout.setContentsMargins(10,10,10,20)
|
|
51
|
+
sub_layout.setContentsMargins(30,30,30,30)
|
|
52
|
+
sub_layout.addWidget(self._modality_lbl)
|
|
53
|
+
|
|
54
|
+
# Create radio buttons
|
|
55
|
+
option_layout = QHBoxLayout()
|
|
56
|
+
option_layout.addWidget(self.gs_btn, alignment=Qt.AlignCenter)
|
|
57
|
+
option_layout.addWidget(self.rgb_btn, alignment=Qt.AlignCenter)
|
|
58
|
+
sub_layout.addLayout(option_layout)
|
|
59
|
+
|
|
60
|
+
btn_hbox = QHBoxLayout()
|
|
61
|
+
btn_hbox.addWidget(QLabel(''), 90)
|
|
62
|
+
btn_hbox.addWidget(self.log_btn, 5,alignment=Qt.AlignRight)
|
|
63
|
+
btn_hbox.addWidget(self.percentile_btn, 5,alignment=Qt.AlignRight)
|
|
64
|
+
sub_layout.addLayout(btn_hbox)
|
|
46
65
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
66
|
+
for i in range(3):
|
|
67
|
+
hlayout = QHBoxLayout()
|
|
68
|
+
hlayout.addWidget(self.channel_cbs_lbls[i], 20)
|
|
69
|
+
hlayout.addWidget(self.channel_cbs[i], 80)
|
|
70
|
+
sub_layout.addLayout(hlayout)
|
|
51
71
|
|
|
72
|
+
hlayout2 = QHBoxLayout()
|
|
73
|
+
hlayout2.addWidget(self.min_val_lbls[i], 20)
|
|
74
|
+
hlayout2.addWidget(self.min_val_les[i], 80)
|
|
75
|
+
sub_layout.addLayout(hlayout2)
|
|
76
|
+
|
|
77
|
+
hlayout3 = QHBoxLayout()
|
|
78
|
+
hlayout3.addWidget(self.max_val_lbls[i], 20)
|
|
79
|
+
hlayout3.addWidget(self.max_val_les[i], 80)
|
|
80
|
+
sub_layout.addLayout(hlayout3)
|
|
52
81
|
|
|
53
|
-
|
|
82
|
+
sub_layout.addWidget(self.hsep)
|
|
83
|
+
hbox_frac = QHBoxLayout()
|
|
84
|
+
hbox_frac.addWidget(self._fraction_lbl, 20)
|
|
85
|
+
hbox_frac.addWidget(self.fraction_slider, 80)
|
|
86
|
+
sub_layout.addLayout(hbox_frac)
|
|
87
|
+
|
|
88
|
+
hbox_interval = QHBoxLayout()
|
|
89
|
+
hbox_interval.addWidget(self._interval_lbl, 20)
|
|
90
|
+
hbox_interval.addWidget(self.interval_slider, 80)
|
|
91
|
+
sub_layout.addLayout(hbox_interval)
|
|
92
|
+
|
|
93
|
+
self._layout.addLayout(sub_layout)
|
|
94
|
+
|
|
95
|
+
self._layout.addWidget(self.submit_btn)
|
|
96
|
+
|
|
97
|
+
def _create_widgets(self):
|
|
54
98
|
|
|
55
99
|
"""
|
|
56
100
|
Create the widgets.
|
|
57
101
|
|
|
58
102
|
"""
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
self.
|
|
62
|
-
|
|
63
|
-
self.
|
|
64
|
-
self.main_layout.setContentsMargins(30,30,30,30)
|
|
65
|
-
|
|
66
|
-
sub_layout = QVBoxLayout()
|
|
67
|
-
sub_layout.setContentsMargins(10,10,10,20)
|
|
68
|
-
|
|
69
|
-
self.button_widget.setLayout(self.main_layout)
|
|
70
|
-
sub_layout.setContentsMargins(30,30,30,30)
|
|
71
|
-
|
|
72
|
-
sub_layout.addWidget(QLabel('Modality: '))
|
|
103
|
+
super()._create_widgets()
|
|
104
|
+
|
|
105
|
+
self._modality_lbl = QLabel("Modality: ")
|
|
106
|
+
self._fraction_lbl = QLabel("fraction: ")
|
|
107
|
+
self._interval_lbl = QLabel('interval [ms]: ')
|
|
73
108
|
|
|
74
|
-
# Create radio buttons
|
|
75
|
-
option_layout = QHBoxLayout()
|
|
76
109
|
self.gs_btn = QRadioButton('grayscale')
|
|
77
110
|
self.gs_btn.setChecked(True)
|
|
78
|
-
|
|
79
|
-
|
|
111
|
+
|
|
80
112
|
self.rgb_btn = QRadioButton('RGB')
|
|
81
|
-
option_layout.addWidget(self.rgb_btn, alignment=Qt.AlignCenter)
|
|
82
|
-
sub_layout.addLayout(option_layout)
|
|
83
113
|
|
|
84
|
-
btn_hbox = QHBoxLayout()
|
|
85
114
|
|
|
86
115
|
self.percentile_btn = QPushButton()
|
|
87
116
|
self.percentile_btn.setIcon(icon(MDI6.percent_circle_outline,color="black"))
|
|
@@ -97,11 +126,6 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
|
|
|
97
126
|
self.log_btn.setToolTip("Log-transform the intensities.")
|
|
98
127
|
self.log_btn.setIconSize(QSize(20, 20))
|
|
99
128
|
|
|
100
|
-
btn_hbox.addWidget(QLabel(''), 90)
|
|
101
|
-
btn_hbox.addWidget(self.log_btn, 5,alignment=Qt.AlignRight)
|
|
102
|
-
btn_hbox.addWidget(self.percentile_btn, 5,alignment=Qt.AlignRight)
|
|
103
|
-
sub_layout.addLayout(btn_hbox)
|
|
104
|
-
|
|
105
129
|
self.channel_cbs = [QComboBox() for i in range(3)]
|
|
106
130
|
self.channel_cbs_lbls = [QLabel() for i in range(3)]
|
|
107
131
|
|
|
@@ -115,23 +139,13 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
|
|
|
115
139
|
|
|
116
140
|
for i in range(3):
|
|
117
141
|
|
|
118
|
-
hlayout = QHBoxLayout()
|
|
119
142
|
self.channel_cbs[i].addItems(self.channel_names)
|
|
120
143
|
self.channel_cbs[i].setCurrentIndex(i)
|
|
121
144
|
self.channel_cbs_lbls[i].setText(self.rgb_text[i])
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
145
|
+
|
|
146
|
+
self.min_val_les[i].setValidator(self._floatValidator)
|
|
147
|
+
self.max_val_les[i].setValidator(self._floatValidator)
|
|
125
148
|
|
|
126
|
-
hlayout2 = QHBoxLayout()
|
|
127
|
-
hlayout2.addWidget(self.min_val_lbls[i], 20)
|
|
128
|
-
hlayout2.addWidget(self.min_val_les[i], 80)
|
|
129
|
-
sub_layout.addLayout(hlayout2)
|
|
130
|
-
|
|
131
|
-
hlayout3 = QHBoxLayout()
|
|
132
|
-
hlayout3.addWidget(self.max_val_lbls[i], 20)
|
|
133
|
-
hlayout3.addWidget(self.max_val_les[i], 80)
|
|
134
|
-
sub_layout.addLayout(hlayout3)
|
|
135
149
|
|
|
136
150
|
self.enable_channels()
|
|
137
151
|
|
|
@@ -139,10 +153,6 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
|
|
|
139
153
|
self.rgb_btn.toggled.connect(self.enable_channels)
|
|
140
154
|
|
|
141
155
|
self.hsep = QHSeperationLine()
|
|
142
|
-
sub_layout.addWidget(self.hsep)
|
|
143
|
-
|
|
144
|
-
hbox_frac = QHBoxLayout()
|
|
145
|
-
hbox_frac.addWidget(QLabel('fraction: '), 20)
|
|
146
156
|
|
|
147
157
|
self.fraction_slider = QLabeledDoubleSlider()
|
|
148
158
|
self.fraction_slider.setSingleStep(0.05)
|
|
@@ -152,13 +162,6 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
|
|
|
152
162
|
self.fraction_slider.setRange(0.1,1)
|
|
153
163
|
self.fraction_slider.setValue(0.25)
|
|
154
164
|
|
|
155
|
-
hbox_frac.addWidget(self.fraction_slider, 80)
|
|
156
|
-
sub_layout.addLayout(hbox_frac)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
hbox_interval = QHBoxLayout()
|
|
160
|
-
hbox_interval.addWidget(QLabel('interval [ms]: '), 20)
|
|
161
|
-
|
|
162
165
|
self.interval_slider = QLabeledSlider()
|
|
163
166
|
self.interval_slider.setSingleStep(1)
|
|
164
167
|
self.interval_slider.setTickInterval(1)
|
|
@@ -166,29 +169,6 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
|
|
|
166
169
|
self.interval_slider.setOrientation(Qt.Horizontal)
|
|
167
170
|
self.interval_slider.setRange(1,1000)
|
|
168
171
|
self.interval_slider.setValue(1)
|
|
169
|
-
hbox_interval.addWidget(self.interval_slider, 80)
|
|
170
|
-
sub_layout.addLayout(hbox_interval)
|
|
171
|
-
|
|
172
|
-
self.main_layout.addLayout(sub_layout)
|
|
173
|
-
|
|
174
|
-
self.submit_btn = QPushButton('Save')
|
|
175
|
-
self.submit_btn.setStyleSheet(self.button_style_sheet)
|
|
176
|
-
self.submit_btn.clicked.connect(self.write_instructions)
|
|
177
|
-
self.main_layout.addWidget(self.submit_btn)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
self.button_widget.adjustSize()
|
|
181
|
-
self.scroll_area.setAlignment(Qt.AlignCenter)
|
|
182
|
-
self.scroll_area.setWidget(self.button_widget)
|
|
183
|
-
self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
184
|
-
self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
185
|
-
self.scroll_area.setWidgetResizable(True)
|
|
186
|
-
self.setCentralWidget(self.scroll_area)
|
|
187
|
-
self.show()
|
|
188
|
-
|
|
189
|
-
self.read_instructions()
|
|
190
|
-
|
|
191
|
-
QApplication.processEvents()
|
|
192
172
|
|
|
193
173
|
def enable_channels(self):
|
|
194
174
|
|
|
@@ -207,7 +187,6 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
|
|
|
207
187
|
self.channel_cbs_lbls[k].setEnabled(False)
|
|
208
188
|
|
|
209
189
|
for k in range(3):
|
|
210
|
-
|
|
211
190
|
self.min_val_les[k].setEnabled(False)
|
|
212
191
|
self.min_val_lbls[k].setEnabled(False)
|
|
213
192
|
self.max_val_les[k].setEnabled(False)
|
|
@@ -219,7 +198,6 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
|
|
|
219
198
|
self.percentile_btn.setEnabled(True)
|
|
220
199
|
|
|
221
200
|
for k in range(3):
|
|
222
|
-
|
|
223
201
|
self.channel_cbs[k].setEnabled(True)
|
|
224
202
|
self.channel_cbs_lbls[k].setEnabled(True)
|
|
225
203
|
|
|
@@ -256,7 +234,7 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
|
|
|
256
234
|
self.max_val_lbls[k].setText('Max percentile: ')
|
|
257
235
|
self.max_val_les[k].setText('99.99')
|
|
258
236
|
|
|
259
|
-
def
|
|
237
|
+
def _write_instructions(self):
|
|
260
238
|
|
|
261
239
|
"""
|
|
262
240
|
Save the current configuration.
|
|
@@ -267,7 +245,7 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
|
|
|
267
245
|
max_i = 3 if self.rgb_btn.isChecked() else 1
|
|
268
246
|
channels = []
|
|
269
247
|
for i in range(max_i):
|
|
270
|
-
channels.append([self.channel_cbs[i].currentText(), float(self.min_val_les[i].text()), float(self.max_val_les[i].text())])
|
|
248
|
+
channels.append([self.channel_cbs[i].currentText(), float(self.min_val_les[i].text().replace(',','.')), float(self.max_val_les[i].text().replace(',','.'))])
|
|
271
249
|
instructions.update({'channels': channels})
|
|
272
250
|
|
|
273
251
|
print('Instructions: ', instructions)
|
|
@@ -277,7 +255,7 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
|
|
|
277
255
|
print('Done.')
|
|
278
256
|
self.close()
|
|
279
257
|
|
|
280
|
-
def
|
|
258
|
+
def _load_previous_instructions(self):
|
|
281
259
|
|
|
282
260
|
"""
|
|
283
261
|
Read and set the widgets to the last configuration.
|
|
@@ -312,8 +290,8 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
|
|
|
312
290
|
for i in range(max_iter):
|
|
313
291
|
idx = self.channel_cbs[i].findText(channels[i][0])
|
|
314
292
|
self.channel_cbs[i].setCurrentIndex(idx)
|
|
315
|
-
self.min_val_les[i].setText(str(channels[i][1]))
|
|
316
|
-
self.max_val_les[i].setText(str(channels[i][2]))
|
|
293
|
+
self.min_val_les[i].setText(str(channels[i][1]).replace('.',','))
|
|
294
|
+
self.max_val_les[i].setText(str(channels[i][2]).replace('.',','))
|
|
317
295
|
|
|
318
296
|
if 'fraction' in instructions:
|
|
319
297
|
fraction = instructions['fraction']
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
"""
|
|
2
|
+
Copyright © 2023 Laboratoire Adhesion et Inflammation, Authored by Remy Torro.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from PyQt5.QtWidgets import QRadioButton, QButtonGroup, QMessageBox, QComboBox, QFrame, QCheckBox, QFileDialog, QGridLayout, QTextEdit, QLineEdit, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
|
|
2
7
|
from PyQt5.QtCore import Qt, QSize
|
|
3
|
-
from PyQt5.QtGui import QDoubleValidator
|
|
4
8
|
|
|
5
|
-
from celldetective.gui.gui_utils import
|
|
9
|
+
from celldetective.gui.gui_utils import FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, help_generic
|
|
6
10
|
from superqt import QLabeledDoubleSlider, QLabeledSlider
|
|
7
11
|
from superqt.fonticon import icon
|
|
8
12
|
from fonticon_mdi6 import MDI6
|
|
@@ -16,10 +20,10 @@ import os
|
|
|
16
20
|
import matplotlib.pyplot as plt
|
|
17
21
|
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
18
22
|
from glob import glob
|
|
19
|
-
from celldetective.gui import
|
|
23
|
+
from celldetective.gui.settings._settings_base import CelldetectiveSettingsPanel
|
|
20
24
|
|
|
21
25
|
|
|
22
|
-
class
|
|
26
|
+
class SettingsTracking(CelldetectiveSettingsPanel):
|
|
23
27
|
|
|
24
28
|
"""
|
|
25
29
|
UI to set tracking parameters for bTrack.
|
|
@@ -28,46 +32,51 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
28
32
|
|
|
29
33
|
def __init__(self, parent_window=None):
|
|
30
34
|
|
|
31
|
-
super().__init__()
|
|
32
35
|
self.parent_window = parent_window
|
|
33
|
-
self.setWindowTitle("Configure tracking")
|
|
34
36
|
self.mode = self.parent_window.mode
|
|
35
37
|
self.exp_dir = self.parent_window.exp_dir
|
|
36
|
-
self.floatValidator = QDoubleValidator()
|
|
37
38
|
|
|
38
39
|
self.config_name = os.sep.join(["configs", f"btrack_config_{self.mode}.json"])
|
|
39
40
|
self.track_instructions_write_path = self.parent_window.exp_dir + os.sep.join(["configs", f"tracking_instructions_{self.mode}.json"])
|
|
40
|
-
self.soft_path = get_software_location()
|
|
41
41
|
|
|
42
42
|
self.config_path = self.exp_dir + self.config_name
|
|
43
43
|
self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
|
|
44
44
|
self.channel_names = np.array(self.channel_names)
|
|
45
45
|
self.channels = np.array(self.channels)
|
|
46
|
-
self.screen_height = self.parent_window.parent_window.parent_window.screen_height
|
|
47
|
-
|
|
48
|
-
center_window(self)
|
|
49
|
-
self.setMinimumWidth(540)
|
|
50
46
|
self.minimum_height = 300
|
|
51
|
-
# self.setMinimumHeight(int(0.3*self.screen_height))
|
|
52
|
-
# self.setMaximumHeight(int(0.8*self.screen_height))
|
|
53
|
-
self.populate_widget()
|
|
54
|
-
self.load_previous_tracking_instructions()
|
|
55
47
|
|
|
56
|
-
|
|
48
|
+
super().__init__(title="Configure tracking")
|
|
49
|
+
|
|
50
|
+
self._add_to_layout()
|
|
51
|
+
self._load_previous_instructions()
|
|
52
|
+
|
|
53
|
+
self._widget.setMinimumWidth(500)
|
|
54
|
+
self._adjustSize()
|
|
55
|
+
self.resize(int(self.width()*1.5), int(self._screen_height * 0.8))
|
|
56
|
+
|
|
57
|
+
def _add_to_layout(self):
|
|
58
|
+
|
|
59
|
+
tracker_hbox = QHBoxLayout()
|
|
60
|
+
tracker_hbox.setContentsMargins(15, 15, 15, 15)
|
|
61
|
+
tracker_hbox.addWidget(self.btrack_option, 50, alignment=Qt.AlignCenter)
|
|
62
|
+
tracker_hbox.addWidget(self.trackpy_option, 50, alignment=Qt.AlignCenter)
|
|
63
|
+
|
|
64
|
+
self._layout.addLayout(tracker_hbox)
|
|
65
|
+
self._layout.addWidget(self.config_frame)
|
|
66
|
+
self._layout.addWidget(self.features_frame)
|
|
67
|
+
self._layout.addWidget(self.config_trackpy_frame)
|
|
68
|
+
self._layout.addWidget(self.post_proc_frame)
|
|
69
|
+
self._layout.addWidget(self.submit_btn)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _create_widgets(self):
|
|
57
73
|
|
|
58
74
|
"""
|
|
59
75
|
Create the multibox design with collapsable frames.
|
|
60
76
|
|
|
61
77
|
"""
|
|
62
78
|
|
|
63
|
-
|
|
64
|
-
self.scroll_area = QScrollArea(self)
|
|
65
|
-
self.button_widget = CelldetectiveWidget()
|
|
66
|
-
main_layout = QVBoxLayout()
|
|
67
|
-
self.button_widget.setLayout(main_layout)
|
|
68
|
-
main_layout.setContentsMargins(30, 30, 30, 30)
|
|
69
|
-
|
|
70
|
-
# First collapsable Frame CONFIG
|
|
79
|
+
super()._create_widgets()
|
|
71
80
|
|
|
72
81
|
self.btrack_option = QRadioButton('bTrack')
|
|
73
82
|
self.btrack_option.setChecked(True)
|
|
@@ -81,69 +90,37 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
81
90
|
self.config_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
82
91
|
self.populate_config_frame()
|
|
83
92
|
|
|
84
|
-
tracker_hbox = QHBoxLayout()
|
|
85
|
-
tracker_hbox.setContentsMargins(15, 15, 15, 15)
|
|
86
|
-
tracker_hbox.addWidget(self.btrack_option, 50, alignment=Qt.AlignCenter)
|
|
87
|
-
tracker_hbox.addWidget(self.trackpy_option, 50, alignment=Qt.AlignCenter)
|
|
88
|
-
main_layout.addLayout(tracker_hbox)
|
|
89
|
-
|
|
90
|
-
main_layout.addWidget(self.config_frame)
|
|
91
|
-
|
|
92
93
|
# Second collapsable frame FEATURES
|
|
93
94
|
self.features_frame = QFrame()
|
|
94
95
|
self.features_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
95
96
|
self.populate_features_frame()
|
|
96
|
-
main_layout.addWidget(self.features_frame)
|
|
97
97
|
|
|
98
98
|
self.config_trackpy_frame = QFrame()
|
|
99
99
|
self.config_trackpy_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
100
100
|
self.populate_config_trackpy_frame()
|
|
101
|
-
main_layout.addWidget(self.config_trackpy_frame)
|
|
102
101
|
self.config_trackpy_frame.hide()
|
|
103
102
|
|
|
104
103
|
# Third collapsable frame POST-PROCESSING
|
|
105
104
|
self.post_proc_frame = QFrame()
|
|
106
105
|
self.post_proc_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
107
106
|
self.populate_post_proc_frame()
|
|
108
|
-
main_layout.addWidget(self.post_proc_frame)
|
|
109
|
-
|
|
110
|
-
self.submit_btn = QPushButton('Save')
|
|
111
|
-
self.submit_btn.setStyleSheet(self.parent_window.parent_window.parent_window.button_style_sheet)
|
|
112
|
-
self.submit_btn.clicked.connect(self.write_instructions)
|
|
113
|
-
main_layout.addWidget(self.submit_btn)
|
|
114
|
-
|
|
115
|
-
#self.populate_left_panel()
|
|
116
|
-
#grid.addLayout(self.left_side, 0, 0, 1, 1)
|
|
117
|
-
self.button_widget.adjustSize()
|
|
118
|
-
|
|
119
|
-
self.scroll_area.setAlignment(Qt.AlignCenter)
|
|
120
|
-
self.scroll_area.setWidget(self.button_widget)
|
|
121
|
-
self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
122
|
-
self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
123
|
-
self.scroll_area.setWidgetResizable(True)
|
|
124
|
-
self.setCentralWidget(self.scroll_area)
|
|
125
|
-
self.show()
|
|
126
107
|
|
|
127
108
|
self.btrack_option.toggled.connect(self.show_tracking_options)
|
|
128
109
|
self.trackpy_option.toggled.connect(self.show_tracking_options)
|
|
129
110
|
|
|
130
|
-
QApplication.processEvents()
|
|
131
|
-
self.adjustScrollArea()
|
|
132
|
-
|
|
133
111
|
def show_tracking_options(self):
|
|
134
112
|
|
|
135
113
|
if self.btrack_option.isChecked():
|
|
136
114
|
self.config_frame.show()
|
|
137
115
|
self.features_frame.show()
|
|
138
116
|
self.config_trackpy_frame.hide()
|
|
139
|
-
#self.
|
|
140
|
-
#self.adjustSize()
|
|
117
|
+
#self._adjustSize()
|
|
141
118
|
else:
|
|
142
119
|
self.config_frame.hide()
|
|
143
120
|
self.features_frame.hide()
|
|
144
121
|
self.config_trackpy_frame.show()
|
|
145
|
-
#self.
|
|
146
|
-
|
|
122
|
+
#self._adjustSize()
|
|
123
|
+
|
|
147
124
|
|
|
148
125
|
def populate_post_proc_frame(self):
|
|
149
126
|
|
|
@@ -203,12 +180,12 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
203
180
|
self.collapse_post_proc_btn.setIcon(icon(MDI6.chevron_down, color="black"))
|
|
204
181
|
self.collapse_post_proc_btn.setIconSize(QSize(20, 20))
|
|
205
182
|
if len(is_open[is_open])==0:
|
|
206
|
-
|
|
207
|
-
self.
|
|
183
|
+
pass
|
|
184
|
+
#self._scroll_area.setMinimumHeight(int(self.minimum_height))
|
|
185
|
+
#self._adjustSize()
|
|
208
186
|
else:
|
|
209
187
|
self.collapse_post_proc_btn.setIcon(icon(MDI6.chevron_up, color="black"))
|
|
210
188
|
self.collapse_post_proc_btn.setIconSize(QSize(20, 20))
|
|
211
|
-
self.scroll_area.setMinimumHeight(min(int(930), int(0.9*self.screen_height)))
|
|
212
189
|
|
|
213
190
|
|
|
214
191
|
def help_post(self):
|
|
@@ -319,12 +296,12 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
319
296
|
self.collapse_features_btn.setIcon(icon(MDI6.chevron_down, color="black"))
|
|
320
297
|
self.collapse_features_btn.setIconSize(QSize(20, 20))
|
|
321
298
|
if len(is_open[is_open])==0:
|
|
322
|
-
|
|
323
|
-
self.
|
|
299
|
+
pass
|
|
300
|
+
#self._scroll_area.setMinimumHeight(int(self.minimum_height))
|
|
301
|
+
#self._adjustSize()
|
|
324
302
|
else:
|
|
325
303
|
self.collapse_features_btn.setIcon(icon(MDI6.chevron_up, color="black"))
|
|
326
304
|
self.collapse_features_btn.setIconSize(QSize(20, 20))
|
|
327
|
-
self.scroll_area.setMinimumHeight(min(int(930), int(0.9*self.screen_height)))
|
|
328
305
|
|
|
329
306
|
|
|
330
307
|
def generate_post_proc_panel_contents(self):
|
|
@@ -614,12 +591,12 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
614
591
|
self.collapse_config_btn.setIcon(icon(MDI6.chevron_down,color="black"))
|
|
615
592
|
self.collapse_config_btn.setIconSize(QSize(20, 20))
|
|
616
593
|
if len(is_open[is_open])==0:
|
|
617
|
-
|
|
618
|
-
self.
|
|
594
|
+
pass
|
|
595
|
+
#self._scroll_area.setMinimumHeight(int(self.minimum_height))
|
|
596
|
+
#self._adjustSize()
|
|
619
597
|
else:
|
|
620
598
|
self.collapse_config_btn.setIcon(icon(MDI6.chevron_up,color="black"))
|
|
621
599
|
self.collapse_config_btn.setIconSize(QSize(20, 20))
|
|
622
|
-
self.scroll_area.setMinimumHeight(min(int(930), int(0.9*self.screen_height)))
|
|
623
600
|
|
|
624
601
|
def collapse_config_trackpy_advanced(self):
|
|
625
602
|
|
|
@@ -635,12 +612,12 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
635
612
|
self.collapse_config_trackpy_btn.setIcon(icon(MDI6.chevron_down,color="black"))
|
|
636
613
|
self.collapse_config_trackpy_btn.setIconSize(QSize(20, 20))
|
|
637
614
|
if len(is_open[is_open])==0:
|
|
638
|
-
|
|
639
|
-
self.
|
|
615
|
+
pass
|
|
616
|
+
#self._scroll_area.setMinimumHeight(int(self.minimum_height))
|
|
617
|
+
#self._adjustSize()
|
|
640
618
|
else:
|
|
641
619
|
self.collapse_config_trackpy_btn.setIcon(icon(MDI6.chevron_up,color="black"))
|
|
642
620
|
self.collapse_config_trackpy_btn.setIconSize(QSize(20, 20))
|
|
643
|
-
self.scroll_area.setMinimumHeight(min(int(930), int(0.9*self.screen_height)))
|
|
644
621
|
|
|
645
622
|
|
|
646
623
|
def generate_config_trackpy_panel_contents(self):
|
|
@@ -653,7 +630,7 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
653
630
|
self.search_range_lbl = QLabel("search range [px]: ")
|
|
654
631
|
self.search_range_le = QLineEdit('30')
|
|
655
632
|
self.search_range_le.setPlaceholderText('search distance in pixels')
|
|
656
|
-
self.search_range_le.setValidator(self.
|
|
633
|
+
self.search_range_le.setValidator(self._floatValidator)
|
|
657
634
|
sr_layout.addWidget(self.search_range_lbl, 30)
|
|
658
635
|
sr_layout.addWidget(self.search_range_le, 70)
|
|
659
636
|
layout.addLayout(sr_layout)
|
|
@@ -731,7 +708,7 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
731
708
|
|
|
732
709
|
self.file_dialog = QFileDialog()
|
|
733
710
|
try:
|
|
734
|
-
modelpath = os.sep.join([self.
|
|
711
|
+
modelpath = os.sep.join([self._software_path, "celldetective","models","tracking_configs"]) + os.sep
|
|
735
712
|
print("Track config path: ", modelpath)
|
|
736
713
|
self.filename = self.file_dialog.getOpenFileName(None, "Load config", modelpath, "json files (*.json)")[0]
|
|
737
714
|
if self.filename!=self.config_path:
|
|
@@ -822,16 +799,16 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
822
799
|
else:
|
|
823
800
|
self.post_proc_ticked = True
|
|
824
801
|
|
|
825
|
-
def adjustScrollArea(self):
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
802
|
+
# def adjustScrollArea(self):
|
|
803
|
+
#
|
|
804
|
+
# """
|
|
805
|
+
# Auto-adjust scroll area to fill space
|
|
806
|
+
# (from https://stackoverflow.com/questions/66417576/make-qscrollarea-use-all-available-space-of-qmainwindow-height-axis)
|
|
807
|
+
# """
|
|
808
|
+
#
|
|
809
|
+
# step = 5
|
|
810
|
+
# while self._scroll_area.verticalScrollBar().isVisible() and self.height() < self.maximumHeight():
|
|
811
|
+
# self.resize(self.width(), self.height() + step)
|
|
835
812
|
|
|
836
813
|
def load_cell_config(self):
|
|
837
814
|
|
|
@@ -844,7 +821,7 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
844
821
|
json_data = json.load(f)
|
|
845
822
|
self.config_le.setText(json.dumps(json_data, indent=4))
|
|
846
823
|
|
|
847
|
-
def
|
|
824
|
+
def _write_instructions(self):
|
|
848
825
|
|
|
849
826
|
"""
|
|
850
827
|
Write the selected options in a json file for later reading by the software.
|
|
@@ -953,7 +930,7 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
953
930
|
else:
|
|
954
931
|
self.haralick_options = None
|
|
955
932
|
|
|
956
|
-
def
|
|
933
|
+
def _load_previous_instructions(self):
|
|
957
934
|
|
|
958
935
|
"""
|
|
959
936
|
Read the tracking options from a previously written json file.
|
celldetective/gui/styles.py
CHANGED
|
@@ -14,7 +14,8 @@ class Styles(object):
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
self.celldetective_blue = "#1565c0"
|
|
17
|
-
self.
|
|
17
|
+
self.celldetective_logo_path = os.sep.join([get_software_location(),'celldetective','icons','logo.png'])
|
|
18
|
+
self.celldetective_icon = QIcon(self.celldetective_logo_path)
|
|
18
19
|
|
|
19
20
|
self.action_lbl_style_sheet = """
|
|
20
21
|
font-size: 10px;
|
celldetective/gui/survival_ui.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import
|
|
1
|
+
from PyQt5.QtWidgets import QComboBox, QLineEdit, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
|
|
2
2
|
from PyQt5.QtCore import Qt
|
|
3
3
|
from PyQt5.QtGui import QDoubleValidator
|
|
4
4
|
from celldetective.gui.gui_utils import center_window, generic_message
|
celldetective/gui/tableUI.py
CHANGED
|
@@ -3,6 +3,9 @@ from PyQt5.QtCore import Qt
|
|
|
3
3
|
from PyQt5.QtGui import QBrush, QColor, QDoubleValidator
|
|
4
4
|
import pandas as pd
|
|
5
5
|
import matplotlib.pyplot as plt
|
|
6
|
+
|
|
7
|
+
from celldetective.gui.table_ops.merge_groups import MergeGroupWidget
|
|
8
|
+
|
|
6
9
|
plt.rcParams['svg.fonttype'] = 'none'
|
|
7
10
|
from celldetective.gui.gui_utils import FigureCanvas, center_window, QHSeperationLine, GenericOpColWidget, PandasModel
|
|
8
11
|
from celldetective.utils import differentiate_per_track, collapse_trajectories_by_status, test_2samp_generic, safe_log
|
|
@@ -650,11 +653,17 @@ class TableUI(CelldetectiveMainWindow):
|
|
|
650
653
|
self.calibrate_action.triggered.connect(self.calibrate_selected_feature)
|
|
651
654
|
self.calibrate_action.setShortcut("Ctrl+C")
|
|
652
655
|
self.mathMenu.addAction(self.calibrate_action)
|
|
656
|
+
|
|
657
|
+
self.merge_classification_action = QAction('&Merge states...', self)
|
|
658
|
+
self.merge_classification_action.triggered.connect(self.merge_classification_features)
|
|
659
|
+
self.mathMenu.addAction(self.merge_classification_action)
|
|
653
660
|
|
|
654
661
|
self.derivative_action = QAction('&Differentiate...', self)
|
|
655
662
|
self.derivative_action.triggered.connect(self.differenciate_selected_feature)
|
|
656
663
|
self.derivative_action.setShortcut("Ctrl+D")
|
|
657
664
|
self.mathMenu.addAction(self.derivative_action)
|
|
665
|
+
if not self.tracks:
|
|
666
|
+
self.derivative_action.setEnabled(False)
|
|
658
667
|
|
|
659
668
|
self.abs_action = QAction('&Absolute value...', self)
|
|
660
669
|
self.abs_action.triggered.connect(self.take_abs_of_selected_feature)
|
|
@@ -993,6 +1002,22 @@ class TableUI(CelldetectiveMainWindow):
|
|
|
993
1002
|
|
|
994
1003
|
self.LogWidget = LogColWidget(self, selected_col)
|
|
995
1004
|
self.LogWidget.show()
|
|
1005
|
+
|
|
1006
|
+
def merge_classification_features(self):
|
|
1007
|
+
|
|
1008
|
+
x = self.table_view.selectedIndexes()
|
|
1009
|
+
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
1010
|
+
|
|
1011
|
+
col_selection = []
|
|
1012
|
+
if isinstance(col_idx, (list, np.ndarray)):
|
|
1013
|
+
cols = np.array(list(self.data.columns))
|
|
1014
|
+
if len(col_idx) > 0:
|
|
1015
|
+
selected_cols = cols[col_idx]
|
|
1016
|
+
col_selection.extend(selected_cols)
|
|
1017
|
+
|
|
1018
|
+
self.merge_classification_widget = MergeGroupWidget(self, columns = col_selection)
|
|
1019
|
+
self.merge_classification_widget.show()
|
|
1020
|
+
|
|
996
1021
|
|
|
997
1022
|
def calibrate_selected_feature(self):
|
|
998
1023
|
|
|
File without changes
|