celldetective 1.3.0.post1__py3-none-any.whl → 1.3.2__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 (36) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/events.py +88 -11
  3. celldetective/extra_properties.py +5 -1
  4. celldetective/gui/InitWindow.py +35 -9
  5. celldetective/gui/classifier_widget.py +99 -23
  6. celldetective/gui/control_panel.py +7 -1
  7. celldetective/gui/generic_signal_plot.py +161 -2
  8. celldetective/gui/gui_utils.py +90 -1
  9. celldetective/gui/layouts.py +128 -7
  10. celldetective/gui/measurement_options.py +3 -3
  11. celldetective/gui/plot_signals_ui.py +8 -3
  12. celldetective/gui/process_block.py +77 -32
  13. celldetective/gui/retrain_segmentation_model_options.py +24 -10
  14. celldetective/gui/signal_annotator.py +53 -26
  15. celldetective/gui/signal_annotator2.py +17 -30
  16. celldetective/gui/survival_ui.py +24 -3
  17. celldetective/gui/tableUI.py +300 -183
  18. celldetective/gui/viewers.py +263 -3
  19. celldetective/io.py +56 -3
  20. celldetective/links/zenodo.json +136 -123
  21. celldetective/measure.py +3 -0
  22. celldetective/models/tracking_configs/biased_motion.json +68 -0
  23. celldetective/models/tracking_configs/no_z_motion.json +202 -0
  24. celldetective/neighborhood.py +154 -69
  25. celldetective/preprocessing.py +172 -3
  26. celldetective/relative_measurements.py +128 -4
  27. celldetective/scripts/measure_cells.py +3 -3
  28. celldetective/signals.py +212 -215
  29. celldetective/tracking.py +7 -3
  30. celldetective/utils.py +22 -6
  31. {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/METADATA +3 -3
  32. {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/RECORD +36 -34
  33. {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/WHEEL +1 -1
  34. {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/LICENSE +0 -0
  35. {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/entry_points.txt +0 -0
  36. {celldetective-1.3.0.post1.dist-info → celldetective-1.3.2.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,11 @@ from PyQt5.QtCore import Qt, QSize
5
5
  from PyQt5.QtGui import QDoubleValidator
6
6
 
7
7
  from celldetective.gui.gui_utils import center_window, FigureCanvas, ExportPlotBtn
8
+ from celldetective.gui.tableUI import TableUI
9
+ from celldetective.io import collect_experiment_metadata
10
+
8
11
  from superqt.fonticon import icon
12
+ from superqt import QLabeledSlider
9
13
  from fonticon_mdi6 import MDI6
10
14
  import numpy as np
11
15
  import json
@@ -16,7 +20,9 @@ from glob import glob
16
20
  from matplotlib.cm import tab10
17
21
  from celldetective.gui import Styles
18
22
  import matplotlib.cm as mcm
23
+ import pandas as pd
19
24
 
25
+ from lifelines.utils import qth_survival_times
20
26
 
21
27
  class GenericSignalPlotWidget(QWidget, Styles):
22
28
 
@@ -136,6 +142,14 @@ class GenericSignalPlotWidget(QWidget, Styles):
136
142
  plot_buttons_hbox.addWidget(self.export_btn, 5, alignment=Qt.AlignRight)
137
143
  self.layout.addLayout(plot_buttons_hbox)
138
144
 
145
+ self.export_tabular_btn = QPushButton('')
146
+ self.export_tabular_btn.setIcon(icon(MDI6.table,color="black"))
147
+ self.export_tabular_btn.setStyleSheet(self.button_select_all)
148
+ self.export_tabular_btn.setToolTip('Tabulate survival values.')
149
+ self.export_tabular_btn.setIconSize(QSize(20, 20))
150
+ plot_buttons_hbox.addWidget(self.export_tabular_btn, 5, alignment=Qt.AlignRight)
151
+ self.export_tabular_btn.hide()
152
+
139
153
  self.ax.set_prop_cycle('color',[self.cmap(i) for i in np.linspace(0, 1, len(self.parent_window.well_indices))])
140
154
 
141
155
  self.fig.set_facecolor('none') # or 'None'
@@ -671,7 +685,9 @@ class SurvivalPlotWidget(GenericSignalPlotWidget):
671
685
  self.class_selection_widget.hide()
672
686
  self.rescale_widget.hide()
673
687
  self.cell_lines_alpha_wdg.hide()
674
-
688
+ self.export_tabular_btn.show()
689
+ self.export_tabular_btn.clicked.connect(self.set_table_options)
690
+
675
691
  def switch_to_log(self):
676
692
 
677
693
  """
@@ -790,4 +806,147 @@ class SurvivalPlotWidget(GenericSignalPlotWidget):
790
806
 
791
807
  # Plot a signal
792
808
  if line==line:
793
- line.plot_survival_function(ci_show=ci_option, ax=self.ax, legend=legend, color=color, label=label, xlabel='timeline [min]')
809
+ line.plot_survival_function(ci_show=ci_option, ax=self.ax, legend=legend, color=color, label=label, xlabel='timeline [min]')
810
+
811
+ def set_table_options(self):
812
+
813
+ self.config_table_wg = QWidget()
814
+ self.config_table_wg.setMinimumWidth(480)
815
+ self.config_table_wg.setWindowTitle('Survival data')
816
+
817
+ layout = QVBoxLayout()
818
+ self.config_table_wg.setLayout(layout)
819
+
820
+ self.all_values_rb = QRadioButton('tabulate all values')
821
+ self.single_timepoint_rb = QRadioButton('survival at single timepoint [min]: ')
822
+ self.ec_rb = QRadioButton(r'EC N% survival: ')
823
+ self.all_values_rb.toggled.connect(self.activate_sliders)
824
+ self.single_timepoint_rb.toggled.connect(self.activate_sliders)
825
+
826
+ self.single_timepoint_slider = QLabeledSlider()
827
+ self.single_timepoint_slider.setRange(0, int(self.df['FRAME'].max()*self.parent_window.FrameToMin))
828
+ self.single_timepoint_slider.setValue(int(self.df['FRAME'].max()*self.parent_window.FrameToMin))
829
+
830
+ self.ec_slider = QLabeledSlider()
831
+ self.ec_slider.setRange(0, 100)
832
+ self.ec_slider.setValue(50)
833
+
834
+ self.ec_rb.toggled.connect(self.activate_sliders)
835
+ self.all_values_rb.click()
836
+
837
+ self.set_btn = QPushButton('Set')
838
+ self.set_btn.setStyleSheet(self.button_style_sheet)
839
+ self.set_btn.clicked.connect(self.assemble_survival_data)
840
+
841
+ layout.addWidget(self.all_values_rb)
842
+
843
+ single_tp_layout = QHBoxLayout()
844
+ single_tp_layout.addWidget(self.single_timepoint_rb, 33)
845
+ single_tp_layout.addWidget(self.single_timepoint_slider, 66)
846
+ layout.addLayout(single_tp_layout)
847
+
848
+ ec_layout = QHBoxLayout()
849
+ ec_layout.addWidget(self.ec_rb, 33)
850
+ ec_layout.addWidget(self.ec_slider, 66)
851
+ layout.addLayout(ec_layout)
852
+
853
+ layout.addWidget(self.set_btn)
854
+ center_window(self.config_table_wg)
855
+ self.config_table_wg.show()
856
+
857
+ def activate_sliders(self):
858
+ if self.all_values_rb.isChecked():
859
+ self.single_timepoint_slider.setEnabled(False)
860
+ self.ec_slider.setEnabled(False)
861
+ elif self.single_timepoint_rb.isChecked():
862
+ self.single_timepoint_slider.setEnabled(True)
863
+ self.ec_slider.setEnabled(False)
864
+ elif self.ec_rb.isChecked():
865
+ self.ec_slider.setEnabled(True)
866
+ self.single_timepoint_slider.setEnabled(False)
867
+
868
+ def assemble_survival_data(self):
869
+
870
+ if self.plot_options[0].isChecked():
871
+ data = self.df_well_info
872
+ groupby = ['well_path']
873
+ if self.plot_options[1].isChecked():
874
+ data = self.df_pos_info
875
+ groupby = ['pos_path']
876
+ if self.plot_options[2].isChecked():
877
+ print('Not implemented yet... Please select "well" or "position" as grouping...')
878
+ return None
879
+
880
+ if self.all_values_rb.isChecked():
881
+
882
+ survival_table = []
883
+ tid=0
884
+ for name,group in data.groupby(groupby):
885
+ print(name)
886
+ if groupby[0]=="pos_path":
887
+ metadata = collect_experiment_metadata(pos_path=name[0])
888
+ elif groupby[0]=="well_path":
889
+ metadata = collect_experiment_metadata(well_path=name[0])
890
+ ks_estimator = group['survival_fit'].values[0]
891
+ if ks_estimator!=ks_estimator:
892
+ continue
893
+ timeline = list(ks_estimator.survival_function_.index)
894
+ survival = ks_estimator.survival_function_['KM_estimate'].values
895
+ lower_error = ks_estimator.confidence_interval_['KM_estimate_lower_0.95'].values
896
+ upper_error = ks_estimator.confidence_interval_['KM_estimate_upper_0.95'].values
897
+ for k in range(len(timeline)):
898
+ dico = metadata.copy()
899
+ dico.update({'TRACK_ID': tid,'FRAME': int(timeline[k] / self.parent_window.FrameToMin),'timeline': timeline[k], 'survival': survival[k], "event_fraction": 1-survival[k], 'KM_estimate_lower_0.95': lower_error[k], 'KM_estimate_upper_0.95': upper_error[k]})
900
+ survival_table.append(dico)
901
+ tid+=1
902
+
903
+ survival_table = pd.DataFrame(survival_table)
904
+ self.table = TableUI(survival_table, f"Survival data", plot_mode="plot_track_signals")
905
+ self.table.show()
906
+
907
+ elif self.single_timepoint_rb.isChecked():
908
+
909
+ survival_table = []
910
+ tid=0
911
+ for name,group in data.groupby(groupby):
912
+ print(name)
913
+ if groupby[0]=="pos_path":
914
+ metadata = collect_experiment_metadata(pos_path=name[0])
915
+ elif groupby[0]=="well_path":
916
+ metadata = collect_experiment_metadata(well_path=name[0])
917
+ ks_estimator = group['survival_fit'].values[0]
918
+ if ks_estimator!=ks_estimator:
919
+ continue
920
+ survival = ks_estimator.survival_function_at_times(self.single_timepoint_slider.value()).values[0]
921
+ dico = metadata.copy()
922
+ dico.update({'timepoint': self.single_timepoint_slider.value(), 'survival': survival, 'event_fraction': 1 - survival})
923
+ survival_table.append(dico)
924
+ tid+=1
925
+
926
+ survival_table = pd.DataFrame(survival_table)
927
+ self.table = TableUI(survival_table, f"Survival data", plot_mode="static")
928
+ self.table.show()
929
+
930
+ elif self.ec_rb.isChecked():
931
+
932
+ survival_table = []
933
+ tid=0
934
+ for name,group in data.groupby(groupby):
935
+ print(name)
936
+ if groupby[0]=="pos_path":
937
+ metadata = collect_experiment_metadata(pos_path=name[0])
938
+ elif groupby[0]=="well_path":
939
+ metadata = collect_experiment_metadata(well_path=name[0])
940
+ ks_estimator = group['survival_fit'].values[0]
941
+ if ks_estimator!=ks_estimator:
942
+ continue
943
+ survival = ks_estimator.survival_function_
944
+ ecN = qth_survival_times(float(self.ec_slider.value())/100.0, survival)
945
+ dico = metadata.copy()
946
+ dico.update({"qth": int(self.ec_slider.value()), f'EC{int(self.ec_slider.value())}% [min]': ecN})
947
+ survival_table.append(dico)
948
+ tid+=1
949
+
950
+ survival_table = pd.DataFrame(survival_table)
951
+ self.table = TableUI(survival_table, f"Survival data", plot_mode="static")
952
+ self.table.show()
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
2
  from PyQt5.QtWidgets import QApplication, QMessageBox, QFrame, QSizePolicy, QWidget, QLineEdit, QListWidget, QVBoxLayout, QComboBox, \
3
3
  QPushButton, QLabel, QHBoxLayout, QCheckBox, QFileDialog
4
- from PyQt5.QtCore import Qt, QSize
4
+ from PyQt5.QtCore import Qt, QSize, QAbstractTableModel
5
5
  from PyQt5.QtGui import QDoubleValidator, QIntValidator
6
6
 
7
7
  from celldetective.gui import Styles
@@ -16,6 +16,95 @@ from inspect import getmembers, isfunction
16
16
  from celldetective.filters import *
17
17
  from os import sep
18
18
 
19
+ class PandasModel(QAbstractTableModel):
20
+
21
+ """
22
+ from https://stackoverflow.com/questions/31475965/fastest-way-to-populate-qtableview-from-pandas-data-frame
23
+ """
24
+
25
+ def __init__(self, data):
26
+ QAbstractTableModel.__init__(self)
27
+ self._data = data
28
+ self.colors = dict()
29
+
30
+ def rowCount(self, parent=None):
31
+ return self._data.shape[0]
32
+
33
+ def columnCount(self, parent=None):
34
+ return self._data.shape[1]
35
+
36
+ def data(self, index, role=Qt.DisplayRole):
37
+ if index.isValid():
38
+ if role == Qt.DisplayRole:
39
+ return str(self._data.iloc[index.row(), index.column()])
40
+ if role == Qt.BackgroundRole:
41
+ color = self.colors.get((index.row(), index.column()))
42
+ if color is not None:
43
+ return color
44
+ return None
45
+
46
+ def headerData(self, rowcol, orientation, role):
47
+ if orientation == Qt.Horizontal and role == Qt.DisplayRole:
48
+ return self._data.columns[rowcol]
49
+ if orientation == Qt.Vertical and role == Qt.DisplayRole:
50
+ return self._data.index[rowcol]
51
+ return None
52
+
53
+ def change_color(self, row, column, color):
54
+ ix = self.index(row, column)
55
+ self.colors[(row, column)] = color
56
+ self.dataChanged.emit(ix, ix, (Qt.BackgroundRole,))
57
+
58
+
59
+ class GenericOpColWidget(QWidget, Styles):
60
+
61
+ def __init__(self, parent_window, column=None, title=''):
62
+
63
+ super().__init__()
64
+
65
+ self.parent_window = parent_window
66
+ self.column = column
67
+ self.title = title
68
+
69
+ self.setWindowTitle(self.title)
70
+ # Create the QComboBox and add some items
71
+
72
+ self.layout = QVBoxLayout(self)
73
+ self.layout.setContentsMargins(30,30,30,30)
74
+
75
+ self.sublayout = QVBoxLayout()
76
+
77
+ self.measurements_cb = QComboBox()
78
+ self.measurements_cb.addItems(list(self.parent_window.data.columns))
79
+ if self.column is not None:
80
+ idx = self.measurements_cb.findText(self.column)
81
+ self.measurements_cb.setCurrentIndex(idx)
82
+
83
+ measurement_layout = QHBoxLayout()
84
+ measurement_layout.addWidget(QLabel('measurements: '), 25)
85
+ measurement_layout.addWidget(self.measurements_cb, 75)
86
+ self.sublayout.addLayout(measurement_layout)
87
+
88
+ self.layout.addLayout(self.sublayout)
89
+
90
+ self.submit_btn = QPushButton('Compute')
91
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
92
+ self.submit_btn.clicked.connect(self.launch_operation)
93
+ self.layout.addWidget(self.submit_btn, 30)
94
+
95
+ self.setAttribute(Qt.WA_DeleteOnClose)
96
+ center_window(self)
97
+
98
+ def launch_operation(self):
99
+
100
+ self.compute()
101
+ self.parent_window.model = PandasModel(self.parent_window.data)
102
+ self.parent_window.table_view.setModel(self.parent_window.model)
103
+ self.close()
104
+
105
+ def compute(self):
106
+ pass
107
+
19
108
 
20
109
  class QuickSliderLayout(QHBoxLayout):
21
110
 
@@ -8,7 +8,7 @@ from superqt import QLabeledRangeSlider, QLabeledDoubleSlider, QLabeledSlider, Q
8
8
  from superqt.fonticon import icon
9
9
  from fonticon_mdi6 import MDI6
10
10
  from celldetective.utils import _extract_channel_indices_from_config
11
- from celldetective.gui.viewers import ThresholdedStackVisualizer, CellEdgeVisualizer, StackVisualizer, CellSizeViewer
11
+ from celldetective.gui.viewers import ThresholdedStackVisualizer, CellEdgeVisualizer, StackVisualizer, CellSizeViewer, ChannelOffsetViewer
12
12
  from celldetective.gui import Styles
13
13
  from celldetective.preprocessing import correct_background_model, correct_background_model_free, estimate_background_per_condition
14
14
  from functools import partial
@@ -535,7 +535,6 @@ class BackgroundFitCorrectionLayout(QGridLayout, Styles):
535
535
  self.threshold_viewer_btn.clicked.connect(self.set_threshold_graphically)
536
536
  self.threshold_viewer_btn.setToolTip('Set the threshold graphically.')
537
537
 
538
-
539
538
  self.model_lbl = QLabel('Model: ')
540
539
  self.model_lbl.setToolTip('2D model to fit the background with.')
541
540
  self.models_cb = QComboBox()
@@ -560,6 +559,8 @@ class BackgroundFitCorrectionLayout(QGridLayout, Styles):
560
559
  self.corrected_stack_viewer,
561
560
  self.add_correction_btn
562
561
  ])
562
+
563
+
563
564
  def add_to_layout(self):
564
565
 
565
566
  channel_layout = QHBoxLayout()
@@ -572,6 +573,7 @@ class BackgroundFitCorrectionLayout(QGridLayout, Styles):
572
573
  subthreshold_layout = QHBoxLayout()
573
574
  subthreshold_layout.addWidget(self.threshold_le, 95)
574
575
  subthreshold_layout.addWidget(self.threshold_viewer_btn, 5)
576
+
575
577
  threshold_layout.addLayout(subthreshold_layout, 75)
576
578
  self.addLayout(threshold_layout, 1, 0, 1, 3)
577
579
 
@@ -878,18 +880,26 @@ class ProtocolDesignerLayout(QVBoxLayout, Styles):
878
880
 
879
881
  def generate_layout(self):
880
882
 
883
+ self.correction_layout = QVBoxLayout()
884
+
885
+ self.background_correction_layout = QVBoxLayout()
886
+ self.background_correction_layout.setContentsMargins(0,0,0,0)
881
887
  self.title_layout = QHBoxLayout()
882
888
  self.title_layout.addWidget(self.title_lbl, 100, alignment=Qt.AlignCenter)
889
+ self.background_correction_layout.addLayout(self.title_layout)
890
+ self.background_correction_layout.addWidget(self.tabs)
891
+ self.correction_layout.addLayout(self.background_correction_layout)
892
+
893
+ self.addLayout(self.correction_layout)
883
894
 
884
- self.addLayout(self.title_layout)
885
- self.addWidget(self.tabs)
886
-
895
+ self.list_layout = QVBoxLayout()
887
896
  list_header_layout = QHBoxLayout()
888
897
  list_header_layout.addWidget(self.protocol_list_lbl)
889
898
  list_header_layout.addWidget(self.delete_protocol_btn, alignment=Qt.AlignRight)
890
- self.addLayout(list_header_layout)
899
+ self.list_layout.addLayout(list_header_layout)
900
+ self.list_layout.addWidget(self.protocol_list)
891
901
 
892
- self.addWidget(self.protocol_list)
902
+ self.addLayout(self.list_layout)
893
903
 
894
904
 
895
905
  def remove_protocol_from_list(self):
@@ -899,6 +909,117 @@ class ProtocolDesignerLayout(QVBoxLayout, Styles):
899
909
  del self.protocols[current_item]
900
910
  self.protocol_list.takeItem(current_item)
901
911
 
912
+ class ChannelOffsetOptionsLayout(QVBoxLayout, Styles):
913
+
914
+ def __init__(self, parent_window=None, *args, **kwargs):
915
+
916
+ super().__init__(*args, **kwargs)
917
+
918
+ self.parent_window = parent_window
919
+ if hasattr(self.parent_window.parent_window, 'exp_config'):
920
+ self.attr_parent = self.parent_window.parent_window
921
+ else:
922
+ self.attr_parent = self.parent_window.parent_window.parent_window
923
+
924
+ self.channel_names = self.attr_parent.exp_channels
925
+
926
+ self.setContentsMargins(15,15,15,15)
927
+ self.generate_widgets()
928
+ self.add_to_layout()
929
+
930
+ def generate_widgets(self):
931
+
932
+ self.channel_lbl = QLabel('Channel: ')
933
+ self.channels_cb = QComboBox()
934
+ self.channels_cb.addItems(self.channel_names)
935
+
936
+ self.shift_lbl = QLabel('Shift: ')
937
+ self.shift_h_lbl = QLabel('(h): ')
938
+ self.shift_v_lbl = QLabel('(v): ')
939
+
940
+ self.set_shift_btn = QPushButton()
941
+ self.set_shift_btn.setIcon(icon(MDI6.image_check, color="k"))
942
+ self.set_shift_btn.setStyleSheet(self.button_select_all)
943
+ self.set_shift_btn.setToolTip('Set the channel shift.')
944
+ self.set_shift_btn.clicked.connect(self.open_offset_viewer)
945
+
946
+ self.add_correction_btn = QPushButton('Add correction')
947
+ self.add_correction_btn.setStyleSheet(self.button_style_sheet_2)
948
+ self.add_correction_btn.setIcon(icon(MDI6.plus, color="#1565c0"))
949
+ self.add_correction_btn.setToolTip('Add correction.')
950
+ self.add_correction_btn.setIconSize(QSize(25, 25))
951
+ self.add_correction_btn.clicked.connect(self.add_instructions_to_parent_list)
952
+
953
+ self.vertical_shift_le = ThresholdLineEdit(init_value=0, connected_buttons=[self.add_correction_btn],placeholder='vertical shift [pixels]', value_type='float')
954
+ self.horizontal_shift_le = ThresholdLineEdit(init_value=0, connected_buttons=[self.add_correction_btn],placeholder='vertical shift [pixels]', value_type='float')
955
+
956
+ def add_to_layout(self):
957
+
958
+ channel_ch_hbox = QHBoxLayout()
959
+ channel_ch_hbox.addWidget(self.channel_lbl, 25)
960
+ channel_ch_hbox.addWidget(self.channels_cb, 75)
961
+ self.addLayout(channel_ch_hbox)
962
+
963
+ shift_hbox = QHBoxLayout()
964
+ shift_hbox.addWidget(self.shift_lbl, 25)
965
+
966
+ shift_subhbox = QHBoxLayout()
967
+ shift_subhbox.addWidget(self.shift_h_lbl, 10)
968
+ shift_subhbox.addWidget(self.horizontal_shift_le, 75//2)
969
+ shift_subhbox.addWidget(self.shift_v_lbl, 10)
970
+ shift_subhbox.addWidget(self.vertical_shift_le, 75//2)
971
+ shift_subhbox.addWidget(self.set_shift_btn, 5)
972
+
973
+ shift_hbox.addLayout(shift_subhbox, 75)
974
+ self.addLayout(shift_hbox)
975
+
976
+ btn_hbox = QHBoxLayout()
977
+ btn_hbox.addWidget(self.add_correction_btn, 95)
978
+ self.addLayout(btn_hbox)
979
+
980
+ def add_instructions_to_parent_list(self):
981
+
982
+ self.generate_instructions()
983
+ self.parent_window.protocol_layout.protocols.append(self.instructions)
984
+ correction_description = ""
985
+ for index, (key, value) in enumerate(self.instructions.items()):
986
+ if index > 0:
987
+ correction_description += ", "
988
+ correction_description += str(key) + " : " + str(value)
989
+ self.parent_window.protocol_layout.protocol_list.addItem(correction_description)
990
+
991
+ def generate_instructions(self):
992
+
993
+ self.instructions = {
994
+ "correction_type": "offset",
995
+ "target_channel": self.channels_cb.currentText(),
996
+ "correction_horizontal": self.horizontal_shift_le.get_threshold(),
997
+ "correction_vertical": self.vertical_shift_le.get_threshold(),
998
+ }
999
+
1000
+
1001
+ def set_target_channel(self):
1002
+
1003
+ channel_indices = _extract_channel_indices_from_config(self.attr_parent.exp_config, [self.channels_cb.currentText()])
1004
+ self.target_channel = channel_indices[0]
1005
+
1006
+ def open_offset_viewer(self):
1007
+
1008
+ self.attr_parent.locate_image()
1009
+ self.set_target_channel()
1010
+
1011
+ if self.attr_parent.current_stack is not None:
1012
+ self.viewer = ChannelOffsetViewer(
1013
+ parent_window = self,
1014
+ stack_path=self.attr_parent.current_stack,
1015
+ channel_names=self.attr_parent.exp_channels,
1016
+ n_channels=len(self.channel_names),
1017
+ channel_cb=True,
1018
+ target_channel=self.target_channel,
1019
+ window_title='offset viewer',
1020
+ )
1021
+ self.viewer.show()
1022
+
902
1023
 
903
1024
  class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
904
1025
 
@@ -154,7 +154,7 @@ class ConfigMeasurements(QMainWindow, Styles):
154
154
 
155
155
  grid = QGridLayout(self.iso_frame)
156
156
 
157
- self.iso_lbl = QLabel("ISOTROPIC MEASUREMENTS")
157
+ self.iso_lbl = QLabel("Position-based measurements".upper())
158
158
  self.iso_lbl.setStyleSheet("""
159
159
  font-weight: bold;
160
160
  padding: 0px;
@@ -171,7 +171,7 @@ class ConfigMeasurements(QMainWindow, Styles):
171
171
 
172
172
  grid = QGridLayout(self.features_frame)
173
173
 
174
- self.feature_lbl = QLabel("FEATURES")
174
+ self.feature_lbl = QLabel("Mask-based measurements".upper())
175
175
  self.feature_lbl.setStyleSheet("""
176
176
  font-weight: bold;
177
177
  padding: 0px;
@@ -262,7 +262,7 @@ class ConfigMeasurements(QMainWindow, Styles):
262
262
  self.add_feature_btn.setToolTip("Add feature")
263
263
  self.add_feature_btn.setIconSize(QSize(20, 20))
264
264
 
265
- self.features_list = ListWidget(FeatureChoice, initial_features=['area', 'intensity_mean', ])
265
+ self.features_list = ListWidget(FeatureChoice, initial_features=['area', 'intensity_nanmean', ])
266
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)
@@ -96,7 +96,7 @@ class ConfigSignalPlot(QWidget, Styles):
96
96
  main_layout.addWidget(panel_title, alignment=Qt.AlignCenter)
97
97
 
98
98
  labels = [QLabel('population: '), QLabel('class: '), QLabel('time of\ninterest: '), QLabel('cmap: ')]
99
- self.cb_options = [['targets','effectors'],['class'], ['t0'], []]
99
+ self.cb_options = [['targets','effectors'],[], [], []]
100
100
  self.cbs = [QComboBox() for i in range(len(labels))]
101
101
  self.cbs[-1] = QColormapComboBox()
102
102
 
@@ -183,6 +183,11 @@ class ConfigSignalPlot(QWidget, Styles):
183
183
  self.auto_close = True
184
184
  return None
185
185
 
186
+ if 'class' in self.all_columns:
187
+ class_columns.append("class")
188
+ if 't0' in self.all_columns:
189
+ time_columns.append('t0')
190
+
186
191
  self.cbs[2].clear()
187
192
  self.cbs[2].addItems(np.unique(self.cb_options[2]+time_columns))
188
193
 
@@ -339,7 +344,7 @@ class ConfigSignalPlot(QWidget, Styles):
339
344
 
340
345
  for block,movie_group in self.df.groupby(['well','position']):
341
346
 
342
- well_signal_mean, well_std_mean, timeline_all, matrix_all = mean_signal(movie_group, self.feature_selected, class_col, time_col=time_col, class_value=[0,1], return_matrix=True, forced_max_duration=max_time)
347
+ well_signal_mean, well_std_mean, timeline_all, matrix_all = mean_signal(movie_group, self.feature_selected, class_col, time_col=time_col, class_value=None, return_matrix=True, forced_max_duration=max_time)
343
348
  well_signal_event, well_std_event, timeline_event, matrix_event = mean_signal(movie_group, self.feature_selected, class_col, time_col=time_col, class_value=[0], return_matrix=True, forced_max_duration=max_time)
344
349
  well_signal_no_event, well_std_no_event, timeline_no_event, matrix_no_event = mean_signal(movie_group, self.feature_selected, class_col, time_col=time_col, class_value=[1], return_matrix=True, forced_max_duration=max_time)
345
350
  self.mean_plots_timeline = timeline_all
@@ -354,7 +359,7 @@ class ConfigSignalPlot(QWidget, Styles):
354
359
  # Per well
355
360
  for well,well_group in self.df.groupby('well'):
356
361
 
357
- well_signal_mean, well_std_mean, timeline_all, matrix_all = mean_signal(well_group, self.feature_selected, class_col, time_col=time_col, class_value=[0,1], return_matrix=True, forced_max_duration=max_time)
362
+ well_signal_mean, well_std_mean, timeline_all, matrix_all = mean_signal(well_group, self.feature_selected, class_col, time_col=time_col, class_value=None, return_matrix=True, forced_max_duration=max_time)
358
363
  well_signal_event, well_std_event, timeline_event, matrix_event = mean_signal(well_group, self.feature_selected, class_col, time_col=time_col, class_value=[0], return_matrix=True, forced_max_duration=max_time)
359
364
  well_signal_no_event, well_std_no_event, timeline_no_event, matrix_no_event = mean_signal(well_group, self.feature_selected, class_col, time_col=time_col, class_value=[1], return_matrix=True, forced_max_duration=max_time)
360
365