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.
- celldetective/__init__.py +0 -3
- celldetective/_version.py +1 -1
- celldetective/events.py +2 -4
- celldetective/extra_properties.py +132 -0
- celldetective/gui/InitWindow.py +33 -45
- celldetective/gui/__init__.py +1 -0
- celldetective/gui/about.py +19 -15
- celldetective/gui/analyze_block.py +34 -19
- celldetective/gui/base_components.py +23 -0
- celldetective/gui/btrack_options.py +26 -34
- celldetective/gui/classifier_widget.py +68 -81
- celldetective/gui/configure_new_exp.py +113 -17
- celldetective/gui/control_panel.py +68 -141
- celldetective/gui/generic_signal_plot.py +9 -12
- celldetective/gui/gui_utils.py +49 -21
- celldetective/gui/json_readers.py +5 -4
- celldetective/gui/layouts.py +246 -22
- celldetective/gui/measurement_options.py +32 -17
- celldetective/gui/neighborhood_options.py +10 -13
- celldetective/gui/plot_measurements.py +21 -17
- celldetective/gui/plot_signals_ui.py +125 -72
- celldetective/gui/process_block.py +180 -123
- celldetective/gui/processes/compute_neighborhood.py +594 -0
- celldetective/gui/processes/measure_cells.py +5 -0
- celldetective/gui/processes/segment_cells.py +27 -6
- celldetective/gui/processes/track_cells.py +6 -0
- celldetective/gui/retrain_segmentation_model_options.py +12 -20
- celldetective/gui/retrain_signal_model_options.py +57 -56
- celldetective/gui/seg_model_loader.py +21 -62
- celldetective/gui/signal_annotator.py +129 -70
- celldetective/gui/signal_annotator2.py +431 -635
- celldetective/gui/signal_annotator_options.py +8 -11
- celldetective/gui/survival_ui.py +49 -95
- celldetective/gui/tableUI.py +28 -25
- celldetective/gui/thresholds_gui.py +617 -1221
- celldetective/gui/viewers.py +106 -39
- celldetective/gui/workers.py +9 -3
- celldetective/io.py +57 -20
- celldetective/measure.py +63 -27
- celldetective/neighborhood.py +342 -268
- celldetective/preprocessing.py +25 -17
- celldetective/relative_measurements.py +50 -29
- celldetective/scripts/analyze_signals.py +4 -1
- celldetective/scripts/measure_relative.py +4 -1
- celldetective/scripts/segment_cells.py +0 -6
- celldetective/scripts/track_cells.py +3 -1
- celldetective/scripts/train_segmentation_model.py +7 -4
- celldetective/signals.py +29 -14
- celldetective/tracking.py +7 -2
- celldetective/utils.py +36 -8
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/METADATA +24 -16
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/RECORD +57 -55
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/WHEEL +1 -1
- tests/test_qt.py +21 -21
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/entry_points.txt +0 -0
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info/licenses}/LICENSE +0 -0
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/top_level.txt +0 -0
celldetective/gui/gui_utils.py
CHANGED
|
@@ -1,23 +1,48 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
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
|
-
|
|
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(
|
|
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=
|
|
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(
|
|
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(
|
|
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
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
5
|
+
from celldetective.gui import CelldetectiveWidget
|
|
6
6
|
|
|
7
|
-
|
|
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 =
|
|
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)
|
celldetective/gui/layouts.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import QCheckBox, QLineEdit,
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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 =
|
|
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,
|
|
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,
|
|
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
|
|
3
|
-
QGridLayout, QLineEdit, QVBoxLayout,
|
|
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
|
|
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
|
|
31
|
-
from celldetective.gui import
|
|
29
|
+
from celldetective.gui.gui_utils import PreprocessingLayout2
|
|
30
|
+
from celldetective.gui import CelldetectiveMainWindow, CelldetectiveWidget
|
|
32
31
|
|
|
33
32
|
|
|
34
|
-
class ConfigMeasurements(
|
|
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
|
-
|
|
51
|
-
|
|
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 =
|
|
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(
|
|
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:
|