celldetective 1.3.9.post5__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 (94) hide show
  1. celldetective/__init__.py +0 -3
  2. celldetective/_version.py +1 -1
  3. celldetective/events.py +2 -4
  4. celldetective/exceptions.py +11 -0
  5. celldetective/extra_properties.py +132 -0
  6. celldetective/filters.py +7 -1
  7. celldetective/gui/InitWindow.py +37 -46
  8. celldetective/gui/__init__.py +3 -9
  9. celldetective/gui/about.py +19 -15
  10. celldetective/gui/analyze_block.py +34 -19
  11. celldetective/gui/base_annotator.py +786 -0
  12. celldetective/gui/base_components.py +23 -0
  13. celldetective/gui/classifier_widget.py +86 -94
  14. celldetective/gui/configure_new_exp.py +163 -46
  15. celldetective/gui/control_panel.py +76 -146
  16. celldetective/gui/{signal_annotator.py → event_annotator.py} +533 -1438
  17. celldetective/gui/generic_signal_plot.py +11 -13
  18. celldetective/gui/gui_utils.py +54 -23
  19. celldetective/gui/help/neighborhood.json +2 -2
  20. celldetective/gui/json_readers.py +5 -4
  21. celldetective/gui/layouts.py +265 -31
  22. celldetective/gui/{signal_annotator2.py → pair_event_annotator.py} +433 -635
  23. celldetective/gui/plot_measurements.py +21 -17
  24. celldetective/gui/plot_signals_ui.py +125 -72
  25. celldetective/gui/process_block.py +283 -188
  26. celldetective/gui/processes/compute_neighborhood.py +594 -0
  27. celldetective/gui/processes/downloader.py +37 -34
  28. celldetective/gui/processes/measure_cells.py +19 -8
  29. celldetective/gui/processes/segment_cells.py +47 -11
  30. celldetective/gui/processes/track_cells.py +18 -13
  31. celldetective/gui/seg_model_loader.py +21 -62
  32. celldetective/gui/settings/__init__.py +7 -0
  33. celldetective/gui/settings/_settings_base.py +70 -0
  34. celldetective/gui/{retrain_signal_model_options.py → settings/_settings_event_model_training.py} +54 -109
  35. celldetective/gui/{measurement_options.py → settings/_settings_measurements.py} +54 -92
  36. celldetective/gui/{neighborhood_options.py → settings/_settings_neighborhood.py} +10 -13
  37. celldetective/gui/settings/_settings_segmentation.py +49 -0
  38. celldetective/gui/{retrain_segmentation_model_options.py → settings/_settings_segmentation_model_training.py} +38 -92
  39. celldetective/gui/{signal_annotator_options.py → settings/_settings_signal_annotator.py} +78 -103
  40. celldetective/gui/{btrack_options.py → settings/_settings_tracking.py} +85 -116
  41. celldetective/gui/styles.py +2 -1
  42. celldetective/gui/survival_ui.py +49 -95
  43. celldetective/gui/tableUI.py +53 -25
  44. celldetective/gui/table_ops/__init__.py +0 -0
  45. celldetective/gui/table_ops/merge_groups.py +118 -0
  46. celldetective/gui/thresholds_gui.py +617 -1221
  47. celldetective/gui/viewers.py +107 -42
  48. celldetective/gui/workers.py +8 -4
  49. celldetective/io.py +137 -57
  50. celldetective/links/zenodo.json +145 -144
  51. celldetective/measure.py +94 -53
  52. celldetective/neighborhood.py +342 -268
  53. celldetective/preprocessing.py +56 -35
  54. celldetective/regionprops/_regionprops.py +16 -5
  55. celldetective/relative_measurements.py +50 -29
  56. celldetective/scripts/analyze_signals.py +4 -1
  57. celldetective/scripts/measure_cells.py +5 -5
  58. celldetective/scripts/measure_relative.py +20 -12
  59. celldetective/scripts/segment_cells.py +4 -10
  60. celldetective/scripts/segment_cells_thresholds.py +3 -3
  61. celldetective/scripts/track_cells.py +10 -8
  62. celldetective/scripts/train_segmentation_model.py +18 -6
  63. celldetective/signals.py +29 -14
  64. celldetective/tracking.py +14 -3
  65. celldetective/utils.py +91 -62
  66. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/METADATA +24 -16
  67. celldetective-1.4.1.dist-info/RECORD +123 -0
  68. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/WHEEL +1 -1
  69. tests/gui/__init__.py +0 -0
  70. tests/gui/test_new_project.py +228 -0
  71. tests/gui/test_project.py +99 -0
  72. tests/test_preprocessing.py +2 -2
  73. celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -79
  74. celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
  75. celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -37
  76. celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -39
  77. celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
  78. celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
  79. celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
  80. celldetective/models/signal_detection/NucCond/config_input.json +0 -1
  81. celldetective/models/signal_detection/NucCond/log_classifier.csv +0 -126
  82. celldetective/models/signal_detection/NucCond/log_regressor.csv +0 -282
  83. celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
  84. celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
  85. celldetective/models/signal_detection/NucCond/scores.npy +0 -0
  86. celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
  87. celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
  88. celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
  89. celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
  90. celldetective-1.3.9.post5.dist-info/RECORD +0 -129
  91. tests/test_qt.py +0 -103
  92. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/entry_points.txt +0 -0
  93. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info/licenses}/LICENSE +0 -0
  94. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/top_level.txt +0 -0
@@ -1,12 +1,9 @@
1
- from PyQt5.QtWidgets import QMainWindow, QApplication, QDialog, QMessageBox, QScrollArea, QComboBox, QFrame, QCheckBox, QFileDialog, QGridLayout, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
1
+ from PyQt5.QtWidgets import QMessageBox, QComboBox, QFrame, QCheckBox, QFileDialog, QGridLayout, QLineEdit, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
2
2
  from PyQt5.QtCore import Qt, QSize
3
- from PyQt5.QtGui import QDoubleValidator, QIntValidator, QIcon
4
- from celldetective.gui.gui_utils import center_window
5
3
  from celldetective.gui.layouts import ChannelNormGenerator
6
4
  from superqt import QLabeledDoubleSlider, QLabeledSlider, QSearchableComboBox
7
5
  from superqt.fonticon import icon
8
6
  from fonticon_mdi6 import MDI6
9
- from celldetective.utils import get_software_location
10
7
  from celldetective.io import locate_signal_dataset, get_signal_datasets_list, load_experiment_tables
11
8
  from celldetective.signals import train_signal_model
12
9
  import numpy as np
@@ -14,12 +11,12 @@ import json
14
11
  import os
15
12
  from glob import glob
16
13
  from datetime import datetime
17
- from celldetective.gui import Styles
18
14
  from pandas.api.types import is_numeric_dtype
19
15
  from celldetective.gui.processes.train_signal_model import TrainSignalModelProcess
20
16
  from celldetective.gui.workers import ProgressWindow
17
+ from celldetective.gui.settings._settings_base import CelldetectiveSettingsPanel
21
18
 
22
- class ConfigSignalModelTraining(QMainWindow, Styles):
19
+ class SettingsEventDetectionModelTraining(CelldetectiveSettingsPanel):
23
20
 
24
21
  """
25
22
  UI to set measurement instructions.
@@ -28,88 +25,62 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
28
25
 
29
26
  def __init__(self, parent_window=None, signal_mode='single-cells'):
30
27
 
31
- super().__init__()
32
28
  self.parent_window = parent_window
33
- self.setWindowTitle("Train signal model")
34
- self.setWindowIcon(QIcon(os.sep.join(['celldetective','icons','mexican-hat.png'])))
35
29
  self.mode = self.parent_window.mode
36
30
  self.exp_dir = self.parent_window.exp_dir
37
- self.soft_path = get_software_location()
38
- self.pretrained_model = None
31
+ self.pretrained_model = None
39
32
  self.dataset_folder = None
40
33
  self.current_neighborhood = None
41
34
  self.reference_population = None
42
35
  self.neighbor_population = None
43
36
  self.signal_mode = signal_mode
37
+
38
+ super().__init__(title="Train event detection model")
44
39
 
45
40
  if self.signal_mode=='single-cells':
46
- self.signal_models_dir = self.soft_path+os.sep+os.sep.join(['celldetective','models','signal_detection'])
41
+ self.signal_models_dir = self._software_path+os.sep+os.sep.join(['celldetective','models','signal_detection'])
47
42
  elif self.signal_mode=='pairs':
48
- self.signal_models_dir = self.soft_path+os.sep+os.sep.join(['celldetective','models','pair_signal_detection'])
43
+ self.signal_models_dir = self._software_path+os.sep+os.sep.join(['celldetective','models','pair_signal_detection'])
49
44
  self.mode = 'pairs'
45
+
46
+ self._add_to_layout()
47
+ self._load_previous_instructions()
48
+
49
+ self._adjustSize()
50
+ new_width = int(self.width()*1.2)
51
+ self.resize(new_width, int(self._screen_height * 0.8))
52
+ self.setMinimumWidth(new_width)
53
+
54
+ def _add_to_layout(self):
55
+ self._layout.addWidget(self.model_frame)
56
+ self._layout.addWidget(self.data_frame)
57
+ self._layout.addWidget(self.hyper_frame)
58
+ self._layout.addWidget(self.submit_btn)
50
59
 
51
- self.onlyFloat = QDoubleValidator()
52
- self.onlyInt = QIntValidator()
53
-
54
- self.screen_height = self.parent_window.parent_window.parent_window.screen_height
55
- center_window(self)
56
-
57
- self.setMinimumWidth(500)
58
- self.setMinimumHeight(int(0.3*self.screen_height))
59
- self.setMaximumHeight(int(0.8*self.screen_height))
60
- self.populate_widget()
61
- #self.load_previous_measurement_instructions()
62
-
63
- def populate_widget(self):
60
+ def _create_widgets(self):
64
61
 
65
62
  """
66
63
  Create the multibox design.
67
64
 
68
65
  """
66
+ super()._create_widgets()
69
67
 
70
- # Create button widget and layout
71
- self.scroll_area = QScrollArea(self)
72
- self.button_widget = QWidget()
73
- self.main_layout = QVBoxLayout()
74
- self.button_widget.setLayout(self.main_layout)
75
- self.main_layout.setContentsMargins(30,30,30,30)
76
-
77
68
  # first frame for FEATURES
78
69
  self.model_frame = QFrame()
79
70
  self.model_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
80
71
  self.populate_model_frame()
81
- self.main_layout.addWidget(self.model_frame)
82
72
 
83
73
  self.data_frame = QFrame()
84
74
  self.data_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
85
75
  self.populate_data_frame()
86
- self.main_layout.addWidget(self.data_frame)
87
76
 
88
77
  self.hyper_frame = QFrame()
89
78
  self.hyper_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
90
79
  self.populate_hyper_frame()
91
- self.main_layout.addWidget(self.hyper_frame)
92
80
 
93
- self.submit_btn = QPushButton('Train')
94
- self.submit_btn.setStyleSheet(self.button_style_sheet)
95
- self.submit_btn.clicked.connect(self.prep_model)
96
- self.main_layout.addWidget(self.submit_btn)
97
81
  self.submit_btn.setEnabled(False)
82
+ self.submit_btn.setText("Train")
98
83
 
99
- #self.populate_left_panel()
100
- #grid.addLayout(self.left_side, 0, 0, 1, 1)
101
- self.button_widget.adjustSize()
102
-
103
- self.scroll_area.setAlignment(Qt.AlignCenter)
104
- self.scroll_area.setWidget(self.button_widget)
105
- self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
106
- self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
107
- self.scroll_area.setWidgetResizable(True)
108
- self.setCentralWidget(self.scroll_area)
109
- self.show()
110
-
111
- QApplication.processEvents()
112
- self.adjustScrollArea()
113
84
 
114
85
  def populate_hyper_frame(self):
115
86
 
@@ -139,14 +110,14 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
139
110
  lr_layout = QHBoxLayout()
140
111
  lr_layout.addWidget(QLabel('learning rate: '),30)
141
112
  self.lr_le = QLineEdit('0,01')
142
- self.lr_le.setValidator(self.onlyFloat)
113
+ self.lr_le.setValidator(self._floatValidator)
143
114
  lr_layout.addWidget(self.lr_le, 70)
144
115
  layout.addLayout(lr_layout)
145
116
 
146
117
  bs_layout = QHBoxLayout()
147
118
  bs_layout.addWidget(QLabel('batch size: '),30)
148
119
  self.bs_le = QLineEdit('64')
149
- self.bs_le.setValidator(self.onlyInt)
120
+ self.bs_le.setValidator(self._intValidator)
150
121
  bs_layout.addWidget(self.bs_le, 70)
151
122
  layout.addLayout(bs_layout)
152
123
 
@@ -156,7 +127,7 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
156
127
  self.epochs_slider.setRange(1,3000)
157
128
  self.epochs_slider.setSingleStep(1)
158
129
  self.epochs_slider.setTickInterval(1)
159
- self.epochs_slider.setOrientation(1)
130
+ self.epochs_slider.setOrientation(Qt.Horizontal)
160
131
  self.epochs_slider.setValue(300)
161
132
  epochs_layout.addWidget(self.epochs_slider, 70)
162
133
  layout.addLayout(epochs_layout)
@@ -243,7 +214,7 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
243
214
  self.augmentation_slider = QLabeledDoubleSlider()
244
215
  self.augmentation_slider.setSingleStep(0.01)
245
216
  self.augmentation_slider.setTickInterval(0.01)
246
- self.augmentation_slider.setOrientation(1)
217
+ self.augmentation_slider.setOrientation(Qt.Horizontal)
247
218
  self.augmentation_slider.setRange(1, 5)
248
219
  self.augmentation_slider.setValue(2)
249
220
 
@@ -255,7 +226,7 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
255
226
  self.validation_slider = QLabeledDoubleSlider()
256
227
  self.validation_slider.setSingleStep(0.01)
257
228
  self.validation_slider.setTickInterval(0.01)
258
- self.validation_slider.setOrientation(1)
229
+ self.validation_slider.setOrientation(Qt.Horizontal)
259
230
  self.validation_slider.setRange(0,0.9)
260
231
  self.validation_slider.setValue(0.25)
261
232
  validation_split_layout.addWidget(self.validation_slider, 70)
@@ -332,7 +303,7 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
332
303
  self.model_length_slider.setSingleStep(1)
333
304
  self.model_length_slider.setTickInterval(1)
334
305
  self.model_length_slider.setSingleStep(1)
335
- self.model_length_slider.setOrientation(1)
306
+ self.model_length_slider.setOrientation(Qt.Horizontal)
336
307
  self.model_length_slider.setRange(0,1024)
337
308
  self.model_length_slider.setValue(128)
338
309
  model_length_layout.addWidget(self.model_length_slider, 70)
@@ -341,25 +312,22 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
341
312
  def neighborhood_changed(self):
342
313
 
343
314
  neigh = self.neighborhood_choice_cb.currentText()
344
- self.current_neighborhood = neigh.replace('target_ref_','').replace('effector_ref_','')
345
- self.reference_population = ['targets' if 'target' in neigh else 'effectors'][0]
346
- if 'target' in neigh:
347
- if 'self' in neigh:
348
- self.neighbor_population = 'targets'
349
- else:
350
- self.neighbor_population = 'effectors'
315
+ self.current_neighborhood = neigh
316
+ for pop in self.dataframes.keys():
317
+ self.current_neighborhood = self.current_neighborhood.replace(f'{pop}_ref_', '')
318
+
319
+ self.reference_population = self.neighborhood_choice_cb.currentText().split('_')[0]
320
+ if '_(' in self.current_neighborhood and ')_' in self.current_neighborhood:
321
+ self.neighbor_population = self.current_neighborhood.split('_(')[-1].split(')_')[0].split('-')[-1]
322
+ self.reference_population = self.current_neighborhood.split('_(')[-1].split(')_')[0].split('-')[0]
351
323
  else:
352
- if 'self' in neigh:
353
- self.neighbor_population = 'effectors'
354
- else:
355
- self.neighbor_population = 'targets'
356
-
324
+ if 'self' in self.current_neighborhood:
325
+ self.neighbor_population = self.reference_population
326
+
357
327
  print(f'Current neighborhood: {self.current_neighborhood}')
358
328
  print(f'New reference population: {self.reference_population}')
359
329
  print(f'New neighbor population: {self.neighbor_population}')
360
330
 
361
- # reload reference signals / neighbor signals / pair signals
362
- # fill the channel cbs
363
331
  self.df_reference = self.dataframes[self.reference_population]
364
332
  self.df_neighbor = self.dataframes[self.neighbor_population]
365
333
  self.df_pairs = load_experiment_tables(self.parent_window.exp_dir, population='pairs', load_pickle=False)
@@ -374,45 +342,19 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
374
342
  self.signals = ['--'] + num_cols_pairs + num_cols_reference + num_cols_neighbor
375
343
 
376
344
  for cb in self.ch_norm.channel_cbs:
377
- # try:
378
- # cb.disconnect()
379
- # except:
380
- # pass
381
345
  cb.clear()
382
346
  cb.addItems(self.signals)
383
347
 
384
348
  def fill_available_neighborhoods(self):
385
-
386
- df_targets = load_experiment_tables(self.parent_window.exp_dir, population='targets', load_pickle=True)
387
- df_effectors = load_experiment_tables(self.parent_window.exp_dir, population='effectors', load_pickle=True)
388
-
389
- self.dataframes = {
390
- 'targets': df_targets,
391
- 'effectors': df_effectors,
392
- }
393
349
 
350
+ self.dataframes = {}
394
351
  self.neighborhood_cols = []
395
- self.reference_populations = []
396
- self.neighbor_populations = []
397
- if df_targets is not None:
398
- self.neighborhood_cols.extend(['target_ref_'+c for c in list(df_targets.columns) if c.startswith('neighborhood')])
399
- self.reference_populations.extend(['targets' for c in list(df_targets.columns) if c.startswith('neighborhood')])
400
- for c in list(df_targets.columns):
401
- if c.startswith('neighborhood') and '_2_' in c:
402
- self.neighbor_populations.append('effectors')
403
- elif c.startswith('neighborhood') and 'self' in c:
404
- self.neighbor_populations.append('targets')
405
-
406
- if df_effectors is not None:
407
- self.neighborhood_cols.extend(['effector_ref_'+c for c in list(df_effectors.columns) if c.startswith('neighborhood')])
408
- self.reference_populations.extend(['effectors' for c in list(df_effectors.columns) if c.startswith('neighborhood')])
409
- for c in list(df_effectors.columns):
410
- if c.startswith('neighborhood') and '_2_' in c:
411
- self.neighbor_populations.append('targets')
412
- elif c.startswith('neighborhood') and 'self' in c:
413
- self.neighbor_populations.append('effectors')
414
-
415
- print(f"The following neighborhoods were detected: {self.neighborhood_cols=} {self.reference_populations=} {self.neighbor_populations=}")
352
+ for population in self.parent_window.parent_window.populations:
353
+ df_pop = load_experiment_tables(self.parent_window.exp_dir, population=population, load_pickle=True)
354
+ self.dataframes.update({population: df_pop})
355
+ if df_pop is not None:
356
+ self.neighborhood_cols.extend(
357
+ [f'{population}_ref_' + c for c in list(df_pop.columns) if c.startswith('neighborhood')])
416
358
 
417
359
  self.neighborhood_choice_cb.addItems(self.neighborhood_cols)
418
360
 
@@ -518,7 +460,7 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
518
460
  while self.scroll_area.verticalScrollBar().isVisible() and self.height() < self.maximumHeight():
519
461
  self.resize(self.width(), self.height() + step)
520
462
 
521
- def prep_model(self):
463
+ def _write_instructions(self):
522
464
 
523
465
  model_name = self.modelname_le.text()
524
466
  pretrained_model = self.pretrained_model
@@ -596,4 +538,7 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
596
538
  # return None
597
539
 
598
540
  train_signal_model(model_folder+"training_instructions.json")
599
- self.parent_window.refresh_signal_models()
541
+ self.parent_window.refresh_signal_models()
542
+
543
+ def _load_previous_instructions(self):
544
+ pass
@@ -1,21 +1,19 @@
1
+ from subprocess import Popen
1
2
 
2
- from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox, QScrollArea, QComboBox, QFrame, QCheckBox, \
3
- QGridLayout, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
3
+ from PyQt5.QtWidgets import QMessageBox, QComboBox, QFrame, QCheckBox, \
4
+ QGridLayout, QLineEdit, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
4
5
  from PyQt5.QtCore import Qt, QSize
5
- from PyQt5.QtGui import QIcon, QDoubleValidator, QIntValidator
6
6
 
7
- from celldetective.gui.gui_utils import center_window, FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, \
7
+ from celldetective.gui.gui_utils import FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, \
8
8
  GeometryChoice, OperationChoice
9
9
  from superqt import QLabeledDoubleSlider
10
10
  from superqt.fonticon import icon
11
11
  from fonticon_mdi6 import MDI6
12
12
 
13
- #from celldetective.gui.thresholds_gui import ThresholdSpot
14
13
  from celldetective.utils import extract_experiment_channels, get_software_location
15
14
  from celldetective.io import load_frames, auto_load_number_of_frames
16
15
  from celldetective.measure import compute_haralick_features
17
16
  import numpy as np
18
- from tifffile import imread
19
17
  import json
20
18
  import os
21
19
  import matplotlib.pyplot as plt
@@ -27,11 +25,11 @@ from pathlib import Path
27
25
 
28
26
  from celldetective.gui.viewers import CellEdgeVisualizer, SpotDetectionVisualizer
29
27
  from celldetective.gui.layouts import ProtocolDesignerLayout, BackgroundFitCorrectionLayout, LocalCorrectionLayout
30
- from celldetective.gui.gui_utils import ThresholdLineEdit, PreprocessingLayout2
31
- from celldetective.gui import Styles
28
+ from celldetective.gui.gui_utils import PreprocessingLayout2
29
+ from celldetective.gui.settings._settings_base import CelldetectiveSettingsPanel
32
30
 
33
31
 
34
- class ConfigMeasurements(QMainWindow, Styles):
32
+ class SettingsMeasurements(CelldetectiveSettingsPanel):
35
33
  """
36
34
  UI to set measurement instructions.
37
35
 
@@ -39,54 +37,33 @@ class ConfigMeasurements(QMainWindow, Styles):
39
37
 
40
38
  def __init__(self, parent_window=None):
41
39
 
42
- super().__init__()
43
-
44
40
  self.parent_window = parent_window
45
- self.setWindowTitle("Configure measurements")
46
- self.setWindowIcon(QIcon(os.sep.join(['celldetective', 'icons', 'mexican-hat.png'])))
47
41
  self.mode = self.parent_window.mode
48
42
  self.exp_dir = self.parent_window.exp_dir
49
43
  self.background_correction = []
50
- if self.mode == "targets":
51
- self.config_name = "btrack_config_targets.json"
52
- self.measure_instructions_path = self.parent_window.exp_dir + "configs/measurement_instructions_targets.json"
53
- elif self.mode == "effectors":
54
- self.config_name = "btrack_config_effectors.json"
55
- self.measure_instructions_path = self.parent_window.exp_dir + "configs/measurement_instructions_effectors.json"
56
- self.soft_path = get_software_location()
44
+ self.config_name = f"btrack_config_{self.mode}.json"
45
+ self.measure_instructions_path = self.parent_window.exp_dir + f"configs/measurement_instructions_{self.mode}.json"
57
46
  self.clear_previous = False
58
-
59
- exp_config = self.exp_dir + "config.ini"
60
47
  self.config_path = self.exp_dir + self.config_name
61
48
  self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
62
49
  self.channel_names = np.array(self.channel_names)
63
50
  self.channels = np.array(self.channels)
51
+
52
+ super().__init__("Configure measurements")
64
53
 
65
- self.screen_height = self.parent_window.parent_window.parent_window.screen_height
66
- center_window(self)
67
-
68
- self.onlyFloat = QDoubleValidator()
69
- self.onlyInt = QIntValidator()
70
-
71
- self.setMinimumWidth(500)
72
- self.setMinimumHeight(int(0.3 * self.screen_height))
73
- self.setMaximumHeight(int(0.8 * self.screen_height))
74
- self.populate_widget()
75
- self.load_previous_measurement_instructions()
76
-
77
- def populate_widget(self):
54
+ self._add_to_layout()
55
+ self._load_previous_instructions()
56
+
57
+ self._adjustSize()
58
+ self.resize(int(self.width()), int(self._screen_height * 0.8))
59
+
60
+ def _create_widgets(self):
78
61
 
79
62
  """
80
63
  Create the multibox design.
81
64
 
82
65
  """
83
-
84
- # Create button widget and layout
85
- self.scroll_area = QScrollArea(self)
86
- self.button_widget = QWidget()
87
- main_layout = QVBoxLayout()
88
- self.button_widget.setLayout(main_layout)
89
- main_layout.setContentsMargins(30, 30, 30, 30)
66
+ super()._create_widgets()
90
67
 
91
68
  self.normalisation_frame = QFrame()
92
69
  self.normalisation_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
@@ -104,48 +81,31 @@ class ConfigMeasurements(QMainWindow, Styles):
104
81
  self.normalisation_frame.setLayout(self.protocol_layout)
105
82
 
106
83
  #self.populate_normalisation_tabs()
107
- main_layout.addWidget(self.normalisation_frame)
108
84
 
109
85
  # first frame for FEATURES
110
86
  self.features_frame = QFrame()
111
87
  self.features_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
112
88
  self.populate_features_frame()
113
- main_layout.addWidget(self.features_frame)
114
89
 
115
90
  # second frame for ISOTROPIC MEASUREMENTS
116
91
  self.iso_frame = QFrame()
117
92
  self.iso_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
118
93
  self.populate_iso_frame()
119
- main_layout.addWidget(self.iso_frame)
120
94
 
121
95
  self.spot_detection_frame = QFrame()
122
96
  self.spot_detection_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
123
97
  self.populate_spot_detection()
124
- main_layout.addWidget(self.spot_detection_frame)
125
98
 
126
99
  self.clear_previous_btn = QCheckBox('clear previous measurements')
127
- main_layout.addWidget(self.clear_previous_btn)
128
-
129
- self.submit_btn = QPushButton('Save')
130
- self.submit_btn.setStyleSheet(self.button_style_sheet)
131
- self.submit_btn.clicked.connect(self.write_instructions)
132
- main_layout.addWidget(self.submit_btn)
133
-
134
- # self.populate_left_panel()
135
- # grid.addLayout(self.left_side, 0, 0, 1, 1)
136
- self.button_widget.adjustSize()
137
-
138
- self.scroll_area.setAlignment(Qt.AlignCenter)
139
- self.scroll_area.setWidget(self.button_widget)
140
- self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
141
- self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
142
- self.scroll_area.setWidgetResizable(True)
143
- self.setCentralWidget(self.scroll_area)
144
- self.show()
145
-
146
- QApplication.processEvents()
147
- self.adjustScrollArea()
148
100
 
101
+ def _add_to_layout(self):
102
+ self._layout.addWidget(self.normalisation_frame)
103
+ self._layout.addWidget(self.features_frame)
104
+ self._layout.addWidget(self.iso_frame)
105
+ self._layout.addWidget(self.spot_detection_frame)
106
+ self._layout.addWidget(self.clear_previous_btn)
107
+ self._layout.addWidget(self.submit_btn)
108
+
149
109
  def populate_iso_frame(self):
150
110
 
151
111
  """
@@ -264,12 +224,21 @@ class ConfigMeasurements(QMainWindow, Styles):
264
224
 
265
225
  self.features_list = ListWidget(FeatureChoice, initial_features=['area', 'intensity_mean', ])
266
226
 
227
+ self.create_feature_btn = QPushButton("")
228
+ self.create_feature_btn.setStyleSheet(self.button_select_all)
229
+ self.create_feature_btn.setIcon(icon(MDI6.file_cog, color="black"))
230
+ self.create_feature_btn.setToolTip("Create new feature")
231
+ self.create_feature_btn.setIconSize(QSize(20, 20))
232
+
267
233
  self.del_feature_btn.clicked.connect(self.features_list.removeSel)
268
234
  self.add_feature_btn.clicked.connect(self.features_list.addItem)
235
+ self.create_feature_btn.clicked.connect(self.go_to_extraprops)
269
236
 
270
237
  feature_layout.addWidget(self.feature_lbl, 90)
271
238
  feature_layout.addWidget(self.del_feature_btn, 5)
272
239
  feature_layout.addWidget(self.add_feature_btn, 5)
240
+ feature_layout.addWidget(self.create_feature_btn, 5)
241
+
273
242
  layout.addLayout(feature_layout)
274
243
  layout.addWidget(self.features_list)
275
244
 
@@ -334,7 +303,7 @@ class ConfigMeasurements(QMainWindow, Styles):
334
303
  self.haralick_scale_slider.setSingleStep(0.05)
335
304
  self.haralick_scale_slider.setTickInterval(0.05)
336
305
  self.haralick_scale_slider.setSingleStep(1)
337
- self.haralick_scale_slider.setOrientation(1)
306
+ self.haralick_scale_slider.setOrientation(Qt.Horizontal)
338
307
  self.haralick_scale_slider.setRange(0, 1)
339
308
  self.haralick_scale_slider.setValue(0.5)
340
309
  self.haralick_scale_lbl = QLabel('Scale: ')
@@ -426,6 +395,18 @@ class ConfigMeasurements(QMainWindow, Styles):
426
395
  self.haralick_normalization_mode_btn.clicked.connect(self.switch_to_absolute_normalization_mode)
427
396
  layout.addLayout(self.haralick_layout)
428
397
 
398
+ def go_to_extraprops(self):
399
+
400
+ path = os.sep.join([self._software_path,'celldetective',os.sep,'extra_properties.py'])
401
+ try:
402
+ Popen(f'explorer {os.path.realpath(path)}')
403
+ except:
404
+
405
+ try:
406
+ os.system('xdg-open "%s"' % path)
407
+ except:
408
+ return None
409
+
429
410
  def switch_to_absolute_normalization_mode(self):
430
411
 
431
412
  if self.percentile_mode:
@@ -472,7 +453,7 @@ class ConfigMeasurements(QMainWindow, Styles):
472
453
  while self.scroll_area.verticalScrollBar().isVisible() and self.height() < self.maximumHeight():
473
454
  self.resize(self.width(), self.height() + step)
474
455
 
475
- def write_instructions(self):
456
+ def _write_instructions(self):
476
457
 
477
458
  """
478
459
  Write the selected options in a json file for later reading by the software.
@@ -551,7 +532,7 @@ class ConfigMeasurements(QMainWindow, Styles):
551
532
  else:
552
533
  self.haralick_options = None
553
534
 
554
- def load_previous_measurement_instructions(self):
535
+ def _load_previous_instructions(self):
555
536
 
556
537
  """
557
538
  Read the measurmeent options from a previously written json file and format properly for the UI.
@@ -670,18 +651,6 @@ class ConfigMeasurements(QMainWindow, Styles):
670
651
  else:
671
652
  self.operations_list.list_widget.clear()
672
653
 
673
- # if 'radial_intensity' in measurement_instructions:
674
- # radial_intensity = measurement_instructions['radial_intensity']
675
- # if radial_intensity is not None:
676
- # self.radial_intensity_btn.setChecked(True)
677
- # self.step_size.setText(str(radial_intensity['radial_step']))
678
- # self.step_size.setEnabled(True)
679
- # self.step_lbl.setEnabled(True)
680
- # for checkbox in self.channel_chechkboxes:
681
- # checkbox.setEnabled(True)
682
- # if checkbox.text() in radial_intensity['radial_channels']:
683
- # checkbox.setChecked(True)
684
-
685
654
  if 'clear_previous' in measurement_instructions:
686
655
  self.clear_previous = measurement_instructions['clear_previous']
687
656
  self.clear_previous_btn.setChecked(self.clear_previous)
@@ -703,6 +672,7 @@ class ConfigMeasurements(QMainWindow, Styles):
703
672
  returnValue = msgBox.exec()
704
673
  if returnValue == QMessageBox.Ok:
705
674
  self.current_stack = None
675
+ self.test_frame = None
706
676
  return None
707
677
  else:
708
678
  self.current_stack = movies[0]
@@ -931,7 +901,7 @@ class ConfigMeasurements(QMainWindow, Styles):
931
901
  diam_hbox = QHBoxLayout()
932
902
  self.diameter_lbl = QLabel('Spot diameter: ')
933
903
  self.diameter_value = QLineEdit()
934
- self.diameter_value.setValidator(self.onlyFloat)
904
+ self.diameter_value.setValidator(self._floatValidator)
935
905
  self.diameter_value.setText('7')
936
906
  self.diameter_value.textChanged.connect(self.enable_spot_preview)
937
907
 
@@ -942,7 +912,7 @@ class ConfigMeasurements(QMainWindow, Styles):
942
912
  thresh_hbox = QHBoxLayout()
943
913
  self.threshold_lbl = QLabel('Spot threshold: ')
944
914
  self.threshold_value = QLineEdit()
945
- self.threshold_value.setValidator(self.onlyFloat)
915
+ self.threshold_value.setValidator(self._floatValidator)
946
916
  self.threshold_value.setText('0')
947
917
  self.threshold_value.textChanged.connect(self.enable_spot_preview)
948
918
 
@@ -950,14 +920,6 @@ class ConfigMeasurements(QMainWindow, Styles):
950
920
  thresh_hbox.addWidget(self.threshold_value, 70)
951
921
  layout.addLayout(thresh_hbox)
952
922
 
953
- # #invert_layout = QHBoxLayout()
954
- # self.invert_check = QCheckBox('invert')
955
- # self.invert_value_le = QLineEdit('65535')
956
- # self.invert_value_le.setValidator(self.onlyFloat)
957
- # layout.addWidget(self.invert_check, 6, 0)
958
- # layout.addWidget(self.invert_value_le, 6, 1)
959
- # #layout.addLayout(invert_layout, 5, 1, 1, 1)
960
-
961
923
  self.spot_detection_widgets = [self.spot_channel,
962
924
  self.spot_channel_lbl,
963
925
  self.diameter_value,
@@ -1,6 +1,5 @@
1
- from PyQt5.QtWidgets import QApplication, QComboBox, QFrame, QCheckBox, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
1
+ from PyQt5.QtWidgets import QApplication, QComboBox, QFrame, QCheckBox, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
2
2
  from PyQt5.QtCore import Qt, QSize
3
- from PyQt5.QtGui import QIcon
4
3
  from celldetective.gui.gui_utils import center_window, ListWidget, DistanceChoice
5
4
  from superqt.fonticon import icon
6
5
  from fonticon_mdi6 import MDI6
@@ -10,9 +9,10 @@ import os
10
9
  from glob import glob
11
10
  import pandas as pd
12
11
  from celldetective.gui.viewers import CellSizeViewer, CellEdgeVisualizer
13
- from celldetective.gui import Styles
12
+ from celldetective.gui import CelldetectiveWidget
14
13
 
15
- class ConfigNeighborhoods(QWidget, Styles):
14
+
15
+ class SettingsNeighborhood(CelldetectiveWidget):
16
16
 
17
17
  """
18
18
  Widget to configure neighborhood measurements.
@@ -24,7 +24,6 @@ class ConfigNeighborhoods(QWidget, Styles):
24
24
  super().__init__(*args, **kwargs)
25
25
  self.parent_window = parent_window
26
26
  self.attr_parent = self.parent_window.parent_window
27
- self.setWindowIcon(QIcon(os.sep.join(['celldetective','icons','logo.png'])))
28
27
 
29
28
  self.neighborhood_type = neighborhood_type
30
29
  self.neighborhood_parameter_name = neighborhood_parameter_name
@@ -43,7 +42,6 @@ class ConfigNeighborhoods(QWidget, Styles):
43
42
  self.generate_main_layout()
44
43
  self.load_previous_neighborhood_instructions()
45
44
  center_window(self)
46
- self.setAttribute(Qt.WA_DeleteOnClose)
47
45
 
48
46
  def generate_main_layout(self):
49
47
 
@@ -140,7 +138,7 @@ class ConfigNeighborhoods(QWidget, Styles):
140
138
 
141
139
  layout.addLayout(list_header_layout)
142
140
 
143
- self.measurements_list = ListWidget(DistanceChoice, initial_features=["60"], dtype=int)
141
+ self.measurements_list = ListWidget(DistanceChoice, initial_features=[], dtype=int)
144
142
  self.measurements_list.setToolTip('Neighborhoods to compute.')
145
143
  layout.addWidget(self.measurements_list)
146
144
 
@@ -211,7 +209,7 @@ class ConfigNeighborhoods(QWidget, Styles):
211
209
  population_layout = QHBoxLayout()
212
210
  population_layout.addWidget(QLabel('population: '),30)
213
211
  self.reference_population_cb = QComboBox()
214
- self.reference_population_cb.addItems(['targets','effectors'])
212
+ self.reference_population_cb.addItems(self.parent_window.parent_window.populations)
215
213
  self.reference_population_cb.setToolTip('Select a reference population.')
216
214
  population_layout.addWidget(self.reference_population_cb,70)
217
215
  layout.addLayout(population_layout)
@@ -271,7 +269,7 @@ class ConfigNeighborhoods(QWidget, Styles):
271
269
  population_layout = QHBoxLayout()
272
270
  population_layout.addWidget(QLabel('population: '),30)
273
271
  self.neighbor_population_cb = QComboBox()
274
- self.neighbor_population_cb.addItems(['targets','effectors'])
272
+ self.neighbor_population_cb.addItems(self.parent_window.parent_window.populations)
275
273
  self.neighbor_population_cb.setToolTip('Select a neighbor population.')
276
274
  population_layout.addWidget(self.neighbor_population_cb,70)
277
275
  layout.addLayout(population_layout)
@@ -315,15 +313,14 @@ class ConfigNeighborhoods(QWidget, Styles):
315
313
  population = self.neighbor_population_cb.currentText()
316
314
  class_cols, status_cols, group_cols, time_cols = self.locate_population_specific_columns(population)
317
315
  self.neighbor_population_status_cb.clear()
318
- self.neighbor_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols+group_cols)
316
+ self.neighbor_population_status_cb.addItems(list(np.unique(['--','class', 'status']+class_cols+status_cols+group_cols)))
319
317
 
320
318
  def fill_cbs_of_reference_population(self):
321
319
 
322
320
  population = self.reference_population_cb.currentText()
323
321
  class_cols, status_cols, group_cols, time_cols = self.locate_population_specific_columns(population)
324
- self.reference_population_status_cb.clear()
325
- self.reference_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols+group_cols)
326
- self.event_time_cb.addItems(['--', 't0']+time_cols)
322
+ self.event_time_cb.clear()
323
+ self.event_time_cb.addItems(list(np.unique(['--', 't0']+time_cols)))
327
324
 
328
325
  def switch_not_reference(self):
329
326