celldetective 1.3.9.post5__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 +132 -0
  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 +68 -81
  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 +125 -72
  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 +129 -70
  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 +57 -20
  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.post5.dist-info → celldetective-1.4.0.dist-info}/METADATA +24 -16
  52. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/RECORD +57 -55
  53. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/WHEEL +1 -1
  54. tests/test_qt.py +21 -21
  55. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/entry_points.txt +0 -0
  56. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info/licenses}/LICENSE +0 -0
  57. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/top_level.txt +0 -0
@@ -1,23 +1,48 @@
1
- import numpy as np
2
- from PyQt5.QtWidgets import QGridLayout, QApplication, QMessageBox, QFrame, QSizePolicy, QWidget, QLineEdit, QListWidget, QVBoxLayout, QComboBox, \
1
+ import os
2
+
3
+ from PyQt5.QtWidgets import QGridLayout, QApplication, QMessageBox, QFrame, QSizePolicy, QLineEdit, QListWidget, QVBoxLayout, QComboBox, \
3
4
  QPushButton, QLabel, QHBoxLayout, QCheckBox, QFileDialog, QToolButton, QMenu, QStylePainter, QStyleOptionComboBox, QStyle
4
5
  from PyQt5.QtCore import Qt, QSize, QAbstractTableModel, QEvent, pyqtSignal
5
6
  from PyQt5.QtGui import QDoubleValidator, QIntValidator, QStandardItemModel, QPalette
6
7
 
7
- from celldetective.gui import Styles
8
+ from celldetective.gui import Styles, CelldetectiveWidget
8
9
  from superqt.fonticon import icon
9
10
  from fonticon_mdi6 import MDI6
10
11
 
11
12
  from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
12
13
  from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT
13
14
  import matplotlib.pyplot as plt
14
- import celldetective.extra_properties as extra_properties
15
+
16
+ from celldetective.utils import get_software_location
17
+
18
+ try:
19
+ import celldetective.extra_properties as extra_properties
20
+ extra_props = True
21
+ except Exception as e:
22
+ print(f"The module extra_properties seems corrupted: {e}... Skip...")
23
+ extra_props = False
24
+
15
25
  from inspect import getmembers, isfunction
16
26
  from celldetective.filters import *
17
27
  from os import sep
18
28
  import json
19
29
 
20
30
 
31
+ def generic_message(message, msg_type="warning"):
32
+
33
+ print(message)
34
+ message_box = QMessageBox()
35
+ if msg_type=="warning":
36
+ message_box.setIcon(QMessageBox.Warning)
37
+ elif msg_type=="info":
38
+ message_box.setIcon(QMessageBox.Information)
39
+ elif msg_type=="critical":
40
+ message_box.setIcon(QMessageBox.Critical)
41
+ message_box.setText(message)
42
+ message_box.setWindowTitle(msg_type)
43
+ message_box.setStandardButtons(QMessageBox.Ok)
44
+ _ = message_box.exec()
45
+
21
46
  class PreprocessingLayout(QVBoxLayout, Styles):
22
47
 
23
48
  """
@@ -328,7 +353,7 @@ class PandasModel(QAbstractTableModel):
328
353
  self.dataChanged.emit(ix, ix, (Qt.BackgroundRole,))
329
354
 
330
355
 
331
- class GenericOpColWidget(QWidget, Styles):
356
+ class GenericOpColWidget(CelldetectiveWidget):
332
357
 
333
358
  def __init__(self, parent_window, column=None, title=''):
334
359
 
@@ -415,7 +440,7 @@ class QuickSliderLayout(QHBoxLayout):
415
440
  The slider widget that allows the user to select a value.
416
441
  """
417
442
 
418
- def __init__(self, label=None, slider=None, layout_ratio=(0.25,0.75), slider_initial_value=1, slider_range=(0,1), slider_tooltip=None, decimal_option=True, precision=1.0E-03, *args):
443
+ def __init__(self, label=None, slider=None, layout_ratio=(0.25,0.75), slider_initial_value=1, slider_range=(0,1), slider_tooltip=None, decimal_option=True, precision=3, *args):
419
444
  super().__init__(*args)
420
445
 
421
446
  if label is not None and isinstance(label,str):
@@ -423,10 +448,11 @@ class QuickSliderLayout(QHBoxLayout):
423
448
  self.addWidget(self.qlabel, int(100*layout_ratio[0]))
424
449
 
425
450
  self.slider = slider
426
- self.slider.setOrientation(1)
451
+ self.slider.setOrientation(Qt.Horizontal)
427
452
  if decimal_option:
428
- self.slider.setSingleStep(precision)
429
- self.slider.setTickInterval(precision)
453
+ self.slider.setSingleStep(1.0*(10**(-precision)))
454
+ self.slider.setTickInterval(1.0*(10**(-precision)))
455
+ self.slider.setDecimals(precision)
430
456
  else:
431
457
  self.slider.setSingleStep(1)
432
458
  self.slider.setTickInterval(1)
@@ -546,7 +572,7 @@ class QHSeperationLine(QFrame):
546
572
  self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum)
547
573
 
548
574
 
549
- class FeatureChoice(QWidget, Styles):
575
+ class FeatureChoice(CelldetectiveWidget):
550
576
 
551
577
  def __init__(self, parent_window):
552
578
  super().__init__()
@@ -575,10 +601,11 @@ class FeatureChoice(QWidget, Styles):
575
601
  "intensity_min",
576
602
  ]
577
603
 
578
- members = getmembers(extra_properties, isfunction)
579
- for o in members:
580
- if isfunction(o[1]) and o[1].__module__=="celldetective.extra_properties":
581
- standard_measurements.append(o[0])
604
+ if extra_props:
605
+ members = getmembers(extra_properties, isfunction)
606
+ for o in members:
607
+ if isfunction(o[1]) and o[1].__module__=="celldetective.extra_properties":
608
+ standard_measurements.append(o[0])
582
609
 
583
610
  self.combo_box.addItems(standard_measurements)
584
611
 
@@ -599,7 +626,7 @@ class FeatureChoice(QWidget, Styles):
599
626
  self.close()
600
627
 
601
628
 
602
- class FilterChoice(QWidget, Styles):
629
+ class FilterChoice(CelldetectiveWidget):
603
630
 
604
631
  def __init__(self, parent_window):
605
632
 
@@ -707,7 +734,7 @@ class FilterChoice(QWidget, Styles):
707
734
  self.arguments_labels[i].setText('')
708
735
 
709
736
 
710
- class OperationChoice(QWidget):
737
+ class OperationChoice(CelldetectiveWidget):
711
738
  """
712
739
  Mini window to select an operation from numpy to apply on the ROI.
713
740
 
@@ -738,7 +765,7 @@ class OperationChoice(QWidget):
738
765
  self.close()
739
766
 
740
767
 
741
- class GeometryChoice(QWidget):
768
+ class GeometryChoice(CelldetectiveWidget):
742
769
 
743
770
  def __init__(self, parent_window):
744
771
 
@@ -802,7 +829,7 @@ class GeometryChoice(QWidget):
802
829
  self.close()
803
830
 
804
831
 
805
- class DistanceChoice(QWidget):
832
+ class DistanceChoice(CelldetectiveWidget):
806
833
 
807
834
  def __init__(self, parent_window):
808
835
  super().__init__()
@@ -836,7 +863,7 @@ class DistanceChoice(QWidget):
836
863
  self.close()
837
864
 
838
865
 
839
- class ListWidget(QWidget):
866
+ class ListWidget(CelldetectiveWidget):
840
867
 
841
868
  """
842
869
  A customizable widget for displaying and managing a list of items, with the
@@ -963,7 +990,7 @@ class ListWidget(QWidget):
963
990
  del self.items[idx]
964
991
 
965
992
 
966
- class FigureCanvas(QWidget):
993
+ class FigureCanvas(CelldetectiveWidget):
967
994
  """
968
995
  Generic figure canvas.
969
996
  """
@@ -1185,6 +1212,7 @@ def color_from_state(state, recently_modified=False):
1185
1212
  unique_values = np.unique(state)
1186
1213
  color_map={}
1187
1214
  for value in unique_values:
1215
+
1188
1216
  if np.isnan(value):
1189
1217
  value = "nan"
1190
1218
  color_map[value] = 'k'
@@ -1219,7 +1247,7 @@ def color_from_class(cclass, recently_modified=False):
1219
1247
  return 'k'
1220
1248
 
1221
1249
 
1222
- class ChannelChoice(QWidget):
1250
+ class ChannelChoice(CelldetectiveWidget):
1223
1251
 
1224
1252
  def __init__(self, parent_window):
1225
1253
  super().__init__()
@@ -1,10 +1,11 @@
1
1
  import configparser
2
- from PyQt5.QtWidgets import QWidget, QVBoxLayout, QScrollArea, QLabel, QHBoxLayout, QLineEdit, QPushButton
2
+ from PyQt5.QtWidgets import QVBoxLayout, QScrollArea, QLabel, QHBoxLayout, QLineEdit, QPushButton
3
3
  from PyQt5.QtCore import Qt
4
4
  import configparser
5
- from celldetective.gui import Styles
5
+ from celldetective.gui import CelldetectiveWidget
6
6
 
7
- class ConfigEditor(QWidget, Styles):
7
+
8
+ class ConfigEditor(CelldetectiveWidget):
8
9
 
9
10
  def __init__(self, parent_window):
10
11
 
@@ -27,7 +28,7 @@ class ConfigEditor(QWidget, Styles):
27
28
  # Create a scroll area to contain the main layout
28
29
  scroll = QScrollArea()
29
30
  scroll.setWidgetResizable(True)
30
- scroll_content = QWidget()
31
+ scroll_content = CelldetectiveWidget()
31
32
  self.scroll_layout = QVBoxLayout(scroll_content)
32
33
  scroll_content.setLayout(self.scroll_layout)
33
34
  scroll.setWidget(scroll_content)
@@ -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: