celldetective 1.3.9.post5__py3-none-any.whl → 1.4.1__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 (94) hide show
  1. celldetective/__init__.py +0 -3
  2. celldetective/_version.py +1 -1
  3. celldetective/events.py +2 -4
  4. celldetective/exceptions.py +11 -0
  5. celldetective/extra_properties.py +132 -0
  6. celldetective/filters.py +7 -1
  7. celldetective/gui/InitWindow.py +37 -46
  8. celldetective/gui/__init__.py +3 -9
  9. celldetective/gui/about.py +19 -15
  10. celldetective/gui/analyze_block.py +34 -19
  11. celldetective/gui/base_annotator.py +786 -0
  12. celldetective/gui/base_components.py +23 -0
  13. celldetective/gui/classifier_widget.py +86 -94
  14. celldetective/gui/configure_new_exp.py +163 -46
  15. celldetective/gui/control_panel.py +76 -146
  16. celldetective/gui/{signal_annotator.py → event_annotator.py} +533 -1438
  17. celldetective/gui/generic_signal_plot.py +11 -13
  18. celldetective/gui/gui_utils.py +54 -23
  19. celldetective/gui/help/neighborhood.json +2 -2
  20. celldetective/gui/json_readers.py +5 -4
  21. celldetective/gui/layouts.py +265 -31
  22. celldetective/gui/{signal_annotator2.py → pair_event_annotator.py} +433 -635
  23. celldetective/gui/plot_measurements.py +21 -17
  24. celldetective/gui/plot_signals_ui.py +125 -72
  25. celldetective/gui/process_block.py +283 -188
  26. celldetective/gui/processes/compute_neighborhood.py +594 -0
  27. celldetective/gui/processes/downloader.py +37 -34
  28. celldetective/gui/processes/measure_cells.py +19 -8
  29. celldetective/gui/processes/segment_cells.py +47 -11
  30. celldetective/gui/processes/track_cells.py +18 -13
  31. celldetective/gui/seg_model_loader.py +21 -62
  32. celldetective/gui/settings/__init__.py +7 -0
  33. celldetective/gui/settings/_settings_base.py +70 -0
  34. celldetective/gui/{retrain_signal_model_options.py → settings/_settings_event_model_training.py} +54 -109
  35. celldetective/gui/{measurement_options.py → settings/_settings_measurements.py} +54 -92
  36. celldetective/gui/{neighborhood_options.py → settings/_settings_neighborhood.py} +10 -13
  37. celldetective/gui/settings/_settings_segmentation.py +49 -0
  38. celldetective/gui/{retrain_segmentation_model_options.py → settings/_settings_segmentation_model_training.py} +38 -92
  39. celldetective/gui/{signal_annotator_options.py → settings/_settings_signal_annotator.py} +78 -103
  40. celldetective/gui/{btrack_options.py → settings/_settings_tracking.py} +85 -116
  41. celldetective/gui/styles.py +2 -1
  42. celldetective/gui/survival_ui.py +49 -95
  43. celldetective/gui/tableUI.py +53 -25
  44. celldetective/gui/table_ops/__init__.py +0 -0
  45. celldetective/gui/table_ops/merge_groups.py +118 -0
  46. celldetective/gui/thresholds_gui.py +617 -1221
  47. celldetective/gui/viewers.py +107 -42
  48. celldetective/gui/workers.py +8 -4
  49. celldetective/io.py +137 -57
  50. celldetective/links/zenodo.json +145 -144
  51. celldetective/measure.py +94 -53
  52. celldetective/neighborhood.py +342 -268
  53. celldetective/preprocessing.py +56 -35
  54. celldetective/regionprops/_regionprops.py +16 -5
  55. celldetective/relative_measurements.py +50 -29
  56. celldetective/scripts/analyze_signals.py +4 -1
  57. celldetective/scripts/measure_cells.py +5 -5
  58. celldetective/scripts/measure_relative.py +20 -12
  59. celldetective/scripts/segment_cells.py +4 -10
  60. celldetective/scripts/segment_cells_thresholds.py +3 -3
  61. celldetective/scripts/track_cells.py +10 -8
  62. celldetective/scripts/train_segmentation_model.py +18 -6
  63. celldetective/signals.py +29 -14
  64. celldetective/tracking.py +14 -3
  65. celldetective/utils.py +91 -62
  66. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/METADATA +24 -16
  67. celldetective-1.4.1.dist-info/RECORD +123 -0
  68. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/WHEEL +1 -1
  69. tests/gui/__init__.py +0 -0
  70. tests/gui/test_new_project.py +228 -0
  71. tests/gui/test_project.py +99 -0
  72. tests/test_preprocessing.py +2 -2
  73. celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -79
  74. celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
  75. celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -37
  76. celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -39
  77. celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
  78. celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
  79. celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
  80. celldetective/models/signal_detection/NucCond/config_input.json +0 -1
  81. celldetective/models/signal_detection/NucCond/log_classifier.csv +0 -126
  82. celldetective/models/signal_detection/NucCond/log_regressor.csv +0 -282
  83. celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
  84. celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
  85. celldetective/models/signal_detection/NucCond/scores.npy +0 -0
  86. celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
  87. celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
  88. celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
  89. celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
  90. celldetective-1.3.9.post5.dist-info/RECORD +0 -129
  91. tests/test_qt.py +0 -103
  92. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/entry_points.txt +0 -0
  93. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info/licenses}/LICENSE +0 -0
  94. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/top_level.txt +0 -0
@@ -1,24 +1,26 @@
1
1
  from PyQt5.QtWidgets import QDialog, QFrame, QGridLayout, QComboBox, QListWidget, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, \
2
- QMessageBox, QWidget
2
+ QMessageBox
3
3
  from PyQt5.QtCore import Qt, QSize
4
4
  from superqt.fonticon import icon
5
5
  from fonticon_mdi6 import MDI6
6
6
  import gc
7
7
  from PyQt5.QtGui import QDoubleValidator, QIntValidator
8
8
 
9
- from celldetective.gui.signal_annotator import MeasureAnnotator
10
- from celldetective.gui.signal_annotator2 import SignalAnnotator2
9
+ from celldetective.gui.processes.compute_neighborhood import NeighborhoodProcess
10
+ from celldetective.gui.event_annotator import MeasureAnnotator
11
11
  from celldetective.io import get_segmentation_models_list, control_segmentation_napari, get_signal_models_list, \
12
12
  control_tracks, load_experiment_tables, get_pair_signal_models_list
13
- from celldetective.io import locate_segmentation_model, extract_position_name, fix_missing_labels, auto_load_number_of_frames, load_frames, locate_signal_model
14
- from celldetective.gui import SegmentationModelLoader, ClassifierWidget, ConfigNeighborhoods, ConfigSegmentationModelTraining, ConfigTracking, SignalAnnotator, ConfigSignalModelTraining, ConfigMeasurements, ConfigSignalAnnotator, TableUI
13
+ from celldetective.io import locate_segmentation_model, extract_position_name, fix_missing_labels, locate_signal_model
14
+ from celldetective.gui import SegmentationModelLoader, ClassifierWidget, \
15
+ EventAnnotator, TableUI, CelldetectiveWidget, PairEventAnnotator
16
+
17
+ from celldetective.gui.settings import SettingsSegmentation, SettingsMeasurements, SettingsTracking, \
18
+ SettingsSignalAnnotator, SettingsNeighborhood, SettingsSegmentationModelTraining, SettingsEventDetectionModelTraining
19
+
15
20
  from celldetective.gui.gui_utils import QHSeperationLine
16
21
  from celldetective.relative_measurements import rel_measure_at_position
17
- from celldetective.segmentation import segment_at_position, segment_from_threshold_at_position
18
- from celldetective.tracking import track_at_position
19
- from celldetective.measure import measure_at_position
20
22
  from celldetective.signals import analyze_signals_at_position, analyze_pair_signals_at_position
21
- from celldetective.utils import extract_experiment_channels
23
+ from celldetective.utils import extract_experiment_channels, remove_file_if_exists
22
24
  import numpy as np
23
25
  from glob import glob
24
26
  from natsort import natsorted
@@ -27,13 +29,9 @@ import pandas as pd
27
29
  from celldetective.gui.gui_utils import center_window
28
30
  from tifffile import imwrite
29
31
  import json
30
- import psutil
31
- from celldetective.neighborhood import compute_neighborhood_at_position, compute_contact_neighborhood_at_position
32
- from celldetective.gui.gui_utils import FigureCanvas
33
32
  from celldetective.preprocessing import correct_background_model_free, correct_background_model, correct_channel_offset
34
- from celldetective.utils import _estimate_scale_factor, _extract_channel_indices_from_config, _extract_channel_indices, ConfigSectionMap, _extract_nbr_channels_from_config, _get_img_num_per_channel, normalize_per_channel
35
- from celldetective.gui.gui_utils import ThresholdLineEdit, QuickSliderLayout, help_generic
36
- from celldetective.gui.layouts import CellposeParamsWidget, StarDistParamsWidget, BackgroundModelFreeCorrectionLayout, ProtocolDesignerLayout, BackgroundFitCorrectionLayout, ChannelOffsetOptionsLayout
33
+ from celldetective.gui.gui_utils import help_generic
34
+ from celldetective.gui.layouts import SignalModelParamsWidget, SegModelParamsWidget, CellposeParamsWidget, StarDistParamsWidget, BackgroundModelFreeCorrectionLayout, ProtocolDesignerLayout, BackgroundFitCorrectionLayout, ChannelOffsetOptionsLayout
37
35
  from celldetective.gui import Styles
38
36
  from celldetective.utils import get_software_location
39
37
 
@@ -42,10 +40,8 @@ from celldetective.gui.processes.segment_cells import SegmentCellThresholdProces
42
40
  from celldetective.gui.processes.track_cells import TrackingProcess
43
41
  from celldetective.gui.processes.measure_cells import MeasurementProcess
44
42
 
45
- import time
46
- import asyncio
47
-
48
43
  class ProcessPanel(QFrame, Styles):
44
+
49
45
  def __init__(self, parent_window, mode):
50
46
 
51
47
  super().__init__()
@@ -55,17 +51,20 @@ class ProcessPanel(QFrame, Styles):
55
51
  self.exp_dir = self.parent_window.exp_dir
56
52
  self.exp_config = self.parent_window.exp_config
57
53
  self.movie_prefix = self.parent_window.movie_prefix
58
- self.threshold_config_targets = None
59
- self.threshold_config_effectors = None
60
- self.wells = np.array(self.parent_window.wells,dtype=str)
54
+ self.threshold_configs = [None for _ in range(len(self.parent_window.populations))]
55
+ self.wells = np.array(self.parent_window.wells, dtype=str)
61
56
  self.cellpose_calibrated = False
62
57
  self.stardist_calibrated = False
58
+ self.segChannelsSet = False
59
+ self.signalChannelsSet = False
60
+ self.flipSeg = False
61
+
63
62
  self.use_gpu = self.parent_window.parent_window.use_gpu
64
63
  self.n_threads = self.parent_window.parent_window.n_threads
65
64
 
66
65
  self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
67
66
  self.grid = QGridLayout(self)
68
- self.grid.setContentsMargins(5,5,5,5)
67
+ self.grid.setContentsMargins(5, 5, 5, 5)
69
68
  self.generate_header()
70
69
 
71
70
  def generate_header(self):
@@ -84,36 +83,35 @@ class ProcessPanel(QFrame, Styles):
84
83
  title_hbox = QHBoxLayout()
85
84
  self.grid.addWidget(panel_title, 0, 0, 1, 4, alignment=Qt.AlignCenter)
86
85
 
87
- self.help_pop_btn = QPushButton()
88
- self.help_pop_btn.setIcon(icon(MDI6.help_circle,color=self.help_color))
89
- self.help_pop_btn.setIconSize(QSize(20, 20))
90
- self.help_pop_btn.clicked.connect(self.help_population)
91
- self.help_pop_btn.setStyleSheet(self.button_select_all)
92
- self.help_pop_btn.setToolTip("Help.")
93
- #self.grid.addWidget(self.help_pop_btn, 0, 0, 1, 3, alignment=Qt.AlignRight)
94
-
95
-
96
- self.select_all_btn = QPushButton()
97
- self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
98
- self.select_all_btn.setIconSize(QSize(20, 20))
99
- self.all_ticked = False
100
- self.select_all_btn.clicked.connect(self.tick_all_actions)
101
- self.select_all_btn.setStyleSheet(self.button_select_all)
86
+ # self.help_pop_btn = QPushButton()
87
+ # self.help_pop_btn.setIcon(icon(MDI6.help_circle, color=self.help_color))
88
+ # self.help_pop_btn.setIconSize(QSize(20, 20))
89
+ # self.help_pop_btn.clicked.connect(self.help_population)
90
+ # self.help_pop_btn.setStyleSheet(self.button_select_all)
91
+ # self.help_pop_btn.setToolTip("Help.")
92
+ # self.grid.addWidget(self.help_pop_btn, 0, 0, 1, 3, alignment=Qt.AlignRight)
93
+
94
+ # self.select_all_btn = QPushButton()
95
+ # self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
96
+ # self.select_all_btn.setIconSize(QSize(20, 20))
97
+ # self.all_ticked = False
98
+ # self.select_all_btn.clicked.connect(self.tick_all_actions)
99
+ # self.select_all_btn.setStyleSheet(self.button_select_all)
102
100
  #self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
103
101
  #self.to_disable.append(self.all_tc_actions)
104
102
 
105
103
  self.collapse_btn = QPushButton()
106
- self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
104
+ self.collapse_btn.setIcon(icon(MDI6.chevron_down, color="black"))
107
105
  self.collapse_btn.setIconSize(QSize(25, 25))
108
106
  self.collapse_btn.setStyleSheet(self.button_select_all)
109
107
  #self.grid.addWidget(self.collapse_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
110
108
 
111
- title_hbox.addWidget(self.select_all_btn, 5)
109
+ title_hbox.addWidget(QLabel(), 5) #self.select_all_btn
112
110
  title_hbox.addWidget(QLabel(), 85, alignment=Qt.AlignCenter)
113
- title_hbox.addWidget(self.help_pop_btn, 5)
111
+ # title_hbox.addWidget(self.help_pop_btn, 5)
114
112
  title_hbox.addWidget(self.collapse_btn, 5)
115
113
 
116
- self.grid.addLayout(title_hbox, 0,0,1,4)
114
+ self.grid.addLayout(title_hbox, 0, 0, 1, 4)
117
115
  self.populate_contents()
118
116
 
119
117
  self.grid.addWidget(self.ContentsFrame, 1, 0, 1, 4, alignment=Qt.AlignTop)
@@ -123,47 +121,46 @@ class ProcessPanel(QFrame, Styles):
123
121
 
124
122
  def collapse_advanced(self):
125
123
 
126
- effector_open = not self.parent_window.ProcessEffectors.ContentsFrame.isHidden()
127
- targets_open = not self.parent_window.ProcessTargets.ContentsFrame.isHidden()
124
+ panels_open = [not p.ContentsFrame.isHidden() for p in self.parent_window.ProcessPopulations]
128
125
  interactions_open = not self.parent_window.NeighPanel.ContentsFrame.isHidden()
129
126
  preprocessing_open = not self.parent_window.PreprocessingPanel.ContentsFrame.isHidden()
130
- is_open = np.array([effector_open, targets_open, interactions_open, preprocessing_open])
127
+ is_open = np.array(panels_open+[interactions_open, preprocessing_open])
131
128
 
132
129
  if self.ContentsFrame.isHidden():
133
- self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
130
+ self.collapse_btn.setIcon(icon(MDI6.chevron_down, color="black"))
134
131
  self.collapse_btn.setIconSize(QSize(20, 20))
135
132
  if len(is_open[is_open])==0:
136
133
  self.parent_window.scroll.setMinimumHeight(int(550))
137
134
  self.parent_window.adjustSize()
138
135
  else:
139
- self.collapse_btn.setIcon(icon(MDI6.chevron_up,color="black"))
136
+ self.collapse_btn.setIcon(icon(MDI6.chevron_up, color="black"))
140
137
  self.collapse_btn.setIconSize(QSize(20, 20))
141
138
  self.parent_window.scroll.setMinimumHeight(min(int(930), int(0.9*self.parent_window.screen_height)))
142
139
 
143
140
 
144
- def help_population(self):
141
+ # def help_population(self):
145
142
 
146
- """
147
- Helper to choose a proper cell population structure.
148
- """
143
+ # """
144
+ # Helper to choose a proper cell population structure.
145
+ # """
149
146
 
150
- dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','cell-populations.json'])
147
+ # dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','cell-populations.json'])
151
148
 
152
- with open(dict_path) as f:
153
- d = json.load(f)
149
+ # with open(dict_path) as f:
150
+ # d = json.load(f)
154
151
 
155
- suggestion = help_generic(d)
156
- if isinstance(suggestion, str):
157
- print(f"{suggestion=}")
158
- msgBox = QMessageBox()
159
- msgBox.setIcon(QMessageBox.Information)
160
- msgBox.setTextFormat(Qt.RichText)
161
- msgBox.setText(suggestion)
162
- msgBox.setWindowTitle("Info")
163
- msgBox.setStandardButtons(QMessageBox.Ok)
164
- returnValue = msgBox.exec()
165
- if returnValue == QMessageBox.Ok:
166
- return None
152
+ # suggestion = help_generic(d)
153
+ # if isinstance(suggestion, str):
154
+ # print(f"{suggestion=}")
155
+ # msgBox = QMessageBox()
156
+ # msgBox.setIcon(QMessageBox.Information)
157
+ # msgBox.setTextFormat(Qt.RichText)
158
+ # msgBox.setText(suggestion)
159
+ # msgBox.setWindowTitle("Info")
160
+ # msgBox.setStandardButtons(QMessageBox.Ok)
161
+ # returnValue = msgBox.exec()
162
+ # if returnValue == QMessageBox.Ok:
163
+ # return None
167
164
 
168
165
  def populate_contents(self):
169
166
  self.ContentsFrame = QFrame()
@@ -289,9 +286,16 @@ class ProcessPanel(QFrame, Styles):
289
286
  self.grid_contents.addLayout(signal_layout,6,0,1,4)
290
287
 
291
288
  def refresh_signal_models(self):
292
- signal_models = get_signal_models_list()
289
+ self.signal_models = get_signal_models_list()
293
290
  self.signal_models_list.clear()
294
- self.signal_models_list.addItems(signal_models)
291
+
292
+ thresh = 35
293
+ models_truncated = [m[:thresh - 3]+'...' if len(m)>thresh else m for m in self.signal_models]
294
+
295
+ self.signal_models_list.addItems(models_truncated)
296
+ for i in range(len(self.signal_models)):
297
+ self.signal_models_list.setItemData(i, self.signal_models[i], Qt.ToolTipRole)
298
+
295
299
 
296
300
  def generate_tracking_options(self):
297
301
 
@@ -352,14 +356,10 @@ class ProcessPanel(QFrame, Styles):
352
356
  if returnValue == QMessageBox.No:
353
357
  return None
354
358
  elif returnValue == QMessageBox.Yes:
355
- if os.path.exists(os.sep.join([self.parent_window.pos,'output','tables',f'trajectories_{self.mode}.csv'])):
356
- os.remove(os.sep.join([self.parent_window.pos,'output','tables',f'trajectories_{self.mode}.csv']))
357
- if os.path.exists(os.sep.join([self.parent_window.pos,'output','tables',f'trajectories_{self.mode}.pkl'])):
358
- os.remove(os.sep.join([self.parent_window.pos,'output','tables',f'trajectories_{self.mode}.pkl']))
359
- if os.path.exists(os.sep.join([self.parent_window.pos,'output','tables',f'napari_{self.mode[:-1]}_trajectories.npy'])):
360
- os.remove(os.sep.join([self.parent_window.pos,'output','tables',f'napari_{self.mode[:-1]}_trajectories.npy']))
361
- if os.path.exists(os.sep.join([self.parent_window.pos,'output','tables',f'trajectories_pairs.csv'])):
362
- os.remove(os.sep.join([self.parent_window.pos,'output','tables',f'trajectories_pairs.csv']))
359
+ remove_file_if_exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.csv']))
360
+ remove_file_if_exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.pkl']))
361
+ remove_file_if_exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'napari_{self.mode[:-1]}_trajectories.npy']))
362
+ remove_file_if_exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_pairs.csv']))
363
363
  self.parent_window.update_position_options()
364
364
  else:
365
365
  return None
@@ -378,6 +378,23 @@ class ProcessPanel(QFrame, Styles):
378
378
  #self.to_disable.append(self.segment_action)
379
379
  grid_segment.addWidget(self.segment_action, 90)
380
380
 
381
+ # self.flip_segment_btn = QPushButton()
382
+ # self.flip_segment_btn.setIcon(icon(MDI6.camera_flip_outline,color="black"))
383
+ # self.flip_segment_btn.setIconSize(QSize(20, 20))
384
+ # self.flip_segment_btn.clicked.connect(self.flip_segmentation)
385
+ # self.flip_segment_btn.setStyleSheet(self.button_select_all)
386
+ # self.flip_segment_btn.setToolTip("Flip the order of the frames for segmentation.")
387
+ # grid_segment.addWidget(self.flip_segment_btn, 5)
388
+
389
+ self.segmentation_config_btn = QPushButton()
390
+ self.segmentation_config_btn.setIcon(icon(MDI6.cog_outline,color="black"))
391
+ self.segmentation_config_btn.setIconSize(QSize(20, 20))
392
+ self.segmentation_config_btn.setToolTip("Configure segmentation.")
393
+ self.segmentation_config_btn.setStyleSheet(self.button_select_all)
394
+ self.segmentation_config_btn.clicked.connect(self.open_segmentation_configuration_ui)
395
+ grid_segment.addWidget(self.segmentation_config_btn, 5)
396
+
397
+
381
398
  self.check_seg_btn = QPushButton()
382
399
  self.check_seg_btn.setIcon(icon(MDI6.eye_check_outline,color="black"))
383
400
  self.check_seg_btn.setIconSize(QSize(20, 20))
@@ -430,13 +447,25 @@ class ProcessPanel(QFrame, Styles):
430
447
  self.seg_model_list.setEnabled(False)
431
448
  self.grid_contents.addLayout(seg_option_vbox, 2, 0, 1, 4)
432
449
 
450
+ def flip_segmentation(self):
451
+ if not self.flipSeg:
452
+ self.flipSeg = True
453
+ self.flip_segment_btn.setIcon(icon(MDI6.camera_flip,color=self.celldetective_blue))
454
+ self.flip_segment_btn.setIconSize(QSize(20, 20))
455
+ self.flip_segment_btn.setToolTip("Unflip the order of the frames for segmentation.")
456
+ else:
457
+ self.flipSeg = False
458
+ self.flip_segment_btn.setIcon(icon(MDI6.camera_flip_outline,color='black'))
459
+ self.flip_segment_btn.setIconSize(QSize(20, 20))
460
+ self.flip_segment_btn.setToolTip("Flip the order of the frames for segmentation.")
461
+
433
462
  def help_segmentation(self):
434
463
 
435
464
  """
436
465
  Widget with different decision helper decision trees.
437
466
  """
438
467
 
439
- self.help_w = QWidget()
468
+ self.help_w = CelldetectiveWidget()
440
469
  self.help_w.setWindowTitle('Helper')
441
470
  layout = QVBoxLayout()
442
471
  seg_strategy_btn = QPushButton('A guide to choose a segmentation strategy.')
@@ -552,16 +581,26 @@ class ProcessPanel(QFrame, Styles):
552
581
  #QApplication.setOverrideCursor(Qt.WaitCursor)
553
582
  test = self.parent_window.locate_selected_position()
554
583
  if test:
555
- print('Memory use: ', dict(psutil.virtual_memory()._asdict()))
584
+ #print('Memory use: ', dict(psutil.virtual_memory()._asdict()))
585
+ print(f"Loading images and labels into napari...")
556
586
  try:
557
587
  control_segmentation_napari(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode,flush_memory=True)
588
+ except FileNotFoundError as e:
589
+ msgBox = QMessageBox()
590
+ msgBox.setIcon(QMessageBox.Warning)
591
+ msgBox.setText(str(e))
592
+ msgBox.setWindowTitle("Warning")
593
+ msgBox.setStandardButtons(QMessageBox.Ok)
594
+ _ = msgBox.exec()
595
+ return
558
596
  except Exception as e:
597
+ print(f'Task unsuccessful... Exception {e}...')
559
598
  msgBox = QMessageBox()
560
599
  msgBox.setIcon(QMessageBox.Warning)
561
600
  msgBox.setText(str(e))
562
601
  msgBox.setWindowTitle("Warning")
563
602
  msgBox.setStandardButtons(QMessageBox.Ok)
564
- returnValue = msgBox.exec()
603
+ _ = msgBox.exec()
565
604
 
566
605
  msgBox = QMessageBox()
567
606
  msgBox.setIcon(QMessageBox.Question)
@@ -586,15 +625,15 @@ class ProcessPanel(QFrame, Styles):
586
625
 
587
626
  test = self.parent_window.locate_selected_position()
588
627
  if test:
589
- self.SignalAnnotator = SignalAnnotator(self)
590
- self.SignalAnnotator.show()
628
+ self.event_annotator = EventAnnotator(self)
629
+ self.event_annotator.show()
591
630
 
592
631
  def check_measurements(self):
593
632
 
594
633
  test = self.parent_window.locate_selected_position()
595
634
  if test:
596
- self.MeasureAnnotator = MeasureAnnotator(self)
597
- self.MeasureAnnotator.show()
635
+ self.measure_annotator = MeasureAnnotator(self)
636
+ self.measure_annotator.show()
598
637
 
599
638
  def enable_segmentation_model_list(self):
600
639
  if self.segment_action.isChecked():
@@ -611,39 +650,40 @@ class ProcessPanel(QFrame, Styles):
611
650
  def init_seg_model_list(self):
612
651
 
613
652
  self.seg_model_list.clear()
614
- self.seg_models = get_segmentation_models_list(mode=self.mode, return_path=False)
615
- thresh = 40
616
- self.models_truncated = [m[:thresh - 3]+'...' if len(m)>thresh else m for m in self.seg_models]
617
- #self.seg_model_list.addItems(models_truncated)
653
+ self.seg_models_specific = get_segmentation_models_list(mode=self.mode, return_path=False)
654
+ self.seg_models = self.seg_models_specific.copy()
655
+ self.n_specific_seg_models = len(self.seg_models)
618
656
 
619
657
  self.seg_models_generic = get_segmentation_models_list(mode="generic", return_path=False)
620
658
  self.seg_models.append('Threshold')
621
659
  self.seg_models.extend(self.seg_models_generic)
622
660
 
623
- #self.seg_models_generic.insert(0,'Threshold')
624
- self.seg_model_list.addItems(self.seg_models)
661
+ thresh = 35
662
+ self.models_truncated = [m[:thresh - 3]+'...' if len(m)>thresh else m for m in self.seg_models]
663
+
664
+ self.seg_model_list.addItems(self.models_truncated)
625
665
 
626
666
  for i in range(len(self.seg_models)):
627
667
  self.seg_model_list.setItemData(i, self.seg_models[i], Qt.ToolTipRole)
628
668
 
629
- self.seg_model_list.insertSeparator(len(self.models_truncated))
630
-
631
- def tick_all_actions(self):
632
- self.switch_all_ticks_option()
633
- if self.all_ticked:
634
- self.select_all_btn.setIcon(icon(MDI6.checkbox_outline,color="black"))
635
- self.select_all_btn.setIconSize(QSize(20, 20))
636
- self.segment_action.setChecked(True)
637
- else:
638
- self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
639
- self.select_all_btn.setIconSize(QSize(20, 20))
640
- self.segment_action.setChecked(False)
641
-
642
- def switch_all_ticks_option(self):
643
- if self.all_ticked == True:
644
- self.all_ticked = False
645
- else:
646
- self.all_ticked = True
669
+ self.seg_model_list.insertSeparator(self.n_specific_seg_models)
670
+
671
+ # def tick_all_actions(self):
672
+ # self.switch_all_ticks_option()
673
+ # if self.all_ticked:
674
+ # self.select_all_btn.setIcon(icon(MDI6.checkbox_outline,color="black"))
675
+ # self.select_all_btn.setIconSize(QSize(20, 20))
676
+ # self.segment_action.setChecked(True)
677
+ # else:
678
+ # self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
679
+ # self.select_all_btn.setIconSize(QSize(20, 20))
680
+ # self.segment_action.setChecked(False)
681
+
682
+ # def switch_all_ticks_option(self):
683
+ # if self.all_ticked == True:
684
+ # self.all_ticked = False
685
+ # else:
686
+ # self.all_ticked = True
647
687
 
648
688
  def upload_segmentation_model(self):
649
689
  print('Load a segmentation model or pipeline...')
@@ -652,23 +692,28 @@ class ProcessPanel(QFrame, Styles):
652
692
 
653
693
  def open_tracking_configuration_ui(self):
654
694
  print('Set the tracking parameters...')
655
- self.ConfigTracking = ConfigTracking(self)
656
- self.ConfigTracking.show()
695
+ self.settings_tracking = SettingsTracking(self)
696
+ self.settings_tracking.show()
657
697
 
658
698
  def open_signal_model_config_ui(self):
659
699
  print('Set the training parameters for new signal models...')
660
- self.ConfigSignalTrain = ConfigSignalModelTraining(self)
661
- self.ConfigSignalTrain.show()
700
+ self.settings_event_detection_training = SettingsEventDetectionModelTraining(self)
701
+ self.settings_event_detection_training.show()
662
702
 
663
703
  def open_segmentation_model_config_ui(self):
664
704
  print('Set the training parameters for a new segmentation model...')
665
- self.ConfigSegmentationTrain = ConfigSegmentationModelTraining(self)
666
- self.ConfigSegmentationTrain.show()
705
+ self.settings_segmentation_training = SettingsSegmentationModelTraining(self)
706
+ self.settings_segmentation_training.show()
667
707
 
668
708
  def open_measurement_configuration_ui(self):
669
709
  print('Set the measurements to be performed...')
670
- self.ConfigMeasurements = ConfigMeasurements(self)
671
- self.ConfigMeasurements.show()
710
+ self.settings_measurements = SettingsMeasurements(self)
711
+ self.settings_measurements.show()
712
+
713
+ def open_segmentation_configuration_ui(self):
714
+ print('Set the segmentation settings to be performed...')
715
+ self.settings_segmentation = SettingsSegmentation(self)
716
+ self.settings_segmentation.show()
672
717
 
673
718
  def open_classifier_ui(self):
674
719
 
@@ -690,12 +735,16 @@ class ProcessPanel(QFrame, Styles):
690
735
  self.ClassifierWidget.show()
691
736
 
692
737
  def open_signal_annotator_configuration_ui(self):
693
- self.ConfigSignalAnnotator = ConfigSignalAnnotator(self)
694
- self.ConfigSignalAnnotator.show()
738
+ self.settings_signal_annotator = SettingsSignalAnnotator(self)
739
+ self.settings_signal_annotator.show()
695
740
 
696
741
  def reset_generalist_setup(self, index):
697
742
  self.cellpose_calibrated = False
698
743
  self.stardist_calibrated = False
744
+ self.segChannelsSet = False
745
+
746
+ def reset_signals(self):
747
+ self.signalChannelsSet = False
699
748
 
700
749
  def process_population(self):
701
750
 
@@ -721,10 +770,8 @@ class ProcessPanel(QFrame, Styles):
721
770
  # self.freeze()
722
771
  # QApplication.setOverrideCursor(Qt.WaitCursor)
723
772
 
724
- if self.mode=="targets":
725
- self.threshold_config = self.threshold_config_targets
726
- elif self.mode=="effectors":
727
- self.threshold_config = self.threshold_config_effectors
773
+ idx = self.parent_window.populations.index(self.mode)
774
+ self.threshold_config = self.threshold_configs[idx]
728
775
 
729
776
  self.load_available_tables()
730
777
 
@@ -742,11 +789,14 @@ class ProcessPanel(QFrame, Styles):
742
789
  else:
743
790
  print('erase tabs!')
744
791
  tabs = [pos+os.sep.join(['output', 'tables', f'trajectories_{self.mode}.csv']) for pos in self.df_pos_info['pos_path'].unique()]
792
+ #tabs += [pos+os.sep.join(['output', 'tables', f'trajectories_pairs.csv']) for pos in self.df_pos_info['pos_path'].unique()]
793
+ tabs += [pos+os.sep.join(['output', 'tables', f'napari_{self.mode}_trajectories.npy']) for pos in self.df_pos_info['pos_path'].unique()]
745
794
  for t in tabs:
746
- if os.path.exists(t.replace('.csv','.pkl')):
747
- os.remove(t.replace('.csv','.pkl'))
748
- os.remove(t)
749
-
795
+ remove_file_if_exists(t.replace('.csv','.pkl'))
796
+ try:
797
+ os.remove(t)
798
+ except:
799
+ pass
750
800
  loop_iter=0
751
801
 
752
802
  if self.parent_window.position_list.isMultipleSelection():
@@ -759,11 +809,10 @@ class ProcessPanel(QFrame, Styles):
759
809
  if returnValue == QMessageBox.No:
760
810
  return None
761
811
 
762
- if self.seg_model_list.currentIndex() > len(self.models_truncated):
812
+ if self.seg_model_list.currentIndex() > self.n_specific_seg_models:
763
813
  self.model_name = self.seg_models[self.seg_model_list.currentIndex()-1]
764
814
  else:
765
815
  self.model_name = self.seg_models[self.seg_model_list.currentIndex()]
766
- print(self.model_name, self.seg_model_list.currentIndex())
767
816
 
768
817
  if self.segment_action.isChecked() and self.model_name.startswith('CP') and self.model_name in self.seg_models_generic and not self.cellpose_calibrated:
769
818
 
@@ -771,12 +820,25 @@ class ProcessPanel(QFrame, Styles):
771
820
  self.diamWidget.show()
772
821
  return None
773
822
 
774
- if self.segment_action.isChecked() and self.model_name.startswith('SD') and self.model_name in self.seg_models_generic and not self.stardist_calibrated:
823
+ elif self.segment_action.isChecked() and self.model_name.startswith('SD') and self.model_name in self.seg_models_generic and not self.stardist_calibrated:
775
824
 
776
825
  self.diamWidget = StarDistParamsWidget(self, model_name = self.model_name)
777
826
  self.diamWidget.show()
778
827
  return None
779
828
 
829
+ elif self.segment_action.isChecked() and self.model_name in self.seg_models_specific and not self.segChannelsSet:
830
+
831
+ self.segChannelWidget = SegModelParamsWidget(self, model_name = self.model_name)
832
+ self.segChannelWidget.show()
833
+ return None
834
+
835
+ if self.signal_analysis_action.isChecked() and not self.signalChannelsSet:
836
+ self.signal_model_name = self.signal_models[self.signal_models_list.currentIndex()]
837
+ self.signalChannelWidget = SignalModelParamsWidget(self, model_name = self.signal_model_name)
838
+ self.signalChannelWidget.show()
839
+ return None
840
+
841
+
780
842
  self.movie_prefix = self.parent_window.movie_prefix
781
843
 
782
844
  for w_idx in self.well_index:
@@ -828,6 +890,7 @@ class ProcessPanel(QFrame, Styles):
828
890
  if result == QDialog.Accepted:
829
891
  pass
830
892
  elif result == QDialog.Rejected:
893
+ self.reset_generalist_setup(0)
831
894
  return None
832
895
  #segment_from_threshold_at_position(self.pos, self.mode, self.threshold_config, threads=self.parent_window.parent_window.n_threads)
833
896
  else:
@@ -842,6 +905,7 @@ class ProcessPanel(QFrame, Styles):
842
905
  if result == QDialog.Accepted:
843
906
  pass
844
907
  elif result == QDialog.Rejected:
908
+ self.reset_generalist_setup(0)
845
909
  return None
846
910
 
847
911
  if self.track_action.isChecked():
@@ -890,7 +954,8 @@ class ProcessPanel(QFrame, Styles):
890
954
  returnValue = msgBox.exec()
891
955
  if returnValue == QMessageBox.No:
892
956
  return None
893
- analyze_signals_at_position(self.pos, self.signal_models_list.currentText(), self.mode)
957
+ self.signal_model_name = self.signal_models[self.signal_models_list.currentIndex()]
958
+ analyze_signals_at_position(self.pos, self.signal_model_name, self.mode)
894
959
 
895
960
 
896
961
  # self.stack = None
@@ -900,10 +965,20 @@ class ProcessPanel(QFrame, Styles):
900
965
  action.setChecked(False)
901
966
 
902
967
  self.reset_generalist_setup(0)
968
+ self.reset_signals()
903
969
 
904
970
  def open_napari_tracking(self):
905
971
  print(f'View the tracks before post-processing for position {self.parent_window.pos} in napari...')
906
- control_tracks(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode, threads=self.parent_window.parent_window.n_threads)
972
+ try:
973
+ control_tracks(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode, threads=self.parent_window.parent_window.n_threads)
974
+ except FileNotFoundError as e:
975
+ msgBox = QMessageBox()
976
+ msgBox.setIcon(QMessageBox.Warning)
977
+ msgBox.setText(str(e))
978
+ msgBox.setWindowTitle("Warning")
979
+ msgBox.setStandardButtons(QMessageBox.Ok)
980
+ _ = msgBox.exec()
981
+ return
907
982
 
908
983
  def view_table_ui(self):
909
984
 
@@ -938,8 +1013,11 @@ class ProcessPanel(QFrame, Styles):
938
1013
  self.position_option = self.parent_window.position_list.getSelectedIndices()
939
1014
 
940
1015
  self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=self.well_option, position_option=self.position_option, population=self.mode, return_pos_info=True)
1016
+ self.signals = []
1017
+ if self.df is not None:
1018
+ self.signals = list(self.df.columns)
941
1019
  if self.df is None:
942
- print('No table could be found...')
1020
+ print('No table could be found for the selected position(s)...')
943
1021
 
944
1022
  def set_cellpose_scale(self):
945
1023
 
@@ -951,7 +1029,6 @@ class ProcessPanel(QFrame, Styles):
951
1029
  model_complete_path = locate_segmentation_model(self.model_name)
952
1030
  input_config_path = model_complete_path+"config_input.json"
953
1031
  new_channels = [self.diamWidget.cellpose_channel_cb[i].currentText() for i in range(2)]
954
- print(new_channels)
955
1032
  with open(input_config_path) as config_file:
956
1033
  input_config = json.load(config_file)
957
1034
 
@@ -983,6 +1060,46 @@ class ProcessPanel(QFrame, Styles):
983
1060
  self.diamWidget.close()
984
1061
  self.process_population()
985
1062
 
1063
+ def set_selected_channels_for_segmentation(self):
1064
+
1065
+ model_complete_path = locate_segmentation_model(self.model_name)
1066
+ input_config_path = model_complete_path+"config_input.json"
1067
+ new_channels = [self.segChannelWidget.channel_cbs[i].currentText() for i in range(len(self.segChannelWidget.channel_cbs))]
1068
+ target_cell_size = None
1069
+ if hasattr(self.segChannelWidget, "diameter_le"):
1070
+ target_cell_size = float(self.segChannelWidget.diameter_le.get_threshold())
1071
+
1072
+ with open(input_config_path) as config_file:
1073
+ input_config = json.load(config_file)
1074
+
1075
+ input_config.update({'selected_channels': new_channels, 'target_cell_size_um': target_cell_size})
1076
+
1077
+ #input_config['channels'] = new_channels
1078
+ with open(input_config_path, 'w') as f:
1079
+ json.dump(input_config, f, indent=4)
1080
+
1081
+ self.segChannelsSet = True
1082
+ self.segChannelWidget.close()
1083
+ self.process_population()
1084
+
1085
+ def set_selected_signals_for_event_detection(self):
1086
+ self.signal_model_name = self.signal_models[self.signal_models_list.currentIndex()]
1087
+ model_complete_path = locate_signal_model(self.signal_model_name)
1088
+ input_config_path = model_complete_path+"config_input.json"
1089
+ new_channels = [self.signalChannelWidget.channel_cbs[i].currentText() for i in range(len(self.signalChannelWidget.channel_cbs))]
1090
+ with open(input_config_path) as config_file:
1091
+ input_config = json.load(config_file)
1092
+
1093
+ input_config.update({'selected_channels': new_channels})
1094
+
1095
+ #input_config['channels'] = new_channels
1096
+ with open(input_config_path, 'w') as f:
1097
+ json.dump(input_config, f, indent=4)
1098
+
1099
+ self.signalChannelsSet = True
1100
+ self.signalChannelWidget.close()
1101
+ self.process_population()
1102
+
986
1103
 
987
1104
 
988
1105
  class NeighPanel(QFrame, Styles):
@@ -1034,11 +1151,10 @@ class NeighPanel(QFrame, Styles):
1034
1151
 
1035
1152
  def collapse_advanced(self):
1036
1153
 
1037
- effector_open = not self.parent_window.ProcessEffectors.ContentsFrame.isHidden()
1038
- targets_open = not self.parent_window.ProcessTargets.ContentsFrame.isHidden()
1154
+ panels_open = [not p.ContentsFrame.isHidden() for p in self.parent_window.ProcessPopulations]
1039
1155
  interactions_open = not self.parent_window.NeighPanel.ContentsFrame.isHidden()
1040
1156
  preprocessing_open = not self.parent_window.PreprocessingPanel.ContentsFrame.isHidden()
1041
- is_open = np.array([effector_open, targets_open, interactions_open, preprocessing_open])
1157
+ is_open = np.array(panels_open+[interactions_open, preprocessing_open])
1042
1158
 
1043
1159
  if self.ContentsFrame.isHidden():
1044
1160
  self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
@@ -1363,12 +1479,12 @@ class NeighPanel(QFrame, Styles):
1363
1479
 
1364
1480
  def open_signal_annotator_configuration_ui(self):
1365
1481
  self.mode = 'pairs'
1366
- self.ConfigSignalAnnotator = ConfigSignalAnnotator(self)
1367
- self.ConfigSignalAnnotator.show()
1482
+ self.config_signal_annotator = SettingsSignalAnnotator(self)
1483
+ self.config_signal_annotator.show()
1368
1484
 
1369
1485
  def open_signal_model_config_ui(self):
1370
- self.ConfigSignalTrain = ConfigSignalModelTraining(self, signal_mode='pairs')
1371
- self.ConfigSignalTrain.show()
1486
+ self.settings_pair_event_detection_training = SettingsEventDetectionModelTraining(self, signal_mode='pairs')
1487
+ self.settings_pair_event_detection_training.show()
1372
1488
 
1373
1489
  def remove_protocol_from_list(self):
1374
1490
 
@@ -1379,7 +1495,7 @@ class NeighPanel(QFrame, Styles):
1379
1495
 
1380
1496
  def open_config_distance_threshold_neighborhood(self):
1381
1497
 
1382
- self.ConfigNeigh = ConfigNeighborhoods(parent_window=self,
1498
+ self.ConfigNeigh = SettingsNeighborhood(parent_window=self,
1383
1499
  neighborhood_type='distance_threshold',
1384
1500
  neighborhood_parameter_name='threshold distance',
1385
1501
  )
@@ -1387,7 +1503,7 @@ class NeighPanel(QFrame, Styles):
1387
1503
 
1388
1504
  def open_config_contact_neighborhood(self):
1389
1505
 
1390
- self.ConfigNeigh = ConfigNeighborhoods(parent_window=self,
1506
+ self.ConfigNeigh = SettingsNeighborhood(parent_window=self,
1391
1507
  neighborhood_type='mask_contact',
1392
1508
  neighborhood_parameter_name='tolerance contact distance',
1393
1509
  )
@@ -1432,6 +1548,7 @@ class NeighPanel(QFrame, Styles):
1432
1548
  for pos_idx in pos_indices:
1433
1549
 
1434
1550
  self.pos = natsorted(glob(well+f"{os.path.split(well)[-1].replace('W','').replace(os.sep,'')}*{os.sep}"))[pos_idx]
1551
+ self.pos_name = extract_position_name(self.pos)
1435
1552
  print(f"Position {self.pos}...\nLoading stack movie...")
1436
1553
 
1437
1554
  if not os.path.exists(self.pos + 'output' + os.sep):
@@ -1442,55 +1559,35 @@ class NeighPanel(QFrame, Styles):
1442
1559
  if self.neigh_action.isChecked():
1443
1560
  for protocol in self.protocols:
1444
1561
 
1445
- if protocol['neighborhood_type']=='distance_threshold':
1446
-
1447
- compute_neighborhood_at_position(self.pos,
1448
- protocol['distance'],
1449
- population=protocol['population'],
1450
- theta_dist=None,
1451
- img_shape=(self.parent_window.shape_x,self.parent_window.shape_y),
1452
- return_tables=False,
1453
- clear_neigh=protocol['clear_neigh'],
1454
- event_time_col=protocol['event_time_col'],
1455
- neighborhood_kwargs=protocol['neighborhood_kwargs'],
1456
- )
1457
-
1458
- elif protocol['neighborhood_type']=='mask_contact':
1459
-
1460
- compute_contact_neighborhood_at_position(self.pos,
1461
- protocol['distance'],
1462
- population=protocol['population'],
1463
- theta_dist=None,
1464
- img_shape=(self.parent_window.shape_x,self.parent_window.shape_y),
1465
- return_tables=False,
1466
- clear_neigh=protocol['clear_neigh'],
1467
- event_time_col=protocol['event_time_col'],
1468
- neighborhood_kwargs=protocol['neighborhood_kwargs'],
1469
- )
1562
+ process_args = {"pos": self.pos, "pos_name": self.pos_name,"protocol": protocol,"img_shape": (self.parent_window.shape_x,self.parent_window.shape_y)} #"n_threads": self.n_threads
1563
+ self.job = ProgressWindow(NeighborhoodProcess, parent_window=self, title="Neighborhood",
1564
+ process_args=process_args)
1565
+ result = self.job.exec_()
1566
+ if result == QDialog.Accepted:
1567
+ pass
1568
+ elif result == QDialog.Rejected:
1569
+ return None
1570
+
1470
1571
  if self.measure_pairs_action.isChecked():
1471
1572
  rel_measure_at_position(self.pos)
1472
1573
 
1473
1574
  if self.signal_analysis_action.isChecked():
1474
1575
 
1475
- analyze_pair_signals_at_position(self.pos, self.pair_signal_models_list.currentText(), use_gpu=self.parent_window.parent_window.use_gpu)
1576
+ analyze_pair_signals_at_position(self.pos, self.pair_signal_models_list.currentText(), use_gpu=self.parent_window.parent_window.use_gpu, populations=self.parent_window.populations)
1476
1577
 
1477
1578
  self.parent_window.update_position_options()
1579
+ for action in [self.neigh_action, self.measure_pairs_action, self.signal_analysis_action]:
1580
+ if action.isChecked():
1581
+ action.setChecked(False)
1582
+
1478
1583
  print('Done.')
1479
1584
 
1480
1585
  def check_signals2(self):
1481
1586
 
1482
1587
  test = self.parent_window.locate_selected_position()
1483
1588
  if test:
1484
- self.SignalAnnotator2 = SignalAnnotator2(self)
1485
- self.SignalAnnotator2.show()
1486
-
1487
- def check_measurements2(self):
1488
-
1489
- test = self.parent_window.locate_selected_position()
1490
- if test:
1491
- self.MeasurementAnnotator2 = MeasureAnnotator2(self)
1492
- self.MeasurementAnnotator2.show()
1493
-
1589
+ self.pair_event_annotator = PairEventAnnotator(self)
1590
+ self.pair_event_annotator.show()
1494
1591
 
1495
1592
 
1496
1593
  class PreprocessingPanel(QFrame, Styles):
@@ -1529,13 +1626,13 @@ class PreprocessingPanel(QFrame, Styles):
1529
1626
 
1530
1627
  self.grid.addWidget(panel_title, 0, 0, 1, 4, alignment=Qt.AlignCenter)
1531
1628
 
1532
- self.select_all_btn = QPushButton()
1533
- self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
1534
- self.select_all_btn.setIconSize(QSize(20, 20))
1535
- self.all_ticked = False
1536
- #self.select_all_btn.clicked.connect(self.tick_all_actions)
1537
- self.select_all_btn.setStyleSheet(self.button_select_all)
1538
- self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
1629
+ # self.select_all_btn = QPushButton()
1630
+ # self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
1631
+ # self.select_all_btn.setIconSize(QSize(20, 20))
1632
+ # self.all_ticked = False
1633
+ # #self.select_all_btn.clicked.connect(self.tick_all_actions)
1634
+ # self.select_all_btn.setStyleSheet(self.button_select_all)
1635
+ # self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
1539
1636
  #self.to_disable.append(self.all_tc_actions)
1540
1637
 
1541
1638
  self.collapse_btn = QPushButton()
@@ -1553,12 +1650,11 @@ class PreprocessingPanel(QFrame, Styles):
1553
1650
 
1554
1651
  def collapse_advanced(self):
1555
1652
 
1556
- effector_open = not self.parent_window.ProcessEffectors.ContentsFrame.isHidden()
1557
- targets_open = not self.parent_window.ProcessTargets.ContentsFrame.isHidden()
1653
+ panels_open = [not p.ContentsFrame.isHidden() for p in self.parent_window.ProcessPopulations]
1558
1654
  interactions_open = not self.parent_window.NeighPanel.ContentsFrame.isHidden()
1559
1655
  preprocessing_open = not self.parent_window.PreprocessingPanel.ContentsFrame.isHidden()
1560
- is_open = np.array([effector_open, targets_open, interactions_open, preprocessing_open])
1561
-
1656
+ is_open = np.array(panels_open+[interactions_open, preprocessing_open])
1657
+
1562
1658
  if self.ContentsFrame.isHidden():
1563
1659
  self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
1564
1660
  self.collapse_btn.setIconSize(QSize(20, 20))
@@ -1592,7 +1688,6 @@ class PreprocessingPanel(QFrame, Styles):
1592
1688
  self.help_background_btn.setToolTip("Help.")
1593
1689
 
1594
1690
  self.protocol_layout.title_layout.addWidget(self.help_background_btn, 5, alignment=Qt.AlignRight)
1595
-
1596
1691
 
1597
1692
  self.channel_offset_correction_layout = QVBoxLayout()
1598
1693