celldetective 1.3.9.post5__py3-none-any.whl → 1.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- celldetective/__init__.py +0 -3
- celldetective/_version.py +1 -1
- celldetective/events.py +2 -4
- celldetective/extra_properties.py +132 -0
- 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 +68 -81
- 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 +125 -72
- 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 +129 -70
- 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 +57 -20
- 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.post5.dist-info → celldetective-1.4.0.dist-info}/METADATA +24 -16
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/RECORD +57 -55
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/WHEEL +1 -1
- tests/test_qt.py +21 -21
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/entry_points.txt +0 -0
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info/licenses}/LICENSE +0 -0
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import
|
|
1
|
+
from PyQt5.QtWidgets import QApplication, QMessageBox, QHBoxLayout, QFileDialog, QVBoxLayout, QScrollArea, QCheckBox, QGridLayout, QLabel, QLineEdit, QPushButton
|
|
2
2
|
from PyQt5.QtGui import QIntValidator, QDoubleValidator
|
|
3
3
|
from celldetective.gui.gui_utils import center_window, help_generic
|
|
4
|
-
from celldetective.gui.styles import Styles
|
|
5
4
|
from celldetective.utils import get_software_location
|
|
6
5
|
import json
|
|
7
6
|
|
|
@@ -13,9 +12,10 @@ from configparser import ConfigParser
|
|
|
13
12
|
import os
|
|
14
13
|
from functools import partial
|
|
15
14
|
import numpy as np
|
|
16
|
-
from celldetective.gui import
|
|
15
|
+
from celldetective.gui import CelldetectiveMainWindow, CelldetectiveWidget
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
class ConfigNewExperiment(CelldetectiveMainWindow):
|
|
19
19
|
|
|
20
20
|
def __init__(self, parent_window=None):
|
|
21
21
|
|
|
@@ -34,11 +34,11 @@ class ConfigNewExperiment(QMainWindow, Styles):
|
|
|
34
34
|
|
|
35
35
|
# Create button widget and layout
|
|
36
36
|
self.scroll_area = QScrollArea(self)
|
|
37
|
-
button_widget =
|
|
37
|
+
button_widget = CelldetectiveWidget()
|
|
38
38
|
self.grid = QGridLayout()
|
|
39
39
|
button_widget.setLayout(self.grid)
|
|
40
40
|
|
|
41
|
-
self.grid.setContentsMargins(30,30,30,30)
|
|
41
|
+
self.grid.setContentsMargins(30, 30, 30, 30)
|
|
42
42
|
self.grid.addWidget(QLabel("Folder:"), 0, 0, 1, 3)
|
|
43
43
|
self.supFolder = QLineEdit()
|
|
44
44
|
self.supFolder.setAlignment(Qt.AlignLeft)
|
|
@@ -64,17 +64,20 @@ class ConfigNewExperiment(QMainWindow, Styles):
|
|
|
64
64
|
self.grid.addWidget(self.expName, 3, 0, 1, 3)
|
|
65
65
|
|
|
66
66
|
self.generate_movie_settings()
|
|
67
|
-
self.grid.addLayout(self.ms_grid,29,0,1,3)
|
|
67
|
+
self.grid.addLayout(self.ms_grid, 29, 0, 1, 3)
|
|
68
68
|
|
|
69
69
|
self.generate_channel_params_box()
|
|
70
|
-
self.grid.addLayout(self.channel_grid,30,0,1,3)
|
|
70
|
+
self.grid.addLayout(self.channel_grid, 30, 0, 1, 3)
|
|
71
|
+
|
|
72
|
+
self.generate_population_params_box()
|
|
73
|
+
self.grid.addLayout(self.population_grid, 31, 0, 1, 3)
|
|
71
74
|
|
|
72
75
|
self.validate_button = QPushButton("Submit")
|
|
73
76
|
self.validate_button.clicked.connect(self.create_config)
|
|
74
77
|
self.validate_button.setStyleSheet(self.button_style_sheet)
|
|
75
78
|
#self.validate_button.setIcon(QIcon_from_svg(abs_path+f"/icons/process.svg", color='white'))
|
|
76
79
|
|
|
77
|
-
self.grid.addWidget(self.validate_button,
|
|
80
|
+
self.grid.addWidget(self.validate_button, 32, 0, 1, 3, alignment = Qt.AlignBottom)
|
|
78
81
|
button_widget.adjustSize()
|
|
79
82
|
|
|
80
83
|
self.scroll_area.setAlignment(Qt.AlignCenter)
|
|
@@ -128,10 +131,9 @@ class ConfigNewExperiment(QMainWindow, Styles):
|
|
|
128
131
|
self.help_btn.setToolTip("Help.")
|
|
129
132
|
self.ms_grid.addWidget(self.help_btn, 1, 0, 1, 3, alignment=Qt.AlignRight)
|
|
130
133
|
|
|
131
|
-
|
|
132
134
|
self.SliderWells = QLabeledSlider(Qt.Horizontal, self)
|
|
133
135
|
self.SliderWells.setMinimum(1)
|
|
134
|
-
self.SliderWells.setMaximum(
|
|
136
|
+
self.SliderWells.setMaximum(512)
|
|
135
137
|
self.ms_grid.addWidget(self.SliderWells, 2, 0, 1, 3, alignment=Qt.AlignTop)
|
|
136
138
|
|
|
137
139
|
self.number_of_positions = QLabel("Number of positions per well:")
|
|
@@ -139,7 +141,7 @@ class ConfigNewExperiment(QMainWindow, Styles):
|
|
|
139
141
|
|
|
140
142
|
self.SliderPos = QLabeledSlider(Qt.Horizontal, self)
|
|
141
143
|
self.SliderPos.setMinimum(1)
|
|
142
|
-
self.SliderPos.setMaximum(
|
|
144
|
+
self.SliderPos.setMaximum(512)
|
|
143
145
|
|
|
144
146
|
self.ms_grid.addWidget(self.SliderPos, 4, 0, 1, 3)
|
|
145
147
|
|
|
@@ -168,7 +170,7 @@ class ConfigNewExperiment(QMainWindow, Styles):
|
|
|
168
170
|
self.ms_grid.addWidget(self.movie_length,9, 0, 1, 3)
|
|
169
171
|
self.MovieLengthSlider = QLabeledSlider(Qt.Horizontal, self)
|
|
170
172
|
self.MovieLengthSlider.setMinimum(2)
|
|
171
|
-
self.MovieLengthSlider.setMaximum(128)
|
|
173
|
+
#self.MovieLengthSlider.setMaximum(128)
|
|
172
174
|
self.ms_grid.addWidget(self.MovieLengthSlider, 10, 0, 1, 3)
|
|
173
175
|
|
|
174
176
|
self.prefix_lbl = QLabel("Prefix for the movies:")
|
|
@@ -181,7 +183,7 @@ class ConfigNewExperiment(QMainWindow, Styles):
|
|
|
181
183
|
self.movie_prefix_field.setText("")
|
|
182
184
|
self.ms_grid.addWidget(self.movie_prefix_field, 12, 0, 1, 3)
|
|
183
185
|
|
|
184
|
-
self.ms_grid.addWidget(QLabel("
|
|
186
|
+
self.ms_grid.addWidget(QLabel("Image width:"), 13, 0, 1, 3)
|
|
185
187
|
self.shape_x_field = QLineEdit()
|
|
186
188
|
self.shape_x_field.setValidator(onlyInt)
|
|
187
189
|
self.shape_x_field.setAlignment(Qt.AlignLeft)
|
|
@@ -190,7 +192,7 @@ class ConfigNewExperiment(QMainWindow, Styles):
|
|
|
190
192
|
self.shape_x_field.setText("2048")
|
|
191
193
|
self.ms_grid.addWidget(self.shape_x_field, 14, 0, 1, 3)
|
|
192
194
|
|
|
193
|
-
self.ms_grid.addWidget(QLabel("
|
|
195
|
+
self.ms_grid.addWidget(QLabel("Image height:"), 15, 0, 1, 3)
|
|
194
196
|
self.shape_y_field = QLineEdit()
|
|
195
197
|
self.shape_y_field.setValidator(onlyInt)
|
|
196
198
|
self.shape_y_field.setAlignment(Qt.AlignLeft)
|
|
@@ -271,7 +273,7 @@ class ConfigNewExperiment(QMainWindow, Styles):
|
|
|
271
273
|
|
|
272
274
|
def add_custom_channel(self):
|
|
273
275
|
|
|
274
|
-
self.CustomChannelWidget =
|
|
276
|
+
self.CustomChannelWidget = CelldetectiveWidget()
|
|
275
277
|
self.CustomChannelWidget.setWindowTitle("Custom channel")
|
|
276
278
|
layout = QVBoxLayout()
|
|
277
279
|
self.CustomChannelWidget.setLayout(layout)
|
|
@@ -323,6 +325,83 @@ class ConfigNewExperiment(QMainWindow, Styles):
|
|
|
323
325
|
else:
|
|
324
326
|
self.sliders[index].setEnabled(False)
|
|
325
327
|
|
|
328
|
+
def generate_population_params_box(self):
|
|
329
|
+
|
|
330
|
+
"""
|
|
331
|
+
Parameters related to the movie channels
|
|
332
|
+
Rewrite all of it
|
|
333
|
+
|
|
334
|
+
"""
|
|
335
|
+
|
|
336
|
+
self.population_grid = QGridLayout()
|
|
337
|
+
self.population_grid.setContentsMargins(21,30,20,30)
|
|
338
|
+
|
|
339
|
+
pop_lbl = QLabel("CELL POPULATIONS")
|
|
340
|
+
pop_lbl.setStyleSheet("""
|
|
341
|
+
font-weight: bold;
|
|
342
|
+
""")
|
|
343
|
+
self.population_grid.addWidget(pop_lbl, 0,0,1,3, alignment=Qt.AlignCenter)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
self.populations = ['effectors','targets']
|
|
347
|
+
self.population_checkboxes = [QCheckBox() for i in range(len(self.populations))]
|
|
348
|
+
|
|
349
|
+
for i in range(len(self.populations)):
|
|
350
|
+
|
|
351
|
+
self.population_checkboxes[i].setText(self.populations[i])
|
|
352
|
+
self.population_checkboxes[i].setChecked(True)
|
|
353
|
+
self.population_grid.addWidget(self.population_checkboxes[i], i+1, 0, 1, 1)
|
|
354
|
+
|
|
355
|
+
# Add channel button
|
|
356
|
+
self.addPopBtn = QPushButton('Add a cell population')
|
|
357
|
+
self.addPopBtn.setIcon(icon(MDI6.plus,color="white"))
|
|
358
|
+
self.addPopBtn.setIconSize(QSize(25, 25))
|
|
359
|
+
self.addPopBtn.setStyleSheet(self.button_style_sheet)
|
|
360
|
+
self.addPopBtn.clicked.connect(self.add_custom_population)
|
|
361
|
+
self.population_grid.addWidget(self.addPopBtn, 1000, 0, 1, 1)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def add_custom_population(self):
|
|
365
|
+
self.CustomPopWidget = CelldetectiveWidget()
|
|
366
|
+
self.CustomPopWidget.setWindowTitle("Define custom population")
|
|
367
|
+
layout = QVBoxLayout()
|
|
368
|
+
self.CustomPopWidget.setLayout(layout)
|
|
369
|
+
|
|
370
|
+
self.name_le = QLineEdit()
|
|
371
|
+
self.name_le.setPlaceholderText('name')
|
|
372
|
+
self.name_le.textChanged.connect(self.check_population_name)
|
|
373
|
+
hbox = QHBoxLayout()
|
|
374
|
+
hbox.addWidget(QLabel('population name: '), 33)
|
|
375
|
+
hbox.addWidget(self.name_le, 66)
|
|
376
|
+
layout.addLayout(hbox)
|
|
377
|
+
|
|
378
|
+
self.addPopBtn = QPushButton('add')
|
|
379
|
+
self.addPopBtn.setStyleSheet(self.button_style_sheet)
|
|
380
|
+
self.addPopBtn.setEnabled(False)
|
|
381
|
+
self.addPopBtn.clicked.connect(self.write_custom_population)
|
|
382
|
+
layout.addWidget(self.addPopBtn)
|
|
383
|
+
center_window(self.CustomPopWidget)
|
|
384
|
+
self.CustomPopWidget.show()
|
|
385
|
+
|
|
386
|
+
def check_population_name(self, text):
|
|
387
|
+
# define all conditions for valid population name (like no space)
|
|
388
|
+
if len(text)>0 and ' ' not in text:
|
|
389
|
+
self.addPopBtn.setEnabled(True)
|
|
390
|
+
else:
|
|
391
|
+
self.addPopBtn.setEnabled(False)
|
|
392
|
+
|
|
393
|
+
def write_custom_population(self):
|
|
394
|
+
|
|
395
|
+
self.new_population_name = self.name_le.text()
|
|
396
|
+
name_map = self.new_population_name
|
|
397
|
+
|
|
398
|
+
self.populations.append(self.new_population_name)
|
|
399
|
+
self.population_checkboxes.append(QCheckBox())
|
|
400
|
+
self.CustomPopWidget.close()
|
|
401
|
+
|
|
402
|
+
self.population_checkboxes[-1].setText(self.populations[-1])
|
|
403
|
+
self.population_grid.addWidget(self.population_checkboxes[-1], len(self.populations)+1, 0, 1, 1)
|
|
404
|
+
|
|
326
405
|
def browse_experiment_folder(self):
|
|
327
406
|
|
|
328
407
|
"""
|
|
@@ -362,6 +441,17 @@ class ConfigNewExperiment(QMainWindow, Styles):
|
|
|
362
441
|
if returnValue == QMessageBox.Ok:
|
|
363
442
|
return None
|
|
364
443
|
|
|
444
|
+
populations_checked = [self.population_checkboxes[i].isChecked() for i in range(len(self.population_checkboxes))]
|
|
445
|
+
if not np.any(populations_checked):
|
|
446
|
+
msgBox = QMessageBox()
|
|
447
|
+
msgBox.setIcon(QMessageBox.Warning)
|
|
448
|
+
msgBox.setText("Please set at least one cell population before proceeding...")
|
|
449
|
+
msgBox.setWindowTitle("Warning")
|
|
450
|
+
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
451
|
+
returnValue = msgBox.exec()
|
|
452
|
+
if returnValue == QMessageBox.Ok:
|
|
453
|
+
return None
|
|
454
|
+
|
|
365
455
|
sorted_list = set(channel_indices)
|
|
366
456
|
expected_list = set(np.arange(max(sorted_list)+1))
|
|
367
457
|
print(sorted_list, expected_list, sorted_list==expected_list)
|
|
@@ -429,8 +519,13 @@ class ConfigNewExperiment(QMainWindow, Styles):
|
|
|
429
519
|
Write all user input parameters to a configuration file associated to an experiment.
|
|
430
520
|
"""
|
|
431
521
|
|
|
522
|
+
|
|
432
523
|
config = ConfigParser(interpolation=None)
|
|
433
524
|
|
|
525
|
+
config.add_section('Populations')
|
|
526
|
+
pops = ','.join([self.populations[i].lower() for i in range(len(self.population_checkboxes)) if self.population_checkboxes[i].isChecked()])
|
|
527
|
+
config.set('Populations','populations', pops)
|
|
528
|
+
|
|
434
529
|
# add a new section and some values
|
|
435
530
|
config.add_section('MovieSettings')
|
|
436
531
|
config.set('MovieSettings', 'PxToUm', self.PxToUm_field.text().replace(',','.'))
|
|
@@ -456,6 +551,7 @@ class ConfigNewExperiment(QMainWindow, Styles):
|
|
|
456
551
|
config.add_section('Metadata')
|
|
457
552
|
config.set('Metadata', 'concentration_units', self.concentration_units)
|
|
458
553
|
|
|
554
|
+
|
|
459
555
|
# save to a file
|
|
460
556
|
with open('config.ini', 'w') as configfile:
|
|
461
557
|
config.write(configfile)
|
|
@@ -464,7 +560,7 @@ class ConfigNewExperiment(QMainWindow, Styles):
|
|
|
464
560
|
print(f'New experiment successfully configured in folder {self.directory}...')
|
|
465
561
|
self.close()
|
|
466
562
|
|
|
467
|
-
class SetupConditionLabels(
|
|
563
|
+
class SetupConditionLabels(CelldetectiveWidget):
|
|
468
564
|
def __init__(self, parent_window, n_wells):
|
|
469
565
|
super().__init__()
|
|
470
566
|
self.parent_window = parent_window
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import
|
|
2
|
-
QTabWidget, QVBoxLayout,
|
|
1
|
+
from PyQt5.QtWidgets import QPushButton, QHBoxLayout, QLabel, QGridLayout, QFrame, \
|
|
2
|
+
QTabWidget, QVBoxLayout, QScrollArea, QDesktopWidget
|
|
3
|
+
from celldetective.gui import CelldetectiveMainWindow, CelldetectiveWidget
|
|
4
|
+
|
|
3
5
|
from PyQt5.QtCore import Qt, QSize
|
|
4
|
-
from celldetective.gui.gui_utils import center_window, QHSeperationLine, QCheckableComboBox
|
|
5
|
-
from celldetective.utils import _extract_labels_from_config, ConfigSectionMap,
|
|
6
|
+
from celldetective.gui.gui_utils import center_window, QHSeperationLine, QCheckableComboBox, generic_message
|
|
7
|
+
from celldetective.utils import _extract_labels_from_config, ConfigSectionMap, extract_identity_col
|
|
6
8
|
from celldetective.gui import ConfigEditor, ProcessPanel, PreprocessingPanel, AnalysisPanel, NeighPanel
|
|
7
|
-
from celldetective.io import get_experiment_wells, get_config, get_spatial_calibration, get_temporal_calibration, get_experiment_concentrations, get_experiment_cell_types, get_experiment_antibodies, get_experiment_pharmaceutical_agents
|
|
9
|
+
from celldetective.io import extract_position_name, get_experiment_wells, get_config, get_spatial_calibration, get_temporal_calibration, get_experiment_concentrations, get_experiment_cell_types, get_experiment_antibodies, get_experiment_pharmaceutical_agents, get_experiment_populations, extract_well_name_and_number
|
|
8
10
|
from natsort import natsorted
|
|
9
11
|
from glob import glob
|
|
10
12
|
import os
|
|
@@ -15,11 +17,10 @@ import gc
|
|
|
15
17
|
import subprocess
|
|
16
18
|
from celldetective.gui.viewers import StackVisualizer
|
|
17
19
|
from celldetective.utils import extract_experiment_channels
|
|
18
|
-
from celldetective.gui import Styles
|
|
19
20
|
import pandas as pd
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
class ControlPanel(
|
|
23
|
+
class ControlPanel(CelldetectiveMainWindow):
|
|
23
24
|
|
|
24
25
|
def __init__(self, parent_window=None, exp_dir=""):
|
|
25
26
|
|
|
@@ -29,21 +30,20 @@ class ControlPanel(QMainWindow, Styles):
|
|
|
29
30
|
if not self.exp_dir.endswith(os.sep):
|
|
30
31
|
self.exp_dir = self.exp_dir+os.sep
|
|
31
32
|
self.setWindowTitle("celldetective")
|
|
32
|
-
self.setWindowIcon(self.celldetective_icon)
|
|
33
33
|
self.parent_window = parent_window
|
|
34
34
|
|
|
35
35
|
self.init_wells_and_positions()
|
|
36
36
|
self.load_configuration()
|
|
37
37
|
|
|
38
|
-
self.w =
|
|
38
|
+
self.w = CelldetectiveWidget()
|
|
39
39
|
self.grid = QGridLayout(self.w)
|
|
40
40
|
self.grid.setSpacing(5)
|
|
41
|
-
self.grid.setContentsMargins(10,10,10,10)
|
|
41
|
+
self.grid.setContentsMargins(10, 10, 10, 10) # left top right bottom
|
|
42
42
|
|
|
43
43
|
self.to_disable = []
|
|
44
44
|
self.generate_header()
|
|
45
|
-
self.
|
|
46
|
-
|
|
45
|
+
self.ProcessPopulations = [ProcessPanel(self, pop) for pop in self.populations]
|
|
46
|
+
|
|
47
47
|
self.NeighPanel = NeighPanel(self)
|
|
48
48
|
self.PreprocessingPanel = PreprocessingPanel(self)
|
|
49
49
|
|
|
@@ -57,9 +57,10 @@ class ControlPanel(QMainWindow, Styles):
|
|
|
57
57
|
self.SurvivalBlock = AnalysisPanel(self,title='Survival')
|
|
58
58
|
|
|
59
59
|
grid_process.addWidget(self.PreprocessingPanel)
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
for panel in self.ProcessPopulations:
|
|
61
|
+
grid_process.addWidget(panel)
|
|
62
62
|
grid_process.addWidget(self.NeighPanel)
|
|
63
|
+
|
|
63
64
|
grid_analyze.addWidget(self.SurvivalBlock)
|
|
64
65
|
|
|
65
66
|
self.scroll=QScrollArea()
|
|
@@ -92,7 +93,6 @@ class ControlPanel(QMainWindow, Styles):
|
|
|
92
93
|
self.screen_width = desktop.screenGeometry().width()
|
|
93
94
|
self.scroll.setMinimumWidth(440)
|
|
94
95
|
|
|
95
|
-
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
96
96
|
center_window(self)
|
|
97
97
|
|
|
98
98
|
|
|
@@ -108,11 +108,9 @@ class ControlPanel(QMainWindow, Styles):
|
|
|
108
108
|
self.wells = get_experiment_wells(self.exp_dir) #natsorted(glob(self.exp_dir + "W*" + os.sep))
|
|
109
109
|
self.positions = []
|
|
110
110
|
for w in self.wells:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
positions_path = natsorted(glob(os.sep.join([root, w, f"{w[1:]}*{os.sep}"])))
|
|
115
|
-
self.positions.append([os.path.split(pos[:-1])[1] for pos in positions_path])
|
|
111
|
+
well_name, well_nbr = extract_well_name_and_number(w)
|
|
112
|
+
positions_path = natsorted(glob(os.sep.join([w,f"{well_nbr}*", os.sep])))
|
|
113
|
+
self.positions.append([extract_position_name(pos) for pos in positions_path])
|
|
116
114
|
|
|
117
115
|
def generate_header(self):
|
|
118
116
|
|
|
@@ -274,15 +272,9 @@ class ControlPanel(QMainWindow, Styles):
|
|
|
274
272
|
movies = glob(self.pos + os.sep.join(['movie', f"{self.movie_prefix}*.tif"]))
|
|
275
273
|
|
|
276
274
|
if len(movies) == 0:
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
msgBox.setWindowTitle("Warning")
|
|
281
|
-
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
282
|
-
returnValue = msgBox.exec()
|
|
283
|
-
if returnValue == QMessageBox.Ok:
|
|
284
|
-
self.current_stack = None
|
|
285
|
-
return None
|
|
275
|
+
generic_message("Please select a position containing a movie...")
|
|
276
|
+
self.current_stack = None
|
|
277
|
+
return None
|
|
286
278
|
else:
|
|
287
279
|
self.current_stack = movies[0]
|
|
288
280
|
|
|
@@ -321,9 +313,11 @@ class ControlPanel(QMainWindow, Styles):
|
|
|
321
313
|
print('Reading experiment configuration...')
|
|
322
314
|
self.exp_config = get_config(self.exp_dir)
|
|
323
315
|
|
|
316
|
+
self.populations = get_experiment_populations(self.exp_dir)
|
|
324
317
|
self.PxToUm = get_spatial_calibration(self.exp_dir)
|
|
325
318
|
self.FrameToMin = get_temporal_calibration(self.exp_dir)
|
|
326
319
|
|
|
320
|
+
|
|
327
321
|
self.len_movie = int(ConfigSectionMap(self.exp_config,"MovieSettings")["len_movie"])
|
|
328
322
|
self.shape_x = int(ConfigSectionMap(self.exp_config,"MovieSettings")["shape_x"])
|
|
329
323
|
self.shape_y = int(ConfigSectionMap(self.exp_config,"MovieSettings")["shape_y"])
|
|
@@ -350,7 +344,7 @@ class ControlPanel(QMainWindow, Styles):
|
|
|
350
344
|
Close child windows if closed.
|
|
351
345
|
"""
|
|
352
346
|
|
|
353
|
-
for process_block in
|
|
347
|
+
for process_block in self.ProcessPopulations:
|
|
354
348
|
try:
|
|
355
349
|
if process_block.SegModelLoader:
|
|
356
350
|
process_block.SegModelLoader.close()
|
|
@@ -436,14 +430,8 @@ class ControlPanel(QMainWindow, Styles):
|
|
|
436
430
|
"""
|
|
437
431
|
|
|
438
432
|
if self.well_list.isMultipleSelection():
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
msgBox.setText("Please select a single well...")
|
|
442
|
-
msgBox.setWindowTitle("Error")
|
|
443
|
-
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
444
|
-
returnValue = msgBox.exec()
|
|
445
|
-
if returnValue == QMessageBox.Ok:
|
|
446
|
-
return False
|
|
433
|
+
generic_message("Please select a single well...")
|
|
434
|
+
return False
|
|
447
435
|
else:
|
|
448
436
|
self.well_index = self.well_list.getSelectedIndices() #[self.well_list.currentIndex()]
|
|
449
437
|
|
|
@@ -451,14 +439,8 @@ class ControlPanel(QMainWindow, Styles):
|
|
|
451
439
|
|
|
452
440
|
pos = self.positions[w_idx]
|
|
453
441
|
if not self.position_list.isSingleSelection():
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
msgBox.setText("Please select a single position...")
|
|
457
|
-
msgBox.setWindowTitle("Error")
|
|
458
|
-
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
459
|
-
returnValue = msgBox.exec()
|
|
460
|
-
if returnValue == QMessageBox.Ok:
|
|
461
|
-
return False
|
|
442
|
+
generic_message("Please select a single position...")
|
|
443
|
+
return False
|
|
462
444
|
else:
|
|
463
445
|
pos_indices = self.position_list.getSelectedIndices()
|
|
464
446
|
|
|
@@ -483,127 +465,72 @@ class ControlPanel(QMainWindow, Styles):
|
|
|
483
465
|
def update_position_options(self):
|
|
484
466
|
|
|
485
467
|
self.pos = self.position_list.currentText()
|
|
486
|
-
panels = [self.ProcessEffectors, self.ProcessTargets]
|
|
487
468
|
|
|
488
469
|
if self.position_list.isMultipleSelection() or not self.position_list.isAnySelected():
|
|
489
470
|
|
|
490
|
-
for p in
|
|
471
|
+
for p in self.ProcessPopulations:
|
|
491
472
|
p.check_seg_btn.setEnabled(False)
|
|
492
473
|
p.check_tracking_result_btn.setEnabled(False)
|
|
474
|
+
p.view_tab_btn.setEnabled(True)
|
|
475
|
+
p.signal_analysis_action.setEnabled(True)
|
|
476
|
+
p.check_seg_btn.setEnabled(False)
|
|
477
|
+
p.check_tracking_result_btn.setEnabled(False)
|
|
478
|
+
p.check_measurements_btn.setEnabled(False)
|
|
479
|
+
p.check_signals_btn.setEnabled(False)
|
|
480
|
+
p.delete_tracks_btn.hide()
|
|
493
481
|
|
|
494
|
-
self.ProcessTargets.view_tab_btn.setEnabled(True)
|
|
495
|
-
self.ProcessEffectors.view_tab_btn.setEnabled(True)
|
|
496
|
-
self.NeighPanel.view_tab_btn.setEnabled(True)
|
|
497
|
-
self.ProcessEffectors.signal_analysis_action.setEnabled(True)
|
|
498
|
-
self.ProcessTargets.signal_analysis_action.setEnabled(True)
|
|
499
|
-
|
|
500
|
-
self.ProcessTargets.check_seg_btn.setEnabled(False)
|
|
501
|
-
self.ProcessEffectors.check_seg_btn.setEnabled(False)
|
|
502
|
-
|
|
503
|
-
self.ProcessTargets.check_tracking_result_btn.setEnabled(False)
|
|
504
|
-
self.ProcessEffectors.check_tracking_result_btn.setEnabled(False)
|
|
505
|
-
|
|
506
|
-
self.ProcessEffectors.check_measurements_btn.setEnabled(False)
|
|
507
|
-
self.ProcessTargets.check_measurements_btn.setEnabled(False)
|
|
508
|
-
#self.ProcessTargets.signal_analysis_action.setEnabled(False)
|
|
509
|
-
#self.ProcessEffectors.signal_analysis_action.setEnabled(False)
|
|
510
482
|
|
|
511
|
-
self.
|
|
512
|
-
self.ProcessEffectors.check_signals_btn.setEnabled(False)
|
|
483
|
+
self.NeighPanel.view_tab_btn.setEnabled(True)
|
|
513
484
|
self.NeighPanel.check_signals_btn.setEnabled(False)
|
|
514
|
-
self.ProcessTargets.delete_tracks_btn.hide()
|
|
515
|
-
self.ProcessEffectors.delete_tracks_btn.hide()
|
|
516
|
-
|
|
517
485
|
self.view_stack_btn.setEnabled(False)
|
|
518
486
|
|
|
519
487
|
elif self.well_list.isMultipleSelection():
|
|
520
488
|
|
|
521
|
-
self.
|
|
522
|
-
|
|
489
|
+
for p in self.ProcessPopulations:
|
|
490
|
+
p.view_tab_btn.setEnabled(True)
|
|
491
|
+
p.signal_analysis_action.setEnabled(True)
|
|
492
|
+
p.delete_tracks_btn.hide()
|
|
493
|
+
|
|
494
|
+
|
|
523
495
|
self.NeighPanel.view_tab_btn.setEnabled(True)
|
|
524
496
|
self.view_stack_btn.setEnabled(False)
|
|
525
|
-
self.ProcessEffectors.signal_analysis_action.setEnabled(True)
|
|
526
|
-
self.ProcessTargets.signal_analysis_action.setEnabled(True)
|
|
527
497
|
if hasattr(self,'delete_tracks_btn'):
|
|
528
498
|
self.delete_tracks_btn.hide()
|
|
529
|
-
self.ProcessTargets.delete_tracks_btn.hide()
|
|
530
|
-
self.ProcessEffectors.delete_tracks_btn.hide()
|
|
531
499
|
else:
|
|
532
500
|
|
|
533
501
|
if self.well_list.isAnySelected() and self.position_list.isAnySelected():
|
|
534
502
|
|
|
535
503
|
self.locate_selected_position()
|
|
536
504
|
self.view_stack_btn.setEnabled(True)
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
if id_col=='TRACK_ID':
|
|
556
|
-
self.ProcessEffectors.check_signals_btn.setEnabled(True)
|
|
557
|
-
self.ProcessEffectors.delete_tracks_btn.show()
|
|
558
|
-
self.ProcessEffectors.signal_analysis_action.setEnabled(True)
|
|
559
|
-
self.ProcessEffectors.check_tracking_result_btn.setEnabled(True)
|
|
505
|
+
for i,p in enumerate(self.ProcessPopulations):
|
|
506
|
+
p.check_seg_btn.setEnabled(True)
|
|
507
|
+
if os.path.exists(os.sep.join([self.pos,'output','tables',f'trajectories_{self.populations[i]}.csv'])):
|
|
508
|
+
df = pd.read_csv(os.sep.join([self.pos,'output','tables',f'trajectories_{self.populations[i]}.csv']), nrows=1)
|
|
509
|
+
id_col = extract_identity_col(df)
|
|
510
|
+
p.check_measurements_btn.setEnabled(True)
|
|
511
|
+
|
|
512
|
+
if id_col=='TRACK_ID':
|
|
513
|
+
p.check_signals_btn.setEnabled(True)
|
|
514
|
+
p.delete_tracks_btn.show()
|
|
515
|
+
p.signal_analysis_action.setEnabled(True)
|
|
516
|
+
p.check_tracking_result_btn.setEnabled(True)
|
|
517
|
+
else:
|
|
518
|
+
p.signal_analysis_action.setEnabled(False)
|
|
519
|
+
p.check_tracking_result_btn.setEnabled(False)
|
|
520
|
+
|
|
521
|
+
p.view_tab_btn.setEnabled(True)
|
|
522
|
+
p.classify_btn.setEnabled(True)
|
|
560
523
|
else:
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
else:
|
|
568
|
-
self.ProcessEffectors.check_measurements_btn.setEnabled(False)
|
|
569
|
-
self.ProcessEffectors.check_signals_btn.setEnabled(False)
|
|
570
|
-
#self.ProcessEffectors.signal_analysis_action.setEnabled(False)
|
|
571
|
-
self.ProcessEffectors.view_tab_btn.setEnabled(False)
|
|
572
|
-
self.ProcessEffectors.classify_btn.setEnabled(False)
|
|
573
|
-
self.ProcessEffectors.delete_tracks_btn.hide()
|
|
574
|
-
self.ProcessEffectors.signal_analysis_action.setEnabled(False)
|
|
575
|
-
|
|
576
|
-
if os.path.exists(os.sep.join([self.pos,'output','tables','trajectories_targets.csv'])):
|
|
577
|
-
df = pd.read_csv(os.sep.join([self.pos,'output','tables','trajectories_targets.csv']), nrows=1)
|
|
578
|
-
id_col = extract_identity_col(df)
|
|
579
|
-
self.ProcessTargets.check_measurements_btn.setEnabled(True)
|
|
580
|
-
if id_col=='TRACK_ID':
|
|
581
|
-
self.ProcessTargets.check_signals_btn.setEnabled(True)
|
|
582
|
-
self.ProcessTargets.signal_analysis_action.setEnabled(True)
|
|
583
|
-
self.ProcessTargets.check_tracking_result_btn.setEnabled(True)
|
|
584
|
-
self.ProcessTargets.delete_tracks_btn.show()
|
|
585
|
-
else:
|
|
586
|
-
self.ProcessTargets.signal_analysis_action.setEnabled(False)
|
|
587
|
-
self.ProcessTargets.check_tracking_result_btn.setEnabled(False)
|
|
588
|
-
|
|
589
|
-
#self.ProcessTargets.signal_analysis_action.setEnabled(True)
|
|
590
|
-
self.ProcessTargets.view_tab_btn.setEnabled(True)
|
|
591
|
-
self.ProcessTargets.classify_btn.setEnabled(True)
|
|
592
|
-
else:
|
|
593
|
-
self.ProcessTargets.check_measurements_btn.setEnabled(False)
|
|
594
|
-
self.ProcessTargets.check_signals_btn.setEnabled(False)
|
|
595
|
-
#self.ProcessTargets.signal_analysis_action.setEnabled(False)
|
|
596
|
-
self.ProcessTargets.view_tab_btn.setEnabled(False)
|
|
597
|
-
self.ProcessTargets.classify_btn.setEnabled(False)
|
|
598
|
-
self.ProcessTargets.signal_analysis_action.setEnabled(False)
|
|
599
|
-
self.ProcessTargets.delete_tracks_btn.hide()
|
|
600
|
-
self.ProcessTargets.signal_analysis_action.setEnabled(False)
|
|
524
|
+
p.check_measurements_btn.setEnabled(False)
|
|
525
|
+
p.check_signals_btn.setEnabled(False)
|
|
526
|
+
p.view_tab_btn.setEnabled(False)
|
|
527
|
+
p.classify_btn.setEnabled(False)
|
|
528
|
+
p.delete_tracks_btn.hide()
|
|
529
|
+
p.signal_analysis_action.setEnabled(False)
|
|
601
530
|
|
|
602
531
|
if os.path.exists(os.sep.join([self.pos,'output','tables','trajectories_pairs.csv'])):
|
|
603
532
|
self.NeighPanel.view_tab_btn.setEnabled(True)
|
|
604
533
|
self.NeighPanel.check_signals_btn.setEnabled(True)
|
|
605
534
|
else:
|
|
606
535
|
self.NeighPanel.view_tab_btn.setEnabled(False)
|
|
607
|
-
self.NeighPanel.check_signals_btn.setEnabled(False)
|
|
608
|
-
|
|
609
|
-
|
|
536
|
+
self.NeighPanel.check_signals_btn.setEnabled(False)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from PyQt5.QtWidgets import QMessageBox,QGridLayout, QButtonGroup, \
|
|
2
|
-
QCheckBox, QLineEdit, QVBoxLayout,
|
|
2
|
+
QCheckBox, QLineEdit, QVBoxLayout, QLabel, QHBoxLayout, QPushButton, \
|
|
3
3
|
QRadioButton
|
|
4
4
|
from PyQt5.QtCore import Qt, QSize
|
|
5
5
|
from PyQt5.QtGui import QDoubleValidator
|
|
@@ -18,13 +18,13 @@ import matplotlib.pyplot as plt
|
|
|
18
18
|
plt.rcParams['svg.fonttype'] = 'none'
|
|
19
19
|
from glob import glob
|
|
20
20
|
from matplotlib.cm import tab10
|
|
21
|
-
from celldetective.gui import
|
|
21
|
+
from celldetective.gui import CelldetectiveWidget
|
|
22
22
|
import matplotlib.cm as mcm
|
|
23
23
|
import pandas as pd
|
|
24
24
|
|
|
25
25
|
from lifelines.utils import qth_survival_times
|
|
26
26
|
|
|
27
|
-
class GenericSignalPlotWidget(
|
|
27
|
+
class GenericSignalPlotWidget(CelldetectiveWidget):
|
|
28
28
|
|
|
29
29
|
def __init__(self, df = None, df_pos_info = None, df_well_info = None, feature_selected = None, parent_window=None, title='plot', *args, **kwargs):
|
|
30
30
|
|
|
@@ -33,7 +33,6 @@ class GenericSignalPlotWidget(QWidget, Styles):
|
|
|
33
33
|
|
|
34
34
|
self.parent_window = parent_window
|
|
35
35
|
self.setWindowTitle(title)
|
|
36
|
-
self.setWindowIcon(self.celldetective_icon)
|
|
37
36
|
|
|
38
37
|
self.show_ci = False
|
|
39
38
|
self.legend_visible = True
|
|
@@ -60,8 +59,6 @@ class GenericSignalPlotWidget(QWidget, Styles):
|
|
|
60
59
|
|
|
61
60
|
self.fig.tight_layout()
|
|
62
61
|
self.setLayout(self.layout)
|
|
63
|
-
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
64
|
-
|
|
65
62
|
|
|
66
63
|
def populate_widget(self):
|
|
67
64
|
|
|
@@ -170,7 +167,7 @@ class GenericSignalPlotWidget(QWidget, Styles):
|
|
|
170
167
|
self.ci_btn.clicked.connect(self.switch_ci)
|
|
171
168
|
self.cell_lines_btn.clicked.connect(self.switch_cell_lines)
|
|
172
169
|
|
|
173
|
-
self.class_selection_widget =
|
|
170
|
+
self.class_selection_widget = CelldetectiveWidget()
|
|
174
171
|
|
|
175
172
|
self.class_selection_lbl = QLabel('class of interest:')
|
|
176
173
|
class_hbox = QHBoxLayout()
|
|
@@ -197,7 +194,7 @@ class GenericSignalPlotWidget(QWidget, Styles):
|
|
|
197
194
|
self.layout.addWidget(self.class_selection_widget) #Layout(class_hbox)
|
|
198
195
|
|
|
199
196
|
# Rescale
|
|
200
|
-
self.rescale_widget =
|
|
197
|
+
self.rescale_widget = CelldetectiveWidget()
|
|
201
198
|
|
|
202
199
|
scale_hbox = QHBoxLayout()
|
|
203
200
|
self.rescale_widget.setLayout(scale_hbox)
|
|
@@ -216,7 +213,7 @@ class GenericSignalPlotWidget(QWidget, Styles):
|
|
|
216
213
|
|
|
217
214
|
|
|
218
215
|
# Rescale
|
|
219
|
-
self.cell_lines_alpha_wdg =
|
|
216
|
+
self.cell_lines_alpha_wdg = CelldetectiveWidget()
|
|
220
217
|
alpha_hbox = QHBoxLayout()
|
|
221
218
|
|
|
222
219
|
self.alpha_slider = QLabeledDoubleSlider()
|
|
@@ -225,7 +222,7 @@ class GenericSignalPlotWidget(QWidget, Styles):
|
|
|
225
222
|
slider_initial_value=self.alpha_setting,
|
|
226
223
|
slider_range=(0,1),
|
|
227
224
|
decimal_option=True,
|
|
228
|
-
precision=
|
|
225
|
+
precision=5,
|
|
229
226
|
)
|
|
230
227
|
self.alpha_slider.valueChanged.connect(self.submit_alpha)
|
|
231
228
|
self.cell_lines_alpha_wdg.setLayout(alpha_hbox)
|
|
@@ -343,7 +340,7 @@ class GenericSignalPlotWidget(QWidget, Styles):
|
|
|
343
340
|
thresh = 20
|
|
344
341
|
self.well_name_truncated = [w[:thresh - 3]+'...' if len(w)>thresh else w for w in self.usable_well_labels]
|
|
345
342
|
|
|
346
|
-
self.line_choice_widget =
|
|
343
|
+
self.line_choice_widget = CelldetectiveWidget()
|
|
347
344
|
self.line_check_vbox = QGridLayout()
|
|
348
345
|
self.line_choice_widget.setLayout(self.line_check_vbox)
|
|
349
346
|
|
|
@@ -828,7 +825,7 @@ class SurvivalPlotWidget(GenericSignalPlotWidget):
|
|
|
828
825
|
|
|
829
826
|
def set_table_options(self):
|
|
830
827
|
|
|
831
|
-
self.config_table_wg =
|
|
828
|
+
self.config_table_wg = CelldetectiveWidget()
|
|
832
829
|
self.config_table_wg.setMinimumWidth(480)
|
|
833
830
|
self.config_table_wg.setWindowTitle('Survival data')
|
|
834
831
|
|