celldetective 1.3.4.post1__py3-none-any.whl → 1.3.6.post1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/events.py +10 -5
  3. celldetective/filters.py +11 -0
  4. celldetective/gui/btrack_options.py +151 -1
  5. celldetective/gui/classifier_widget.py +44 -15
  6. celldetective/gui/configure_new_exp.py +13 -0
  7. celldetective/gui/control_panel.py +4 -2
  8. celldetective/gui/generic_signal_plot.py +2 -6
  9. celldetective/gui/gui_utils.py +170 -12
  10. celldetective/gui/measurement_options.py +85 -54
  11. celldetective/gui/neighborhood_options.py +1 -1
  12. celldetective/gui/plot_signals_ui.py +3 -4
  13. celldetective/gui/process_block.py +8 -6
  14. celldetective/gui/signal_annotator.py +10 -3
  15. celldetective/gui/signal_annotator2.py +146 -193
  16. celldetective/gui/survival_ui.py +121 -34
  17. celldetective/gui/tableUI.py +26 -12
  18. celldetective/gui/thresholds_gui.py +9 -52
  19. celldetective/gui/viewers.py +58 -21
  20. celldetective/io.py +1087 -161
  21. celldetective/measure.py +175 -102
  22. celldetective/preprocessing.py +2 -2
  23. celldetective/relative_measurements.py +6 -9
  24. celldetective/scripts/measure_cells.py +13 -3
  25. celldetective/scripts/segment_cells.py +0 -1
  26. celldetective/scripts/track_cells.py +25 -1
  27. celldetective/signals.py +9 -7
  28. celldetective/tracking.py +130 -81
  29. celldetective/utils.py +28 -7
  30. {celldetective-1.3.4.post1.dist-info → celldetective-1.3.6.post1.dist-info}/METADATA +3 -2
  31. {celldetective-1.3.4.post1.dist-info → celldetective-1.3.6.post1.dist-info}/RECORD +35 -35
  32. {celldetective-1.3.4.post1.dist-info → celldetective-1.3.6.post1.dist-info}/LICENSE +0 -0
  33. {celldetective-1.3.4.post1.dist-info → celldetective-1.3.6.post1.dist-info}/WHEEL +0 -0
  34. {celldetective-1.3.4.post1.dist-info → celldetective-1.3.6.post1.dist-info}/entry_points.txt +0 -0
  35. {celldetective-1.3.4.post1.dist-info → celldetective-1.3.6.post1.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
- from PyQt5.QtWidgets import QApplication, QMessageBox, QFrame, QSizePolicy, QWidget, QLineEdit, QListWidget, QVBoxLayout, QComboBox, \
2
+ from PyQt5.QtWidgets import QGridLayout, QApplication, QMessageBox, QFrame, QSizePolicy, QWidget, QLineEdit, QListWidget, QVBoxLayout, QComboBox, \
3
3
  QPushButton, QLabel, QHBoxLayout, QCheckBox, QFileDialog, QToolButton, QMenu, QStylePainter, QStyleOptionComboBox, QStyle
4
- from PyQt5.QtCore import Qt, QSize, QAbstractTableModel
4
+ from PyQt5.QtCore import Qt, QSize, QAbstractTableModel, QEvent, pyqtSignal
5
5
  from PyQt5.QtGui import QDoubleValidator, QIntValidator, QStandardItemModel, QPalette
6
6
 
7
7
  from celldetective.gui import Styles
@@ -15,6 +15,125 @@ import celldetective.extra_properties as extra_properties
15
15
  from inspect import getmembers, isfunction
16
16
  from celldetective.filters import *
17
17
  from os import sep
18
+ import json
19
+
20
+
21
+ class PreprocessingLayout(QVBoxLayout, Styles):
22
+
23
+ """
24
+ A widget that allows user to choose preprocessing filters for an image
25
+ """
26
+
27
+ def __init__(self, parent_window=None, apply_btn_option=True, *args, **kwargs):
28
+ super().__init__(*args, **kwargs)
29
+
30
+ self.parent_window = parent_window
31
+ self.apply_btn_option = apply_btn_option
32
+ self.generate_components()
33
+ self.add_to_layout()
34
+
35
+ def add_to_layout(self):
36
+
37
+ self.setContentsMargins(20, 20, 20, 20)
38
+
39
+ button_layout = QHBoxLayout()
40
+ button_layout.addWidget(self.preprocess_lbl, 85, alignment=Qt.AlignLeft)
41
+ button_layout.addWidget(self.delete_filter_btn, 5)
42
+ button_layout.addWidget(self.add_filter_btn, 5)
43
+ button_layout.addWidget(self.help_prefilter_btn, 5)
44
+ self.addLayout(button_layout, 25)
45
+
46
+ self.addWidget(self.list, 70)
47
+ if self.apply_btn_option:
48
+ self.addWidget(self.apply_btn, 5)
49
+
50
+ def generate_components(self):
51
+
52
+ self.list = ListWidget(FilterChoice, [])
53
+
54
+ self.preprocess_lbl = QLabel('Preprocessing')
55
+ self.preprocess_lbl.setStyleSheet("font-weight: bold;")
56
+
57
+ self.delete_filter_btn = QPushButton()
58
+ self.delete_filter_btn.setStyleSheet(self.button_select_all)
59
+ self.delete_filter_btn.setIcon(icon(MDI6.trash_can, color="black"))
60
+ self.delete_filter_btn.setToolTip("Remove filter")
61
+ self.delete_filter_btn.setIconSize(QSize(20, 20))
62
+ self.delete_filter_btn.clicked.connect(self.list.removeSel)
63
+
64
+ self.add_filter_btn = QPushButton()
65
+ self.add_filter_btn.setStyleSheet(self.button_select_all)
66
+ self.add_filter_btn.setIcon(icon(MDI6.filter_plus, color="black"))
67
+ self.add_filter_btn.setToolTip("Add filter")
68
+ self.add_filter_btn.setIconSize(QSize(20, 20))
69
+ self.add_filter_btn.clicked.connect(self.list.addItem)
70
+
71
+ self.help_prefilter_btn = QPushButton()
72
+ self.help_prefilter_btn.setIcon(icon(MDI6.help_circle,color=self.help_color))
73
+ self.help_prefilter_btn.setIconSize(QSize(20, 20))
74
+ self.help_prefilter_btn.clicked.connect(self.help_prefilter)
75
+ self.help_prefilter_btn.setStyleSheet(self.button_select_all)
76
+ self.help_prefilter_btn.setToolTip("Help.")
77
+
78
+ if self.apply_btn_option:
79
+ self.apply_btn = QPushButton("Apply")
80
+ self.apply_btn.setIcon(icon(MDI6.filter_cog_outline, color="white"))
81
+ self.apply_btn.setIconSize(QSize(20, 20))
82
+ self.apply_btn.setStyleSheet(self.button_style_sheet)
83
+ self.apply_btn.clicked.connect(self.parent_window.preprocess_image)
84
+
85
+ def help_prefilter(self):
86
+
87
+ """
88
+ Helper for prefiltering strategy
89
+ """
90
+
91
+ dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','prefilter-for-segmentation.json'])
92
+
93
+ with open(dict_path) as f:
94
+ d = json.load(f)
95
+
96
+ suggestion = help_generic(d)
97
+ if isinstance(suggestion, str):
98
+ print(f"{suggestion=}")
99
+ msgBox = QMessageBox()
100
+ msgBox.setIcon(QMessageBox.Information)
101
+ msgBox.setTextFormat(Qt.RichText)
102
+ msgBox.setText(f"The suggested technique is to {suggestion}.\nSee a tutorial <a href='https://celldetective.readthedocs.io/en/latest/segment.html'>here</a>.")
103
+ msgBox.setWindowTitle("Info")
104
+ msgBox.setStandardButtons(QMessageBox.Ok)
105
+ returnValue = msgBox.exec()
106
+ if returnValue == QMessageBox.Ok:
107
+ return None
108
+
109
+
110
+ class PreprocessingLayout2(PreprocessingLayout):
111
+
112
+ def __init__(self, fraction=75, *args, **kwargs):
113
+
114
+ self.fraction = fraction
115
+ super().__init__(apply_btn_option=False, *args, **kwargs)
116
+ self.preprocess_lbl.setText('Preprocessing: ')
117
+ self.preprocess_lbl.setStyleSheet("")
118
+ self.setContentsMargins(0,0,0,0)
119
+
120
+ def add_to_layout(self):
121
+
122
+ main_layout = QHBoxLayout()
123
+ main_layout.setContentsMargins(0,0,0,0)
124
+ main_layout.setSpacing(5)
125
+ main_layout.addWidget(self.preprocess_lbl, self.fraction, alignment=Qt.AlignTop)
126
+
127
+ list_grid = QGridLayout()
128
+ list_grid.addWidget(self.list, 0, 0, 2, 2)
129
+ list_grid.addWidget(self.add_filter_btn, 0, 2, 1, 1)
130
+ list_grid.addWidget(self.delete_filter_btn, 1, 2, 1, 1)
131
+ main_layout.addLayout(list_grid, 100-self.fraction)
132
+ self.add_filter_btn.setFixedWidth(35) # Ensure the button width is fixed
133
+ self.delete_filter_btn.setFixedWidth(35)
134
+ list_grid.setColumnStretch(2, 0)
135
+
136
+ self.addLayout(main_layout)
18
137
 
19
138
 
20
139
  class QCheckableComboBox(QComboBox):
@@ -23,12 +142,13 @@ class QCheckableComboBox(QComboBox):
23
142
  adapted from https://stackoverflow.com/questions/22775095/pyqt-how-to-set-combobox-items-be-checkable
24
143
  """
25
144
 
145
+ activated = pyqtSignal(str)
146
+
26
147
  def __init__(self, obj='', parent_window=None, *args, **kwargs):
27
148
 
28
149
  super().__init__(parent_window, *args, **kwargs)
29
150
 
30
151
  self.setTitle('')
31
- self.view().pressed.connect(self.handleItemPressed)
32
152
  self.setModel(QStandardItemModel(self))
33
153
  self.obj = obj
34
154
  self.toolButton = QToolButton(parent_window)
@@ -38,6 +158,9 @@ class QCheckableComboBox(QComboBox):
38
158
  self.toolButton.setPopupMode(QToolButton.InstantPopup)
39
159
  self.anySelected = False
40
160
 
161
+ self.view().viewport().installEventFilter(self)
162
+ self.view().pressed.connect(self.handleItemPressed)
163
+
41
164
  def clear(self):
42
165
 
43
166
  self.unselectAll()
@@ -70,6 +193,8 @@ class QCheckableComboBox(QComboBox):
70
193
  elif len(options_checked[options_checked])==0:
71
194
  self.setTitle(f"No {self.obj} selected...")
72
195
  self.anySelected = False
196
+
197
+ self.activated.emit(self.title())
73
198
 
74
199
  def setCurrentIndex(self, index):
75
200
 
@@ -101,6 +226,7 @@ class QCheckableComboBox(QComboBox):
101
226
 
102
227
  def setTitle(self, title):
103
228
  self._title = title
229
+ self.update()
104
230
  self.repaint()
105
231
 
106
232
  def paintEvent(self, event):
@@ -155,7 +281,12 @@ class QCheckableComboBox(QComboBox):
155
281
 
156
282
  def isAnySelected(self):
157
283
  return not self.title().startswith('No')
158
-
284
+
285
+ def eventFilter(self, source, event):
286
+ if source is self.view().viewport():
287
+ if event.type() == QEvent.MouseButtonRelease:
288
+ return True # Prevent the popup from closing
289
+ return super().eventFilter(source, event)
159
290
 
160
291
  class PandasModel(QAbstractTableModel):
161
292
 
@@ -415,7 +546,7 @@ class QHSeperationLine(QFrame):
415
546
  self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum)
416
547
 
417
548
 
418
- class FeatureChoice(QWidget):
549
+ class FeatureChoice(QWidget, Styles):
419
550
 
420
551
  def __init__(self, parent_window):
421
552
  super().__init__()
@@ -423,7 +554,6 @@ class FeatureChoice(QWidget):
423
554
  self.setWindowTitle("Add feature")
424
555
  # Create the QComboBox and add some items
425
556
  self.combo_box = QComboBox(self)
426
- center_window(self)
427
557
 
428
558
  standard_measurements = ["area",
429
559
  "area_bbox",
@@ -453,12 +583,15 @@ class FeatureChoice(QWidget):
453
583
  self.combo_box.addItems(standard_measurements)
454
584
 
455
585
  self.add_btn = QPushButton("Add")
586
+ self.add_btn.setStyleSheet(self.button_style_sheet)
456
587
  self.add_btn.clicked.connect(self.add_current_feature)
457
588
 
458
589
  # Create the layout
459
590
  layout = QVBoxLayout(self)
460
591
  layout.addWidget(self.combo_box)
461
592
  layout.addWidget(self.add_btn)
593
+ center_window(self)
594
+
462
595
 
463
596
  def add_current_feature(self):
464
597
  filtername = self.combo_box.currentText()
@@ -466,7 +599,7 @@ class FeatureChoice(QWidget):
466
599
  self.close()
467
600
 
468
601
 
469
- class FilterChoice(QWidget):
602
+ class FilterChoice(QWidget, Styles):
470
603
 
471
604
  def __init__(self, parent_window):
472
605
 
@@ -487,6 +620,7 @@ class FilterChoice(QWidget):
487
620
  'laplace_filter': None,
488
621
  'abs_filter': None,
489
622
  'ln_filter': None,
623
+ 'invert_filter': {'value': 65535},
490
624
  'subtract_filter': {'value': 1},
491
625
  'dog_filter': {'sigma_low': 0.8, 'sigma_high': 1.6},
492
626
  'log_filter': {'sigma': 2},
@@ -503,20 +637,26 @@ class FilterChoice(QWidget):
503
637
  self.combo_box.currentTextChanged.connect(self.update_arguments)
504
638
  layout.addWidget(self.combo_box)
505
639
 
640
+ self.floatValidator = QDoubleValidator()
506
641
  self.arguments_le = [QLineEdit() for i in range(3)]
642
+ for i in range(3):
643
+ self.arguments_le[i].setValidator(self.floatValidator)
644
+
507
645
  self.arguments_labels = [QLabel('') for i in range(3)]
508
646
  for i in range(2):
509
647
  hbox = QHBoxLayout()
510
- hbox.addWidget(self.arguments_labels[i], 20)
511
- hbox.addWidget(self.arguments_le[i], 80)
648
+ hbox.addWidget(self.arguments_labels[i], 40)
649
+ hbox.addWidget(self.arguments_le[i], 60)
512
650
  layout.addLayout(hbox)
513
651
 
514
652
  self.add_btn = QPushButton("Add")
653
+ self.add_btn.setStyleSheet(self.button_style_sheet)
515
654
  self.add_btn.clicked.connect(self.add_current_feature)
516
655
  layout.addWidget(self.add_btn)
517
656
 
518
657
  self.combo_box.setCurrentIndex(0)
519
658
  self.update_arguments()
659
+ center_window(self)
520
660
 
521
661
  def add_current_feature(self):
522
662
 
@@ -525,8 +665,10 @@ class FilterChoice(QWidget):
525
665
 
526
666
  filter_instructions = [filtername.split('_')[0]]
527
667
  for a in self.arguments_le:
528
- arg = a.text()
668
+
669
+ arg = a.text().replace(',','.')
529
670
  arg_num = arg
671
+
530
672
  if (arg != '') and arg_num.replace('.', '').replace(',', '').isnumeric():
531
673
  num = float(arg)
532
674
  if num.is_integer():
@@ -666,12 +808,14 @@ class DistanceChoice(QWidget):
666
808
  super().__init__()
667
809
  self.parent_window = parent_window
668
810
  self.setWindowTitle("Set distances")
811
+ self.floatValidator = QDoubleValidator()
669
812
  center_window(self)
670
813
 
671
814
  # Create the QComboBox and add some items
672
815
 
673
816
  self.dist_label = QLabel('Distance [px]: ')
674
817
  self.dist_le = QLineEdit('10')
818
+ self.dist_le.setValidator(self.floatValidator)
675
819
 
676
820
  self.add_btn = QPushButton("Add")
677
821
  self.add_btn.clicked.connect(self.add_current_feature)
@@ -686,7 +830,7 @@ class DistanceChoice(QWidget):
686
830
  layout.addWidget(self.add_btn)
687
831
 
688
832
  def add_current_feature(self):
689
- value = self.dist_le.text()
833
+ value = self.dist_le.text().replace(',','.')
690
834
  values = [value]
691
835
  self.parent_window.list_widget.addItems(values)
692
836
  self.close()
@@ -767,6 +911,9 @@ class ListWidget(QWidget):
767
911
  self.addItemWindow = self.choiceWidget(self)
768
912
  self.addItemWindow.show()
769
913
 
914
+ def addItemToList(self, item):
915
+ self.list_widget.addItems([item])
916
+
770
917
  def getItems(self):
771
918
 
772
919
  """
@@ -794,6 +941,9 @@ class ListWidget(QWidget):
794
941
  items.append(self.dtype(self.list_widget.item(x).text()))
795
942
  return items
796
943
 
944
+ def clear(self):
945
+ self.items = []
946
+ self.list_widget.clear()
797
947
 
798
948
  def removeSel(self):
799
949
 
@@ -827,13 +977,21 @@ class FigureCanvas(QWidget):
827
977
  if interactive:
828
978
  self.toolbar = NavigationToolbar2QT(self.canvas)
829
979
  self.layout = QVBoxLayout(self)
830
- self.layout.addWidget(self.canvas)
980
+ self.layout.addWidget(self.canvas,90)
831
981
  if interactive:
832
982
  self.layout.addWidget(self.toolbar)
833
983
 
834
984
  center_window(self)
835
985
  self.setAttribute(Qt.WA_DeleteOnClose)
836
986
 
987
+ def resizeEvent(self, event):
988
+
989
+ super().resizeEvent(event)
990
+ try:
991
+ self.fig.tight_layout()
992
+ except:
993
+ pass
994
+
837
995
  def draw(self):
838
996
  self.canvas.draw()
839
997
 
@@ -27,9 +27,10 @@ from pathlib import Path
27
27
 
28
28
  from celldetective.gui.viewers import CellEdgeVisualizer, SpotDetectionVisualizer
29
29
  from celldetective.gui.layouts import ProtocolDesignerLayout, BackgroundFitCorrectionLayout, LocalCorrectionLayout
30
- from celldetective.gui.gui_utils import ThresholdLineEdit
30
+ from celldetective.gui.gui_utils import ThresholdLineEdit, PreprocessingLayout2
31
31
  from celldetective.gui import Styles
32
32
 
33
+
33
34
  class ConfigMeasurements(QMainWindow, Styles):
34
35
  """
35
36
  UI to set measurement instructions.
@@ -261,7 +262,7 @@ class ConfigMeasurements(QMainWindow, Styles):
261
262
  self.add_feature_btn.setToolTip("Add feature")
262
263
  self.add_feature_btn.setIconSize(QSize(20, 20))
263
264
 
264
- self.features_list = ListWidget(FeatureChoice, initial_features=['area', 'intensity_nanmean', ])
265
+ self.features_list = ListWidget(FeatureChoice, initial_features=['area', 'intensity_mean', ])
265
266
 
266
267
  self.del_feature_btn.clicked.connect(self.features_list.removeSel)
267
268
  self.add_feature_btn.clicked.connect(self.features_list.addItem)
@@ -313,25 +314,6 @@ class ConfigMeasurements(QMainWindow, Styles):
313
314
 
314
315
  self.feat_sep3 = QHSeperationLine()
315
316
  layout.addWidget(self.feat_sep3)
316
- # self.radial_intensity_btn = QCheckBox('Measure radial intensity distribution')
317
- # layout.addWidget(self.radial_intensity_btn)
318
- # self.radial_intensity_btn.clicked.connect(self.enable_step_size)
319
- # self.channel_chechkboxes=[]
320
- # for channel in self.channel_names:
321
- # channel_checkbox=QCheckBox(channel)
322
- # self.channel_chechkboxes.append(channel_checkbox)
323
- # layout.addWidget(channel_checkbox)
324
- # channel_checkbox.setEnabled(False)
325
- # step_box=QHBoxLayout()
326
- # self.step_lbl=QLabel("Step size (in px)")
327
- # self.step_size=QLineEdit()
328
- # self.step_lbl.setEnabled(False)
329
- # self.step_size.setEnabled(False)
330
- # step_box.addWidget(self.step_lbl)
331
- # step_box.addWidget(self.step_size)
332
- # layout.addLayout(step_box)
333
- # self.feat_sep4 = QHSeperationLine()
334
- # layout.addWidget(self.feat_sep4)
335
317
 
336
318
  # Haralick features parameters
337
319
  self.activate_haralick_btn = QCheckBox('Measure Haralick texture features')
@@ -496,6 +478,8 @@ class ConfigMeasurements(QMainWindow, Styles):
496
478
  Write the selected options in a json file for later reading by the software.
497
479
  """
498
480
 
481
+ print(f"{self.spot_preprocessing.list.items=}")
482
+
499
483
  print('Writing instructions...')
500
484
  measurement_options = {}
501
485
  background_correction = self.protocol_layout.protocols
@@ -511,16 +495,6 @@ class ConfigMeasurements(QMainWindow, Styles):
511
495
  if not border_distances:
512
496
  border_distances = None
513
497
  measurement_options.update({'border_distances': border_distances})
514
- # radial_intensity = {}
515
- # radial_step = int(self.step_size.text())
516
- # radial_channels = []
517
- # for checkbox in self.channel_chechkboxes:
518
- # if checkbox.isChecked():
519
- # radial_channels.append(checkbox.text())
520
- # radial_intensity={'radial_step': radial_step, 'radial_channels': radial_channels}
521
- # if not self.radial_intensity_btn.isChecked():
522
- # radial_intensity = None
523
- # measurement_options.update({'radial_intensity' : radial_intensity})
524
498
 
525
499
  self.extract_haralick_options()
526
500
  measurement_options.update({'haralick_options': self.haralick_options})
@@ -537,8 +511,11 @@ class ConfigMeasurements(QMainWindow, Styles):
537
511
  'isotropic_operations': isotropic_operations})
538
512
  spot_detection = None
539
513
  if self.spot_check.isChecked():
514
+ image_preprocessing = self.spot_preprocessing.list.items
515
+ if image_preprocessing==[]:
516
+ image_preprocessing = None
540
517
  spot_detection = {'channel': self.spot_channel.currentText(), 'diameter': float(self.diameter_value.text().replace(',','.')),
541
- 'threshold': float(self.threshold_value.text().replace(',','.'))}
518
+ 'threshold': float(self.threshold_value.text().replace(',','.')), 'image_preprocessing': image_preprocessing}
542
519
  measurement_options.update({'spot_detection': spot_detection})
543
520
  if self.clear_previous_btn.isChecked():
544
521
  self.clear_previous = True
@@ -547,7 +524,6 @@ class ConfigMeasurements(QMainWindow, Styles):
547
524
  measurement_options.update({'clear_previous': self.clear_previous})
548
525
 
549
526
 
550
-
551
527
  print('Measurement instructions: ', measurement_options)
552
528
  file_name = self.measure_instructions_path
553
529
  with open(file_name, 'w') as f:
@@ -618,7 +594,13 @@ class ConfigMeasurements(QMainWindow, Styles):
618
594
  self.spot_channel.setCurrentText(idx)
619
595
  self.diameter_value.setText(str(spot_detection['diameter']))
620
596
  self.threshold_value.setText(str(spot_detection['threshold']))
621
-
597
+ if 'image_preprocessing' in spot_detection:
598
+ items = spot_detection['image_preprocessing']
599
+ if items is not None:
600
+ items_for_list = [a[0] for a in items]
601
+ for it in items_for_list:
602
+ self.spot_preprocessing.list.addItemToList(it)
603
+ self.spot_preprocessing.list.items = items
622
604
 
623
605
  if 'border_distances' in measurement_instructions:
624
606
  border_distances = measurement_instructions['border_distances']
@@ -915,43 +897,81 @@ class ConfigMeasurements(QMainWindow, Styles):
915
897
 
916
898
  def populate_spot_detection(self):
917
899
 
918
- layout = QGridLayout(self.spot_detection_frame)
900
+ layout = QVBoxLayout(self.spot_detection_frame)
901
+
919
902
  self.spot_detection_lbl = QLabel("SPOT DETECTION")
920
903
  self.spot_detection_lbl.setStyleSheet("""font-weight: bold;padding: 0px;""")
921
- layout.addWidget(self.spot_detection_lbl, 0, 0, 1, 2, alignment=Qt.AlignCenter)
922
- self.spot_check= QCheckBox('Perform spot detection')
904
+ layout.addWidget(self.spot_detection_lbl, alignment=Qt.AlignCenter)
905
+
906
+ perform_hbox = QHBoxLayout()
907
+ self.spot_check = QCheckBox('Perform spot detection')
923
908
  self.spot_check.toggled.connect(self.enable_spot_detection)
924
- layout.addWidget(self.spot_check, 1, 0)
925
- self.spot_channel_lbl = QLabel("Choose channel for spot detection: ")
909
+ perform_hbox.addWidget(self.spot_check, 95)
910
+
911
+ self.spot_viewer_btn = QPushButton()
912
+ self.spot_viewer_btn.clicked.connect(self.spot_preview)
913
+ self.spot_viewer_btn.setIcon(icon(MDI6.image_check, color="k"))
914
+ self.spot_viewer_btn.setStyleSheet(self.button_select_all)
915
+ self.spot_viewer_btn.setToolTip('Set detection parameters visually.')
916
+ perform_hbox.addWidget(self.spot_viewer_btn, 5)
917
+ layout.addLayout(perform_hbox)
918
+
919
+ channel_hbox = QHBoxLayout()
920
+ self.spot_channel_lbl = QLabel("Channel: ")
926
921
  self.spot_channel = QComboBox()
927
922
  self.spot_channel.addItems(self.channel_names)
928
- layout.addWidget(self.spot_channel_lbl, 2, 0)
929
- layout.addWidget(self.spot_channel, 2, 1)
923
+ channel_hbox.addWidget(self.spot_channel_lbl, 30)
924
+ channel_hbox.addWidget(self.spot_channel, 70)
925
+ layout.addLayout(channel_hbox)
926
+
927
+ self.spot_preprocessing = PreprocessingLayout2(fraction=30, parent_window=self)
928
+ layout.addLayout(self.spot_preprocessing)
929
+
930
+ # continue switching to VBox + HBox down
931
+ diam_hbox = QHBoxLayout()
930
932
  self.diameter_lbl = QLabel('Spot diameter: ')
931
933
  self.diameter_value = QLineEdit()
932
934
  self.diameter_value.setValidator(self.onlyFloat)
933
935
  self.diameter_value.setText('7')
934
936
  self.diameter_value.textChanged.connect(self.enable_spot_preview)
935
937
 
936
- layout.addWidget(self.diameter_lbl, 3, 0)
937
- layout.addWidget(self.diameter_value, 3, 1)
938
+ diam_hbox.addWidget(self.diameter_lbl,30)
939
+ diam_hbox.addWidget(self.diameter_value, 70)
940
+ layout.addLayout(diam_hbox)
941
+
942
+ thresh_hbox = QHBoxLayout()
938
943
  self.threshold_lbl = QLabel('Spot threshold: ')
939
944
  self.threshold_value = QLineEdit()
940
945
  self.threshold_value.setValidator(self.onlyFloat)
941
946
  self.threshold_value.setText('0')
942
947
  self.threshold_value.textChanged.connect(self.enable_spot_preview)
943
948
 
944
- layout.addWidget(self.threshold_lbl, 4, 0)
945
- layout.addWidget(self.threshold_value, 4, 1)
946
-
947
- self.spot_viewer_btn = QPushButton()
948
- self.spot_viewer_btn.clicked.connect(self.spot_preview)
949
- self.spot_viewer_btn.setIcon(icon(MDI6.image_check, color="k"))
950
- self.spot_viewer_btn.setStyleSheet(self.button_select_all)
951
- self.spot_viewer_btn.setToolTip('Set detection parameters visually.')
952
- layout.addWidget(self.spot_viewer_btn, 1, 1, 1, 1, alignment=Qt.AlignRight)
953
-
954
- self.spot_detection_widgets = [self.spot_channel, self.spot_channel_lbl, self.diameter_value, self.diameter_lbl, self.threshold_value, self.threshold_lbl, self.spot_viewer_btn]
949
+ thresh_hbox.addWidget(self.threshold_lbl, 30)
950
+ thresh_hbox.addWidget(self.threshold_value, 70)
951
+ layout.addLayout(thresh_hbox)
952
+
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
+ self.spot_detection_widgets = [self.spot_channel,
962
+ self.spot_channel_lbl,
963
+ self.diameter_value,
964
+ self.diameter_lbl,
965
+ self.threshold_value,
966
+ self.threshold_lbl,
967
+ self.spot_viewer_btn,
968
+ #self.invert_check,
969
+ #self.invert_value_le,
970
+ self.spot_preprocessing.list,
971
+ self.spot_preprocessing.add_filter_btn,
972
+ self.spot_preprocessing.delete_filter_btn,
973
+ self.spot_preprocessing.preprocess_lbl
974
+ ]
955
975
  for wg in self.spot_detection_widgets:
956
976
  wg.setEnabled(False)
957
977
 
@@ -969,6 +989,13 @@ class ConfigMeasurements(QMainWindow, Styles):
969
989
  if self.test_frame is not None:
970
990
  self.locate_mask()
971
991
  if self.test_mask is not None:
992
+
993
+ # invert_value = self.invert_value_le.text().replace(',','.')
994
+ # if invert_value != '':
995
+ # invert_value = float(invert_value)
996
+ # else:
997
+ # invert_value = None
998
+
972
999
  self.spot_visual = SpotDetectionVisualizer(frame_slider=True,
973
1000
  contrast_slider=True,
974
1001
  cell_type=self.mode,
@@ -981,6 +1008,10 @@ class ConfigMeasurements(QMainWindow, Styles):
981
1008
  parent_channel_cb=self.spot_channel,
982
1009
  parent_diameter_le=self.diameter_value,
983
1010
  parent_threshold_le=self.threshold_value,
1011
+ parent_preprocessing_list=self.spot_preprocessing.list,
1012
+ #parent_invert_check=self.invert_check,
1013
+ #invert = self.invert_check.isChecked(),
1014
+ #invert_value = self.invert_value_le.text().replace(',','.'),
984
1015
  PxToUm = 1,)
985
1016
  self.spot_visual.show()
986
1017
  #self.spot_visual = ThresholdSpot(current_channel=self.spot_channel.currentIndex(), img=self.test_frame,
@@ -174,7 +174,7 @@ class ConfigNeighborhoods(QWidget, Styles):
174
174
  self.attr_parent.locate_image()
175
175
  if self.attr_parent.current_stack is not None:
176
176
  self.viewer = CellEdgeVisualizer(
177
- cell_type='effectors',
177
+ cell_type=self.reference_population_cb.currentText(),
178
178
  edge_range=(1,30),
179
179
  invert=True,
180
180
  initial_edge=3,
@@ -19,6 +19,7 @@ import pandas as pd
19
19
  import math
20
20
  from celldetective.gui import Styles
21
21
  from matplotlib import colormaps
22
+ import matplotlib.cm
22
23
 
23
24
 
24
25
  class ConfigSignalPlot(QWidget, Styles):
@@ -110,10 +111,8 @@ class ConfigSignalPlot(QWidget, Styles):
110
111
 
111
112
  all_cms = list(colormaps)
112
113
  for cm in all_cms:
113
- try:
114
- self.cbs[-1].addColormap(cm)
115
- except:
116
- pass
114
+ if hasattr(matplotlib.cm, str(cm).lower()):
115
+ self.cbs[-1].addColormap(cm.lower())
117
116
 
118
117
  self.cbs[0].setCurrentIndex(1)
119
118
  self.cbs[0].setCurrentIndex(0)
@@ -9,7 +9,7 @@ from PyQt5.QtGui import QDoubleValidator, QIntValidator
9
9
  from celldetective.gui.signal_annotator import MeasureAnnotator
10
10
  from celldetective.gui.signal_annotator2 import SignalAnnotator2
11
11
  from celldetective.io import get_segmentation_models_list, control_segmentation_napari, get_signal_models_list, \
12
- control_tracking_btrack, load_experiment_tables, get_pair_signal_models_list
12
+ control_tracks, load_experiment_tables, get_pair_signal_models_list
13
13
  from celldetective.io import locate_segmentation_model, fix_missing_labels, auto_load_number_of_frames, load_frames, locate_signal_model
14
14
  from celldetective.gui import SegmentationModelLoader, ClassifierWidget, ConfigNeighborhoods, ConfigSegmentationModelTraining, ConfigTracking, SignalAnnotator, ConfigSignalModelTraining, ConfigMeasurements, ConfigSignalAnnotator, TableUI
15
15
  from celldetective.gui.gui_utils import QHSeperationLine
@@ -291,7 +291,7 @@ class ProcessPanel(QFrame, Styles):
291
291
  self.track_action.setStyleSheet(self.menu_check_style)
292
292
  self.track_action.setIcon(icon(MDI6.chart_timeline_variant,color="black"))
293
293
  self.track_action.setIconSize(QSize(20, 20))
294
- self.track_action.setToolTip("Track the target cells using bTrack.")
294
+ self.track_action.setToolTip(f"Track the {self.mode[:-1]} cells.")
295
295
  grid_track.addWidget(self.track_action, 75)
296
296
 
297
297
  self.delete_tracks_btn = QPushButton()
@@ -307,7 +307,7 @@ class ProcessPanel(QFrame, Styles):
307
307
  self.check_tracking_result_btn = QPushButton()
308
308
  self.check_tracking_result_btn.setIcon(icon(MDI6.eye_check_outline,color="black"))
309
309
  self.check_tracking_result_btn.setIconSize(QSize(20, 20))
310
- self.check_tracking_result_btn.setToolTip("View raw bTrack output in napari.")
310
+ self.check_tracking_result_btn.setToolTip("View tracking output in napari.")
311
311
  self.check_tracking_result_btn.setStyleSheet(self.button_select_all)
312
312
  self.check_tracking_result_btn.clicked.connect(self.open_napari_tracking)
313
313
  self.check_tracking_result_btn.setEnabled(False)
@@ -845,14 +845,16 @@ class ProcessPanel(QFrame, Styles):
845
845
 
846
846
  # self.stack = None
847
847
  self.parent_window.update_position_options()
848
- if self.segment_action.isChecked():
849
- self.segment_action.setChecked(False)
848
+
849
+ for action in [self.segment_action, self.track_action, self.measure_action, self.signal_analysis_action]:
850
+ if action.isChecked():
851
+ action.setChecked(False)
850
852
 
851
853
  self.cellpose_calibrated = False
852
854
 
853
855
  def open_napari_tracking(self):
854
856
  print(f'View the tracks before post-processing for position {self.parent_window.pos} in napari...')
855
- control_tracking_btrack(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode, threads=self.parent_window.parent_window.n_threads)
857
+ control_tracks(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode, threads=self.parent_window.parent_window.n_threads)
856
858
 
857
859
  def view_table_ui(self):
858
860
 
@@ -7,7 +7,7 @@ from celldetective.gui.gui_utils import center_window, color_from_state
7
7
  from superqt import QLabeledDoubleSlider, QLabeledDoubleRangeSlider, QSearchableComboBox
8
8
  from celldetective.utils import extract_experiment_channels, get_software_location, _get_img_num_per_channel
9
9
  from celldetective.io import auto_load_number_of_frames, load_frames, \
10
- load_napari_data
10
+ load_napari_data, get_experiment_metadata
11
11
  from celldetective.gui.gui_utils import FigureCanvas, color_from_status, color_from_class, ExportPlotBtn
12
12
  import json
13
13
  import numpy as np
@@ -457,8 +457,10 @@ class SignalAnnotator(QMainWindow, Styles):
457
457
  cols = np.array(self.df_tracks.columns)
458
458
  self.class_cols = np.array([c.startswith('class') for c in list(self.df_tracks.columns)])
459
459
  self.class_cols = list(cols[self.class_cols])
460
- self.class_cols.remove('class_id')
461
- self.class_cols.remove('class_color')
460
+ if 'class_id' in self.class_cols:
461
+ self.class_cols.remove('class_id')
462
+ if 'class_color' in self.class_cols:
463
+ self.class_cols.remove('class_color')
462
464
  self.class_choice_cb.addItems(self.class_cols)
463
465
  idx = self.class_choice_cb.findText(self.target_class)
464
466
  self.class_choice_cb.setCurrentIndex(idx)
@@ -779,6 +781,11 @@ class SignalAnnotator(QMainWindow, Styles):
779
781
  'state', 'generation', 'root', 'parent', 'class_id', 'class', 't0', 'POSITION_X',
780
782
  'POSITION_Y', 'position', 'well', 'well_index', 'well_name', 'pos_name', 'index',
781
783
  'concentration', 'cell_type', 'antibody', 'pharmaceutical_agent'] + self.class_cols
784
+ meta = get_experiment_metadata(self.exp_dir)
785
+ if meta is not None:
786
+ keys = list(meta.keys())
787
+ cols_to_remove.extend(keys)
788
+
782
789
  cols = np.array(list(self.df_tracks.columns))
783
790
  time_cols = np.array([c.startswith('t_') for c in cols])
784
791
  time_cols = list(cols[time_cols])