celldetective 1.3.9.post4__py3-none-any.whl → 1.4.0__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 (57) hide show
  1. celldetective/__init__.py +0 -3
  2. celldetective/_version.py +1 -1
  3. celldetective/events.py +2 -4
  4. celldetective/extra_properties.py +320 -24
  5. celldetective/gui/InitWindow.py +33 -45
  6. celldetective/gui/__init__.py +1 -0
  7. celldetective/gui/about.py +19 -15
  8. celldetective/gui/analyze_block.py +34 -19
  9. celldetective/gui/base_components.py +23 -0
  10. celldetective/gui/btrack_options.py +26 -34
  11. celldetective/gui/classifier_widget.py +71 -80
  12. celldetective/gui/configure_new_exp.py +113 -17
  13. celldetective/gui/control_panel.py +68 -141
  14. celldetective/gui/generic_signal_plot.py +9 -12
  15. celldetective/gui/gui_utils.py +49 -21
  16. celldetective/gui/json_readers.py +5 -4
  17. celldetective/gui/layouts.py +246 -22
  18. celldetective/gui/measurement_options.py +32 -17
  19. celldetective/gui/neighborhood_options.py +10 -13
  20. celldetective/gui/plot_measurements.py +21 -17
  21. celldetective/gui/plot_signals_ui.py +131 -75
  22. celldetective/gui/process_block.py +180 -123
  23. celldetective/gui/processes/compute_neighborhood.py +594 -0
  24. celldetective/gui/processes/measure_cells.py +5 -0
  25. celldetective/gui/processes/segment_cells.py +27 -6
  26. celldetective/gui/processes/track_cells.py +6 -0
  27. celldetective/gui/retrain_segmentation_model_options.py +12 -20
  28. celldetective/gui/retrain_signal_model_options.py +57 -56
  29. celldetective/gui/seg_model_loader.py +21 -62
  30. celldetective/gui/signal_annotator.py +139 -72
  31. celldetective/gui/signal_annotator2.py +431 -635
  32. celldetective/gui/signal_annotator_options.py +8 -11
  33. celldetective/gui/survival_ui.py +49 -95
  34. celldetective/gui/tableUI.py +28 -25
  35. celldetective/gui/thresholds_gui.py +617 -1221
  36. celldetective/gui/viewers.py +106 -39
  37. celldetective/gui/workers.py +9 -3
  38. celldetective/io.py +73 -27
  39. celldetective/measure.py +63 -27
  40. celldetective/neighborhood.py +342 -268
  41. celldetective/preprocessing.py +25 -17
  42. celldetective/relative_measurements.py +50 -29
  43. celldetective/scripts/analyze_signals.py +4 -1
  44. celldetective/scripts/measure_relative.py +4 -1
  45. celldetective/scripts/segment_cells.py +0 -6
  46. celldetective/scripts/track_cells.py +3 -1
  47. celldetective/scripts/train_segmentation_model.py +7 -4
  48. celldetective/signals.py +29 -14
  49. celldetective/tracking.py +7 -2
  50. celldetective/utils.py +36 -8
  51. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/METADATA +24 -16
  52. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/RECORD +57 -55
  53. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/WHEEL +1 -1
  54. tests/test_qt.py +21 -21
  55. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/entry_points.txt +0 -0
  56. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info/licenses}/LICENSE +0 -0
  57. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
- from PyQt5.QtWidgets import QCheckBox, QLineEdit, QWidget, QListWidget, QTabWidget, QHBoxLayout,QMessageBox, QPushButton, QVBoxLayout, QRadioButton, QLabel, QButtonGroup, QSizePolicy, QComboBox,QSpacerItem, QGridLayout
1
+ from PyQt5.QtWidgets import QCheckBox, QLineEdit, QListWidget, QTabWidget, QHBoxLayout,QMessageBox, QPushButton, QVBoxLayout, QRadioButton, QLabel, QButtonGroup, QSizePolicy, QComboBox,QSpacerItem, QGridLayout
2
2
  from celldetective.gui.gui_utils import ThresholdLineEdit, QuickSliderLayout, center_window
3
3
  from PyQt5.QtCore import Qt, QSize
4
- from PyQt5.QtGui import QIntValidator
4
+ from PyQt5.QtGui import QIntValidator, QDoubleValidator
5
5
 
6
6
  from superqt import QLabeledRangeSlider, QLabeledDoubleSlider, QLabeledSlider, QLabeledDoubleRangeSlider, QSearchableComboBox
7
7
 
@@ -9,15 +9,223 @@ from superqt.fonticon import icon
9
9
  from fonticon_mdi6 import MDI6
10
10
  from celldetective.utils import _extract_channel_indices_from_config
11
11
  from celldetective.gui.viewers import ThresholdedStackVisualizer, CellEdgeVisualizer, StackVisualizer, CellSizeViewer, ChannelOffsetViewer
12
- from celldetective.gui import Styles
12
+ from celldetective.gui import Styles, CelldetectiveWidget
13
13
  from celldetective.preprocessing import correct_background_model, correct_background_model_free, estimate_background_per_condition
14
14
  from functools import partial
15
15
  from glob import glob
16
16
  import os
17
17
  import pandas as pd
18
18
  import numpy as np
19
+ from celldetective.io import locate_segmentation_model, locate_signal_model
20
+ import json
19
21
 
20
- class StarDistParamsWidget(QWidget, Styles):
22
+
23
+ class SignalModelParamsWidget(CelldetectiveWidget):
24
+
25
+ def __init__(self, parent_window=None, model_name=None, *args, **kwargs):
26
+
27
+ super().__init__(*args)
28
+ self.setWindowTitle('Signals')
29
+ self.parent_window = parent_window
30
+ self.model_name = model_name
31
+ self.locate_model_path()
32
+ self.required_channels = self.input_config["channels"]
33
+ self.onlyFloat = QDoubleValidator()
34
+
35
+ # Setting up references to parent window attributes
36
+ if hasattr(self.parent_window.parent_window, 'locate_image'):
37
+ self.attr_parent = self.parent_window.parent_window
38
+ elif hasattr(self.parent_window.parent_window.parent_window, 'locate_image'):
39
+ self.attr_parent = self.parent_window.parent_window.parent_window
40
+ else:
41
+ self.attr_parent = self.parent_window.parent_window.parent_window.parent_window
42
+
43
+ # Set up layout and widgets
44
+ self.layout = QVBoxLayout()
45
+ self.populate_widgets()
46
+ self.setLayout(self.layout)
47
+ center_window(self)
48
+
49
+ def locate_model_path(self):
50
+
51
+ self.model_complete_path = locate_signal_model(self.model_name)
52
+ if self.model_complete_path is None:
53
+ print('Model could not be found. Abort.')
54
+ self.abort_process()
55
+ else:
56
+ print(f'Model path: {self.model_complete_path}...')
57
+
58
+ if not os.path.exists(self.model_complete_path+"config_input.json"):
59
+ print('The configuration for the inputs to the model could not be located. Abort.')
60
+ self.abort_process()
61
+
62
+ with open(self.model_complete_path+"config_input.json") as config_file:
63
+ self.input_config = json.load(config_file)
64
+
65
+ def populate_widgets(self):
66
+
67
+ self.n_channels = len(self.required_channels)
68
+ self.channel_cbs = [QComboBox() for i in range(self.n_channels)]
69
+
70
+ self.parent_window.load_available_tables()
71
+ available_channels = list(self.parent_window.signals)+['None']
72
+ # Populate the comboboxes with available channels from the experiment
73
+ for k in range(self.n_channels):
74
+ hbox_channel = QHBoxLayout()
75
+ hbox_channel.addWidget(QLabel(f'channel {k+1}: '), 33)
76
+
77
+ ch_vbox = QVBoxLayout()
78
+ ch_vbox.addWidget(QLabel(f'Req: {self.required_channels[k]}'), alignment=Qt.AlignLeft)
79
+ ch_vbox.addWidget(self.channel_cbs[k])
80
+
81
+ self.channel_cbs[k].addItems(available_channels) #Give none option for more than one channel input
82
+ idx = self.channel_cbs[k].findText(self.required_channels[k])
83
+
84
+ if idx>=0:
85
+ self.channel_cbs[k].setCurrentIndex(idx)
86
+ else:
87
+ self.channel_cbs[k].setCurrentIndex(len(available_channels)-1)
88
+
89
+ hbox_channel.addLayout(ch_vbox, 66)
90
+ self.layout.addLayout(hbox_channel)
91
+
92
+ # Button to apply the StarDist settings
93
+ self.set_btn = QPushButton('set')
94
+ self.set_btn.setStyleSheet(self.button_style_sheet)
95
+ self.set_btn.clicked.connect(self.parent_window.set_selected_signals_for_event_detection)
96
+ self.layout.addWidget(self.set_btn)
97
+
98
+
99
+
100
+ class SegModelParamsWidget(CelldetectiveWidget):
101
+
102
+ def __init__(self, parent_window=None, model_name='SD_versatile_fluo', *args, **kwargs):
103
+
104
+ super().__init__(*args)
105
+ self.setWindowTitle('Channels')
106
+ self.parent_window = parent_window
107
+ self.model_name = model_name
108
+ self.locate_model_path()
109
+ self.required_channels = self.input_config["channels"]
110
+ self.onlyFloat = QDoubleValidator()
111
+
112
+ # Setting up references to parent window attributes
113
+ if hasattr(self.parent_window.parent_window, 'locate_image'):
114
+ self.attr_parent = self.parent_window.parent_window
115
+ elif hasattr(self.parent_window.parent_window.parent_window, 'locate_image'):
116
+ self.attr_parent = self.parent_window.parent_window.parent_window
117
+ else:
118
+ self.attr_parent = self.parent_window.parent_window.parent_window.parent_window
119
+
120
+ # Set up layout and widgets
121
+ self.layout = QVBoxLayout()
122
+ self.populate_widgets()
123
+ self.setLayout(self.layout)
124
+ center_window(self)
125
+
126
+ def locate_model_path(self):
127
+
128
+ self.model_complete_path = locate_segmentation_model(self.model_name)
129
+ if self.model_complete_path is None:
130
+ print('Model could not be found. Abort.')
131
+ self.abort_process()
132
+ else:
133
+ print(f'Model path: {self.model_complete_path}...')
134
+
135
+ if not os.path.exists(self.model_complete_path+"config_input.json"):
136
+ print('The configuration for the inputs to the model could not be located. Abort.')
137
+ self.abort_process()
138
+
139
+ with open(self.model_complete_path+"config_input.json") as config_file:
140
+ self.input_config = json.load(config_file)
141
+
142
+ def populate_widgets(self):
143
+
144
+ self.n_channels = len(self.required_channels)
145
+ self.channel_cbs = [QComboBox() for i in range(self.n_channels)]
146
+
147
+ # Button to view the current stack with a scale bar
148
+ self.view_diameter_btn = QPushButton()
149
+ self.view_diameter_btn.setStyleSheet(self.button_select_all)
150
+ self.view_diameter_btn.setIcon(icon(MDI6.image_check, color="black"))
151
+ self.view_diameter_btn.setToolTip("View stack.")
152
+ self.view_diameter_btn.setIconSize(QSize(20, 20))
153
+ self.view_diameter_btn.clicked.connect(self.view_current_stack_with_scale_bar)
154
+
155
+ # Line edit for entering cell diameter
156
+ self.diameter_le = ThresholdLineEdit(init_value=40, connected_buttons=[self.view_diameter_btn],placeholder='cell diameter in µm', value_type='float')
157
+
158
+ available_channels = list(self.attr_parent.exp_channels)+['None']
159
+ # Populate the comboboxes with available channels from the experiment
160
+ for k in range(self.n_channels):
161
+ hbox_channel = QHBoxLayout()
162
+ hbox_channel.addWidget(QLabel(f'channel {k+1}: '), 33)
163
+
164
+ ch_vbox = QVBoxLayout()
165
+ ch_vbox.addWidget(QLabel(f'Req: {self.required_channels[k]}'), alignment=Qt.AlignLeft)
166
+ ch_vbox.addWidget(self.channel_cbs[k])
167
+
168
+ self.channel_cbs[k].addItems(available_channels) #Give none option for more than one channel input
169
+ idx = self.channel_cbs[k].findText(self.required_channels[k])
170
+
171
+ if idx>=0:
172
+ self.channel_cbs[k].setCurrentIndex(idx)
173
+ else:
174
+ self.channel_cbs[k].setCurrentIndex(len(available_channels)-1)
175
+
176
+ hbox_channel.addLayout(ch_vbox, 66)
177
+ self.layout.addLayout(hbox_channel)
178
+
179
+ if 'cell_size_um' in self.input_config:
180
+
181
+ # Layout for diameter input and button
182
+ hbox = QHBoxLayout()
183
+ hbox.addWidget(QLabel('cell size [µm]: '), 33)
184
+ hbox.addWidget(self.diameter_le, 61)
185
+ hbox.addWidget(self.view_diameter_btn)
186
+ self.layout.addLayout(hbox)
187
+
188
+ self.diameter_le.set_threshold(self.input_config['cell_size_um'])
189
+
190
+ # size_hbox = QHBoxLayout()
191
+ # size_hbox.addWidget(QLabel('cell size [µm]: '), 33)
192
+ # self.size_le = QLineEdit(str(self.input_config['cell_size_um']).replace('.',','))
193
+ # self.size_le.setValidator(self.onlyFloat)
194
+ # size_hbox.addWidget(self.size_le, 66)
195
+ # self.layout.addLayout(size_hbox)
196
+
197
+ # Button to apply the StarDist settings
198
+ self.set_btn = QPushButton('set')
199
+ self.set_btn.setStyleSheet(self.button_style_sheet)
200
+ self.set_btn.clicked.connect(self.parent_window.set_selected_channels_for_segmentation)
201
+ self.layout.addWidget(self.set_btn)
202
+
203
+ def view_current_stack_with_scale_bar(self):
204
+
205
+ """
206
+ Displays the current image stack with a scale bar, allowing users to visually estimate cell diameters.
207
+ """
208
+
209
+ self.attr_parent.locate_image()
210
+ if self.attr_parent.current_stack is not None:
211
+ max_size = np.amax([self.attr_parent.shape_x, self.attr_parent.shape_y])
212
+ self.viewer = CellSizeViewer(
213
+ initial_diameter = float(self.diameter_le.text().replace(',', '.')),
214
+ parent_le = self.diameter_le,
215
+ stack_path=self.attr_parent.current_stack,
216
+ window_title=f'Position {self.attr_parent.position_list.currentText()}',
217
+ diameter_slider_range=(0,max_size*self.attr_parent.PxToUm),
218
+ frame_slider = True,
219
+ contrast_slider = True,
220
+ channel_cb = True,
221
+ channel_names = self.attr_parent.exp_channels,
222
+ n_channels = self.attr_parent.nbr_channels,
223
+ PxToUm = self.attr_parent.PxToUm,
224
+ )
225
+ self.viewer.show()
226
+
227
+
228
+ class StarDistParamsWidget(CelldetectiveWidget):
21
229
 
22
230
  """
23
231
  A widget to configure parameters for StarDist segmentation.
@@ -101,7 +309,7 @@ class StarDistParamsWidget(QWidget, Styles):
101
309
  self.layout.addWidget(self.set_stardist_scale_btn)
102
310
 
103
311
 
104
- class CellposeParamsWidget(QWidget, Styles):
312
+ class CellposeParamsWidget(CelldetectiveWidget):
105
313
 
106
314
  """
107
315
  A widget to configure parameters for Cellpose segmentation, allowing users to set the cell diameter,
@@ -204,7 +412,7 @@ class CellposeParamsWidget(QWidget, Styles):
204
412
 
205
413
  # Flow threshold slider
206
414
  self.flow_slider = QLabeledDoubleSlider()
207
- self.flow_slider.setOrientation(1)
415
+ self.flow_slider.setOrientation(Qt.Horizontal)
208
416
  self.flow_slider.setRange(-6,6)
209
417
  self.flow_slider.setValue(0.4)
210
418
  hbox = QHBoxLayout()
@@ -214,7 +422,7 @@ class CellposeParamsWidget(QWidget, Styles):
214
422
 
215
423
  # Cell probability threshold slider
216
424
  self.cellprob_slider = QLabeledDoubleSlider()
217
- self.cellprob_slider.setOrientation(1)
425
+ self.cellprob_slider.setOrientation(Qt.Horizontal)
218
426
  self.cellprob_slider.setRange(-6,6)
219
427
  self.cellprob_slider.setValue(0.)
220
428
  hbox = QHBoxLayout()
@@ -236,18 +444,20 @@ class CellposeParamsWidget(QWidget, Styles):
236
444
 
237
445
  self.attr_parent.locate_image()
238
446
  if self.attr_parent.current_stack is not None:
447
+ max_size = np.amax([self.attr_parent.shape_x, self.attr_parent.shape_y])
239
448
  self.viewer = CellSizeViewer(
240
- initial_diameter = float(self.diameter_le.text().replace(',', '.')),
241
- parent_le = self.diameter_le,
242
- stack_path=self.attr_parent.current_stack,
243
- window_title=f'Position {self.attr_parent.position_list.currentText()}',
244
- frame_slider = True,
245
- contrast_slider = True,
246
- channel_cb = True,
247
- channel_names = self.attr_parent.exp_channels,
248
- n_channels = self.attr_parent.nbr_channels,
249
- PxToUm = 1,
250
- )
449
+ initial_diameter = float(self.diameter_le.text().replace(',', '.')),
450
+ parent_le = self.diameter_le,
451
+ stack_path=self.attr_parent.current_stack,
452
+ window_title=f'Position {self.attr_parent.position_list.currentText()}',
453
+ diameter_slider_range=(0, max_size),
454
+ frame_slider = True,
455
+ contrast_slider = True,
456
+ channel_cb = True,
457
+ channel_names = self.attr_parent.exp_channels,
458
+ n_channels = self.attr_parent.nbr_channels,
459
+ PxToUm = 1,
460
+ )
251
461
  self.viewer.show()
252
462
 
253
463
  class ChannelNormGenerator(QVBoxLayout, Styles):
@@ -864,7 +1074,7 @@ class ProtocolDesignerLayout(QVBoxLayout, Styles):
864
1074
  self.tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
865
1075
 
866
1076
  for k in range(len(self.tab_layouts)):
867
- wg = QWidget()
1077
+ wg = CelldetectiveWidget()
868
1078
  self.tab_layouts[k].parent_window = self
869
1079
  wg.setLayout(self.tab_layouts[k])
870
1080
  self.tabs.addTab(wg, self.tab_names[k])
@@ -1173,13 +1383,21 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
1173
1383
  coef_nbr_layout.addWidget(self.nbr_coef_le, 75)
1174
1384
  self.addLayout(coef_nbr_layout, 7,0,1,3)
1175
1385
 
1386
+ offset_layout = QHBoxLayout()
1387
+ offset_layout.addWidget(QLabel("Offset: "), 25)
1388
+ self.camera_offset_le = QLineEdit("0")
1389
+ self.camera_offset_le.setPlaceholderText('camera black level')
1390
+ self.camera_offset_le.setValidator(QDoubleValidator())
1391
+ offset_layout.addWidget(self.camera_offset_le, 75)
1392
+ self.addLayout(offset_layout, 8, 0, 1, 3)
1393
+
1176
1394
  self.operation_layout = OperationLayout()
1177
- self.addLayout(self.operation_layout, 8, 0, 1, 3)
1395
+ self.addLayout(self.operation_layout, 9, 0, 1, 3)
1178
1396
 
1179
1397
  correction_layout = QHBoxLayout()
1180
1398
  correction_layout.addWidget(self.add_correction_btn, 95)
1181
1399
  correction_layout.addWidget(self.corrected_stack_viewer_btn, 5)
1182
- self.addLayout(correction_layout, 9, 0, 1, 3)
1400
+ self.addLayout(correction_layout, 10, 0, 1, 3)
1183
1401
 
1184
1402
  # verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
1185
1403
  # self.addItem(verticalSpacer, 5, 0, 1, 3)
@@ -1221,6 +1439,11 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
1221
1439
  clip = True
1222
1440
  else:
1223
1441
  clip = False
1442
+
1443
+ if self.camera_offset_le.text()=="":
1444
+ offset = None
1445
+ else:
1446
+ offset = float(self.camera_offset_le.text().replace(",","."))
1224
1447
 
1225
1448
  self.instructions = {
1226
1449
  "target_channel": self.channels_cb.currentText(),
@@ -1232,7 +1455,8 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
1232
1455
  "opt_coef_range": opt_coef_range,
1233
1456
  "opt_coef_nbr": opt_coef_nbr,
1234
1457
  "operation": operation,
1235
- "clip": clip
1458
+ "clip": clip,
1459
+ "offset": offset
1236
1460
  }
1237
1461
 
1238
1462
  def set_target_channel(self):
@@ -1,8 +1,9 @@
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 QApplication, QMessageBox, QScrollArea, 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
+ from PyQt5.QtGui import QDoubleValidator, QIntValidator
6
7
 
7
8
  from celldetective.gui.gui_utils import center_window, FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, \
8
9
  GeometryChoice, OperationChoice
@@ -10,12 +11,10 @@ from superqt import QLabeledDoubleSlider
10
11
  from superqt.fonticon import icon
11
12
  from fonticon_mdi6 import MDI6
12
13
 
13
- #from celldetective.gui.thresholds_gui import ThresholdSpot
14
14
  from celldetective.utils import extract_experiment_channels, get_software_location
15
15
  from celldetective.io import load_frames, auto_load_number_of_frames
16
16
  from celldetective.measure import compute_haralick_features
17
17
  import numpy as np
18
- from tifffile import imread
19
18
  import json
20
19
  import os
21
20
  import matplotlib.pyplot as plt
@@ -27,11 +26,11 @@ from pathlib import Path
27
26
 
28
27
  from celldetective.gui.viewers import CellEdgeVisualizer, SpotDetectionVisualizer
29
28
  from celldetective.gui.layouts import ProtocolDesignerLayout, BackgroundFitCorrectionLayout, LocalCorrectionLayout
30
- from celldetective.gui.gui_utils import ThresholdLineEdit, PreprocessingLayout2
31
- from celldetective.gui import Styles
29
+ from celldetective.gui.gui_utils import PreprocessingLayout2
30
+ from celldetective.gui import CelldetectiveMainWindow, CelldetectiveWidget
32
31
 
33
32
 
34
- class ConfigMeasurements(QMainWindow, Styles):
33
+ class ConfigMeasurements(CelldetectiveMainWindow):
35
34
  """
36
35
  UI to set measurement instructions.
37
36
 
@@ -43,16 +42,11 @@ class ConfigMeasurements(QMainWindow, Styles):
43
42
 
44
43
  self.parent_window = parent_window
45
44
  self.setWindowTitle("Configure measurements")
46
- self.setWindowIcon(QIcon(os.sep.join(['celldetective', 'icons', 'mexican-hat.png'])))
47
45
  self.mode = self.parent_window.mode
48
46
  self.exp_dir = self.parent_window.exp_dir
49
47
  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"
48
+ self.config_name = f"btrack_config_{self.mode}.json"
49
+ self.measure_instructions_path = self.parent_window.exp_dir + f"configs/measurement_instructions_{self.mode}.json"
56
50
  self.soft_path = get_software_location()
57
51
  self.clear_previous = False
58
52
 
@@ -83,7 +77,7 @@ class ConfigMeasurements(QMainWindow, Styles):
83
77
 
84
78
  # Create button widget and layout
85
79
  self.scroll_area = QScrollArea(self)
86
- self.button_widget = QWidget()
80
+ self.button_widget = CelldetectiveWidget()
87
81
  main_layout = QVBoxLayout()
88
82
  self.button_widget.setLayout(main_layout)
89
83
  main_layout.setContentsMargins(30, 30, 30, 30)
@@ -264,12 +258,21 @@ class ConfigMeasurements(QMainWindow, Styles):
264
258
 
265
259
  self.features_list = ListWidget(FeatureChoice, initial_features=['area', 'intensity_mean', ])
266
260
 
261
+ self.create_feature_btn = QPushButton("")
262
+ self.create_feature_btn.setStyleSheet(self.button_select_all)
263
+ self.create_feature_btn.setIcon(icon(MDI6.file_cog, color="black"))
264
+ self.create_feature_btn.setToolTip("Create new feature")
265
+ self.create_feature_btn.setIconSize(QSize(20, 20))
266
+
267
267
  self.del_feature_btn.clicked.connect(self.features_list.removeSel)
268
268
  self.add_feature_btn.clicked.connect(self.features_list.addItem)
269
+ self.create_feature_btn.clicked.connect(self.go_to_extraprops)
269
270
 
270
271
  feature_layout.addWidget(self.feature_lbl, 90)
271
272
  feature_layout.addWidget(self.del_feature_btn, 5)
272
273
  feature_layout.addWidget(self.add_feature_btn, 5)
274
+ feature_layout.addWidget(self.create_feature_btn, 5)
275
+
273
276
  layout.addLayout(feature_layout)
274
277
  layout.addWidget(self.features_list)
275
278
 
@@ -334,7 +337,7 @@ class ConfigMeasurements(QMainWindow, Styles):
334
337
  self.haralick_scale_slider.setSingleStep(0.05)
335
338
  self.haralick_scale_slider.setTickInterval(0.05)
336
339
  self.haralick_scale_slider.setSingleStep(1)
337
- self.haralick_scale_slider.setOrientation(1)
340
+ self.haralick_scale_slider.setOrientation(Qt.Horizontal)
338
341
  self.haralick_scale_slider.setRange(0, 1)
339
342
  self.haralick_scale_slider.setValue(0.5)
340
343
  self.haralick_scale_lbl = QLabel('Scale: ')
@@ -426,6 +429,18 @@ class ConfigMeasurements(QMainWindow, Styles):
426
429
  self.haralick_normalization_mode_btn.clicked.connect(self.switch_to_absolute_normalization_mode)
427
430
  layout.addLayout(self.haralick_layout)
428
431
 
432
+ def go_to_extraprops(self):
433
+
434
+ path = os.sep.join([self.soft_path,'celldetective',os.sep,'extra_properties.py'])
435
+ try:
436
+ Popen(f'explorer {os.path.realpath(path)}')
437
+ except:
438
+
439
+ try:
440
+ os.system('xdg-open "%s"' % path)
441
+ except:
442
+ return None
443
+
429
444
  def switch_to_absolute_normalization_mode(self):
430
445
 
431
446
  if self.percentile_mode:
@@ -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 ConfigNeighborhoods(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
 
@@ -1,10 +1,10 @@
1
1
  from PyQt5.QtWidgets import QMessageBox, QScrollArea, QButtonGroup, QComboBox, \
2
- QCheckBox, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton, \
2
+ QCheckBox, QVBoxLayout, QLabel, QHBoxLayout, QPushButton, \
3
3
  QRadioButton, QSizePolicy
4
4
  from PyQt5.QtCore import Qt, QRect
5
- from PyQt5.QtGui import QIcon, QDoubleValidator
5
+ from PyQt5.QtGui import QDoubleValidator
6
6
 
7
- from celldetective.gui import Styles
7
+ from celldetective.gui import CelldetectiveWidget
8
8
  from celldetective.gui.gui_utils import center_window, FigureCanvas
9
9
 
10
10
  from superqt.fonticon import icon
@@ -12,7 +12,6 @@ from fonticon_mdi6 import MDI6
12
12
  from celldetective.utils import get_software_location, _extract_labels_from_config
13
13
  from celldetective.io import load_experiment_tables, get_experiment_antibodies, get_experiment_cell_types, get_experiment_concentrations, \
14
14
  get_positions_in_well, get_experiment_wells
15
- from celldetective.signals import mean_signal
16
15
  import numpy as np
17
16
  import json
18
17
  import os
@@ -26,7 +25,7 @@ import math
26
25
  import seaborn as sns
27
26
 
28
27
 
29
- class ConfigMeasurementsPlot(QWidget,Styles):
28
+ class ConfigMeasurementsPlot(CelldetectiveWidget):
30
29
  """
31
30
  UI to set survival instructions.
32
31
 
@@ -37,7 +36,6 @@ class ConfigMeasurementsPlot(QWidget,Styles):
37
36
  super().__init__()
38
37
  self.parent_window = parent_window
39
38
  self.setWindowTitle("Configure signal plot")
40
- self.setWindowIcon(QIcon(os.sep.join(['celldetective', 'icons', 'mexican-hat.png'])))
41
39
  self.exp_dir = self.parent_window.exp_dir
42
40
  self.soft_path = get_software_location()
43
41
  self.exp_config = self.exp_dir + "config.ini"
@@ -109,7 +107,7 @@ class ConfigMeasurementsPlot(QWidget,Styles):
109
107
  main_layout.addWidget(panel_title, alignment=Qt.AlignCenter)
110
108
 
111
109
  labels = [QLabel('population: '), QLabel('class: '), QLabel('group: ')] # , QLabel('time of\ninterest: ')]
112
- self.cb_options = [['targets', 'effectors'], ['class'], ['group']] # , ['t0','first detection']]
110
+ self.cb_options = [self.parent_window.parent_window.populations, ['class'], ['group']] # , ['t0','first detection']]
113
111
  self.cbs = [QComboBox() for i in range(len(labels))]
114
112
  self.cbs[0].currentIndexChanged.connect(self.set_classes_and_times)
115
113
 
@@ -145,7 +143,7 @@ class ConfigMeasurementsPlot(QWidget,Styles):
145
143
  # self.abs_time_checkbox = QCheckBox('absolute time')
146
144
  # self.frame_slider = QLabeledSlider()
147
145
  # self.frame_slider.setSingleStep(1)
148
- # self.frame_slider.setOrientation(1)
146
+ # self.frame_slider.setOrientation(Qt.Horizontal)
149
147
  # self.frame_slider.setRange(0,self.parent.parent.len_movie)
150
148
  # self.frame_slider.setValue(0)
151
149
  # self.frame_slider.setEnabled(False)
@@ -202,14 +200,20 @@ class ConfigMeasurementsPlot(QWidget,Styles):
202
200
  class_idx = np.array([s.startswith('class_') for s in self.all_columns])
203
201
  group_idx = np.array([s.startswith('group_') for s in self.all_columns])
204
202
 
203
+ print(f'{class_idx=} {group_idx=} {self.all_columns=}')
205
204
  # time_idx = np.array([s.startswith('t_') for s in self.all_columns])
206
-
207
205
  try:
208
- class_columns = list(self.all_columns[class_idx])
209
- group_columns = list(self.all_columns[group_idx])
206
+ if len(class_idx)>0:
207
+ class_columns = list(self.all_columns[class_idx])
208
+ else:
209
+ class_columns = []
210
+ if len(group_idx)>0:
211
+ group_columns = list(self.all_columns[group_idx])
212
+ else:
213
+ group_columns = []
210
214
  # time_columns = list(self.all_columns[time_idx])
211
- except:
212
- print('columns not found')
215
+ except Exception as e:
216
+ print(f'L210 columns not found {e}')
213
217
  self.auto_close = True
214
218
  return None
215
219
 
@@ -227,7 +231,7 @@ class ConfigMeasurementsPlot(QWidget,Styles):
227
231
  is_number = np.vectorize(lambda x: np.issubdtype(x, np.number))
228
232
  feats = cols[is_number(self.df.dtypes)]
229
233
 
230
- self.feature_choice_widget = QWidget()
234
+ self.feature_choice_widget = CelldetectiveWidget()
231
235
  self.feature_choice_widget.setWindowTitle("Select numeric feature")
232
236
  layout = QVBoxLayout()
233
237
  self.feature_choice_widget.setLayout(layout)
@@ -250,7 +254,7 @@ class ConfigMeasurementsPlot(QWidget,Styles):
250
254
  is_number = np.vectorize(lambda x: np.issubdtype(x, np.number))
251
255
  feats = cols[is_number(self.df.dtypes)]
252
256
 
253
- self.feature_choice_widget = QWidget()
257
+ self.feature_choice_widget = CelldetectiveWidget()
254
258
  self.feature_choice_widget.setWindowTitle("Select numeric feature")
255
259
  layout = QVBoxLayout()
256
260
  self.feature_choice_widget.setLayout(layout)
@@ -299,7 +303,7 @@ class ConfigMeasurementsPlot(QWidget,Styles):
299
303
  # prepare survival
300
304
 
301
305
  # plot survival
302
- self.survivalWidget = QWidget()
306
+ self.survivalWidget = CelldetectiveWidget()
303
307
  self.scroll = QScrollArea()
304
308
  self.survivalWidget.setMinimumHeight(int(0.8 * self.screen_height))
305
309
  self.survivalWidget.setWindowTitle('signals')
@@ -500,7 +504,7 @@ class ConfigMeasurementsPlot(QWidget,Styles):
500
504
  if name + ':' in lbl:
501
505
  self.usable_well_labels.append(lbl)
502
506
 
503
- self.line_choice_widget = QWidget()
507
+ self.line_choice_widget = CelldetectiveWidget()
504
508
  self.line_check_vbox = QVBoxLayout()
505
509
  self.line_choice_widget.setLayout(self.line_check_vbox)
506
510
  if len(self.well_indices) > 1: