celldetective 1.3.9.post4__py3-none-any.whl → 1.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- celldetective/__init__.py +0 -3
- celldetective/_version.py +1 -1
- celldetective/events.py +2 -4
- celldetective/extra_properties.py +320 -24
- celldetective/gui/InitWindow.py +33 -45
- celldetective/gui/__init__.py +1 -0
- celldetective/gui/about.py +19 -15
- celldetective/gui/analyze_block.py +34 -19
- celldetective/gui/base_components.py +23 -0
- celldetective/gui/btrack_options.py +26 -34
- celldetective/gui/classifier_widget.py +71 -80
- celldetective/gui/configure_new_exp.py +113 -17
- celldetective/gui/control_panel.py +68 -141
- celldetective/gui/generic_signal_plot.py +9 -12
- celldetective/gui/gui_utils.py +49 -21
- celldetective/gui/json_readers.py +5 -4
- celldetective/gui/layouts.py +246 -22
- celldetective/gui/measurement_options.py +32 -17
- celldetective/gui/neighborhood_options.py +10 -13
- celldetective/gui/plot_measurements.py +21 -17
- celldetective/gui/plot_signals_ui.py +131 -75
- celldetective/gui/process_block.py +180 -123
- celldetective/gui/processes/compute_neighborhood.py +594 -0
- celldetective/gui/processes/measure_cells.py +5 -0
- celldetective/gui/processes/segment_cells.py +27 -6
- celldetective/gui/processes/track_cells.py +6 -0
- celldetective/gui/retrain_segmentation_model_options.py +12 -20
- celldetective/gui/retrain_signal_model_options.py +57 -56
- celldetective/gui/seg_model_loader.py +21 -62
- celldetective/gui/signal_annotator.py +139 -72
- celldetective/gui/signal_annotator2.py +431 -635
- celldetective/gui/signal_annotator_options.py +8 -11
- celldetective/gui/survival_ui.py +49 -95
- celldetective/gui/tableUI.py +28 -25
- celldetective/gui/thresholds_gui.py +617 -1221
- celldetective/gui/viewers.py +106 -39
- celldetective/gui/workers.py +9 -3
- celldetective/io.py +73 -27
- celldetective/measure.py +63 -27
- celldetective/neighborhood.py +342 -268
- celldetective/preprocessing.py +25 -17
- celldetective/relative_measurements.py +50 -29
- celldetective/scripts/analyze_signals.py +4 -1
- celldetective/scripts/measure_relative.py +4 -1
- celldetective/scripts/segment_cells.py +0 -6
- celldetective/scripts/track_cells.py +3 -1
- celldetective/scripts/train_segmentation_model.py +7 -4
- celldetective/signals.py +29 -14
- celldetective/tracking.py +7 -2
- celldetective/utils.py +36 -8
- {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/METADATA +24 -16
- {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/RECORD +57 -55
- {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/WHEEL +1 -1
- tests/test_qt.py +21 -21
- {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/entry_points.txt +0 -0
- {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info/licenses}/LICENSE +0 -0
- {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/top_level.txt +0 -0
celldetective/gui/layouts.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import QCheckBox, QLineEdit,
|
|
1
|
+
from PyQt5.QtWidgets import QCheckBox, QLineEdit, QListWidget, QTabWidget, QHBoxLayout,QMessageBox, QPushButton, QVBoxLayout, QRadioButton, QLabel, QButtonGroup, QSizePolicy, QComboBox,QSpacerItem, QGridLayout
|
|
2
2
|
from celldetective.gui.gui_utils import ThresholdLineEdit, QuickSliderLayout, center_window
|
|
3
3
|
from PyQt5.QtCore import Qt, QSize
|
|
4
|
-
from PyQt5.QtGui import QIntValidator
|
|
4
|
+
from PyQt5.QtGui import QIntValidator, QDoubleValidator
|
|
5
5
|
|
|
6
6
|
from superqt import QLabeledRangeSlider, QLabeledDoubleSlider, QLabeledSlider, QLabeledDoubleRangeSlider, QSearchableComboBox
|
|
7
7
|
|
|
@@ -9,15 +9,223 @@ from superqt.fonticon import icon
|
|
|
9
9
|
from fonticon_mdi6 import MDI6
|
|
10
10
|
from celldetective.utils import _extract_channel_indices_from_config
|
|
11
11
|
from celldetective.gui.viewers import ThresholdedStackVisualizer, CellEdgeVisualizer, StackVisualizer, CellSizeViewer, ChannelOffsetViewer
|
|
12
|
-
from celldetective.gui import Styles
|
|
12
|
+
from celldetective.gui import Styles, CelldetectiveWidget
|
|
13
13
|
from celldetective.preprocessing import correct_background_model, correct_background_model_free, estimate_background_per_condition
|
|
14
14
|
from functools import partial
|
|
15
15
|
from glob import glob
|
|
16
16
|
import os
|
|
17
17
|
import pandas as pd
|
|
18
18
|
import numpy as np
|
|
19
|
+
from celldetective.io import locate_segmentation_model, locate_signal_model
|
|
20
|
+
import json
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
|
|
23
|
+
class SignalModelParamsWidget(CelldetectiveWidget):
|
|
24
|
+
|
|
25
|
+
def __init__(self, parent_window=None, model_name=None, *args, **kwargs):
|
|
26
|
+
|
|
27
|
+
super().__init__(*args)
|
|
28
|
+
self.setWindowTitle('Signals')
|
|
29
|
+
self.parent_window = parent_window
|
|
30
|
+
self.model_name = model_name
|
|
31
|
+
self.locate_model_path()
|
|
32
|
+
self.required_channels = self.input_config["channels"]
|
|
33
|
+
self.onlyFloat = QDoubleValidator()
|
|
34
|
+
|
|
35
|
+
# Setting up references to parent window attributes
|
|
36
|
+
if hasattr(self.parent_window.parent_window, 'locate_image'):
|
|
37
|
+
self.attr_parent = self.parent_window.parent_window
|
|
38
|
+
elif hasattr(self.parent_window.parent_window.parent_window, 'locate_image'):
|
|
39
|
+
self.attr_parent = self.parent_window.parent_window.parent_window
|
|
40
|
+
else:
|
|
41
|
+
self.attr_parent = self.parent_window.parent_window.parent_window.parent_window
|
|
42
|
+
|
|
43
|
+
# Set up layout and widgets
|
|
44
|
+
self.layout = QVBoxLayout()
|
|
45
|
+
self.populate_widgets()
|
|
46
|
+
self.setLayout(self.layout)
|
|
47
|
+
center_window(self)
|
|
48
|
+
|
|
49
|
+
def locate_model_path(self):
|
|
50
|
+
|
|
51
|
+
self.model_complete_path = locate_signal_model(self.model_name)
|
|
52
|
+
if self.model_complete_path is None:
|
|
53
|
+
print('Model could not be found. Abort.')
|
|
54
|
+
self.abort_process()
|
|
55
|
+
else:
|
|
56
|
+
print(f'Model path: {self.model_complete_path}...')
|
|
57
|
+
|
|
58
|
+
if not os.path.exists(self.model_complete_path+"config_input.json"):
|
|
59
|
+
print('The configuration for the inputs to the model could not be located. Abort.')
|
|
60
|
+
self.abort_process()
|
|
61
|
+
|
|
62
|
+
with open(self.model_complete_path+"config_input.json") as config_file:
|
|
63
|
+
self.input_config = json.load(config_file)
|
|
64
|
+
|
|
65
|
+
def populate_widgets(self):
|
|
66
|
+
|
|
67
|
+
self.n_channels = len(self.required_channels)
|
|
68
|
+
self.channel_cbs = [QComboBox() for i in range(self.n_channels)]
|
|
69
|
+
|
|
70
|
+
self.parent_window.load_available_tables()
|
|
71
|
+
available_channels = list(self.parent_window.signals)+['None']
|
|
72
|
+
# Populate the comboboxes with available channels from the experiment
|
|
73
|
+
for k in range(self.n_channels):
|
|
74
|
+
hbox_channel = QHBoxLayout()
|
|
75
|
+
hbox_channel.addWidget(QLabel(f'channel {k+1}: '), 33)
|
|
76
|
+
|
|
77
|
+
ch_vbox = QVBoxLayout()
|
|
78
|
+
ch_vbox.addWidget(QLabel(f'Req: {self.required_channels[k]}'), alignment=Qt.AlignLeft)
|
|
79
|
+
ch_vbox.addWidget(self.channel_cbs[k])
|
|
80
|
+
|
|
81
|
+
self.channel_cbs[k].addItems(available_channels) #Give none option for more than one channel input
|
|
82
|
+
idx = self.channel_cbs[k].findText(self.required_channels[k])
|
|
83
|
+
|
|
84
|
+
if idx>=0:
|
|
85
|
+
self.channel_cbs[k].setCurrentIndex(idx)
|
|
86
|
+
else:
|
|
87
|
+
self.channel_cbs[k].setCurrentIndex(len(available_channels)-1)
|
|
88
|
+
|
|
89
|
+
hbox_channel.addLayout(ch_vbox, 66)
|
|
90
|
+
self.layout.addLayout(hbox_channel)
|
|
91
|
+
|
|
92
|
+
# Button to apply the StarDist settings
|
|
93
|
+
self.set_btn = QPushButton('set')
|
|
94
|
+
self.set_btn.setStyleSheet(self.button_style_sheet)
|
|
95
|
+
self.set_btn.clicked.connect(self.parent_window.set_selected_signals_for_event_detection)
|
|
96
|
+
self.layout.addWidget(self.set_btn)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class SegModelParamsWidget(CelldetectiveWidget):
|
|
101
|
+
|
|
102
|
+
def __init__(self, parent_window=None, model_name='SD_versatile_fluo', *args, **kwargs):
|
|
103
|
+
|
|
104
|
+
super().__init__(*args)
|
|
105
|
+
self.setWindowTitle('Channels')
|
|
106
|
+
self.parent_window = parent_window
|
|
107
|
+
self.model_name = model_name
|
|
108
|
+
self.locate_model_path()
|
|
109
|
+
self.required_channels = self.input_config["channels"]
|
|
110
|
+
self.onlyFloat = QDoubleValidator()
|
|
111
|
+
|
|
112
|
+
# Setting up references to parent window attributes
|
|
113
|
+
if hasattr(self.parent_window.parent_window, 'locate_image'):
|
|
114
|
+
self.attr_parent = self.parent_window.parent_window
|
|
115
|
+
elif hasattr(self.parent_window.parent_window.parent_window, 'locate_image'):
|
|
116
|
+
self.attr_parent = self.parent_window.parent_window.parent_window
|
|
117
|
+
else:
|
|
118
|
+
self.attr_parent = self.parent_window.parent_window.parent_window.parent_window
|
|
119
|
+
|
|
120
|
+
# Set up layout and widgets
|
|
121
|
+
self.layout = QVBoxLayout()
|
|
122
|
+
self.populate_widgets()
|
|
123
|
+
self.setLayout(self.layout)
|
|
124
|
+
center_window(self)
|
|
125
|
+
|
|
126
|
+
def locate_model_path(self):
|
|
127
|
+
|
|
128
|
+
self.model_complete_path = locate_segmentation_model(self.model_name)
|
|
129
|
+
if self.model_complete_path is None:
|
|
130
|
+
print('Model could not be found. Abort.')
|
|
131
|
+
self.abort_process()
|
|
132
|
+
else:
|
|
133
|
+
print(f'Model path: {self.model_complete_path}...')
|
|
134
|
+
|
|
135
|
+
if not os.path.exists(self.model_complete_path+"config_input.json"):
|
|
136
|
+
print('The configuration for the inputs to the model could not be located. Abort.')
|
|
137
|
+
self.abort_process()
|
|
138
|
+
|
|
139
|
+
with open(self.model_complete_path+"config_input.json") as config_file:
|
|
140
|
+
self.input_config = json.load(config_file)
|
|
141
|
+
|
|
142
|
+
def populate_widgets(self):
|
|
143
|
+
|
|
144
|
+
self.n_channels = len(self.required_channels)
|
|
145
|
+
self.channel_cbs = [QComboBox() for i in range(self.n_channels)]
|
|
146
|
+
|
|
147
|
+
# Button to view the current stack with a scale bar
|
|
148
|
+
self.view_diameter_btn = QPushButton()
|
|
149
|
+
self.view_diameter_btn.setStyleSheet(self.button_select_all)
|
|
150
|
+
self.view_diameter_btn.setIcon(icon(MDI6.image_check, color="black"))
|
|
151
|
+
self.view_diameter_btn.setToolTip("View stack.")
|
|
152
|
+
self.view_diameter_btn.setIconSize(QSize(20, 20))
|
|
153
|
+
self.view_diameter_btn.clicked.connect(self.view_current_stack_with_scale_bar)
|
|
154
|
+
|
|
155
|
+
# Line edit for entering cell diameter
|
|
156
|
+
self.diameter_le = ThresholdLineEdit(init_value=40, connected_buttons=[self.view_diameter_btn],placeholder='cell diameter in µm', value_type='float')
|
|
157
|
+
|
|
158
|
+
available_channels = list(self.attr_parent.exp_channels)+['None']
|
|
159
|
+
# Populate the comboboxes with available channels from the experiment
|
|
160
|
+
for k in range(self.n_channels):
|
|
161
|
+
hbox_channel = QHBoxLayout()
|
|
162
|
+
hbox_channel.addWidget(QLabel(f'channel {k+1}: '), 33)
|
|
163
|
+
|
|
164
|
+
ch_vbox = QVBoxLayout()
|
|
165
|
+
ch_vbox.addWidget(QLabel(f'Req: {self.required_channels[k]}'), alignment=Qt.AlignLeft)
|
|
166
|
+
ch_vbox.addWidget(self.channel_cbs[k])
|
|
167
|
+
|
|
168
|
+
self.channel_cbs[k].addItems(available_channels) #Give none option for more than one channel input
|
|
169
|
+
idx = self.channel_cbs[k].findText(self.required_channels[k])
|
|
170
|
+
|
|
171
|
+
if idx>=0:
|
|
172
|
+
self.channel_cbs[k].setCurrentIndex(idx)
|
|
173
|
+
else:
|
|
174
|
+
self.channel_cbs[k].setCurrentIndex(len(available_channels)-1)
|
|
175
|
+
|
|
176
|
+
hbox_channel.addLayout(ch_vbox, 66)
|
|
177
|
+
self.layout.addLayout(hbox_channel)
|
|
178
|
+
|
|
179
|
+
if 'cell_size_um' in self.input_config:
|
|
180
|
+
|
|
181
|
+
# Layout for diameter input and button
|
|
182
|
+
hbox = QHBoxLayout()
|
|
183
|
+
hbox.addWidget(QLabel('cell size [µm]: '), 33)
|
|
184
|
+
hbox.addWidget(self.diameter_le, 61)
|
|
185
|
+
hbox.addWidget(self.view_diameter_btn)
|
|
186
|
+
self.layout.addLayout(hbox)
|
|
187
|
+
|
|
188
|
+
self.diameter_le.set_threshold(self.input_config['cell_size_um'])
|
|
189
|
+
|
|
190
|
+
# size_hbox = QHBoxLayout()
|
|
191
|
+
# size_hbox.addWidget(QLabel('cell size [µm]: '), 33)
|
|
192
|
+
# self.size_le = QLineEdit(str(self.input_config['cell_size_um']).replace('.',','))
|
|
193
|
+
# self.size_le.setValidator(self.onlyFloat)
|
|
194
|
+
# size_hbox.addWidget(self.size_le, 66)
|
|
195
|
+
# self.layout.addLayout(size_hbox)
|
|
196
|
+
|
|
197
|
+
# Button to apply the StarDist settings
|
|
198
|
+
self.set_btn = QPushButton('set')
|
|
199
|
+
self.set_btn.setStyleSheet(self.button_style_sheet)
|
|
200
|
+
self.set_btn.clicked.connect(self.parent_window.set_selected_channels_for_segmentation)
|
|
201
|
+
self.layout.addWidget(self.set_btn)
|
|
202
|
+
|
|
203
|
+
def view_current_stack_with_scale_bar(self):
|
|
204
|
+
|
|
205
|
+
"""
|
|
206
|
+
Displays the current image stack with a scale bar, allowing users to visually estimate cell diameters.
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
self.attr_parent.locate_image()
|
|
210
|
+
if self.attr_parent.current_stack is not None:
|
|
211
|
+
max_size = np.amax([self.attr_parent.shape_x, self.attr_parent.shape_y])
|
|
212
|
+
self.viewer = CellSizeViewer(
|
|
213
|
+
initial_diameter = float(self.diameter_le.text().replace(',', '.')),
|
|
214
|
+
parent_le = self.diameter_le,
|
|
215
|
+
stack_path=self.attr_parent.current_stack,
|
|
216
|
+
window_title=f'Position {self.attr_parent.position_list.currentText()}',
|
|
217
|
+
diameter_slider_range=(0,max_size*self.attr_parent.PxToUm),
|
|
218
|
+
frame_slider = True,
|
|
219
|
+
contrast_slider = True,
|
|
220
|
+
channel_cb = True,
|
|
221
|
+
channel_names = self.attr_parent.exp_channels,
|
|
222
|
+
n_channels = self.attr_parent.nbr_channels,
|
|
223
|
+
PxToUm = self.attr_parent.PxToUm,
|
|
224
|
+
)
|
|
225
|
+
self.viewer.show()
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class StarDistParamsWidget(CelldetectiveWidget):
|
|
21
229
|
|
|
22
230
|
"""
|
|
23
231
|
A widget to configure parameters for StarDist segmentation.
|
|
@@ -101,7 +309,7 @@ class StarDistParamsWidget(QWidget, Styles):
|
|
|
101
309
|
self.layout.addWidget(self.set_stardist_scale_btn)
|
|
102
310
|
|
|
103
311
|
|
|
104
|
-
class CellposeParamsWidget(
|
|
312
|
+
class CellposeParamsWidget(CelldetectiveWidget):
|
|
105
313
|
|
|
106
314
|
"""
|
|
107
315
|
A widget to configure parameters for Cellpose segmentation, allowing users to set the cell diameter,
|
|
@@ -204,7 +412,7 @@ class CellposeParamsWidget(QWidget, Styles):
|
|
|
204
412
|
|
|
205
413
|
# Flow threshold slider
|
|
206
414
|
self.flow_slider = QLabeledDoubleSlider()
|
|
207
|
-
self.flow_slider.setOrientation(
|
|
415
|
+
self.flow_slider.setOrientation(Qt.Horizontal)
|
|
208
416
|
self.flow_slider.setRange(-6,6)
|
|
209
417
|
self.flow_slider.setValue(0.4)
|
|
210
418
|
hbox = QHBoxLayout()
|
|
@@ -214,7 +422,7 @@ class CellposeParamsWidget(QWidget, Styles):
|
|
|
214
422
|
|
|
215
423
|
# Cell probability threshold slider
|
|
216
424
|
self.cellprob_slider = QLabeledDoubleSlider()
|
|
217
|
-
self.cellprob_slider.setOrientation(
|
|
425
|
+
self.cellprob_slider.setOrientation(Qt.Horizontal)
|
|
218
426
|
self.cellprob_slider.setRange(-6,6)
|
|
219
427
|
self.cellprob_slider.setValue(0.)
|
|
220
428
|
hbox = QHBoxLayout()
|
|
@@ -236,18 +444,20 @@ class CellposeParamsWidget(QWidget, Styles):
|
|
|
236
444
|
|
|
237
445
|
self.attr_parent.locate_image()
|
|
238
446
|
if self.attr_parent.current_stack is not None:
|
|
447
|
+
max_size = np.amax([self.attr_parent.shape_x, self.attr_parent.shape_y])
|
|
239
448
|
self.viewer = CellSizeViewer(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
449
|
+
initial_diameter = float(self.diameter_le.text().replace(',', '.')),
|
|
450
|
+
parent_le = self.diameter_le,
|
|
451
|
+
stack_path=self.attr_parent.current_stack,
|
|
452
|
+
window_title=f'Position {self.attr_parent.position_list.currentText()}',
|
|
453
|
+
diameter_slider_range=(0, max_size),
|
|
454
|
+
frame_slider = True,
|
|
455
|
+
contrast_slider = True,
|
|
456
|
+
channel_cb = True,
|
|
457
|
+
channel_names = self.attr_parent.exp_channels,
|
|
458
|
+
n_channels = self.attr_parent.nbr_channels,
|
|
459
|
+
PxToUm = 1,
|
|
460
|
+
)
|
|
251
461
|
self.viewer.show()
|
|
252
462
|
|
|
253
463
|
class ChannelNormGenerator(QVBoxLayout, Styles):
|
|
@@ -864,7 +1074,7 @@ class ProtocolDesignerLayout(QVBoxLayout, Styles):
|
|
|
864
1074
|
self.tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
865
1075
|
|
|
866
1076
|
for k in range(len(self.tab_layouts)):
|
|
867
|
-
wg =
|
|
1077
|
+
wg = CelldetectiveWidget()
|
|
868
1078
|
self.tab_layouts[k].parent_window = self
|
|
869
1079
|
wg.setLayout(self.tab_layouts[k])
|
|
870
1080
|
self.tabs.addTab(wg, self.tab_names[k])
|
|
@@ -1173,13 +1383,21 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
|
|
|
1173
1383
|
coef_nbr_layout.addWidget(self.nbr_coef_le, 75)
|
|
1174
1384
|
self.addLayout(coef_nbr_layout, 7,0,1,3)
|
|
1175
1385
|
|
|
1386
|
+
offset_layout = QHBoxLayout()
|
|
1387
|
+
offset_layout.addWidget(QLabel("Offset: "), 25)
|
|
1388
|
+
self.camera_offset_le = QLineEdit("0")
|
|
1389
|
+
self.camera_offset_le.setPlaceholderText('camera black level')
|
|
1390
|
+
self.camera_offset_le.setValidator(QDoubleValidator())
|
|
1391
|
+
offset_layout.addWidget(self.camera_offset_le, 75)
|
|
1392
|
+
self.addLayout(offset_layout, 8, 0, 1, 3)
|
|
1393
|
+
|
|
1176
1394
|
self.operation_layout = OperationLayout()
|
|
1177
|
-
self.addLayout(self.operation_layout,
|
|
1395
|
+
self.addLayout(self.operation_layout, 9, 0, 1, 3)
|
|
1178
1396
|
|
|
1179
1397
|
correction_layout = QHBoxLayout()
|
|
1180
1398
|
correction_layout.addWidget(self.add_correction_btn, 95)
|
|
1181
1399
|
correction_layout.addWidget(self.corrected_stack_viewer_btn, 5)
|
|
1182
|
-
self.addLayout(correction_layout,
|
|
1400
|
+
self.addLayout(correction_layout, 10, 0, 1, 3)
|
|
1183
1401
|
|
|
1184
1402
|
# verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
|
1185
1403
|
# self.addItem(verticalSpacer, 5, 0, 1, 3)
|
|
@@ -1221,6 +1439,11 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
|
|
|
1221
1439
|
clip = True
|
|
1222
1440
|
else:
|
|
1223
1441
|
clip = False
|
|
1442
|
+
|
|
1443
|
+
if self.camera_offset_le.text()=="":
|
|
1444
|
+
offset = None
|
|
1445
|
+
else:
|
|
1446
|
+
offset = float(self.camera_offset_le.text().replace(",","."))
|
|
1224
1447
|
|
|
1225
1448
|
self.instructions = {
|
|
1226
1449
|
"target_channel": self.channels_cb.currentText(),
|
|
@@ -1232,7 +1455,8 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
|
|
|
1232
1455
|
"opt_coef_range": opt_coef_range,
|
|
1233
1456
|
"opt_coef_nbr": opt_coef_nbr,
|
|
1234
1457
|
"operation": operation,
|
|
1235
|
-
"clip": clip
|
|
1458
|
+
"clip": clip,
|
|
1459
|
+
"offset": offset
|
|
1236
1460
|
}
|
|
1237
1461
|
|
|
1238
1462
|
def set_target_channel(self):
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
from subprocess import Popen
|
|
1
2
|
|
|
2
|
-
from PyQt5.QtWidgets import
|
|
3
|
-
QGridLayout, QLineEdit, QVBoxLayout,
|
|
3
|
+
from PyQt5.QtWidgets import QApplication, QMessageBox, QScrollArea, QComboBox, QFrame, QCheckBox, \
|
|
4
|
+
QGridLayout, QLineEdit, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
|
|
4
5
|
from PyQt5.QtCore import Qt, QSize
|
|
5
|
-
from PyQt5.QtGui import
|
|
6
|
+
from PyQt5.QtGui import QDoubleValidator, QIntValidator
|
|
6
7
|
|
|
7
8
|
from celldetective.gui.gui_utils import center_window, FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, \
|
|
8
9
|
GeometryChoice, OperationChoice
|
|
@@ -10,12 +11,10 @@ from superqt import QLabeledDoubleSlider
|
|
|
10
11
|
from superqt.fonticon import icon
|
|
11
12
|
from fonticon_mdi6 import MDI6
|
|
12
13
|
|
|
13
|
-
#from celldetective.gui.thresholds_gui import ThresholdSpot
|
|
14
14
|
from celldetective.utils import extract_experiment_channels, get_software_location
|
|
15
15
|
from celldetective.io import load_frames, auto_load_number_of_frames
|
|
16
16
|
from celldetective.measure import compute_haralick_features
|
|
17
17
|
import numpy as np
|
|
18
|
-
from tifffile import imread
|
|
19
18
|
import json
|
|
20
19
|
import os
|
|
21
20
|
import matplotlib.pyplot as plt
|
|
@@ -27,11 +26,11 @@ from pathlib import Path
|
|
|
27
26
|
|
|
28
27
|
from celldetective.gui.viewers import CellEdgeVisualizer, SpotDetectionVisualizer
|
|
29
28
|
from celldetective.gui.layouts import ProtocolDesignerLayout, BackgroundFitCorrectionLayout, LocalCorrectionLayout
|
|
30
|
-
from celldetective.gui.gui_utils import
|
|
31
|
-
from celldetective.gui import
|
|
29
|
+
from celldetective.gui.gui_utils import PreprocessingLayout2
|
|
30
|
+
from celldetective.gui import CelldetectiveMainWindow, CelldetectiveWidget
|
|
32
31
|
|
|
33
32
|
|
|
34
|
-
class ConfigMeasurements(
|
|
33
|
+
class ConfigMeasurements(CelldetectiveMainWindow):
|
|
35
34
|
"""
|
|
36
35
|
UI to set measurement instructions.
|
|
37
36
|
|
|
@@ -43,16 +42,11 @@ class ConfigMeasurements(QMainWindow, Styles):
|
|
|
43
42
|
|
|
44
43
|
self.parent_window = parent_window
|
|
45
44
|
self.setWindowTitle("Configure measurements")
|
|
46
|
-
self.setWindowIcon(QIcon(os.sep.join(['celldetective', 'icons', 'mexican-hat.png'])))
|
|
47
45
|
self.mode = self.parent_window.mode
|
|
48
46
|
self.exp_dir = self.parent_window.exp_dir
|
|
49
47
|
self.background_correction = []
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
self.measure_instructions_path = self.parent_window.exp_dir + "configs/measurement_instructions_targets.json"
|
|
53
|
-
elif self.mode == "effectors":
|
|
54
|
-
self.config_name = "btrack_config_effectors.json"
|
|
55
|
-
self.measure_instructions_path = self.parent_window.exp_dir + "configs/measurement_instructions_effectors.json"
|
|
48
|
+
self.config_name = f"btrack_config_{self.mode}.json"
|
|
49
|
+
self.measure_instructions_path = self.parent_window.exp_dir + f"configs/measurement_instructions_{self.mode}.json"
|
|
56
50
|
self.soft_path = get_software_location()
|
|
57
51
|
self.clear_previous = False
|
|
58
52
|
|
|
@@ -83,7 +77,7 @@ class ConfigMeasurements(QMainWindow, Styles):
|
|
|
83
77
|
|
|
84
78
|
# Create button widget and layout
|
|
85
79
|
self.scroll_area = QScrollArea(self)
|
|
86
|
-
self.button_widget =
|
|
80
|
+
self.button_widget = CelldetectiveWidget()
|
|
87
81
|
main_layout = QVBoxLayout()
|
|
88
82
|
self.button_widget.setLayout(main_layout)
|
|
89
83
|
main_layout.setContentsMargins(30, 30, 30, 30)
|
|
@@ -264,12 +258,21 @@ class ConfigMeasurements(QMainWindow, Styles):
|
|
|
264
258
|
|
|
265
259
|
self.features_list = ListWidget(FeatureChoice, initial_features=['area', 'intensity_mean', ])
|
|
266
260
|
|
|
261
|
+
self.create_feature_btn = QPushButton("")
|
|
262
|
+
self.create_feature_btn.setStyleSheet(self.button_select_all)
|
|
263
|
+
self.create_feature_btn.setIcon(icon(MDI6.file_cog, color="black"))
|
|
264
|
+
self.create_feature_btn.setToolTip("Create new feature")
|
|
265
|
+
self.create_feature_btn.setIconSize(QSize(20, 20))
|
|
266
|
+
|
|
267
267
|
self.del_feature_btn.clicked.connect(self.features_list.removeSel)
|
|
268
268
|
self.add_feature_btn.clicked.connect(self.features_list.addItem)
|
|
269
|
+
self.create_feature_btn.clicked.connect(self.go_to_extraprops)
|
|
269
270
|
|
|
270
271
|
feature_layout.addWidget(self.feature_lbl, 90)
|
|
271
272
|
feature_layout.addWidget(self.del_feature_btn, 5)
|
|
272
273
|
feature_layout.addWidget(self.add_feature_btn, 5)
|
|
274
|
+
feature_layout.addWidget(self.create_feature_btn, 5)
|
|
275
|
+
|
|
273
276
|
layout.addLayout(feature_layout)
|
|
274
277
|
layout.addWidget(self.features_list)
|
|
275
278
|
|
|
@@ -334,7 +337,7 @@ class ConfigMeasurements(QMainWindow, Styles):
|
|
|
334
337
|
self.haralick_scale_slider.setSingleStep(0.05)
|
|
335
338
|
self.haralick_scale_slider.setTickInterval(0.05)
|
|
336
339
|
self.haralick_scale_slider.setSingleStep(1)
|
|
337
|
-
self.haralick_scale_slider.setOrientation(
|
|
340
|
+
self.haralick_scale_slider.setOrientation(Qt.Horizontal)
|
|
338
341
|
self.haralick_scale_slider.setRange(0, 1)
|
|
339
342
|
self.haralick_scale_slider.setValue(0.5)
|
|
340
343
|
self.haralick_scale_lbl = QLabel('Scale: ')
|
|
@@ -426,6 +429,18 @@ class ConfigMeasurements(QMainWindow, Styles):
|
|
|
426
429
|
self.haralick_normalization_mode_btn.clicked.connect(self.switch_to_absolute_normalization_mode)
|
|
427
430
|
layout.addLayout(self.haralick_layout)
|
|
428
431
|
|
|
432
|
+
def go_to_extraprops(self):
|
|
433
|
+
|
|
434
|
+
path = os.sep.join([self.soft_path,'celldetective',os.sep,'extra_properties.py'])
|
|
435
|
+
try:
|
|
436
|
+
Popen(f'explorer {os.path.realpath(path)}')
|
|
437
|
+
except:
|
|
438
|
+
|
|
439
|
+
try:
|
|
440
|
+
os.system('xdg-open "%s"' % path)
|
|
441
|
+
except:
|
|
442
|
+
return None
|
|
443
|
+
|
|
429
444
|
def switch_to_absolute_normalization_mode(self):
|
|
430
445
|
|
|
431
446
|
if self.percentile_mode:
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import QApplication, QComboBox, QFrame, QCheckBox, QVBoxLayout,
|
|
1
|
+
from PyQt5.QtWidgets import QApplication, QComboBox, QFrame, QCheckBox, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
|
|
2
2
|
from PyQt5.QtCore import Qt, QSize
|
|
3
|
-
from PyQt5.QtGui import QIcon
|
|
4
3
|
from celldetective.gui.gui_utils import center_window, ListWidget, DistanceChoice
|
|
5
4
|
from superqt.fonticon import icon
|
|
6
5
|
from fonticon_mdi6 import MDI6
|
|
@@ -10,9 +9,10 @@ import os
|
|
|
10
9
|
from glob import glob
|
|
11
10
|
import pandas as pd
|
|
12
11
|
from celldetective.gui.viewers import CellSizeViewer, CellEdgeVisualizer
|
|
13
|
-
from celldetective.gui import
|
|
12
|
+
from celldetective.gui import CelldetectiveWidget
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
|
|
15
|
+
class ConfigNeighborhoods(CelldetectiveWidget):
|
|
16
16
|
|
|
17
17
|
"""
|
|
18
18
|
Widget to configure neighborhood measurements.
|
|
@@ -24,7 +24,6 @@ class ConfigNeighborhoods(QWidget, Styles):
|
|
|
24
24
|
super().__init__(*args, **kwargs)
|
|
25
25
|
self.parent_window = parent_window
|
|
26
26
|
self.attr_parent = self.parent_window.parent_window
|
|
27
|
-
self.setWindowIcon(QIcon(os.sep.join(['celldetective','icons','logo.png'])))
|
|
28
27
|
|
|
29
28
|
self.neighborhood_type = neighborhood_type
|
|
30
29
|
self.neighborhood_parameter_name = neighborhood_parameter_name
|
|
@@ -43,7 +42,6 @@ class ConfigNeighborhoods(QWidget, Styles):
|
|
|
43
42
|
self.generate_main_layout()
|
|
44
43
|
self.load_previous_neighborhood_instructions()
|
|
45
44
|
center_window(self)
|
|
46
|
-
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
47
45
|
|
|
48
46
|
def generate_main_layout(self):
|
|
49
47
|
|
|
@@ -140,7 +138,7 @@ class ConfigNeighborhoods(QWidget, Styles):
|
|
|
140
138
|
|
|
141
139
|
layout.addLayout(list_header_layout)
|
|
142
140
|
|
|
143
|
-
self.measurements_list = ListWidget(DistanceChoice, initial_features=[
|
|
141
|
+
self.measurements_list = ListWidget(DistanceChoice, initial_features=[], dtype=int)
|
|
144
142
|
self.measurements_list.setToolTip('Neighborhoods to compute.')
|
|
145
143
|
layout.addWidget(self.measurements_list)
|
|
146
144
|
|
|
@@ -211,7 +209,7 @@ class ConfigNeighborhoods(QWidget, Styles):
|
|
|
211
209
|
population_layout = QHBoxLayout()
|
|
212
210
|
population_layout.addWidget(QLabel('population: '),30)
|
|
213
211
|
self.reference_population_cb = QComboBox()
|
|
214
|
-
self.reference_population_cb.addItems(
|
|
212
|
+
self.reference_population_cb.addItems(self.parent_window.parent_window.populations)
|
|
215
213
|
self.reference_population_cb.setToolTip('Select a reference population.')
|
|
216
214
|
population_layout.addWidget(self.reference_population_cb,70)
|
|
217
215
|
layout.addLayout(population_layout)
|
|
@@ -271,7 +269,7 @@ class ConfigNeighborhoods(QWidget, Styles):
|
|
|
271
269
|
population_layout = QHBoxLayout()
|
|
272
270
|
population_layout.addWidget(QLabel('population: '),30)
|
|
273
271
|
self.neighbor_population_cb = QComboBox()
|
|
274
|
-
self.neighbor_population_cb.addItems(
|
|
272
|
+
self.neighbor_population_cb.addItems(self.parent_window.parent_window.populations)
|
|
275
273
|
self.neighbor_population_cb.setToolTip('Select a neighbor population.')
|
|
276
274
|
population_layout.addWidget(self.neighbor_population_cb,70)
|
|
277
275
|
layout.addLayout(population_layout)
|
|
@@ -315,15 +313,14 @@ class ConfigNeighborhoods(QWidget, Styles):
|
|
|
315
313
|
population = self.neighbor_population_cb.currentText()
|
|
316
314
|
class_cols, status_cols, group_cols, time_cols = self.locate_population_specific_columns(population)
|
|
317
315
|
self.neighbor_population_status_cb.clear()
|
|
318
|
-
self.neighbor_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols+group_cols)
|
|
316
|
+
self.neighbor_population_status_cb.addItems(list(np.unique(['--','class', 'status']+class_cols+status_cols+group_cols)))
|
|
319
317
|
|
|
320
318
|
def fill_cbs_of_reference_population(self):
|
|
321
319
|
|
|
322
320
|
population = self.reference_population_cb.currentText()
|
|
323
321
|
class_cols, status_cols, group_cols, time_cols = self.locate_population_specific_columns(population)
|
|
324
|
-
self.
|
|
325
|
-
self.
|
|
326
|
-
self.event_time_cb.addItems(['--', 't0']+time_cols)
|
|
322
|
+
self.event_time_cb.clear()
|
|
323
|
+
self.event_time_cb.addItems(list(np.unique(['--', 't0']+time_cols)))
|
|
327
324
|
|
|
328
325
|
def switch_not_reference(self):
|
|
329
326
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from PyQt5.QtWidgets import QMessageBox, QScrollArea, QButtonGroup, QComboBox, \
|
|
2
|
-
QCheckBox, QVBoxLayout,
|
|
2
|
+
QCheckBox, QVBoxLayout, QLabel, QHBoxLayout, QPushButton, \
|
|
3
3
|
QRadioButton, QSizePolicy
|
|
4
4
|
from PyQt5.QtCore import Qt, QRect
|
|
5
|
-
from PyQt5.QtGui import
|
|
5
|
+
from PyQt5.QtGui import QDoubleValidator
|
|
6
6
|
|
|
7
|
-
from celldetective.gui import
|
|
7
|
+
from celldetective.gui import CelldetectiveWidget
|
|
8
8
|
from celldetective.gui.gui_utils import center_window, FigureCanvas
|
|
9
9
|
|
|
10
10
|
from superqt.fonticon import icon
|
|
@@ -12,7 +12,6 @@ from fonticon_mdi6 import MDI6
|
|
|
12
12
|
from celldetective.utils import get_software_location, _extract_labels_from_config
|
|
13
13
|
from celldetective.io import load_experiment_tables, get_experiment_antibodies, get_experiment_cell_types, get_experiment_concentrations, \
|
|
14
14
|
get_positions_in_well, get_experiment_wells
|
|
15
|
-
from celldetective.signals import mean_signal
|
|
16
15
|
import numpy as np
|
|
17
16
|
import json
|
|
18
17
|
import os
|
|
@@ -26,7 +25,7 @@ import math
|
|
|
26
25
|
import seaborn as sns
|
|
27
26
|
|
|
28
27
|
|
|
29
|
-
class ConfigMeasurementsPlot(
|
|
28
|
+
class ConfigMeasurementsPlot(CelldetectiveWidget):
|
|
30
29
|
"""
|
|
31
30
|
UI to set survival instructions.
|
|
32
31
|
|
|
@@ -37,7 +36,6 @@ class ConfigMeasurementsPlot(QWidget,Styles):
|
|
|
37
36
|
super().__init__()
|
|
38
37
|
self.parent_window = parent_window
|
|
39
38
|
self.setWindowTitle("Configure signal plot")
|
|
40
|
-
self.setWindowIcon(QIcon(os.sep.join(['celldetective', 'icons', 'mexican-hat.png'])))
|
|
41
39
|
self.exp_dir = self.parent_window.exp_dir
|
|
42
40
|
self.soft_path = get_software_location()
|
|
43
41
|
self.exp_config = self.exp_dir + "config.ini"
|
|
@@ -109,7 +107,7 @@ class ConfigMeasurementsPlot(QWidget,Styles):
|
|
|
109
107
|
main_layout.addWidget(panel_title, alignment=Qt.AlignCenter)
|
|
110
108
|
|
|
111
109
|
labels = [QLabel('population: '), QLabel('class: '), QLabel('group: ')] # , QLabel('time of\ninterest: ')]
|
|
112
|
-
self.cb_options = [
|
|
110
|
+
self.cb_options = [self.parent_window.parent_window.populations, ['class'], ['group']] # , ['t0','first detection']]
|
|
113
111
|
self.cbs = [QComboBox() for i in range(len(labels))]
|
|
114
112
|
self.cbs[0].currentIndexChanged.connect(self.set_classes_and_times)
|
|
115
113
|
|
|
@@ -145,7 +143,7 @@ class ConfigMeasurementsPlot(QWidget,Styles):
|
|
|
145
143
|
# self.abs_time_checkbox = QCheckBox('absolute time')
|
|
146
144
|
# self.frame_slider = QLabeledSlider()
|
|
147
145
|
# self.frame_slider.setSingleStep(1)
|
|
148
|
-
# self.frame_slider.setOrientation(
|
|
146
|
+
# self.frame_slider.setOrientation(Qt.Horizontal)
|
|
149
147
|
# self.frame_slider.setRange(0,self.parent.parent.len_movie)
|
|
150
148
|
# self.frame_slider.setValue(0)
|
|
151
149
|
# self.frame_slider.setEnabled(False)
|
|
@@ -202,14 +200,20 @@ class ConfigMeasurementsPlot(QWidget,Styles):
|
|
|
202
200
|
class_idx = np.array([s.startswith('class_') for s in self.all_columns])
|
|
203
201
|
group_idx = np.array([s.startswith('group_') for s in self.all_columns])
|
|
204
202
|
|
|
203
|
+
print(f'{class_idx=} {group_idx=} {self.all_columns=}')
|
|
205
204
|
# time_idx = np.array([s.startswith('t_') for s in self.all_columns])
|
|
206
|
-
|
|
207
205
|
try:
|
|
208
|
-
|
|
209
|
-
|
|
206
|
+
if len(class_idx)>0:
|
|
207
|
+
class_columns = list(self.all_columns[class_idx])
|
|
208
|
+
else:
|
|
209
|
+
class_columns = []
|
|
210
|
+
if len(group_idx)>0:
|
|
211
|
+
group_columns = list(self.all_columns[group_idx])
|
|
212
|
+
else:
|
|
213
|
+
group_columns = []
|
|
210
214
|
# time_columns = list(self.all_columns[time_idx])
|
|
211
|
-
except:
|
|
212
|
-
print('columns not found')
|
|
215
|
+
except Exception as e:
|
|
216
|
+
print(f'L210 columns not found {e}')
|
|
213
217
|
self.auto_close = True
|
|
214
218
|
return None
|
|
215
219
|
|
|
@@ -227,7 +231,7 @@ class ConfigMeasurementsPlot(QWidget,Styles):
|
|
|
227
231
|
is_number = np.vectorize(lambda x: np.issubdtype(x, np.number))
|
|
228
232
|
feats = cols[is_number(self.df.dtypes)]
|
|
229
233
|
|
|
230
|
-
self.feature_choice_widget =
|
|
234
|
+
self.feature_choice_widget = CelldetectiveWidget()
|
|
231
235
|
self.feature_choice_widget.setWindowTitle("Select numeric feature")
|
|
232
236
|
layout = QVBoxLayout()
|
|
233
237
|
self.feature_choice_widget.setLayout(layout)
|
|
@@ -250,7 +254,7 @@ class ConfigMeasurementsPlot(QWidget,Styles):
|
|
|
250
254
|
is_number = np.vectorize(lambda x: np.issubdtype(x, np.number))
|
|
251
255
|
feats = cols[is_number(self.df.dtypes)]
|
|
252
256
|
|
|
253
|
-
self.feature_choice_widget =
|
|
257
|
+
self.feature_choice_widget = CelldetectiveWidget()
|
|
254
258
|
self.feature_choice_widget.setWindowTitle("Select numeric feature")
|
|
255
259
|
layout = QVBoxLayout()
|
|
256
260
|
self.feature_choice_widget.setLayout(layout)
|
|
@@ -299,7 +303,7 @@ class ConfigMeasurementsPlot(QWidget,Styles):
|
|
|
299
303
|
# prepare survival
|
|
300
304
|
|
|
301
305
|
# plot survival
|
|
302
|
-
self.survivalWidget =
|
|
306
|
+
self.survivalWidget = CelldetectiveWidget()
|
|
303
307
|
self.scroll = QScrollArea()
|
|
304
308
|
self.survivalWidget.setMinimumHeight(int(0.8 * self.screen_height))
|
|
305
309
|
self.survivalWidget.setWindowTitle('signals')
|
|
@@ -500,7 +504,7 @@ class ConfigMeasurementsPlot(QWidget,Styles):
|
|
|
500
504
|
if name + ':' in lbl:
|
|
501
505
|
self.usable_well_labels.append(lbl)
|
|
502
506
|
|
|
503
|
-
self.line_choice_widget =
|
|
507
|
+
self.line_choice_widget = CelldetectiveWidget()
|
|
504
508
|
self.line_check_vbox = QVBoxLayout()
|
|
505
509
|
self.line_choice_widget.setLayout(self.line_check_vbox)
|
|
506
510
|
if len(self.well_indices) > 1:
|