celldetective 1.4.2__py3-none-any.whl → 1.5.0b1__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 +25 -0
- celldetective/__main__.py +62 -43
- celldetective/_version.py +1 -1
- celldetective/extra_properties.py +477 -399
- celldetective/filters.py +192 -97
- celldetective/gui/InitWindow.py +541 -411
- celldetective/gui/__init__.py +0 -15
- celldetective/gui/about.py +44 -39
- celldetective/gui/analyze_block.py +120 -84
- celldetective/gui/base/__init__.py +0 -0
- celldetective/gui/base/channel_norm_generator.py +335 -0
- celldetective/gui/base/components.py +249 -0
- celldetective/gui/base/feature_choice.py +92 -0
- celldetective/gui/base/figure_canvas.py +52 -0
- celldetective/gui/base/list_widget.py +133 -0
- celldetective/gui/{styles.py → base/styles.py} +92 -36
- celldetective/gui/base/utils.py +33 -0
- celldetective/gui/base_annotator.py +900 -767
- celldetective/gui/classifier_widget.py +6 -22
- celldetective/gui/configure_new_exp.py +777 -671
- celldetective/gui/control_panel.py +635 -524
- celldetective/gui/dynamic_progress.py +449 -0
- celldetective/gui/event_annotator.py +2023 -1662
- celldetective/gui/generic_signal_plot.py +1292 -944
- celldetective/gui/gui_utils.py +899 -1289
- celldetective/gui/interactions_block.py +658 -0
- celldetective/gui/interactive_timeseries_viewer.py +447 -0
- celldetective/gui/json_readers.py +48 -15
- celldetective/gui/layouts/__init__.py +5 -0
- celldetective/gui/layouts/background_model_free_layout.py +537 -0
- celldetective/gui/layouts/channel_offset_layout.py +134 -0
- celldetective/gui/layouts/local_correction_layout.py +91 -0
- celldetective/gui/layouts/model_fit_layout.py +372 -0
- celldetective/gui/layouts/operation_layout.py +68 -0
- celldetective/gui/layouts/protocol_designer_layout.py +96 -0
- celldetective/gui/pair_event_annotator.py +3130 -2435
- celldetective/gui/plot_measurements.py +586 -267
- celldetective/gui/plot_signals_ui.py +724 -506
- celldetective/gui/preprocessing_block.py +395 -0
- celldetective/gui/process_block.py +1678 -1831
- celldetective/gui/seg_model_loader.py +580 -473
- celldetective/gui/settings/__init__.py +0 -7
- celldetective/gui/settings/_cellpose_model_params.py +181 -0
- celldetective/gui/settings/_event_detection_model_params.py +95 -0
- celldetective/gui/settings/_segmentation_model_params.py +159 -0
- celldetective/gui/settings/_settings_base.py +77 -65
- celldetective/gui/settings/_settings_event_model_training.py +752 -526
- celldetective/gui/settings/_settings_measurements.py +1133 -964
- celldetective/gui/settings/_settings_neighborhood.py +574 -488
- celldetective/gui/settings/_settings_segmentation_model_training.py +779 -564
- celldetective/gui/settings/_settings_signal_annotator.py +329 -305
- celldetective/gui/settings/_settings_tracking.py +1304 -1094
- celldetective/gui/settings/_stardist_model_params.py +98 -0
- celldetective/gui/survival_ui.py +422 -312
- celldetective/gui/tableUI.py +1665 -1701
- celldetective/gui/table_ops/_maths.py +295 -0
- celldetective/gui/table_ops/_merge_groups.py +140 -0
- celldetective/gui/table_ops/_merge_one_hot.py +95 -0
- celldetective/gui/table_ops/_query_table.py +43 -0
- celldetective/gui/table_ops/_rename_col.py +44 -0
- celldetective/gui/thresholds_gui.py +382 -179
- celldetective/gui/viewers/__init__.py +0 -0
- celldetective/gui/viewers/base_viewer.py +700 -0
- celldetective/gui/viewers/channel_offset_viewer.py +331 -0
- celldetective/gui/viewers/contour_viewer.py +394 -0
- celldetective/gui/viewers/size_viewer.py +153 -0
- celldetective/gui/viewers/spot_detection_viewer.py +341 -0
- celldetective/gui/viewers/threshold_viewer.py +309 -0
- celldetective/gui/workers.py +403 -126
- celldetective/log_manager.py +92 -0
- celldetective/measure.py +1895 -1478
- celldetective/napari/__init__.py +0 -0
- celldetective/napari/utils.py +1025 -0
- celldetective/neighborhood.py +1914 -1448
- celldetective/preprocessing.py +1620 -1220
- celldetective/processes/__init__.py +0 -0
- celldetective/processes/background_correction.py +271 -0
- celldetective/processes/compute_neighborhood.py +894 -0
- celldetective/processes/detect_events.py +246 -0
- celldetective/processes/downloader.py +137 -0
- celldetective/processes/measure_cells.py +565 -0
- celldetective/processes/segment_cells.py +760 -0
- celldetective/processes/track_cells.py +435 -0
- celldetective/processes/train_segmentation_model.py +694 -0
- celldetective/processes/train_signal_model.py +265 -0
- celldetective/processes/unified_process.py +292 -0
- celldetective/regionprops/_regionprops.py +358 -317
- celldetective/relative_measurements.py +987 -710
- celldetective/scripts/measure_cells.py +313 -212
- celldetective/scripts/measure_relative.py +90 -46
- celldetective/scripts/segment_cells.py +165 -104
- celldetective/scripts/segment_cells_thresholds.py +96 -68
- celldetective/scripts/track_cells.py +198 -149
- celldetective/scripts/train_segmentation_model.py +324 -201
- celldetective/scripts/train_signal_model.py +87 -45
- celldetective/segmentation.py +844 -749
- celldetective/signals.py +3514 -2861
- celldetective/tracking.py +30 -15
- celldetective/utils/__init__.py +0 -0
- celldetective/utils/cellpose_utils/__init__.py +133 -0
- celldetective/utils/color_mappings.py +42 -0
- celldetective/utils/data_cleaning.py +630 -0
- celldetective/utils/data_loaders.py +450 -0
- celldetective/utils/dataset_helpers.py +207 -0
- celldetective/utils/downloaders.py +235 -0
- celldetective/utils/event_detection/__init__.py +8 -0
- celldetective/utils/experiment.py +1782 -0
- celldetective/utils/image_augmenters.py +308 -0
- celldetective/utils/image_cleaning.py +74 -0
- celldetective/utils/image_loaders.py +926 -0
- celldetective/utils/image_transforms.py +335 -0
- celldetective/utils/io.py +62 -0
- celldetective/utils/mask_cleaning.py +348 -0
- celldetective/utils/mask_transforms.py +5 -0
- celldetective/utils/masks.py +184 -0
- celldetective/utils/maths.py +351 -0
- celldetective/utils/model_getters.py +325 -0
- celldetective/utils/model_loaders.py +296 -0
- celldetective/utils/normalization.py +380 -0
- celldetective/utils/parsing.py +465 -0
- celldetective/utils/plots/__init__.py +0 -0
- celldetective/utils/plots/regression.py +53 -0
- celldetective/utils/resources.py +34 -0
- celldetective/utils/stardist_utils/__init__.py +104 -0
- celldetective/utils/stats.py +90 -0
- celldetective/utils/types.py +21 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/METADATA +1 -1
- celldetective-1.5.0b1.dist-info/RECORD +187 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/WHEEL +1 -1
- tests/gui/test_new_project.py +129 -117
- tests/gui/test_project.py +127 -79
- tests/test_filters.py +39 -15
- tests/test_notebooks.py +8 -0
- tests/test_tracking.py +232 -13
- tests/test_utils.py +123 -77
- celldetective/gui/base_components.py +0 -23
- celldetective/gui/layouts.py +0 -1602
- celldetective/gui/processes/compute_neighborhood.py +0 -594
- celldetective/gui/processes/downloader.py +0 -111
- celldetective/gui/processes/measure_cells.py +0 -360
- celldetective/gui/processes/segment_cells.py +0 -499
- celldetective/gui/processes/track_cells.py +0 -303
- celldetective/gui/processes/train_segmentation_model.py +0 -270
- celldetective/gui/processes/train_signal_model.py +0 -108
- celldetective/gui/table_ops/merge_groups.py +0 -118
- celldetective/gui/viewers.py +0 -1354
- celldetective/io.py +0 -3663
- celldetective/utils.py +0 -3108
- celldetective-1.4.2.dist-info/RECORD +0 -123
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/licenses/LICENSE +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/top_level.txt +0 -0
celldetective/gui/__init__.py
CHANGED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
from .styles import Styles
|
|
2
|
-
from .base_components import CelldetectiveWidget, CelldetectiveMainWindow
|
|
3
|
-
from .json_readers import ConfigEditor
|
|
4
|
-
from .tableUI import TableUI
|
|
5
|
-
from .classifier_widget import ClassifierWidget
|
|
6
|
-
from .survival_ui import ConfigSurvival
|
|
7
|
-
from .plot_signals_ui import ConfigSignalPlot
|
|
8
|
-
from .event_annotator import EventAnnotator
|
|
9
|
-
from .pair_event_annotator import PairEventAnnotator
|
|
10
|
-
from .thresholds_gui import ThresholdConfigWizard
|
|
11
|
-
from .seg_model_loader import SegmentationModelLoader
|
|
12
|
-
from .process_block import ProcessPanel, NeighPanel, PreprocessingPanel
|
|
13
|
-
from .analyze_block import AnalysisPanel
|
|
14
|
-
from .control_panel import ControlPanel
|
|
15
|
-
from .configure_new_exp import ConfigNewExperiment
|
celldetective/gui/about.py
CHANGED
|
@@ -2,47 +2,52 @@ from PyQt5.QtWidgets import QVBoxLayout, QLabel
|
|
|
2
2
|
from PyQt5.QtGui import QPixmap
|
|
3
3
|
from PyQt5.QtCore import Qt
|
|
4
4
|
|
|
5
|
-
from celldetective.gui import CelldetectiveWidget
|
|
6
|
-
from celldetective.utils import
|
|
7
|
-
import os
|
|
8
|
-
from celldetective.gui.gui_utils import center_window
|
|
5
|
+
from celldetective.gui.base.components import CelldetectiveWidget
|
|
6
|
+
from celldetective.gui.base.utils import center_window
|
|
9
7
|
from celldetective._version import __version__
|
|
10
8
|
|
|
11
9
|
|
|
12
10
|
class AboutWidget(CelldetectiveWidget):
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
super().__init__()
|
|
14
|
+
self.setWindowTitle("About celldetective")
|
|
15
|
+
self.setMaximumWidth(320)
|
|
16
|
+
center_window(self)
|
|
17
|
+
|
|
18
|
+
logo = QPixmap(self.celldetective_logo_path)
|
|
19
|
+
|
|
20
|
+
# Create the layout
|
|
21
|
+
layout = QVBoxLayout(self)
|
|
22
|
+
img_label = QLabel("")
|
|
23
|
+
img_label.setPixmap(logo)
|
|
24
|
+
layout.addWidget(img_label, alignment=Qt.AlignCenter)
|
|
25
|
+
|
|
26
|
+
self.soft_name = QLabel("celldetective")
|
|
27
|
+
self.soft_name.setStyleSheet(
|
|
28
|
+
"""font-weight: bold;
|
|
30
29
|
font-size: 18px;
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
30
|
+
"""
|
|
31
|
+
)
|
|
32
|
+
layout.addWidget(self.soft_name, alignment=Qt.AlignCenter)
|
|
33
|
+
|
|
34
|
+
self.version_lbl = QLabel(
|
|
35
|
+
f'Version {__version__} <a href="https://github.com/celldetective/celldetective'
|
|
36
|
+
f'/releases">(release notes)</a>'
|
|
37
|
+
)
|
|
38
|
+
self.version_lbl.setOpenExternalLinks(True)
|
|
39
|
+
layout.addWidget(self.version_lbl, alignment=Qt.AlignCenter)
|
|
40
|
+
|
|
41
|
+
self.lab_lbl = QLabel(
|
|
42
|
+
"Developed at Laboratoire Adhésion et Inflammation (LAI) INSERM U1067 CNRS UMR 7333"
|
|
43
|
+
)
|
|
44
|
+
self.lab_lbl.setWordWrap(True)
|
|
45
|
+
layout.addWidget(self.lab_lbl, alignment=Qt.AlignCenter)
|
|
46
|
+
|
|
47
|
+
self.centuri_mention = QLabel(
|
|
48
|
+
"The project leading to this publication has received funding from France 2030, the French Government "
|
|
49
|
+
"program managed by the French National Research Agency (ANR-16-CONV-0001) and from Excellence Initiative "
|
|
50
|
+
"of Aix-Marseille University - AMIDEX')"
|
|
51
|
+
)
|
|
52
|
+
self.centuri_mention.setWordWrap(True)
|
|
53
|
+
layout.addWidget(self.centuri_mention, alignment=Qt.AlignCenter)
|
|
@@ -1,96 +1,132 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import
|
|
2
|
-
|
|
1
|
+
from PyQt5.QtWidgets import (
|
|
2
|
+
QFrame,
|
|
3
|
+
QLabel,
|
|
4
|
+
QPushButton,
|
|
5
|
+
QVBoxLayout,
|
|
6
|
+
QSpacerItem,
|
|
7
|
+
QSizePolicy,
|
|
8
|
+
)
|
|
3
9
|
from PyQt5.QtCore import Qt, QSize
|
|
4
10
|
from PyQt5.QtGui import QIcon
|
|
5
|
-
from celldetective.gui import ConfigSurvival, ConfigSignalPlot
|
|
6
11
|
import os
|
|
7
|
-
from celldetective.gui import Styles
|
|
12
|
+
from celldetective.gui.base.styles import Styles
|
|
8
13
|
from glob import glob
|
|
9
14
|
|
|
10
|
-
from celldetective.gui.
|
|
15
|
+
from celldetective.gui.base.components import generic_message
|
|
16
|
+
from celldetective.gui.base.utils import center_window
|
|
11
17
|
|
|
12
18
|
|
|
13
19
|
class AnalysisPanel(QFrame, Styles):
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
20
|
+
def __init__(self, parent_window, title=None):
|
|
21
|
+
|
|
22
|
+
super().__init__()
|
|
23
|
+
self.parent_window = parent_window
|
|
24
|
+
self.title = title
|
|
25
|
+
if self.title is None:
|
|
26
|
+
self.title = ""
|
|
27
|
+
self.exp_channels = self.parent_window.exp_channels
|
|
28
|
+
self.exp_dir = self.parent_window.exp_dir
|
|
29
|
+
self.soft_path = self.parent_window.parent_window.soft_path
|
|
30
|
+
self.pop_exists = False
|
|
31
|
+
|
|
32
|
+
self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
33
|
+
self.grid = QVBoxLayout(self)
|
|
34
|
+
self.grid.setSpacing(20)
|
|
35
|
+
self.generate_header()
|
|
36
|
+
|
|
37
|
+
def generate_header(self):
|
|
38
|
+
"""
|
|
39
|
+
Read the mode and prepare a collapsable block to process a specific cell population.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
panel_title = QLabel("Survival")
|
|
44
|
+
panel_title.setStyleSheet(
|
|
45
|
+
"""
|
|
40
46
|
font-weight: bold;
|
|
41
47
|
padding: 0px;
|
|
42
|
-
"""
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
"""
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
self.grid.addWidget(panel_title, alignment=Qt.AlignCenter)
|
|
52
|
+
|
|
53
|
+
self.survival_btn = QPushButton("plot survival")
|
|
54
|
+
self.survival_btn.setIcon(
|
|
55
|
+
QIcon(
|
|
56
|
+
QIcon(
|
|
57
|
+
os.sep.join(
|
|
58
|
+
[self.soft_path, "celldetective", "icons", "survival2.png"]
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
self.survival_btn.setStyleSheet(self.button_style_sheet_2)
|
|
64
|
+
self.survival_btn.setIconSize(QSize(35, 35))
|
|
65
|
+
self.survival_btn.clicked.connect(self.configure_survival)
|
|
66
|
+
self.grid.addWidget(self.survival_btn)
|
|
67
|
+
|
|
68
|
+
signal_lbl = QLabel("Single-cell signals")
|
|
69
|
+
signal_lbl.setStyleSheet(
|
|
70
|
+
"""
|
|
55
71
|
font-weight: bold;
|
|
56
72
|
padding: 0px;
|
|
57
|
-
"""
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
73
|
+
"""
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
self.grid.addWidget(signal_lbl, alignment=Qt.AlignCenter)
|
|
77
|
+
|
|
78
|
+
self.plot_signal_btn = QPushButton("plot signals")
|
|
79
|
+
self.plot_signal_btn.setIcon(
|
|
80
|
+
QIcon(
|
|
81
|
+
QIcon(
|
|
82
|
+
os.sep.join(
|
|
83
|
+
[self.soft_path, "celldetective", "icons", "signals_icon.png"]
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
self.plot_signal_btn.setStyleSheet(self.button_style_sheet_2)
|
|
89
|
+
self.plot_signal_btn.setIconSize(QSize(35, 35))
|
|
90
|
+
self.plot_signal_btn.clicked.connect(self.configure_plot_signals)
|
|
91
|
+
self.grid.addWidget(self.plot_signal_btn)
|
|
92
|
+
|
|
93
|
+
vertical_spacer = QSpacerItem(
|
|
94
|
+
20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding
|
|
95
|
+
)
|
|
96
|
+
self.grid.addItem(vertical_spacer)
|
|
97
|
+
|
|
98
|
+
def check_for_tables(self):
|
|
99
|
+
|
|
100
|
+
for population in self.parent_window.populations:
|
|
101
|
+
tables = glob(
|
|
102
|
+
self.exp_dir
|
|
103
|
+
+ os.sep.join(
|
|
104
|
+
["W*", "*", "output", "tables", f"trajectories_{population}.csv"]
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
if len(tables) > 0:
|
|
108
|
+
self.pop_exists = True
|
|
109
|
+
|
|
110
|
+
def configure_survival(self):
|
|
111
|
+
from celldetective.gui.survival_ui import ConfigSurvival
|
|
112
|
+
|
|
113
|
+
self.check_for_tables()
|
|
114
|
+
if self.pop_exists:
|
|
115
|
+
self.config_survival = ConfigSurvival(self)
|
|
116
|
+
self.config_survival.show()
|
|
117
|
+
center_window(self.config_survival)
|
|
118
|
+
else:
|
|
119
|
+
generic_message("No population table could be found... Abort...")
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
def configure_plot_signals(self):
|
|
123
|
+
from celldetective.gui.plot_signals_ui import ConfigSignalPlot
|
|
124
|
+
|
|
125
|
+
self.check_for_tables()
|
|
126
|
+
if self.pop_exists:
|
|
127
|
+
self.config_signal_plot = ConfigSignalPlot(self)
|
|
128
|
+
self.config_signal_plot.show()
|
|
129
|
+
center_window(self.config_signal_plot)
|
|
130
|
+
else:
|
|
131
|
+
generic_message("No population table could be found... Abort...")
|
|
132
|
+
return None
|
|
File without changes
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from functools import partial
|
|
3
|
+
from glob import glob
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from PyQt5.QtCore import QSize, Qt
|
|
7
|
+
from PyQt5.QtWidgets import QVBoxLayout, QLabel, QPushButton, QLineEdit, QHBoxLayout
|
|
8
|
+
from fonticon_mdi6 import MDI6
|
|
9
|
+
from superqt import QSearchableComboBox
|
|
10
|
+
from superqt.fonticon import icon
|
|
11
|
+
|
|
12
|
+
from celldetective.gui.base.styles import Styles
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ChannelNormGenerator(QVBoxLayout, Styles):
|
|
16
|
+
"""Generator for list of channels"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, parent_window=None, init_n_channels=4, mode="signals", *args):
|
|
19
|
+
super().__init__(*args)
|
|
20
|
+
|
|
21
|
+
self.parent_window = parent_window
|
|
22
|
+
self.mode = mode
|
|
23
|
+
self.init_n_channels = init_n_channels
|
|
24
|
+
|
|
25
|
+
if hasattr(self.parent_window.parent_window, "locate_image"):
|
|
26
|
+
self.attr_parent = self.parent_window.parent_window
|
|
27
|
+
elif hasattr(self.parent_window.parent_window.parent_window, "locate_image"):
|
|
28
|
+
self.attr_parent = self.parent_window.parent_window.parent_window
|
|
29
|
+
else:
|
|
30
|
+
self.attr_parent = (
|
|
31
|
+
self.parent_window.parent_window.parent_window.parent_window
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
self.channel_names = self.attr_parent.exp_channels
|
|
35
|
+
self.setContentsMargins(15, 15, 15, 15)
|
|
36
|
+
self.generate_widgets()
|
|
37
|
+
self.add_to_layout()
|
|
38
|
+
|
|
39
|
+
def generate_widgets(self):
|
|
40
|
+
|
|
41
|
+
self.channel_cbs = [QSearchableComboBox() for i in range(self.init_n_channels)]
|
|
42
|
+
self.channel_labels = [QLabel() for i in range(self.init_n_channels)]
|
|
43
|
+
|
|
44
|
+
self.normalization_mode_btns = [
|
|
45
|
+
QPushButton("") for i in range(self.init_n_channels)
|
|
46
|
+
]
|
|
47
|
+
self.normalization_mode = [True for i in range(self.init_n_channels)]
|
|
48
|
+
self.normalization_clip_btns = [
|
|
49
|
+
QPushButton("") for i in range(self.init_n_channels)
|
|
50
|
+
]
|
|
51
|
+
self.clip_option = [False for i in range(self.init_n_channels)]
|
|
52
|
+
|
|
53
|
+
for i in range(self.init_n_channels):
|
|
54
|
+
self.normalization_mode_btns[i].setIcon(
|
|
55
|
+
icon(MDI6.percent_circle, color="#1565c0")
|
|
56
|
+
)
|
|
57
|
+
self.normalization_mode_btns[i].setIconSize(QSize(20, 20))
|
|
58
|
+
self.normalization_mode_btns[i].setStyleSheet(self.button_select_all)
|
|
59
|
+
self.normalization_mode_btns[i].setToolTip(
|
|
60
|
+
"Switch to absolute normalization values."
|
|
61
|
+
)
|
|
62
|
+
self.normalization_mode_btns[i].clicked.connect(
|
|
63
|
+
partial(self.switch_normalization_mode, i)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
self.normalization_clip_btns[i].setIcon(
|
|
67
|
+
icon(MDI6.content_cut, color="black")
|
|
68
|
+
)
|
|
69
|
+
self.normalization_clip_btns[i].setIconSize(QSize(20, 20))
|
|
70
|
+
self.normalization_clip_btns[i].setStyleSheet(self.button_select_all)
|
|
71
|
+
self.normalization_clip_btns[i].clicked.connect(
|
|
72
|
+
partial(self.switch_clipping_mode, i)
|
|
73
|
+
)
|
|
74
|
+
self.normalization_clip_btns[i].setToolTip("clip")
|
|
75
|
+
|
|
76
|
+
self.normalization_min_value_lbl = [
|
|
77
|
+
QLabel("Min %: ") for i in range(self.init_n_channels)
|
|
78
|
+
]
|
|
79
|
+
self.normalization_min_value_le = [
|
|
80
|
+
QLineEdit("0.1") for i in range(self.init_n_channels)
|
|
81
|
+
]
|
|
82
|
+
self.normalization_max_value_lbl = [
|
|
83
|
+
QLabel("Max %: ") for i in range(self.init_n_channels)
|
|
84
|
+
]
|
|
85
|
+
self.normalization_max_value_le = [
|
|
86
|
+
QLineEdit("99.99") for i in range(self.init_n_channels)
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
if self.mode == "signals":
|
|
90
|
+
tables = glob(
|
|
91
|
+
self.parent_window.exp_dir
|
|
92
|
+
+ os.sep.join(
|
|
93
|
+
[
|
|
94
|
+
"W*",
|
|
95
|
+
"*",
|
|
96
|
+
"output",
|
|
97
|
+
"tables",
|
|
98
|
+
f"trajectories_{self.parent_window.mode}.csv",
|
|
99
|
+
]
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
all_measurements = []
|
|
103
|
+
for tab in tables:
|
|
104
|
+
import pandas as pd
|
|
105
|
+
|
|
106
|
+
cols = pd.read_csv(tab, nrows=1).columns.tolist()
|
|
107
|
+
all_measurements.extend(cols)
|
|
108
|
+
all_measurements = np.unique(all_measurements)
|
|
109
|
+
|
|
110
|
+
if self.mode == "signals":
|
|
111
|
+
generic_measurements = [
|
|
112
|
+
"brightfield_channel",
|
|
113
|
+
"live_nuclei_channel",
|
|
114
|
+
"dead_nuclei_channel",
|
|
115
|
+
"effector_fluo_channel",
|
|
116
|
+
"adhesion_channel",
|
|
117
|
+
"fluo_channel_1",
|
|
118
|
+
"fluo_channel_2",
|
|
119
|
+
"area",
|
|
120
|
+
"area_bbox",
|
|
121
|
+
"area_convex",
|
|
122
|
+
"area_filled",
|
|
123
|
+
"major_axis_length",
|
|
124
|
+
"minor_axis_length",
|
|
125
|
+
"eccentricity",
|
|
126
|
+
"equivalent_diameter_area",
|
|
127
|
+
"euler_number",
|
|
128
|
+
"extent",
|
|
129
|
+
"feret_diameter_max",
|
|
130
|
+
"orientation",
|
|
131
|
+
"perimeter",
|
|
132
|
+
"perimeter_crofton",
|
|
133
|
+
"solidity",
|
|
134
|
+
"angular_second_moment",
|
|
135
|
+
"contrast",
|
|
136
|
+
"correlation",
|
|
137
|
+
"sum_of_square_variance",
|
|
138
|
+
"inverse_difference_moment",
|
|
139
|
+
"sum_average",
|
|
140
|
+
"sum_variance",
|
|
141
|
+
"sum_entropy",
|
|
142
|
+
"entropy",
|
|
143
|
+
"difference_variance",
|
|
144
|
+
"difference_entropy",
|
|
145
|
+
"information_measure_of_correlation_1",
|
|
146
|
+
"information_measure_of_correlation_2",
|
|
147
|
+
"maximal_correlation_coefficient",
|
|
148
|
+
"POSITION_X",
|
|
149
|
+
"POSITION_Y",
|
|
150
|
+
]
|
|
151
|
+
elif self.mode == "channels":
|
|
152
|
+
generic_measurements = [
|
|
153
|
+
"brightfield_channel",
|
|
154
|
+
"live_nuclei_channel",
|
|
155
|
+
"dead_nuclei_channel",
|
|
156
|
+
"effector_fluo_channel",
|
|
157
|
+
"adhesion_channel",
|
|
158
|
+
"fluo_channel_1",
|
|
159
|
+
"fluo_channel_2",
|
|
160
|
+
"None",
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
if self.mode == "channels":
|
|
164
|
+
all_measurements = []
|
|
165
|
+
exp_ch = self.attr_parent.exp_channels
|
|
166
|
+
for c in exp_ch:
|
|
167
|
+
all_measurements.append(c)
|
|
168
|
+
|
|
169
|
+
self.channel_items = np.unique(generic_measurements + list(all_measurements))
|
|
170
|
+
self.channel_items = np.insert(self.channel_items, 0, "--")
|
|
171
|
+
|
|
172
|
+
self.add_col_btn = QPushButton("Add channel")
|
|
173
|
+
self.add_col_btn.clicked.connect(self.add_channel)
|
|
174
|
+
self.add_col_btn.setStyleSheet(self.button_add)
|
|
175
|
+
self.add_col_btn.setIcon(icon(MDI6.plus, color="black"))
|
|
176
|
+
|
|
177
|
+
def add_channel(self):
|
|
178
|
+
|
|
179
|
+
self.channel_cbs.append(QSearchableComboBox())
|
|
180
|
+
self.channel_labels.append(QLabel())
|
|
181
|
+
self.channel_cbs[-1].addItems(self.channel_items)
|
|
182
|
+
self.channel_cbs[-1].currentIndexChanged.connect(self.check_valid_channels)
|
|
183
|
+
self.channel_labels[-1].setText(f"channel {len(self.channel_cbs)-1}: ")
|
|
184
|
+
|
|
185
|
+
self.normalization_mode_btns.append(QPushButton(""))
|
|
186
|
+
self.normalization_mode.append(True)
|
|
187
|
+
self.normalization_clip_btns.append(QPushButton(""))
|
|
188
|
+
self.clip_option.append(False)
|
|
189
|
+
|
|
190
|
+
self.normalization_mode_btns[-1].setIcon(
|
|
191
|
+
icon(MDI6.percent_circle, color="#1565c0")
|
|
192
|
+
)
|
|
193
|
+
self.normalization_mode_btns[-1].setIconSize(QSize(20, 20))
|
|
194
|
+
self.normalization_mode_btns[-1].setStyleSheet(self.button_select_all)
|
|
195
|
+
self.normalization_mode_btns[-1].setToolTip(
|
|
196
|
+
"Switch to absolute normalization values."
|
|
197
|
+
)
|
|
198
|
+
self.normalization_mode_btns[-1].clicked.connect(
|
|
199
|
+
partial(self.switch_normalization_mode, len(self.channel_cbs) - 1)
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
self.normalization_clip_btns[-1].setIcon(icon(MDI6.content_cut, color="black"))
|
|
203
|
+
self.normalization_clip_btns[-1].setIconSize(QSize(20, 20))
|
|
204
|
+
self.normalization_clip_btns[-1].setStyleSheet(self.button_select_all)
|
|
205
|
+
self.normalization_clip_btns[-1].clicked.connect(
|
|
206
|
+
partial(self.switch_clipping_mode, len(self.channel_cbs) - 1)
|
|
207
|
+
)
|
|
208
|
+
self.normalization_clip_btns[-1].setToolTip("clip")
|
|
209
|
+
|
|
210
|
+
self.normalization_min_value_lbl.append(QLabel("Min %: "))
|
|
211
|
+
self.normalization_min_value_le.append(QLineEdit("0.1"))
|
|
212
|
+
self.normalization_max_value_lbl.append(QLabel("Max %: "))
|
|
213
|
+
self.normalization_max_value_le.append(QLineEdit("99.99"))
|
|
214
|
+
|
|
215
|
+
ch_layout = QHBoxLayout()
|
|
216
|
+
ch_layout.addWidget(self.channel_labels[-1], 30)
|
|
217
|
+
ch_layout.addWidget(self.channel_cbs[-1], 70)
|
|
218
|
+
self.channels_vb.addLayout(ch_layout)
|
|
219
|
+
|
|
220
|
+
channel_norm_options_layout = QHBoxLayout()
|
|
221
|
+
channel_norm_options_layout.addWidget(QLabel(""), 30)
|
|
222
|
+
ch_norm_sublayout = QHBoxLayout()
|
|
223
|
+
ch_norm_sublayout.addWidget(self.normalization_min_value_lbl[-1])
|
|
224
|
+
ch_norm_sublayout.addWidget(self.normalization_min_value_le[-1])
|
|
225
|
+
ch_norm_sublayout.addWidget(self.normalization_max_value_lbl[-1])
|
|
226
|
+
ch_norm_sublayout.addWidget(self.normalization_max_value_le[-1])
|
|
227
|
+
ch_norm_sublayout.addWidget(self.normalization_clip_btns[-1])
|
|
228
|
+
ch_norm_sublayout.addWidget(self.normalization_mode_btns[-1])
|
|
229
|
+
channel_norm_options_layout.addLayout(ch_norm_sublayout, 70)
|
|
230
|
+
|
|
231
|
+
self.channels_vb.addLayout(channel_norm_options_layout)
|
|
232
|
+
|
|
233
|
+
def add_to_layout(self):
|
|
234
|
+
|
|
235
|
+
self.channels_vb = QVBoxLayout()
|
|
236
|
+
self.channel_option_layouts = []
|
|
237
|
+
for i in range(len(self.channel_cbs)):
|
|
238
|
+
|
|
239
|
+
ch_layout = QHBoxLayout()
|
|
240
|
+
self.channel_labels[i].setText(f"channel {i}: ")
|
|
241
|
+
ch_layout.addWidget(self.channel_labels[i], 30)
|
|
242
|
+
self.channel_cbs[i].addItems(self.channel_items)
|
|
243
|
+
self.channel_cbs[i].currentIndexChanged.connect(self.check_valid_channels)
|
|
244
|
+
ch_layout.addWidget(self.channel_cbs[i], 70)
|
|
245
|
+
self.channels_vb.addLayout(ch_layout)
|
|
246
|
+
|
|
247
|
+
channel_norm_options_layout = QHBoxLayout()
|
|
248
|
+
# channel_norm_options_layout.setContentsMargins(130,0,0,0)
|
|
249
|
+
channel_norm_options_layout.addWidget(QLabel(""), 30)
|
|
250
|
+
ch_norm_sublayout = QHBoxLayout()
|
|
251
|
+
ch_norm_sublayout.addWidget(self.normalization_min_value_lbl[i])
|
|
252
|
+
ch_norm_sublayout.addWidget(self.normalization_min_value_le[i])
|
|
253
|
+
ch_norm_sublayout.addWidget(self.normalization_max_value_lbl[i])
|
|
254
|
+
ch_norm_sublayout.addWidget(self.normalization_max_value_le[i])
|
|
255
|
+
ch_norm_sublayout.addWidget(self.normalization_clip_btns[i])
|
|
256
|
+
ch_norm_sublayout.addWidget(self.normalization_mode_btns[i])
|
|
257
|
+
channel_norm_options_layout.addLayout(ch_norm_sublayout, 70)
|
|
258
|
+
self.channels_vb.addLayout(channel_norm_options_layout)
|
|
259
|
+
|
|
260
|
+
self.addLayout(self.channels_vb)
|
|
261
|
+
|
|
262
|
+
add_hbox = QHBoxLayout()
|
|
263
|
+
add_hbox.addWidget(QLabel(""), 66)
|
|
264
|
+
add_hbox.addWidget(self.add_col_btn, 33, alignment=Qt.AlignRight)
|
|
265
|
+
self.addLayout(add_hbox)
|
|
266
|
+
|
|
267
|
+
def switch_normalization_mode(self, index):
|
|
268
|
+
"""
|
|
269
|
+
Use absolute or percentile values for the normalization of each individual channel.
|
|
270
|
+
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
currentNormMode = self.normalization_mode[index]
|
|
274
|
+
self.normalization_mode[index] = not currentNormMode
|
|
275
|
+
|
|
276
|
+
if self.normalization_mode[index]:
|
|
277
|
+
self.normalization_mode_btns[index].setIcon(
|
|
278
|
+
icon(MDI6.percent_circle, color="#1565c0")
|
|
279
|
+
)
|
|
280
|
+
self.normalization_mode_btns[index].setIconSize(QSize(20, 20))
|
|
281
|
+
self.normalization_mode_btns[index].setStyleSheet(self.button_select_all)
|
|
282
|
+
self.normalization_mode_btns[index].setToolTip(
|
|
283
|
+
"Switch to absolute normalization values."
|
|
284
|
+
)
|
|
285
|
+
self.normalization_min_value_lbl[index].setText("Min %: ")
|
|
286
|
+
self.normalization_max_value_lbl[index].setText("Max %: ")
|
|
287
|
+
self.normalization_min_value_le[index].setText("0.1")
|
|
288
|
+
self.normalization_max_value_le[index].setText("99.99")
|
|
289
|
+
|
|
290
|
+
else:
|
|
291
|
+
self.normalization_mode_btns[index].setIcon(
|
|
292
|
+
icon(MDI6.percent_circle_outline, color="black")
|
|
293
|
+
)
|
|
294
|
+
self.normalization_mode_btns[index].setIconSize(QSize(20, 20))
|
|
295
|
+
self.normalization_mode_btns[index].setStyleSheet(self.button_select_all)
|
|
296
|
+
self.normalization_mode_btns[index].setToolTip(
|
|
297
|
+
"Switch to percentile normalization values."
|
|
298
|
+
)
|
|
299
|
+
self.normalization_min_value_lbl[index].setText("Min: ")
|
|
300
|
+
self.normalization_min_value_le[index].setText("0")
|
|
301
|
+
self.normalization_max_value_lbl[index].setText("Max: ")
|
|
302
|
+
self.normalization_max_value_le[index].setText("1000")
|
|
303
|
+
|
|
304
|
+
def switch_clipping_mode(self, index):
|
|
305
|
+
|
|
306
|
+
currentClipMode = self.clip_option[index]
|
|
307
|
+
self.clip_option[index] = not currentClipMode
|
|
308
|
+
|
|
309
|
+
if self.clip_option[index]:
|
|
310
|
+
self.normalization_clip_btns[index].setIcon(
|
|
311
|
+
icon(MDI6.content_cut, color="#1565c0")
|
|
312
|
+
)
|
|
313
|
+
self.normalization_clip_btns[index].setIconSize(QSize(20, 20))
|
|
314
|
+
self.normalization_clip_btns[index].setStyleSheet(self.button_select_all)
|
|
315
|
+
|
|
316
|
+
else:
|
|
317
|
+
self.normalization_clip_btns[index].setIcon(
|
|
318
|
+
icon(MDI6.content_cut, color="black")
|
|
319
|
+
)
|
|
320
|
+
self.normalization_clip_btns[index].setIconSize(QSize(20, 20))
|
|
321
|
+
self.normalization_clip_btns[index].setStyleSheet(self.button_select_all)
|
|
322
|
+
|
|
323
|
+
def check_valid_channels(self):
|
|
324
|
+
|
|
325
|
+
if hasattr(self.parent_window, "submit_btn"):
|
|
326
|
+
if np.all([cb.currentText() == "--" for cb in self.channel_cbs]):
|
|
327
|
+
self.parent_window.submit_btn.setEnabled(False)
|
|
328
|
+
|
|
329
|
+
if hasattr(self.parent_window, "spatial_calib_le") and hasattr(
|
|
330
|
+
self.parent_window, "submit_btn"
|
|
331
|
+
):
|
|
332
|
+
if self.parent_window.spatial_calib_le.text() != "--":
|
|
333
|
+
self.parent_window.submit_btn.setEnabled(True)
|
|
334
|
+
elif hasattr(self.parent_window, "submit_btn"):
|
|
335
|
+
self.parent_window.submit_btn.setEnabled(True)
|