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.
Files changed (78) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/exceptions.py +11 -0
  3. celldetective/filters.py +7 -1
  4. celldetective/gui/InitWindow.py +4 -1
  5. celldetective/gui/__init__.py +2 -9
  6. celldetective/gui/about.py +2 -2
  7. celldetective/gui/base_annotator.py +786 -0
  8. celldetective/gui/classifier_widget.py +18 -13
  9. celldetective/gui/configure_new_exp.py +51 -30
  10. celldetective/gui/control_panel.py +10 -7
  11. celldetective/gui/{signal_annotator.py → event_annotator.py} +473 -1437
  12. celldetective/gui/generic_signal_plot.py +2 -1
  13. celldetective/gui/gui_utils.py +5 -2
  14. celldetective/gui/help/neighborhood.json +2 -2
  15. celldetective/gui/layouts.py +21 -11
  16. celldetective/gui/{signal_annotator2.py → pair_event_annotator.py} +3 -1
  17. celldetective/gui/process_block.py +129 -91
  18. celldetective/gui/processes/downloader.py +37 -34
  19. celldetective/gui/processes/measure_cells.py +14 -8
  20. celldetective/gui/processes/segment_cells.py +438 -314
  21. celldetective/gui/processes/track_cells.py +12 -13
  22. celldetective/gui/settings/__init__.py +7 -0
  23. celldetective/gui/settings/_settings_base.py +70 -0
  24. celldetective/gui/{retrain_signal_model_options.py → settings/_settings_event_model_training.py} +35 -91
  25. celldetective/gui/{measurement_options.py → settings/_settings_measurements.py} +28 -81
  26. celldetective/gui/{neighborhood_options.py → settings/_settings_neighborhood.py} +1 -1
  27. celldetective/gui/settings/_settings_segmentation.py +49 -0
  28. celldetective/gui/{retrain_segmentation_model_options.py → settings/_settings_segmentation_model_training.py} +33 -79
  29. celldetective/gui/{signal_annotator_options.py → settings/_settings_signal_annotator.py} +73 -95
  30. celldetective/gui/{btrack_options.py → settings/_settings_tracking.py} +64 -87
  31. celldetective/gui/styles.py +2 -1
  32. celldetective/gui/survival_ui.py +1 -1
  33. celldetective/gui/tableUI.py +25 -0
  34. celldetective/gui/table_ops/__init__.py +0 -0
  35. celldetective/gui/table_ops/merge_groups.py +118 -0
  36. celldetective/gui/viewers.py +3 -5
  37. celldetective/gui/workers.py +0 -2
  38. celldetective/io.py +98 -55
  39. celldetective/links/zenodo.json +145 -144
  40. celldetective/measure.py +31 -26
  41. celldetective/preprocessing.py +34 -21
  42. celldetective/regionprops/_regionprops.py +16 -5
  43. celldetective/scripts/measure_cells.py +5 -5
  44. celldetective/scripts/measure_relative.py +16 -11
  45. celldetective/scripts/segment_cells.py +4 -4
  46. celldetective/scripts/segment_cells_thresholds.py +3 -3
  47. celldetective/scripts/track_cells.py +7 -7
  48. celldetective/scripts/train_segmentation_model.py +10 -1
  49. celldetective/tracking.py +10 -4
  50. celldetective/utils.py +59 -58
  51. {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/METADATA +1 -1
  52. celldetective-1.4.1.post1.dist-info/RECORD +123 -0
  53. tests/gui/__init__.py +0 -0
  54. tests/gui/test_new_project.py +228 -0
  55. tests/{test_qt.py → gui/test_project.py} +22 -26
  56. tests/test_preprocessing.py +2 -2
  57. celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -79
  58. celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
  59. celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -37
  60. celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -39
  61. celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
  62. celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
  63. celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
  64. celldetective/models/signal_detection/NucCond/config_input.json +0 -1
  65. celldetective/models/signal_detection/NucCond/log_classifier.csv +0 -126
  66. celldetective/models/signal_detection/NucCond/log_regressor.csv +0 -282
  67. celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
  68. celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
  69. celldetective/models/signal_detection/NucCond/scores.npy +0 -0
  70. celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
  71. celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
  72. celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
  73. celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
  74. celldetective-1.4.0.dist-info/RECORD +0 -131
  75. {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/WHEEL +0 -0
  76. {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/entry_points.txt +0 -0
  77. {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/licenses/LICENSE +0 -0
  78. {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,12 @@
1
- from PyQt5.QtWidgets import QRadioButton, QButtonGroup, QApplication, QMessageBox, QScrollArea, QComboBox, QFrame, QCheckBox, QFileDialog, QGridLayout, QTextEdit, QLineEdit, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
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 center_window, FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, help_generic
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 CelldetectiveWidget, CelldetectiveMainWindow
23
+ from celldetective.gui.settings._settings_base import CelldetectiveSettingsPanel
20
24
 
21
25
 
22
- class ConfigTracking(CelldetectiveMainWindow):
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
- def populate_widget(self):
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
- # Create button widget and layout
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.scroll_area.setMinimumHeight(min(int(930), int(0.9*self.screen_height)))
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.scroll_area.setMinimumHeight(self.minimum_height)
146
- #self.adjustSize()
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
- self.scroll_area.setMinimumHeight(int(self.minimum_height))
207
- self.adjustSize()
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
- self.scroll_area.setMinimumHeight(int(self.minimum_height))
323
- self.adjustSize()
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
- self.scroll_area.setMinimumHeight(int(self.minimum_height))
618
- self.adjustSize()
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
- self.scroll_area.setMinimumHeight(int(self.minimum_height))
639
- self.adjustSize()
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.floatValidator)
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.soft_path, "celldetective","models","tracking_configs"]) + os.sep
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
- Auto-adjust scroll area to fill space
829
- (from https://stackoverflow.com/questions/66417576/make-qscrollarea-use-all-available-space-of-qmainwindow-height-axis)
830
- """
831
-
832
- step = 5
833
- while self.scroll_area.verticalScrollBar().isVisible() and self.height() < self.maximumHeight():
834
- self.resize(self.width(), self.height() + step)
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 write_instructions(self):
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 load_previous_tracking_instructions(self):
933
+ def _load_previous_instructions(self):
957
934
 
958
935
  """
959
936
  Read the tracking options from a previously written json file.
@@ -14,7 +14,8 @@ class Styles(object):
14
14
 
15
15
 
16
16
  self.celldetective_blue = "#1565c0"
17
- self.celldetective_icon = QIcon(os.sep.join([get_software_location(),'celldetective','icons','logo.png']))
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;
@@ -1,4 +1,4 @@
1
- from PyQt5.QtWidgets import QMessageBox, QComboBox, QLineEdit, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
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
@@ -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...")
@@ -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, center_window, QuickSliderLayout, QHSeperationLine, ThresholdLineEdit, PreprocessingLayout2
18
- from celldetective.gui import Styles, CelldetectiveWidget
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):
@@ -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):