celldetective 1.0.2__py3-none-any.whl → 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. celldetective/__main__.py +2 -2
  2. celldetective/events.py +2 -44
  3. celldetective/filters.py +4 -5
  4. celldetective/gui/__init__.py +1 -1
  5. celldetective/gui/analyze_block.py +37 -10
  6. celldetective/gui/btrack_options.py +24 -23
  7. celldetective/gui/classifier_widget.py +62 -19
  8. celldetective/gui/configure_new_exp.py +32 -35
  9. celldetective/gui/control_panel.py +115 -81
  10. celldetective/gui/gui_utils.py +674 -396
  11. celldetective/gui/json_readers.py +7 -6
  12. celldetective/gui/layouts.py +755 -0
  13. celldetective/gui/measurement_options.py +168 -487
  14. celldetective/gui/neighborhood_options.py +322 -270
  15. celldetective/gui/plot_measurements.py +1114 -0
  16. celldetective/gui/plot_signals_ui.py +20 -20
  17. celldetective/gui/process_block.py +449 -169
  18. celldetective/gui/retrain_segmentation_model_options.py +27 -26
  19. celldetective/gui/retrain_signal_model_options.py +25 -24
  20. celldetective/gui/seg_model_loader.py +31 -27
  21. celldetective/gui/signal_annotator.py +2326 -2295
  22. celldetective/gui/signal_annotator_options.py +18 -16
  23. celldetective/gui/styles.py +16 -1
  24. celldetective/gui/survival_ui.py +61 -39
  25. celldetective/gui/tableUI.py +60 -23
  26. celldetective/gui/thresholds_gui.py +68 -66
  27. celldetective/gui/viewers.py +596 -0
  28. celldetective/io.py +234 -23
  29. celldetective/measure.py +37 -32
  30. celldetective/neighborhood.py +495 -27
  31. celldetective/preprocessing.py +683 -0
  32. celldetective/scripts/analyze_signals.py +7 -0
  33. celldetective/scripts/measure_cells.py +12 -0
  34. celldetective/scripts/segment_cells.py +5 -0
  35. celldetective/scripts/track_cells.py +11 -0
  36. celldetective/signals.py +221 -98
  37. celldetective/tracking.py +0 -1
  38. celldetective/utils.py +178 -36
  39. celldetective-1.1.0.dist-info/METADATA +305 -0
  40. celldetective-1.1.0.dist-info/RECORD +80 -0
  41. {celldetective-1.0.2.dist-info → celldetective-1.1.0.dist-info}/top_level.txt +1 -0
  42. tests/__init__.py +0 -0
  43. tests/test_events.py +28 -0
  44. tests/test_filters.py +24 -0
  45. tests/test_io.py +70 -0
  46. tests/test_measure.py +141 -0
  47. tests/test_neighborhood.py +70 -0
  48. tests/test_segmentation.py +93 -0
  49. tests/test_signals.py +135 -0
  50. tests/test_tracking.py +164 -0
  51. tests/test_utils.py +71 -0
  52. celldetective-1.0.2.dist-info/METADATA +0 -192
  53. celldetective-1.0.2.dist-info/RECORD +0 -66
  54. {celldetective-1.0.2.dist-info → celldetective-1.1.0.dist-info}/LICENSE +0 -0
  55. {celldetective-1.0.2.dist-info → celldetective-1.1.0.dist-info}/WHEEL +0 -0
  56. {celldetective-1.0.2.dist-info → celldetective-1.1.0.dist-info}/entry_points.txt +0 -0
celldetective/__main__.py CHANGED
@@ -15,10 +15,10 @@ class AppInitWindow(QMainWindow):
15
15
  Initial window to set the experiment folder or create a new one.
16
16
  """
17
17
 
18
- def __init__(self, parent=None):
18
+ def __init__(self, parent_window=None):
19
19
  super().__init__()
20
20
 
21
- self.parent = parent
21
+ self.parent_window = parent_window
22
22
  self.Styles = Styles()
23
23
  self.init_styles()
24
24
  self.setWindowTitle("celldetective")
celldetective/events.py CHANGED
@@ -1,49 +1,7 @@
1
1
  import numpy as np
2
2
 
3
- def switch_to_events(classes, times, max_times, first_detections=None, left_censored=False, FrameToMin=None):
4
-
5
- events = []
6
- survival_times = []
7
- if first_detections is None:
8
- first_detections = np.zeros_like(max_times)
9
-
10
- for c,t,mt,ft in zip(classes, times, max_times, first_detections):
11
-
12
- if left_censored:
13
- #print('left censored is True: exclude cells that exist in first frame')
14
- if ft>0.:
15
- if c==0:
16
- if t>0:
17
- dt = t - ft
18
- #print('event: dt = ',dt, t, ft)
19
- if dt>0:
20
- events.append(1)
21
- survival_times.append(dt)
22
- elif c==1:
23
- dt = mt - ft
24
- if dt>0:
25
- events.append(0)
26
- survival_times.append(dt)
27
- else:
28
- pass
29
- else:
30
- if c==0:
31
- if t>0:
32
- events.append(1)
33
- survival_times.append(t - ft)
34
- elif c==1:
35
- events.append(0)
36
- survival_times.append(mt - ft)
37
- else:
38
- pass
39
3
 
40
- if FrameToMin is not None:
41
- print('convert to minutes!', FrameToMin)
42
- survival_times = [s*FrameToMin for s in survival_times]
43
- return events, survival_times
44
-
45
-
46
- def switch_to_events_v2(classes, event_times, max_times, origin_times=None, left_censored=True, FrameToMin=None):
4
+ def switch_to_events(classes, event_times, max_times, origin_times=None, left_censored=True, FrameToMin=None):
47
5
 
48
6
 
49
7
  """
@@ -144,6 +102,6 @@ def switch_to_events_v2(classes, event_times, max_times, origin_times=None, left
144
102
  pass
145
103
 
146
104
  if FrameToMin is not None:
147
- print('convert to minutes!', FrameToMin)
105
+ #print('convert to minutes!', FrameToMin)
148
106
  survival_times = [s*FrameToMin for s in survival_times]
149
107
  return events, survival_times
celldetective/filters.py CHANGED
@@ -35,8 +35,8 @@ def variance_filter(img, size):
35
35
 
36
36
  size = int(size)
37
37
  img = img.astype(float)
38
- win_mean = snd.uniform_filter(img, (size,size))
39
- win_sqr_mean = snd.uniform_filter(img**2, (size,size))
38
+ win_mean = snd.uniform_filter(img, (size,size), mode='wrap')
39
+ win_sqr_mean = snd.uniform_filter(img**2, (size,size), mode='wrap')
40
40
  img = win_sqr_mean - win_mean**2
41
41
 
42
42
  return img
@@ -45,8 +45,8 @@ def std_filter(img, size):
45
45
 
46
46
  size = int(size)
47
47
  img = img.astype(float)
48
- win_mean = snd.uniform_filter(img, (size,size))
49
- win_sqr_mean = snd.uniform_filter(img**2, (size, size))
48
+ win_mean = snd.uniform_filter(img, (size,size), mode='wrap')
49
+ win_sqr_mean = snd.uniform_filter(img**2, (size, size), mode='wrap')
50
50
  win_sqr_mean[win_sqr_mean!=win_sqr_mean] = 0.
51
51
  win_sqr_mean[win_sqr_mean<=0.] = 0. # add this to prevent sqrt from breaking
52
52
  img = np.sqrt(win_sqr_mean - win_mean**2)
@@ -65,7 +65,6 @@ def otsu_filter(img, *kwargs):
65
65
  return binary.astype(float)
66
66
 
67
67
  def local_filter(img, *kwargs):
68
- print(*kwargs)
69
68
  thresh = threshold_local(img.astype(float), *kwargs)
70
69
  binary = img >= thresh
71
70
  return binary.astype(float)
@@ -13,7 +13,7 @@ from .retrain_signal_model_options import ConfigSignalModelTraining
13
13
  from .retrain_segmentation_model_options import ConfigSegmentationModelTraining
14
14
  from .thresholds_gui import ThresholdConfigWizard
15
15
  from .seg_model_loader import SegmentationModelLoader
16
- from .process_block import ProcessPanel, NeighPanel
16
+ from .process_block import ProcessPanel, NeighPanel, PreprocessingPanel
17
17
  from .analyze_block import AnalysisPanel
18
18
  from .control_panel import ControlPanel
19
19
  from .configure_new_exp import ConfigNewExperiment
@@ -1,10 +1,12 @@
1
- from PyQt5.QtWidgets import QFrame, QGridLayout, QComboBox, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, QMessageBox
1
+ from PyQt5.QtWidgets import QFrame, QGridLayout, QComboBox, QSizePolicy, QSpacerItem, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, QMessageBox
2
2
  from PyQt5.QtCore import Qt, QSize
3
3
  from PyQt5.QtGui import QIcon
4
4
 
5
5
  from superqt.fonticon import icon
6
6
  from fonticon_mdi6 import MDI6
7
7
  import gc
8
+
9
+ from celldetective.gui.plot_measurements import ConfigMeasurementsPlot
8
10
  from celldetective.io import get_segmentation_models_list, control_segmentation_napari, get_signal_models_list, control_tracking_btrack
9
11
  from celldetective.gui import ConfigSurvival, ConfigSignalPlot
10
12
  from celldetective.gui.gui_utils import QHSeperationLine
@@ -17,21 +19,23 @@ from glob import glob
17
19
  from natsort import natsorted
18
20
  import os
19
21
  import pandas as pd
22
+ from celldetective.gui import Styles
20
23
 
21
- class AnalysisPanel(QFrame):
22
- def __init__(self, parent, title=None):
24
+ class AnalysisPanel(QFrame, Styles):
25
+ def __init__(self, parent_window, title=None):
23
26
 
24
27
  super().__init__()
25
- self.parent = parent
28
+ self.parent_window = parent_window
26
29
  self.title = title
27
30
  if self.title is None:
28
31
  self.title=''
29
- self.exp_channels = self.parent.exp_channels
30
- self.exp_dir = self.parent.exp_dir
31
- self.soft_path = self.parent.parent.soft_path
32
+ self.exp_channels = self.parent_window.exp_channels
33
+ self.exp_dir = self.parent_window.exp_dir
34
+ self.soft_path = self.parent_window.parent_window.soft_path
32
35
 
33
36
  self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
34
37
  self.grid = QVBoxLayout(self)
38
+ self.grid.setSpacing(20)
35
39
  self.generate_header()
36
40
 
37
41
  def generate_header(self):
@@ -41,7 +45,7 @@ class AnalysisPanel(QFrame):
41
45
 
42
46
  """
43
47
 
44
- panel_title = QLabel(self.title)
48
+ panel_title = QLabel("Survival")
45
49
  panel_title.setStyleSheet("""
46
50
  font-weight: bold;
47
51
  padding: 0px;
@@ -51,19 +55,37 @@ class AnalysisPanel(QFrame):
51
55
 
52
56
  self.survival_btn = QPushButton("plot survival")
53
57
  self.survival_btn.setIcon(QIcon(QIcon(os.sep.join([self.soft_path,'celldetective','icons','survival2.png']))))
54
- self.survival_btn.setStyleSheet(self.parent.parent.button_style_sheet_2)
58
+ self.survival_btn.setStyleSheet(self.button_style_sheet_2)
55
59
  self.survival_btn.setIconSize(QSize(35, 35))
56
60
  self.survival_btn.clicked.connect(self.configure_survival)
57
61
  self.grid.addWidget(self.survival_btn)
58
62
 
63
+ signal_lbl = QLabel("Single-cell signals")
64
+ signal_lbl.setStyleSheet("""
65
+ font-weight: bold;
66
+ padding: 0px;
67
+ """)
68
+
69
+ self.grid.addWidget(signal_lbl, alignment=Qt.AlignCenter)
59
70
 
60
71
  self.plot_signal_btn = QPushButton("plot signals")
61
72
  self.plot_signal_btn.setIcon(QIcon(QIcon(os.sep.join([self.soft_path,'celldetective','icons','signals_icon.png']))))
62
- self.plot_signal_btn.setStyleSheet(self.parent.parent.button_style_sheet_2)
73
+ self.plot_signal_btn.setStyleSheet(self.button_style_sheet_2)
63
74
  self.plot_signal_btn.setIconSize(QSize(35, 35))
64
75
  self.plot_signal_btn.clicked.connect(self.configure_plot_signals)
65
76
  self.grid.addWidget(self.plot_signal_btn)
66
77
 
78
+ # self.plot_measurements_btn = QPushButton("plot measurements")
79
+ # self.plot_measurements_btn.setIcon(icon(MDI6.chart_waterfall, color='black'))
80
+ # #self.plot_measurements_btn.setIcon(QIcon(QIcon(os.sep.join([self.soft_path,'celldetective','icons','signals_icon.png']))))
81
+ # self.plot_measurements_btn.setStyleSheet(self.button_style_sheet_2)
82
+ # self.plot_measurements_btn.setIconSize(QSize(35, 35))
83
+ # self.plot_measurements_btn.clicked.connect(self.configure_plot_measurements)
84
+ # self.grid.addWidget(self.plot_measurements_btn)
85
+
86
+
87
+ verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
88
+ self.grid.addItem(verticalSpacer)
67
89
 
68
90
  def configure_survival(self):
69
91
  print('survival analysis starting!!!')
@@ -75,6 +97,11 @@ class AnalysisPanel(QFrame):
75
97
  self.ConfigSignalPlot = ConfigSignalPlot(self)
76
98
  self.ConfigSignalPlot.show()
77
99
 
100
+ def configure_plot_measurements(self):
101
+ print('plot measurements analysis starting!!!')
102
+ self.ConfigMeasurementsPLot = ConfigMeasurementsPlot(self)
103
+ self.ConfigMeasurementsPLot.show()
104
+
78
105
 
79
106
  # self.select_all_btn = QPushButton()
80
107
  # self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
@@ -14,27 +14,28 @@ import os
14
14
  import matplotlib.pyplot as plt
15
15
  from mpl_toolkits.axes_grid1 import make_axes_locatable
16
16
  from glob import glob
17
+ from celldetective.gui import Styles
17
18
 
18
- class ConfigTracking(QMainWindow):
19
+ class ConfigTracking(QMainWindow, Styles):
19
20
 
20
21
  """
21
22
  UI to set tracking parameters for bTrack.
22
23
 
23
24
  """
24
25
 
25
- def __init__(self, parent=None):
26
+ def __init__(self, parent_window=None):
26
27
 
27
28
  super().__init__()
28
- self.parent = parent
29
+ self.parent_window = parent_window
29
30
  self.setWindowTitle("Configure tracking")
30
- self.mode = self.parent.mode
31
- self.exp_dir = self.parent.exp_dir
31
+ self.mode = self.parent_window.mode
32
+ self.exp_dir = self.parent_window.exp_dir
32
33
  if self.mode=="targets":
33
34
  self.config_name = os.sep.join(["configs", "btrack_config_targets.json"])
34
- self.track_instructions_write_path = self.parent.exp_dir + os.sep.join(["configs","tracking_instructions_targets.json"])
35
+ self.track_instructions_write_path = self.parent_window.exp_dir + os.sep.join(["configs","tracking_instructions_targets.json"])
35
36
  elif self.mode=="effectors":
36
37
  self.config_name = os.sep.join(["configs","btrack_config_effectors.json"])
37
- self.track_instructions_write_path = self.parent.exp_dir + os.sep.join(["configs", "tracking_instructions_effectors.json"])
38
+ self.track_instructions_write_path = self.parent_window.exp_dir + os.sep.join(["configs", "tracking_instructions_effectors.json"])
38
39
  self.soft_path = get_software_location()
39
40
 
40
41
  exp_config = self.exp_dir +"config.ini"
@@ -42,7 +43,7 @@ class ConfigTracking(QMainWindow):
42
43
  self.channel_names, self.channels = extract_experiment_channels(exp_config)
43
44
  self.channel_names = np.array(self.channel_names)
44
45
  self.channels = np.array(self.channels)
45
- self.screen_height = self.parent.parent.parent.screen_height
46
+ self.screen_height = self.parent_window.parent_window.parent_window.screen_height
46
47
 
47
48
  center_window(self)
48
49
  self.setMinimumWidth(540)
@@ -84,7 +85,7 @@ class ConfigTracking(QMainWindow):
84
85
  main_layout.addWidget(self.post_proc_frame)
85
86
 
86
87
  self.submit_btn = QPushButton('Save')
87
- self.submit_btn.setStyleSheet(self.parent.parent.parent.button_style_sheet)
88
+ self.submit_btn.setStyleSheet(self.parent_window.parent_window.parent_window.button_style_sheet)
88
89
  self.submit_btn.clicked.connect(self.write_instructions)
89
90
  main_layout.addWidget(self.submit_btn)
90
91
 
@@ -114,7 +115,7 @@ class ConfigTracking(QMainWindow):
114
115
 
115
116
  self.select_post_proc_btn = QPushButton()
116
117
  self.select_post_proc_btn.clicked.connect(self.activate_post_proc_options)
117
- self.select_post_proc_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
118
+ self.select_post_proc_btn.setStyleSheet(self.button_select_all)
118
119
  grid.addWidget(self.select_post_proc_btn, 0,0,1,4,alignment=Qt.AlignLeft)
119
120
 
120
121
  self.post_proc_lbl = QLabel("POST-PROCESSING")
@@ -127,7 +128,7 @@ class ConfigTracking(QMainWindow):
127
128
  self.collapse_post_proc_btn = QPushButton()
128
129
  self.collapse_post_proc_btn.setIcon(icon(MDI6.chevron_down,color="black"))
129
130
  self.collapse_post_proc_btn.setIconSize(QSize(20, 20))
130
- self.collapse_post_proc_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
131
+ self.collapse_post_proc_btn.setStyleSheet(self.button_select_all)
131
132
  grid.addWidget(self.collapse_post_proc_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
132
133
 
133
134
  self.generate_post_proc_panel_contents()
@@ -148,7 +149,7 @@ class ConfigTracking(QMainWindow):
148
149
 
149
150
  self.select_features_btn = QPushButton()
150
151
  self.select_features_btn.clicked.connect(self.activate_feature_options)
151
- self.select_features_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
152
+ self.select_features_btn.setStyleSheet(self.button_select_all)
152
153
  grid.addWidget(self.select_features_btn, 0,0,1,4,alignment=Qt.AlignLeft)
153
154
 
154
155
 
@@ -162,7 +163,7 @@ class ConfigTracking(QMainWindow):
162
163
  self.collapse_features_btn = QPushButton()
163
164
  self.collapse_features_btn.setIcon(icon(MDI6.chevron_down,color="black"))
164
165
  self.collapse_features_btn.setIconSize(QSize(20, 20))
165
- self.collapse_features_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
166
+ self.collapse_features_btn.setStyleSheet(self.button_select_all)
166
167
  grid.addWidget(self.collapse_features_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
167
168
 
168
169
  self.generate_feature_panel_contents()
@@ -209,7 +210,7 @@ class ConfigTracking(QMainWindow):
209
210
  self.min_tracklength_slider.setTickInterval(1)
210
211
  self.min_tracklength_slider.setSingleStep(1)
211
212
  self.min_tracklength_slider.setOrientation(1)
212
- self.min_tracklength_slider.setRange(0,self.parent.parent.len_movie)
213
+ self.min_tracklength_slider.setRange(0,self.parent_window.parent_window.len_movie)
213
214
  self.min_tracklength_slider.setValue(0)
214
215
  tracklength_layout.addWidget(QLabel('Min. tracklength: '),40)
215
216
  tracklength_layout.addWidget(self.min_tracklength_slider, 60)
@@ -261,13 +262,13 @@ class ConfigTracking(QMainWindow):
261
262
 
262
263
  self.feature_lbl = QLabel("Add features:")
263
264
  self.del_feature_btn = QPushButton("")
264
- self.del_feature_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
265
+ self.del_feature_btn.setStyleSheet(self.button_select_all)
265
266
  self.del_feature_btn.setIcon(icon(MDI6.trash_can,color="black"))
266
267
  self.del_feature_btn.setToolTip("Remove feature")
267
268
  self.del_feature_btn.setIconSize(QSize(20, 20))
268
269
 
269
270
  self.add_feature_btn = QPushButton("")
270
- self.add_feature_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
271
+ self.add_feature_btn.setStyleSheet(self.button_select_all)
271
272
  self.add_feature_btn.setIcon(icon(MDI6.filter_plus,color="black"))
272
273
  self.add_feature_btn.setToolTip("Add feature")
273
274
  self.add_feature_btn.setIconSize(QSize(20, 20))
@@ -324,7 +325,7 @@ class ConfigTracking(QMainWindow):
324
325
  self.haralick_percentile_min_le = QLineEdit('0.01')
325
326
  self.haralick_percentile_max_le = QLineEdit('99.9')
326
327
  self.haralick_normalization_mode_btn = QPushButton()
327
- self.haralick_normalization_mode_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
328
+ self.haralick_normalization_mode_btn.setStyleSheet(self.button_select_all)
328
329
  self.haralick_normalization_mode_btn.setIcon(icon(MDI6.percent_circle,color="black"))
329
330
  self.haralick_normalization_mode_btn.setIconSize(QSize(20, 20))
330
331
  self.haralick_normalization_mode_btn.setToolTip("Switch to absolute normalization values.")
@@ -336,12 +337,12 @@ class ConfigTracking(QMainWindow):
336
337
  self.haralick_hist_btn = QPushButton()
337
338
  self.haralick_hist_btn.clicked.connect(self.control_haralick_intensity_histogram)
338
339
  self.haralick_hist_btn.setIcon(icon(MDI6.poll,color="k"))
339
- self.haralick_hist_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
340
+ self.haralick_hist_btn.setStyleSheet(self.button_select_all)
340
341
 
341
342
  self.haralick_digit_btn = QPushButton()
342
343
  self.haralick_digit_btn.clicked.connect(self.control_haralick_digitalization)
343
344
  self.haralick_digit_btn.setIcon(icon(MDI6.image_check,color="k"))
344
- self.haralick_digit_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
345
+ self.haralick_digit_btn.setStyleSheet(self.button_select_all)
345
346
 
346
347
  self.haralick_layout = QVBoxLayout()
347
348
  self.haralick_layout.setContentsMargins(20,20,20,20)
@@ -431,7 +432,7 @@ class ConfigTracking(QMainWindow):
431
432
  self.collapse_config_btn = QPushButton()
432
433
  self.collapse_config_btn.setIcon(icon(MDI6.chevron_down,color="black"))
433
434
  self.collapse_config_btn.setIconSize(QSize(20, 20))
434
- self.collapse_config_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
435
+ self.collapse_config_btn.setStyleSheet(self.button_select_all)
435
436
  grid.addWidget(self.collapse_config_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
436
437
 
437
438
  self.generate_config_panel_contents()
@@ -471,7 +472,7 @@ class ConfigTracking(QMainWindow):
471
472
  self.upload_btrack_config_btn.setIcon(icon(MDI6.plus,color="black"))
472
473
  self.upload_btrack_config_btn.setIconSize(QSize(20, 20))
473
474
  self.upload_btrack_config_btn.setToolTip("Upload a new bTrack configuration.")
474
- self.upload_btrack_config_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
475
+ self.upload_btrack_config_btn.setStyleSheet(self.button_select_all)
475
476
  self.upload_btrack_config_btn.clicked.connect(self.upload_btrack_config)
476
477
  btrack_config_layout.addWidget(self.upload_btrack_config_btn, 5) #4,3,1,1, alignment=Qt.AlignLeft
477
478
 
@@ -479,7 +480,7 @@ class ConfigTracking(QMainWindow):
479
480
  self.reset_config_btn.setIcon(icon(MDI6.arrow_u_right_top,color="black"))
480
481
  self.reset_config_btn.setIconSize(QSize(20, 20))
481
482
  self.reset_config_btn.setToolTip("Reset the configuration to the default bTrack config.")
482
- self.reset_config_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
483
+ self.reset_config_btn.setStyleSheet(self.button_select_all)
483
484
  self.reset_config_btn.clicked.connect(self.reset_btrack_config)
484
485
  btrack_config_layout.addWidget(self.reset_config_btn, 5) #4,3,1,1, alignment=Qt.AlignLeft
485
486
 
@@ -817,7 +818,7 @@ class ConfigTracking(QMainWindow):
817
818
  Load the first frame of the first movie found in the experiment folder as a sample.
818
819
  """
819
820
 
820
- movies = glob(self.parent.parent.exp_dir + os.sep.join(["*","*","movie",self.parent.parent.movie_prefix+"*.tif"]))
821
+ movies = glob(self.parent_window.parent_window.exp_dir + os.sep.join(["*","*","movie",self.parent_window.parent_window.movie_prefix+"*.tif"]))
821
822
  if len(movies)==0:
822
823
  msgBox = QMessageBox()
823
824
  msgBox.setIcon(QMessageBox.Warning)
@@ -1,5 +1,5 @@
1
1
  from PyQt5.QtWidgets import QWidget, QLineEdit, QMessageBox, QHBoxLayout, QVBoxLayout, QPushButton, QLabel, QComboBox, \
2
- QCheckBox
2
+ QCheckBox, QRadioButton, QButtonGroup
3
3
  from celldetective.gui.gui_utils import FigureCanvas, center_window, color_from_class
4
4
  import numpy as np
5
5
  import matplotlib.pyplot as plt
@@ -10,25 +10,27 @@ from PyQt5.QtCore import Qt, QSize
10
10
  import os
11
11
  from sklearn.metrics import r2_score
12
12
  from scipy.optimize import curve_fit
13
+ from celldetective.gui import Styles
14
+ from math import ceil
13
15
 
14
16
  def step_function(t, t_shift, dt):
15
17
  return 1/(1+np.exp(-(t-t_shift)/dt))
16
18
 
17
- class ClassifierWidget(QWidget):
19
+ class ClassifierWidget(QWidget, Styles):
18
20
 
19
- def __init__(self, parent):
21
+ def __init__(self, parent_window):
20
22
 
21
23
  super().__init__()
22
24
 
23
- self.parent = parent
24
- self.screen_height = self.parent.parent.parent.screen_height
25
- self.screen_width = self.parent.parent.parent.screen_width
25
+ self.parent_window = parent_window
26
+ self.screen_height = self.parent_window.parent_window.parent_window.screen_height
27
+ self.screen_width = self.parent_window.parent_window.parent_window.screen_width
26
28
  self.currentAlpha = 1.0
27
29
 
28
30
  self.setWindowTitle("Custom classification")
29
31
 
30
- self.mode = self.parent.mode
31
- self.df = self.parent.df
32
+ self.mode = self.parent_window.mode
33
+ self.df = self.parent_window.df
32
34
 
33
35
  is_number = np.vectorize(lambda x: np.issubdtype(x, np.number))
34
36
  is_number_test = is_number(self.df.dtypes)
@@ -53,7 +55,7 @@ class ClassifierWidget(QWidget):
53
55
  fig_btn_hbox = QHBoxLayout()
54
56
  fig_btn_hbox.addWidget(QLabel(''), 95)
55
57
  self.project_times_btn = QPushButton('')
56
- self.project_times_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
58
+ self.project_times_btn.setStyleSheet(self.parent_window.parent_window.parent_window.button_select_all)
57
59
  self.project_times_btn.setIcon(icon(MDI6.math_integral,color="black"))
58
60
  self.project_times_btn.setToolTip("Project measurements at all times.")
59
61
  self.project_times_btn.setIconSize(QSize(20, 20))
@@ -111,7 +113,7 @@ class ClassifierWidget(QWidget):
111
113
  self.features_cb[i].setCurrentIndex(i)
112
114
 
113
115
  self.log_btns[i].setIcon(icon(MDI6.math_log,color="black"))
114
- self.log_btns[i].setStyleSheet(self.parent.parent.parent.button_select_all)
116
+ self.log_btns[i].setStyleSheet(self.button_select_all)
115
117
  self.log_btns[i].clicked.connect(lambda ch, i=i: self.switch_to_log(i))
116
118
 
117
119
  hbox_classify = QHBoxLayout()
@@ -124,19 +126,47 @@ class ClassifierWidget(QWidget):
124
126
  hbox_classify.addWidget(self.submit_query_btn, 20)
125
127
  layout.addLayout(hbox_classify)
126
128
 
127
- self.time_corr = QCheckBox('Time correlated event')
129
+ self.time_corr = QCheckBox('Time correlated')
130
+ self.time_corr.toggled.connect(self.activate_time_corr_options)
128
131
  if "TRACK_ID" in self.df.columns:
129
132
  self.time_corr.setEnabled(True)
130
133
  else:
131
134
  self.time_corr.setEnabled(False)
132
135
  layout.addWidget(self.time_corr,alignment=Qt.AlignCenter)
136
+
137
+ self.irreversible_event_btn = QRadioButton('irreversible event')
138
+ self.unique_state_btn = QRadioButton('unique state')
139
+ self.time_corr_options = [self.irreversible_event_btn, self.unique_state_btn]
140
+ time_corr_btn_group = QButtonGroup()
141
+ self.unique_state_btn.click()
142
+ for btn in self.time_corr_options:
143
+ time_corr_btn_group.addButton(btn)
144
+ btn.setEnabled(False)
145
+
146
+ time_corr_layout = QHBoxLayout()
147
+ time_corr_layout.addWidget(self.unique_state_btn, 50, alignment=Qt.AlignCenter)
148
+ time_corr_layout.addWidget(self.irreversible_event_btn, 50,alignment=Qt.AlignCenter)
149
+ layout.addLayout(time_corr_layout)
150
+
151
+ layout.addWidget(QLabel())
152
+
133
153
  self.submit_btn = QPushButton('apply')
154
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
134
155
  self.submit_btn.clicked.connect(self.submit_classification)
135
156
  layout.addWidget(self.submit_btn, 30)
136
157
 
137
158
  self.frame_slider.valueChanged.connect(self.set_frame)
138
159
  self.alpha_slider.valueChanged.connect(self.set_transparency)
139
160
 
161
+ def activate_time_corr_options(self):
162
+
163
+ if self.time_corr.isChecked():
164
+ for btn in self.time_corr_options:
165
+ btn.setEnabled(True)
166
+ else:
167
+ for btn in self.time_corr_options:
168
+ btn.setEnabled(False)
169
+
140
170
  def init_class(self):
141
171
 
142
172
  self.class_name = 'custom'
@@ -277,14 +307,27 @@ class ClassifierWidget(QWidget):
277
307
  for tid,track in self.df.groupby(['position','TRACK_ID']):
278
308
  indices = track[self.class_name_user].index
279
309
  status_values = track[stat_col].to_numpy()
280
- if np.all([s==0 for s in status_values]):
281
- self.df.loc[indices, self.class_name_user] = 1
282
- elif np.all([s==1 for s in status_values]):
283
- self.df.loc[indices, self.class_name_user] = 2
284
- self.df.loc[indices, self.class_name_user.replace('class','status')] = 2
285
- else:
286
- self.df.loc[indices, self.class_name_user] = 2
287
- self.estimate_time()
310
+ if self.irreversible_event_btn.isChecked():
311
+ if np.all([s==0 for s in status_values]):
312
+ self.df.loc[indices, self.class_name_user] = 1
313
+ elif np.all([s==1 for s in status_values]):
314
+ self.df.loc[indices, self.class_name_user] = 2
315
+ self.df.loc[indices, self.class_name_user.replace('class','status')] = 2
316
+ else:
317
+ self.df.loc[indices, self.class_name_user] = 2
318
+ elif self.unique_state_btn.isChecked():
319
+ frames = track['FRAME'].to_numpy()
320
+ t_first = track['t_firstdetection'].to_numpy()[0]
321
+ median_status = np.nanmedian(status_values[frames>=t_first])
322
+ c = ceil(median_status)
323
+ if c==0:
324
+ self.df.loc[indices, self.class_name_user] = 1
325
+ self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
326
+ elif c==1:
327
+ self.df.loc[indices, self.class_name_user] = 2
328
+ self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
329
+ if self.irreversible_event_btn.isChecked():
330
+ self.estimate_time()
288
331
  else:
289
332
  self.group_name_user = 'group_' + self.name_le.text()
290
333
  print(f'User defined characteristic group name: {self.group_name_user}.')