celldetective 1.4.0__py3-none-any.whl → 1.4.1.post1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- celldetective/_version.py +1 -1
- celldetective/exceptions.py +11 -0
- celldetective/filters.py +7 -1
- celldetective/gui/InitWindow.py +4 -1
- celldetective/gui/__init__.py +2 -9
- celldetective/gui/about.py +2 -2
- celldetective/gui/base_annotator.py +786 -0
- celldetective/gui/classifier_widget.py +18 -13
- celldetective/gui/configure_new_exp.py +51 -30
- celldetective/gui/control_panel.py +10 -7
- celldetective/gui/{signal_annotator.py → event_annotator.py} +473 -1437
- celldetective/gui/generic_signal_plot.py +2 -1
- celldetective/gui/gui_utils.py +5 -2
- celldetective/gui/help/neighborhood.json +2 -2
- celldetective/gui/layouts.py +21 -11
- celldetective/gui/{signal_annotator2.py → pair_event_annotator.py} +3 -1
- celldetective/gui/process_block.py +129 -91
- celldetective/gui/processes/downloader.py +37 -34
- celldetective/gui/processes/measure_cells.py +14 -8
- celldetective/gui/processes/segment_cells.py +438 -314
- celldetective/gui/processes/track_cells.py +12 -13
- celldetective/gui/settings/__init__.py +7 -0
- celldetective/gui/settings/_settings_base.py +70 -0
- celldetective/gui/{retrain_signal_model_options.py → settings/_settings_event_model_training.py} +35 -91
- celldetective/gui/{measurement_options.py → settings/_settings_measurements.py} +28 -81
- celldetective/gui/{neighborhood_options.py → settings/_settings_neighborhood.py} +1 -1
- celldetective/gui/settings/_settings_segmentation.py +49 -0
- celldetective/gui/{retrain_segmentation_model_options.py → settings/_settings_segmentation_model_training.py} +33 -79
- celldetective/gui/{signal_annotator_options.py → settings/_settings_signal_annotator.py} +73 -95
- celldetective/gui/{btrack_options.py → settings/_settings_tracking.py} +64 -87
- celldetective/gui/styles.py +2 -1
- celldetective/gui/survival_ui.py +1 -1
- celldetective/gui/tableUI.py +25 -0
- celldetective/gui/table_ops/__init__.py +0 -0
- celldetective/gui/table_ops/merge_groups.py +118 -0
- celldetective/gui/viewers.py +3 -5
- celldetective/gui/workers.py +0 -2
- celldetective/io.py +98 -55
- celldetective/links/zenodo.json +145 -144
- celldetective/measure.py +31 -26
- celldetective/preprocessing.py +34 -21
- celldetective/regionprops/_regionprops.py +16 -5
- celldetective/scripts/measure_cells.py +5 -5
- celldetective/scripts/measure_relative.py +16 -11
- celldetective/scripts/segment_cells.py +4 -4
- celldetective/scripts/segment_cells_thresholds.py +3 -3
- celldetective/scripts/track_cells.py +7 -7
- celldetective/scripts/train_segmentation_model.py +10 -1
- celldetective/tracking.py +10 -4
- celldetective/utils.py +59 -58
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/METADATA +1 -1
- celldetective-1.4.1.post1.dist-info/RECORD +123 -0
- tests/gui/__init__.py +0 -0
- tests/gui/test_new_project.py +228 -0
- tests/{test_qt.py → gui/test_project.py} +22 -26
- tests/test_preprocessing.py +2 -2
- celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -79
- celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
- celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -37
- celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -39
- celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
- celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
- celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
- celldetective/models/signal_detection/NucCond/config_input.json +0 -1
- celldetective/models/signal_detection/NucCond/log_classifier.csv +0 -126
- celldetective/models/signal_detection/NucCond/log_regressor.csv +0 -282
- celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
- celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
- celldetective/models/signal_detection/NucCond/scores.npy +0 -0
- celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
- celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
- celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
- celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
- celldetective-1.4.0.dist-info/RECORD +0 -131
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/WHEEL +0 -0
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/licenses/LICENSE +0 -0
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
"""
|
|
2
|
+
Copyright © 2023 Laboratoire Adhesion et Inflammation, Authored by Remy Torro.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from PyQt5.QtWidgets import QRadioButton, QButtonGroup, QMessageBox, QComboBox, QFrame, QCheckBox, QFileDialog, QGridLayout, QTextEdit, QLineEdit, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
|
|
2
7
|
from PyQt5.QtCore import Qt, QSize
|
|
3
|
-
from PyQt5.QtGui import QDoubleValidator
|
|
4
8
|
|
|
5
|
-
from celldetective.gui.gui_utils import
|
|
9
|
+
from celldetective.gui.gui_utils import FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, help_generic
|
|
6
10
|
from superqt import QLabeledDoubleSlider, QLabeledSlider
|
|
7
11
|
from superqt.fonticon import icon
|
|
8
12
|
from fonticon_mdi6 import MDI6
|
|
@@ -16,10 +20,10 @@ import os
|
|
|
16
20
|
import matplotlib.pyplot as plt
|
|
17
21
|
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
18
22
|
from glob import glob
|
|
19
|
-
from celldetective.gui import
|
|
23
|
+
from celldetective.gui.settings._settings_base import CelldetectiveSettingsPanel
|
|
20
24
|
|
|
21
25
|
|
|
22
|
-
class
|
|
26
|
+
class SettingsTracking(CelldetectiveSettingsPanel):
|
|
23
27
|
|
|
24
28
|
"""
|
|
25
29
|
UI to set tracking parameters for bTrack.
|
|
@@ -28,46 +32,51 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
28
32
|
|
|
29
33
|
def __init__(self, parent_window=None):
|
|
30
34
|
|
|
31
|
-
super().__init__()
|
|
32
35
|
self.parent_window = parent_window
|
|
33
|
-
self.setWindowTitle("Configure tracking")
|
|
34
36
|
self.mode = self.parent_window.mode
|
|
35
37
|
self.exp_dir = self.parent_window.exp_dir
|
|
36
|
-
self.floatValidator = QDoubleValidator()
|
|
37
38
|
|
|
38
39
|
self.config_name = os.sep.join(["configs", f"btrack_config_{self.mode}.json"])
|
|
39
40
|
self.track_instructions_write_path = self.parent_window.exp_dir + os.sep.join(["configs", f"tracking_instructions_{self.mode}.json"])
|
|
40
|
-
self.soft_path = get_software_location()
|
|
41
41
|
|
|
42
42
|
self.config_path = self.exp_dir + self.config_name
|
|
43
43
|
self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
|
|
44
44
|
self.channel_names = np.array(self.channel_names)
|
|
45
45
|
self.channels = np.array(self.channels)
|
|
46
|
-
self.screen_height = self.parent_window.parent_window.parent_window.screen_height
|
|
47
|
-
|
|
48
|
-
center_window(self)
|
|
49
|
-
self.setMinimumWidth(540)
|
|
50
46
|
self.minimum_height = 300
|
|
51
|
-
# self.setMinimumHeight(int(0.3*self.screen_height))
|
|
52
|
-
# self.setMaximumHeight(int(0.8*self.screen_height))
|
|
53
|
-
self.populate_widget()
|
|
54
|
-
self.load_previous_tracking_instructions()
|
|
55
47
|
|
|
56
|
-
|
|
48
|
+
super().__init__(title="Configure tracking")
|
|
49
|
+
|
|
50
|
+
self._add_to_layout()
|
|
51
|
+
self._load_previous_instructions()
|
|
52
|
+
|
|
53
|
+
self._widget.setMinimumWidth(500)
|
|
54
|
+
self._adjustSize()
|
|
55
|
+
self.resize(int(self.width()*1.5), int(self._screen_height * 0.8))
|
|
56
|
+
|
|
57
|
+
def _add_to_layout(self):
|
|
58
|
+
|
|
59
|
+
tracker_hbox = QHBoxLayout()
|
|
60
|
+
tracker_hbox.setContentsMargins(15, 15, 15, 15)
|
|
61
|
+
tracker_hbox.addWidget(self.btrack_option, 50, alignment=Qt.AlignCenter)
|
|
62
|
+
tracker_hbox.addWidget(self.trackpy_option, 50, alignment=Qt.AlignCenter)
|
|
63
|
+
|
|
64
|
+
self._layout.addLayout(tracker_hbox)
|
|
65
|
+
self._layout.addWidget(self.config_frame)
|
|
66
|
+
self._layout.addWidget(self.features_frame)
|
|
67
|
+
self._layout.addWidget(self.config_trackpy_frame)
|
|
68
|
+
self._layout.addWidget(self.post_proc_frame)
|
|
69
|
+
self._layout.addWidget(self.submit_btn)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _create_widgets(self):
|
|
57
73
|
|
|
58
74
|
"""
|
|
59
75
|
Create the multibox design with collapsable frames.
|
|
60
76
|
|
|
61
77
|
"""
|
|
62
78
|
|
|
63
|
-
|
|
64
|
-
self.scroll_area = QScrollArea(self)
|
|
65
|
-
self.button_widget = CelldetectiveWidget()
|
|
66
|
-
main_layout = QVBoxLayout()
|
|
67
|
-
self.button_widget.setLayout(main_layout)
|
|
68
|
-
main_layout.setContentsMargins(30, 30, 30, 30)
|
|
69
|
-
|
|
70
|
-
# First collapsable Frame CONFIG
|
|
79
|
+
super()._create_widgets()
|
|
71
80
|
|
|
72
81
|
self.btrack_option = QRadioButton('bTrack')
|
|
73
82
|
self.btrack_option.setChecked(True)
|
|
@@ -81,69 +90,37 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
81
90
|
self.config_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
82
91
|
self.populate_config_frame()
|
|
83
92
|
|
|
84
|
-
tracker_hbox = QHBoxLayout()
|
|
85
|
-
tracker_hbox.setContentsMargins(15, 15, 15, 15)
|
|
86
|
-
tracker_hbox.addWidget(self.btrack_option, 50, alignment=Qt.AlignCenter)
|
|
87
|
-
tracker_hbox.addWidget(self.trackpy_option, 50, alignment=Qt.AlignCenter)
|
|
88
|
-
main_layout.addLayout(tracker_hbox)
|
|
89
|
-
|
|
90
|
-
main_layout.addWidget(self.config_frame)
|
|
91
|
-
|
|
92
93
|
# Second collapsable frame FEATURES
|
|
93
94
|
self.features_frame = QFrame()
|
|
94
95
|
self.features_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
95
96
|
self.populate_features_frame()
|
|
96
|
-
main_layout.addWidget(self.features_frame)
|
|
97
97
|
|
|
98
98
|
self.config_trackpy_frame = QFrame()
|
|
99
99
|
self.config_trackpy_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
100
100
|
self.populate_config_trackpy_frame()
|
|
101
|
-
main_layout.addWidget(self.config_trackpy_frame)
|
|
102
101
|
self.config_trackpy_frame.hide()
|
|
103
102
|
|
|
104
103
|
# Third collapsable frame POST-PROCESSING
|
|
105
104
|
self.post_proc_frame = QFrame()
|
|
106
105
|
self.post_proc_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
107
106
|
self.populate_post_proc_frame()
|
|
108
|
-
main_layout.addWidget(self.post_proc_frame)
|
|
109
|
-
|
|
110
|
-
self.submit_btn = QPushButton('Save')
|
|
111
|
-
self.submit_btn.setStyleSheet(self.parent_window.parent_window.parent_window.button_style_sheet)
|
|
112
|
-
self.submit_btn.clicked.connect(self.write_instructions)
|
|
113
|
-
main_layout.addWidget(self.submit_btn)
|
|
114
|
-
|
|
115
|
-
#self.populate_left_panel()
|
|
116
|
-
#grid.addLayout(self.left_side, 0, 0, 1, 1)
|
|
117
|
-
self.button_widget.adjustSize()
|
|
118
|
-
|
|
119
|
-
self.scroll_area.setAlignment(Qt.AlignCenter)
|
|
120
|
-
self.scroll_area.setWidget(self.button_widget)
|
|
121
|
-
self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
122
|
-
self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
123
|
-
self.scroll_area.setWidgetResizable(True)
|
|
124
|
-
self.setCentralWidget(self.scroll_area)
|
|
125
|
-
self.show()
|
|
126
107
|
|
|
127
108
|
self.btrack_option.toggled.connect(self.show_tracking_options)
|
|
128
109
|
self.trackpy_option.toggled.connect(self.show_tracking_options)
|
|
129
110
|
|
|
130
|
-
QApplication.processEvents()
|
|
131
|
-
self.adjustScrollArea()
|
|
132
|
-
|
|
133
111
|
def show_tracking_options(self):
|
|
134
112
|
|
|
135
113
|
if self.btrack_option.isChecked():
|
|
136
114
|
self.config_frame.show()
|
|
137
115
|
self.features_frame.show()
|
|
138
116
|
self.config_trackpy_frame.hide()
|
|
139
|
-
#self.
|
|
140
|
-
#self.adjustSize()
|
|
117
|
+
#self._adjustSize()
|
|
141
118
|
else:
|
|
142
119
|
self.config_frame.hide()
|
|
143
120
|
self.features_frame.hide()
|
|
144
121
|
self.config_trackpy_frame.show()
|
|
145
|
-
#self.
|
|
146
|
-
|
|
122
|
+
#self._adjustSize()
|
|
123
|
+
|
|
147
124
|
|
|
148
125
|
def populate_post_proc_frame(self):
|
|
149
126
|
|
|
@@ -203,12 +180,12 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
203
180
|
self.collapse_post_proc_btn.setIcon(icon(MDI6.chevron_down, color="black"))
|
|
204
181
|
self.collapse_post_proc_btn.setIconSize(QSize(20, 20))
|
|
205
182
|
if len(is_open[is_open])==0:
|
|
206
|
-
|
|
207
|
-
self.
|
|
183
|
+
pass
|
|
184
|
+
#self._scroll_area.setMinimumHeight(int(self.minimum_height))
|
|
185
|
+
#self._adjustSize()
|
|
208
186
|
else:
|
|
209
187
|
self.collapse_post_proc_btn.setIcon(icon(MDI6.chevron_up, color="black"))
|
|
210
188
|
self.collapse_post_proc_btn.setIconSize(QSize(20, 20))
|
|
211
|
-
self.scroll_area.setMinimumHeight(min(int(930), int(0.9*self.screen_height)))
|
|
212
189
|
|
|
213
190
|
|
|
214
191
|
def help_post(self):
|
|
@@ -319,12 +296,12 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
319
296
|
self.collapse_features_btn.setIcon(icon(MDI6.chevron_down, color="black"))
|
|
320
297
|
self.collapse_features_btn.setIconSize(QSize(20, 20))
|
|
321
298
|
if len(is_open[is_open])==0:
|
|
322
|
-
|
|
323
|
-
self.
|
|
299
|
+
pass
|
|
300
|
+
#self._scroll_area.setMinimumHeight(int(self.minimum_height))
|
|
301
|
+
#self._adjustSize()
|
|
324
302
|
else:
|
|
325
303
|
self.collapse_features_btn.setIcon(icon(MDI6.chevron_up, color="black"))
|
|
326
304
|
self.collapse_features_btn.setIconSize(QSize(20, 20))
|
|
327
|
-
self.scroll_area.setMinimumHeight(min(int(930), int(0.9*self.screen_height)))
|
|
328
305
|
|
|
329
306
|
|
|
330
307
|
def generate_post_proc_panel_contents(self):
|
|
@@ -614,12 +591,12 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
614
591
|
self.collapse_config_btn.setIcon(icon(MDI6.chevron_down,color="black"))
|
|
615
592
|
self.collapse_config_btn.setIconSize(QSize(20, 20))
|
|
616
593
|
if len(is_open[is_open])==0:
|
|
617
|
-
|
|
618
|
-
self.
|
|
594
|
+
pass
|
|
595
|
+
#self._scroll_area.setMinimumHeight(int(self.minimum_height))
|
|
596
|
+
#self._adjustSize()
|
|
619
597
|
else:
|
|
620
598
|
self.collapse_config_btn.setIcon(icon(MDI6.chevron_up,color="black"))
|
|
621
599
|
self.collapse_config_btn.setIconSize(QSize(20, 20))
|
|
622
|
-
self.scroll_area.setMinimumHeight(min(int(930), int(0.9*self.screen_height)))
|
|
623
600
|
|
|
624
601
|
def collapse_config_trackpy_advanced(self):
|
|
625
602
|
|
|
@@ -635,12 +612,12 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
635
612
|
self.collapse_config_trackpy_btn.setIcon(icon(MDI6.chevron_down,color="black"))
|
|
636
613
|
self.collapse_config_trackpy_btn.setIconSize(QSize(20, 20))
|
|
637
614
|
if len(is_open[is_open])==0:
|
|
638
|
-
|
|
639
|
-
self.
|
|
615
|
+
pass
|
|
616
|
+
#self._scroll_area.setMinimumHeight(int(self.minimum_height))
|
|
617
|
+
#self._adjustSize()
|
|
640
618
|
else:
|
|
641
619
|
self.collapse_config_trackpy_btn.setIcon(icon(MDI6.chevron_up,color="black"))
|
|
642
620
|
self.collapse_config_trackpy_btn.setIconSize(QSize(20, 20))
|
|
643
|
-
self.scroll_area.setMinimumHeight(min(int(930), int(0.9*self.screen_height)))
|
|
644
621
|
|
|
645
622
|
|
|
646
623
|
def generate_config_trackpy_panel_contents(self):
|
|
@@ -653,7 +630,7 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
653
630
|
self.search_range_lbl = QLabel("search range [px]: ")
|
|
654
631
|
self.search_range_le = QLineEdit('30')
|
|
655
632
|
self.search_range_le.setPlaceholderText('search distance in pixels')
|
|
656
|
-
self.search_range_le.setValidator(self.
|
|
633
|
+
self.search_range_le.setValidator(self._floatValidator)
|
|
657
634
|
sr_layout.addWidget(self.search_range_lbl, 30)
|
|
658
635
|
sr_layout.addWidget(self.search_range_le, 70)
|
|
659
636
|
layout.addLayout(sr_layout)
|
|
@@ -731,7 +708,7 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
731
708
|
|
|
732
709
|
self.file_dialog = QFileDialog()
|
|
733
710
|
try:
|
|
734
|
-
modelpath = os.sep.join([self.
|
|
711
|
+
modelpath = os.sep.join([self._software_path, "celldetective","models","tracking_configs"]) + os.sep
|
|
735
712
|
print("Track config path: ", modelpath)
|
|
736
713
|
self.filename = self.file_dialog.getOpenFileName(None, "Load config", modelpath, "json files (*.json)")[0]
|
|
737
714
|
if self.filename!=self.config_path:
|
|
@@ -822,16 +799,16 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
822
799
|
else:
|
|
823
800
|
self.post_proc_ticked = True
|
|
824
801
|
|
|
825
|
-
def adjustScrollArea(self):
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
802
|
+
# def adjustScrollArea(self):
|
|
803
|
+
#
|
|
804
|
+
# """
|
|
805
|
+
# Auto-adjust scroll area to fill space
|
|
806
|
+
# (from https://stackoverflow.com/questions/66417576/make-qscrollarea-use-all-available-space-of-qmainwindow-height-axis)
|
|
807
|
+
# """
|
|
808
|
+
#
|
|
809
|
+
# step = 5
|
|
810
|
+
# while self._scroll_area.verticalScrollBar().isVisible() and self.height() < self.maximumHeight():
|
|
811
|
+
# self.resize(self.width(), self.height() + step)
|
|
835
812
|
|
|
836
813
|
def load_cell_config(self):
|
|
837
814
|
|
|
@@ -844,7 +821,7 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
844
821
|
json_data = json.load(f)
|
|
845
822
|
self.config_le.setText(json.dumps(json_data, indent=4))
|
|
846
823
|
|
|
847
|
-
def
|
|
824
|
+
def _write_instructions(self):
|
|
848
825
|
|
|
849
826
|
"""
|
|
850
827
|
Write the selected options in a json file for later reading by the software.
|
|
@@ -953,7 +930,7 @@ class ConfigTracking(CelldetectiveMainWindow):
|
|
|
953
930
|
else:
|
|
954
931
|
self.haralick_options = None
|
|
955
932
|
|
|
956
|
-
def
|
|
933
|
+
def _load_previous_instructions(self):
|
|
957
934
|
|
|
958
935
|
"""
|
|
959
936
|
Read the tracking options from a previously written json file.
|
celldetective/gui/styles.py
CHANGED
|
@@ -14,7 +14,8 @@ class Styles(object):
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
self.celldetective_blue = "#1565c0"
|
|
17
|
-
self.
|
|
17
|
+
self.celldetective_logo_path = os.sep.join([get_software_location(),'celldetective','icons','logo.png'])
|
|
18
|
+
self.celldetective_icon = QIcon(self.celldetective_logo_path)
|
|
18
19
|
|
|
19
20
|
self.action_lbl_style_sheet = """
|
|
20
21
|
font-size: 10px;
|
celldetective/gui/survival_ui.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import
|
|
1
|
+
from PyQt5.QtWidgets import QComboBox, QLineEdit, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
|
|
2
2
|
from PyQt5.QtCore import Qt
|
|
3
3
|
from PyQt5.QtGui import QDoubleValidator
|
|
4
4
|
from celldetective.gui.gui_utils import center_window, generic_message
|
celldetective/gui/tableUI.py
CHANGED
|
@@ -3,6 +3,9 @@ from PyQt5.QtCore import Qt
|
|
|
3
3
|
from PyQt5.QtGui import QBrush, QColor, QDoubleValidator
|
|
4
4
|
import pandas as pd
|
|
5
5
|
import matplotlib.pyplot as plt
|
|
6
|
+
|
|
7
|
+
from celldetective.gui.table_ops.merge_groups import MergeGroupWidget
|
|
8
|
+
|
|
6
9
|
plt.rcParams['svg.fonttype'] = 'none'
|
|
7
10
|
from celldetective.gui.gui_utils import FigureCanvas, center_window, QHSeperationLine, GenericOpColWidget, PandasModel
|
|
8
11
|
from celldetective.utils import differentiate_per_track, collapse_trajectories_by_status, test_2samp_generic, safe_log
|
|
@@ -650,11 +653,17 @@ class TableUI(CelldetectiveMainWindow):
|
|
|
650
653
|
self.calibrate_action.triggered.connect(self.calibrate_selected_feature)
|
|
651
654
|
self.calibrate_action.setShortcut("Ctrl+C")
|
|
652
655
|
self.mathMenu.addAction(self.calibrate_action)
|
|
656
|
+
|
|
657
|
+
self.merge_classification_action = QAction('&Merge states...', self)
|
|
658
|
+
self.merge_classification_action.triggered.connect(self.merge_classification_features)
|
|
659
|
+
self.mathMenu.addAction(self.merge_classification_action)
|
|
653
660
|
|
|
654
661
|
self.derivative_action = QAction('&Differentiate...', self)
|
|
655
662
|
self.derivative_action.triggered.connect(self.differenciate_selected_feature)
|
|
656
663
|
self.derivative_action.setShortcut("Ctrl+D")
|
|
657
664
|
self.mathMenu.addAction(self.derivative_action)
|
|
665
|
+
if not self.tracks:
|
|
666
|
+
self.derivative_action.setEnabled(False)
|
|
658
667
|
|
|
659
668
|
self.abs_action = QAction('&Absolute value...', self)
|
|
660
669
|
self.abs_action.triggered.connect(self.take_abs_of_selected_feature)
|
|
@@ -993,6 +1002,22 @@ class TableUI(CelldetectiveMainWindow):
|
|
|
993
1002
|
|
|
994
1003
|
self.LogWidget = LogColWidget(self, selected_col)
|
|
995
1004
|
self.LogWidget.show()
|
|
1005
|
+
|
|
1006
|
+
def merge_classification_features(self):
|
|
1007
|
+
|
|
1008
|
+
x = self.table_view.selectedIndexes()
|
|
1009
|
+
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
1010
|
+
|
|
1011
|
+
col_selection = []
|
|
1012
|
+
if isinstance(col_idx, (list, np.ndarray)):
|
|
1013
|
+
cols = np.array(list(self.data.columns))
|
|
1014
|
+
if len(col_idx) > 0:
|
|
1015
|
+
selected_cols = cols[col_idx]
|
|
1016
|
+
col_selection.extend(selected_cols)
|
|
1017
|
+
|
|
1018
|
+
self.merge_classification_widget = MergeGroupWidget(self, columns = col_selection)
|
|
1019
|
+
self.merge_classification_widget.show()
|
|
1020
|
+
|
|
996
1021
|
|
|
997
1022
|
def calibrate_selected_feature(self):
|
|
998
1023
|
|
|
File without changes
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from PyQt5.QtCore import QSize, Qt
|
|
5
|
+
from PyQt5.QtWidgets import QComboBox, QHBoxLayout, QLabel, QLineEdit, QPushButton, QVBoxLayout
|
|
6
|
+
from fonticon_mdi6 import MDI6
|
|
7
|
+
from superqt.fonticon import icon
|
|
8
|
+
|
|
9
|
+
from celldetective.gui import CelldetectiveWidget
|
|
10
|
+
from celldetective.gui.gui_utils import PandasModel, center_window
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MergeGroupWidget(CelldetectiveWidget):
|
|
14
|
+
def __init__(self, parent_window, columns: List[str] = [], n_cols_init: int = 3):
|
|
15
|
+
|
|
16
|
+
super().__init__()
|
|
17
|
+
self.parent_window = parent_window
|
|
18
|
+
|
|
19
|
+
self.setWindowTitle("Merge classifications")
|
|
20
|
+
self.group_cols = [c for c in list(self.parent_window.data.columns) if c.startswith("group_") or c.startswith("status_")]
|
|
21
|
+
self.group_cols.insert(0, "--")
|
|
22
|
+
if len(columns) > n_cols_init:
|
|
23
|
+
n_cols_init = len(columns)
|
|
24
|
+
|
|
25
|
+
center_window(self)
|
|
26
|
+
|
|
27
|
+
layout = QVBoxLayout(self)
|
|
28
|
+
layout.setContentsMargins(30, 10, 30, 30)
|
|
29
|
+
|
|
30
|
+
label = QLabel(
|
|
31
|
+
"Merge several binary or multi-label classification features into a multi-label classification feature where each state is one of the possible combinations.\n")
|
|
32
|
+
|
|
33
|
+
label.setWordWrap(True) # enables automatic line breaking
|
|
34
|
+
label.setTextInteractionFlags(Qt.TextSelectableByMouse) # optional, to allow copy
|
|
35
|
+
label.setStyleSheet("color: gray;") # optional style
|
|
36
|
+
|
|
37
|
+
layout.addWidget(label)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
self.name_le = QLineEdit("group_multilabel")
|
|
41
|
+
name_layout = QHBoxLayout()
|
|
42
|
+
name_layout.addWidget(QLabel("name: "), 25)
|
|
43
|
+
name_layout.addWidget(self.name_le, 75)
|
|
44
|
+
layout.addLayout(name_layout)
|
|
45
|
+
|
|
46
|
+
self.cbs_layout = QVBoxLayout()
|
|
47
|
+
self.cbs_layout.setContentsMargins(0,10,0,0)
|
|
48
|
+
|
|
49
|
+
self.cbs = []
|
|
50
|
+
for i in range(n_cols_init):
|
|
51
|
+
cb_i = QComboBox()
|
|
52
|
+
cb_i.addItems(self.group_cols)
|
|
53
|
+
if i < len(columns):
|
|
54
|
+
selection = columns[i]
|
|
55
|
+
idx = cb_i.findText(selection)
|
|
56
|
+
if idx>=0:
|
|
57
|
+
cb_i.setCurrentIndex(idx)
|
|
58
|
+
else:
|
|
59
|
+
cb_i.setCurrentIndex(0)
|
|
60
|
+
self.cbs.append(cb_i)
|
|
61
|
+
|
|
62
|
+
col_layout = QHBoxLayout()
|
|
63
|
+
col_layout.addWidget(QLabel(f'state {i}: '), 25)
|
|
64
|
+
col_layout.addWidget(cb_i, 75)
|
|
65
|
+
self.cbs_layout.addLayout(col_layout)
|
|
66
|
+
|
|
67
|
+
layout.addLayout(self.cbs_layout)
|
|
68
|
+
|
|
69
|
+
self.add_feature_btn = QPushButton()
|
|
70
|
+
self.add_feature_btn.setIcon(icon(MDI6.plus, color=self.help_color))
|
|
71
|
+
self.add_feature_btn.setIconSize(QSize(20, 20))
|
|
72
|
+
self.add_feature_btn.clicked.connect(self.add_col)
|
|
73
|
+
self.add_feature_btn.setStyleSheet(self.button_select_all)
|
|
74
|
+
layout.addWidget(self.add_feature_btn, alignment=Qt.AlignRight)
|
|
75
|
+
|
|
76
|
+
self.submit_btn = QPushButton('Compute')
|
|
77
|
+
self.submit_btn.setStyleSheet(self.button_style_sheet)
|
|
78
|
+
self.submit_btn.clicked.connect(self.compute)
|
|
79
|
+
layout.addWidget(self.submit_btn, 30)
|
|
80
|
+
|
|
81
|
+
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
82
|
+
|
|
83
|
+
def add_col(self):
|
|
84
|
+
cb_i = QComboBox()
|
|
85
|
+
cb_i.addItems(self.group_cols)
|
|
86
|
+
self.cbs.append(cb_i)
|
|
87
|
+
|
|
88
|
+
col_layout = QHBoxLayout()
|
|
89
|
+
col_layout.addWidget(QLabel(f'state {len(self.cbs)}: '), 25)
|
|
90
|
+
col_layout.addWidget(cb_i, 75)
|
|
91
|
+
|
|
92
|
+
self.cbs_layout.addLayout(col_layout)
|
|
93
|
+
|
|
94
|
+
def compute(self):
|
|
95
|
+
|
|
96
|
+
cols_to_merge = [cb_i.currentText() for cb_i in self.cbs if cb_i.currentText() != "--"]
|
|
97
|
+
name = self.name_le.text()
|
|
98
|
+
if " " in name:
|
|
99
|
+
name.replace(" ","_")
|
|
100
|
+
if name == '':
|
|
101
|
+
name = "multilabel"
|
|
102
|
+
if not name.startswith("group_"):
|
|
103
|
+
name = "group_" + name
|
|
104
|
+
|
|
105
|
+
if len(cols_to_merge) > 1:
|
|
106
|
+
print("Computing a multi-label classification from the classification feature sources...")
|
|
107
|
+
bases = [int(self.parent_window.data[c].max()) + 1 for c in cols_to_merge]
|
|
108
|
+
multipliers = np.concatenate(([1], np.cumprod(bases[:-1])))
|
|
109
|
+
self.parent_window.data[name] = (self.parent_window.data[cols_to_merge] * multipliers).sum(axis=1)
|
|
110
|
+
self.parent_window.data.loc[self.parent_window.data[cols_to_merge].isna().any(axis=1), name] = np.nan
|
|
111
|
+
|
|
112
|
+
self.parent_window.model = PandasModel(self.parent_window.data)
|
|
113
|
+
self.parent_window.table_view.setModel(self.parent_window.model)
|
|
114
|
+
self.close()
|
|
115
|
+
elif len(cols_to_merge) == 1:
|
|
116
|
+
print("Only one classification feature was selected, nothing to merge...")
|
|
117
|
+
else:
|
|
118
|
+
print("No classification feature was selected...")
|
celldetective/gui/viewers.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import numpy as np
|
|
2
1
|
from celldetective.io import auto_load_number_of_frames, load_frames
|
|
3
2
|
from celldetective.filters import *
|
|
4
3
|
from celldetective.segmentation import filter_image, threshold_image
|
|
@@ -11,17 +10,16 @@ from natsort import natsorted
|
|
|
11
10
|
from glob import glob
|
|
12
11
|
import os
|
|
13
12
|
|
|
14
|
-
from PyQt5.QtWidgets import QHBoxLayout, QPushButton, QLabel, QComboBox, QLineEdit, QListWidget, QShortcut
|
|
13
|
+
from PyQt5.QtWidgets import QHBoxLayout, QMessageBox, QPushButton, QLabel, QComboBox, QLineEdit, QListWidget, QShortcut
|
|
15
14
|
from PyQt5.QtCore import Qt, QSize
|
|
16
15
|
from PyQt5.QtGui import QKeySequence, QDoubleValidator
|
|
17
|
-
from celldetective.gui.gui_utils import FigureCanvas,
|
|
18
|
-
from celldetective.gui import
|
|
16
|
+
from celldetective.gui.gui_utils import FigureCanvas, QuickSliderLayout, QHSeperationLine, ThresholdLineEdit, PreprocessingLayout2
|
|
17
|
+
from celldetective.gui import CelldetectiveWidget
|
|
19
18
|
from superqt import QLabeledDoubleSlider, QLabeledSlider, QLabeledDoubleRangeSlider
|
|
20
19
|
from superqt.fonticon import icon
|
|
21
20
|
from fonticon_mdi6 import MDI6
|
|
22
21
|
from matplotlib_scalebar.scalebar import ScaleBar
|
|
23
22
|
import gc
|
|
24
|
-
from celldetective.utils import mask_edges
|
|
25
23
|
from scipy.ndimage import shift
|
|
26
24
|
|
|
27
25
|
class StackVisualizer(CelldetectiveWidget):
|
celldetective/gui/workers.py
CHANGED
|
@@ -4,8 +4,6 @@ from PyQt5.QtCore import QRunnable, QObject, pyqtSignal, QThreadPool, QSize, Qt
|
|
|
4
4
|
|
|
5
5
|
from celldetective.gui.base_components import CelldetectiveDialog
|
|
6
6
|
from celldetective.gui.gui_utils import center_window
|
|
7
|
-
from celldetective.gui import Styles
|
|
8
|
-
import time
|
|
9
7
|
import math
|
|
10
8
|
|
|
11
9
|
class ProgressWindow(CelldetectiveDialog):
|