celldetective 1.4.0__py3-none-any.whl → 1.4.1__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 +21 -6
  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.dist-info}/METADATA +1 -1
  52. celldetective-1.4.1.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.dist-info}/WHEEL +0 -0
  76. {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/entry_points.txt +0 -0
  77. {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/licenses/LICENSE +0 -0
  78. {celldetective-1.4.0.dist-info → celldetective-1.4.1.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  """
2
- Copright © 2023 Laboratoire Adhesion et Inflammation, Authored by Remy Torro.
2
+ Copyright © 2023 Laboratoire Adhesion et Inflammation, Authored by Remy Torro.
3
3
  """
4
4
 
5
5
  from PyQt5.QtWidgets import QComboBox, QLabel, QRadioButton, QLineEdit, QApplication, QPushButton, QScrollArea, QVBoxLayout, QHBoxLayout
@@ -12,9 +12,9 @@ import numpy as np
12
12
  from superqt.fonticon import icon
13
13
  from fonticon_mdi6 import MDI6
14
14
  import os
15
- from celldetective.gui import CelldetectiveWidget, CelldetectiveMainWindow
15
+ from celldetective.gui.settings._settings_base import CelldetectiveSettingsPanel
16
16
 
17
- class ConfigSignalAnnotator(CelldetectiveMainWindow):
17
+ class SettingsSignalAnnotator(CelldetectiveSettingsPanel):
18
18
 
19
19
  """
20
20
  UI to set normalization and animation parameters for the annotator tool.
@@ -23,65 +23,94 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
23
23
 
24
24
  def __init__(self, parent_window=None):
25
25
 
26
- super().__init__()
27
26
  self.parent_window = parent_window
28
- self.setWindowTitle("Configure signal annotator")
29
27
  self.mode = self.parent_window.mode
30
28
  self.exp_dir = self.parent_window.exp_dir
31
- self.soft_path = get_software_location()
32
29
 
33
30
  self.instructions_path = self.parent_window.exp_dir + f"configs/signal_annotator_config_{self.mode}.json"
34
31
  if self.mode == "pairs":
35
32
  self.instructions_path = self.parent_window.exp_dir + "configs/signal_annotator_config_neighborhood.json"
36
33
 
37
- exp_config = self.exp_dir +"config.ini"
38
- #self.config_path = self.exp_dir + self.config_name
39
34
  self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
40
35
  self.channel_names = np.array(self.channel_names)
41
36
  self.channels = np.array(self.channels)
42
37
  self.log_option = False
38
+
39
+ super().__init__(title="Configure signal annotator")
43
40
 
44
- self.screen_height = self.parent_window.parent_window.parent_window.screen_height
45
- center_window(self)
41
+ self._add_to_layout()
42
+ self._load_previous_instructions()
43
+
44
+ self._adjustSize()
45
+ self.resize(int(self.width()), int(self._screen_height * 0.55))
46
+
47
+ def _add_to_layout(self):
48
+
49
+ sub_layout = QVBoxLayout()
50
+ sub_layout.setContentsMargins(10,10,10,20)
51
+ sub_layout.setContentsMargins(30,30,30,30)
52
+ sub_layout.addWidget(self._modality_lbl)
53
+
54
+ # Create radio buttons
55
+ option_layout = QHBoxLayout()
56
+ option_layout.addWidget(self.gs_btn, alignment=Qt.AlignCenter)
57
+ option_layout.addWidget(self.rgb_btn, alignment=Qt.AlignCenter)
58
+ sub_layout.addLayout(option_layout)
59
+
60
+ btn_hbox = QHBoxLayout()
61
+ btn_hbox.addWidget(QLabel(''), 90)
62
+ btn_hbox.addWidget(self.log_btn, 5,alignment=Qt.AlignRight)
63
+ btn_hbox.addWidget(self.percentile_btn, 5,alignment=Qt.AlignRight)
64
+ sub_layout.addLayout(btn_hbox)
46
65
 
47
- self.setMinimumHeight(int(0.4*self.screen_height))
48
- self.setMaximumHeight(int(0.8*self.screen_height))
49
- self.populate_widget()
50
- #self.load_previous_measurement_instructions()
66
+ for i in range(3):
67
+ hlayout = QHBoxLayout()
68
+ hlayout.addWidget(self.channel_cbs_lbls[i], 20)
69
+ hlayout.addWidget(self.channel_cbs[i], 80)
70
+ sub_layout.addLayout(hlayout)
51
71
 
72
+ hlayout2 = QHBoxLayout()
73
+ hlayout2.addWidget(self.min_val_lbls[i], 20)
74
+ hlayout2.addWidget(self.min_val_les[i], 80)
75
+ sub_layout.addLayout(hlayout2)
76
+
77
+ hlayout3 = QHBoxLayout()
78
+ hlayout3.addWidget(self.max_val_lbls[i], 20)
79
+ hlayout3.addWidget(self.max_val_les[i], 80)
80
+ sub_layout.addLayout(hlayout3)
52
81
 
53
- def populate_widget(self):
82
+ sub_layout.addWidget(self.hsep)
83
+ hbox_frac = QHBoxLayout()
84
+ hbox_frac.addWidget(self._fraction_lbl, 20)
85
+ hbox_frac.addWidget(self.fraction_slider, 80)
86
+ sub_layout.addLayout(hbox_frac)
87
+
88
+ hbox_interval = QHBoxLayout()
89
+ hbox_interval.addWidget(self._interval_lbl, 20)
90
+ hbox_interval.addWidget(self.interval_slider, 80)
91
+ sub_layout.addLayout(hbox_interval)
92
+
93
+ self._layout.addLayout(sub_layout)
94
+
95
+ self._layout.addWidget(self.submit_btn)
96
+
97
+ def _create_widgets(self):
54
98
 
55
99
  """
56
100
  Create the widgets.
57
101
 
58
102
  """
59
-
60
- self.scroll_area = QScrollArea(self)
61
- self.button_widget = CelldetectiveWidget()
62
-
63
- self.main_layout = QVBoxLayout()
64
- self.main_layout.setContentsMargins(30,30,30,30)
65
-
66
- sub_layout = QVBoxLayout()
67
- sub_layout.setContentsMargins(10,10,10,20)
68
-
69
- self.button_widget.setLayout(self.main_layout)
70
- sub_layout.setContentsMargins(30,30,30,30)
71
-
72
- sub_layout.addWidget(QLabel('Modality: '))
103
+ super()._create_widgets()
104
+
105
+ self._modality_lbl = QLabel("Modality: ")
106
+ self._fraction_lbl = QLabel("fraction: ")
107
+ self._interval_lbl = QLabel('interval [ms]: ')
73
108
 
74
- # Create radio buttons
75
- option_layout = QHBoxLayout()
76
109
  self.gs_btn = QRadioButton('grayscale')
77
110
  self.gs_btn.setChecked(True)
78
- option_layout.addWidget(self.gs_btn, alignment=Qt.AlignCenter)
79
-
111
+
80
112
  self.rgb_btn = QRadioButton('RGB')
81
- option_layout.addWidget(self.rgb_btn, alignment=Qt.AlignCenter)
82
- sub_layout.addLayout(option_layout)
83
113
 
84
- btn_hbox = QHBoxLayout()
85
114
 
86
115
  self.percentile_btn = QPushButton()
87
116
  self.percentile_btn.setIcon(icon(MDI6.percent_circle_outline,color="black"))
@@ -97,11 +126,6 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
97
126
  self.log_btn.setToolTip("Log-transform the intensities.")
98
127
  self.log_btn.setIconSize(QSize(20, 20))
99
128
 
100
- btn_hbox.addWidget(QLabel(''), 90)
101
- btn_hbox.addWidget(self.log_btn, 5,alignment=Qt.AlignRight)
102
- btn_hbox.addWidget(self.percentile_btn, 5,alignment=Qt.AlignRight)
103
- sub_layout.addLayout(btn_hbox)
104
-
105
129
  self.channel_cbs = [QComboBox() for i in range(3)]
106
130
  self.channel_cbs_lbls = [QLabel() for i in range(3)]
107
131
 
@@ -115,23 +139,13 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
115
139
 
116
140
  for i in range(3):
117
141
 
118
- hlayout = QHBoxLayout()
119
142
  self.channel_cbs[i].addItems(self.channel_names)
120
143
  self.channel_cbs[i].setCurrentIndex(i)
121
144
  self.channel_cbs_lbls[i].setText(self.rgb_text[i])
122
- hlayout.addWidget(self.channel_cbs_lbls[i], 20)
123
- hlayout.addWidget(self.channel_cbs[i], 80)
124
- sub_layout.addLayout(hlayout)
145
+
146
+ self.min_val_les[i].setValidator(self._floatValidator)
147
+ self.max_val_les[i].setValidator(self._floatValidator)
125
148
 
126
- hlayout2 = QHBoxLayout()
127
- hlayout2.addWidget(self.min_val_lbls[i], 20)
128
- hlayout2.addWidget(self.min_val_les[i], 80)
129
- sub_layout.addLayout(hlayout2)
130
-
131
- hlayout3 = QHBoxLayout()
132
- hlayout3.addWidget(self.max_val_lbls[i], 20)
133
- hlayout3.addWidget(self.max_val_les[i], 80)
134
- sub_layout.addLayout(hlayout3)
135
149
 
136
150
  self.enable_channels()
137
151
 
@@ -139,10 +153,6 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
139
153
  self.rgb_btn.toggled.connect(self.enable_channels)
140
154
 
141
155
  self.hsep = QHSeperationLine()
142
- sub_layout.addWidget(self.hsep)
143
-
144
- hbox_frac = QHBoxLayout()
145
- hbox_frac.addWidget(QLabel('fraction: '), 20)
146
156
 
147
157
  self.fraction_slider = QLabeledDoubleSlider()
148
158
  self.fraction_slider.setSingleStep(0.05)
@@ -152,13 +162,6 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
152
162
  self.fraction_slider.setRange(0.1,1)
153
163
  self.fraction_slider.setValue(0.25)
154
164
 
155
- hbox_frac.addWidget(self.fraction_slider, 80)
156
- sub_layout.addLayout(hbox_frac)
157
-
158
-
159
- hbox_interval = QHBoxLayout()
160
- hbox_interval.addWidget(QLabel('interval [ms]: '), 20)
161
-
162
165
  self.interval_slider = QLabeledSlider()
163
166
  self.interval_slider.setSingleStep(1)
164
167
  self.interval_slider.setTickInterval(1)
@@ -166,29 +169,6 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
166
169
  self.interval_slider.setOrientation(Qt.Horizontal)
167
170
  self.interval_slider.setRange(1,1000)
168
171
  self.interval_slider.setValue(1)
169
- hbox_interval.addWidget(self.interval_slider, 80)
170
- sub_layout.addLayout(hbox_interval)
171
-
172
- self.main_layout.addLayout(sub_layout)
173
-
174
- self.submit_btn = QPushButton('Save')
175
- self.submit_btn.setStyleSheet(self.button_style_sheet)
176
- self.submit_btn.clicked.connect(self.write_instructions)
177
- self.main_layout.addWidget(self.submit_btn)
178
-
179
-
180
- self.button_widget.adjustSize()
181
- self.scroll_area.setAlignment(Qt.AlignCenter)
182
- self.scroll_area.setWidget(self.button_widget)
183
- self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
184
- self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
185
- self.scroll_area.setWidgetResizable(True)
186
- self.setCentralWidget(self.scroll_area)
187
- self.show()
188
-
189
- self.read_instructions()
190
-
191
- QApplication.processEvents()
192
172
 
193
173
  def enable_channels(self):
194
174
 
@@ -207,7 +187,6 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
207
187
  self.channel_cbs_lbls[k].setEnabled(False)
208
188
 
209
189
  for k in range(3):
210
-
211
190
  self.min_val_les[k].setEnabled(False)
212
191
  self.min_val_lbls[k].setEnabled(False)
213
192
  self.max_val_les[k].setEnabled(False)
@@ -219,7 +198,6 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
219
198
  self.percentile_btn.setEnabled(True)
220
199
 
221
200
  for k in range(3):
222
-
223
201
  self.channel_cbs[k].setEnabled(True)
224
202
  self.channel_cbs_lbls[k].setEnabled(True)
225
203
 
@@ -256,7 +234,7 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
256
234
  self.max_val_lbls[k].setText('Max percentile: ')
257
235
  self.max_val_les[k].setText('99.99')
258
236
 
259
- def write_instructions(self):
237
+ def _write_instructions(self):
260
238
 
261
239
  """
262
240
  Save the current configuration.
@@ -267,7 +245,7 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
267
245
  max_i = 3 if self.rgb_btn.isChecked() else 1
268
246
  channels = []
269
247
  for i in range(max_i):
270
- channels.append([self.channel_cbs[i].currentText(), float(self.min_val_les[i].text()), float(self.max_val_les[i].text())])
248
+ channels.append([self.channel_cbs[i].currentText(), float(self.min_val_les[i].text().replace(',','.')), float(self.max_val_les[i].text().replace(',','.'))])
271
249
  instructions.update({'channels': channels})
272
250
 
273
251
  print('Instructions: ', instructions)
@@ -277,7 +255,7 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
277
255
  print('Done.')
278
256
  self.close()
279
257
 
280
- def read_instructions(self):
258
+ def _load_previous_instructions(self):
281
259
 
282
260
  """
283
261
  Read and set the widgets to the last configuration.
@@ -312,8 +290,8 @@ class ConfigSignalAnnotator(CelldetectiveMainWindow):
312
290
  for i in range(max_iter):
313
291
  idx = self.channel_cbs[i].findText(channels[i][0])
314
292
  self.channel_cbs[i].setCurrentIndex(idx)
315
- self.min_val_les[i].setText(str(channels[i][1]))
316
- self.max_val_les[i].setText(str(channels[i][2]))
293
+ self.min_val_les[i].setText(str(channels[i][1]).replace('.',','))
294
+ self.max_val_les[i].setText(str(channels[i][2]).replace('.',','))
317
295
 
318
296
  if 'fraction' in instructions:
319
297
  fraction = instructions['fraction']
@@ -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