celldetective 1.3.9.post4__py3-none-any.whl → 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. celldetective/__init__.py +0 -3
  2. celldetective/_version.py +1 -1
  3. celldetective/events.py +2 -4
  4. celldetective/extra_properties.py +320 -24
  5. celldetective/gui/InitWindow.py +33 -45
  6. celldetective/gui/__init__.py +1 -0
  7. celldetective/gui/about.py +19 -15
  8. celldetective/gui/analyze_block.py +34 -19
  9. celldetective/gui/base_components.py +23 -0
  10. celldetective/gui/btrack_options.py +26 -34
  11. celldetective/gui/classifier_widget.py +71 -80
  12. celldetective/gui/configure_new_exp.py +113 -17
  13. celldetective/gui/control_panel.py +68 -141
  14. celldetective/gui/generic_signal_plot.py +9 -12
  15. celldetective/gui/gui_utils.py +49 -21
  16. celldetective/gui/json_readers.py +5 -4
  17. celldetective/gui/layouts.py +246 -22
  18. celldetective/gui/measurement_options.py +32 -17
  19. celldetective/gui/neighborhood_options.py +10 -13
  20. celldetective/gui/plot_measurements.py +21 -17
  21. celldetective/gui/plot_signals_ui.py +131 -75
  22. celldetective/gui/process_block.py +180 -123
  23. celldetective/gui/processes/compute_neighborhood.py +594 -0
  24. celldetective/gui/processes/measure_cells.py +5 -0
  25. celldetective/gui/processes/segment_cells.py +27 -6
  26. celldetective/gui/processes/track_cells.py +6 -0
  27. celldetective/gui/retrain_segmentation_model_options.py +12 -20
  28. celldetective/gui/retrain_signal_model_options.py +57 -56
  29. celldetective/gui/seg_model_loader.py +21 -62
  30. celldetective/gui/signal_annotator.py +139 -72
  31. celldetective/gui/signal_annotator2.py +431 -635
  32. celldetective/gui/signal_annotator_options.py +8 -11
  33. celldetective/gui/survival_ui.py +49 -95
  34. celldetective/gui/tableUI.py +28 -25
  35. celldetective/gui/thresholds_gui.py +617 -1221
  36. celldetective/gui/viewers.py +106 -39
  37. celldetective/gui/workers.py +9 -3
  38. celldetective/io.py +73 -27
  39. celldetective/measure.py +63 -27
  40. celldetective/neighborhood.py +342 -268
  41. celldetective/preprocessing.py +25 -17
  42. celldetective/relative_measurements.py +50 -29
  43. celldetective/scripts/analyze_signals.py +4 -1
  44. celldetective/scripts/measure_relative.py +4 -1
  45. celldetective/scripts/segment_cells.py +0 -6
  46. celldetective/scripts/track_cells.py +3 -1
  47. celldetective/scripts/train_segmentation_model.py +7 -4
  48. celldetective/signals.py +29 -14
  49. celldetective/tracking.py +7 -2
  50. celldetective/utils.py +36 -8
  51. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/METADATA +24 -16
  52. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/RECORD +57 -55
  53. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/WHEEL +1 -1
  54. tests/test_qt.py +21 -21
  55. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/entry_points.txt +0 -0
  56. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info/licenses}/LICENSE +0 -0
  57. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/top_level.txt +0 -0
@@ -1,22 +1,22 @@
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.processes.compute_neighborhood import NeighborhoodProcess
9
10
  from celldetective.gui.signal_annotator import MeasureAnnotator
10
11
  from celldetective.gui.signal_annotator2 import SignalAnnotator2
11
12
  from celldetective.io import get_segmentation_models_list, control_segmentation_napari, get_signal_models_list, \
12
13
  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
14
+ from celldetective.io import locate_segmentation_model, extract_position_name, fix_missing_labels, locate_signal_model
15
+ from celldetective.gui import SegmentationModelLoader, ClassifierWidget, ConfigNeighborhoods, \
16
+ ConfigSegmentationModelTraining, ConfigTracking, SignalAnnotator, ConfigSignalModelTraining, ConfigMeasurements, \
17
+ ConfigSignalAnnotator, TableUI, CelldetectiveWidget
15
18
  from celldetective.gui.gui_utils import QHSeperationLine
16
19
  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
20
  from celldetective.signals import analyze_signals_at_position, analyze_pair_signals_at_position
21
21
  from celldetective.utils import extract_experiment_channels
22
22
  import numpy as np
@@ -27,13 +27,9 @@ import pandas as pd
27
27
  from celldetective.gui.gui_utils import center_window
28
28
  from tifffile import imwrite
29
29
  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
30
  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
31
+ from celldetective.gui.gui_utils import help_generic
32
+ from celldetective.gui.layouts import SignalModelParamsWidget, SegModelParamsWidget, CellposeParamsWidget, StarDistParamsWidget, BackgroundModelFreeCorrectionLayout, ProtocolDesignerLayout, BackgroundFitCorrectionLayout, ChannelOffsetOptionsLayout
37
33
  from celldetective.gui import Styles
38
34
  from celldetective.utils import get_software_location
39
35
 
@@ -42,10 +38,8 @@ from celldetective.gui.processes.segment_cells import SegmentCellThresholdProces
42
38
  from celldetective.gui.processes.track_cells import TrackingProcess
43
39
  from celldetective.gui.processes.measure_cells import MeasurementProcess
44
40
 
45
- import time
46
- import asyncio
47
-
48
41
  class ProcessPanel(QFrame, Styles):
42
+
49
43
  def __init__(self, parent_window, mode):
50
44
 
51
45
  super().__init__()
@@ -55,17 +49,20 @@ class ProcessPanel(QFrame, Styles):
55
49
  self.exp_dir = self.parent_window.exp_dir
56
50
  self.exp_config = self.parent_window.exp_config
57
51
  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)
52
+ self.threshold_configs = [None for _ in range(len(self.parent_window.populations))]
53
+ self.wells = np.array(self.parent_window.wells, dtype=str)
61
54
  self.cellpose_calibrated = False
62
55
  self.stardist_calibrated = False
56
+ self.segChannelsSet = False
57
+ self.signalChannelsSet = False
58
+ self.flipSeg = False
59
+
63
60
  self.use_gpu = self.parent_window.parent_window.use_gpu
64
61
  self.n_threads = self.parent_window.parent_window.n_threads
65
62
 
66
63
  self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
67
64
  self.grid = QGridLayout(self)
68
- self.grid.setContentsMargins(5,5,5,5)
65
+ self.grid.setContentsMargins(5, 5, 5, 5)
69
66
  self.generate_header()
70
67
 
71
68
  def generate_header(self):
@@ -85,35 +82,34 @@ class ProcessPanel(QFrame, Styles):
85
82
  self.grid.addWidget(panel_title, 0, 0, 1, 4, alignment=Qt.AlignCenter)
86
83
 
87
84
  self.help_pop_btn = QPushButton()
88
- self.help_pop_btn.setIcon(icon(MDI6.help_circle,color=self.help_color))
85
+ self.help_pop_btn.setIcon(icon(MDI6.help_circle, color=self.help_color))
89
86
  self.help_pop_btn.setIconSize(QSize(20, 20))
90
87
  self.help_pop_btn.clicked.connect(self.help_population)
91
88
  self.help_pop_btn.setStyleSheet(self.button_select_all)
92
89
  self.help_pop_btn.setToolTip("Help.")
93
- #self.grid.addWidget(self.help_pop_btn, 0, 0, 1, 3, alignment=Qt.AlignRight)
90
+ self.grid.addWidget(self.help_pop_btn, 0, 0, 1, 3, alignment=Qt.AlignRight)
94
91
 
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)
92
+ # self.select_all_btn = QPushButton()
93
+ # self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
94
+ # self.select_all_btn.setIconSize(QSize(20, 20))
95
+ # self.all_ticked = False
96
+ # self.select_all_btn.clicked.connect(self.tick_all_actions)
97
+ # self.select_all_btn.setStyleSheet(self.button_select_all)
102
98
  #self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
103
99
  #self.to_disable.append(self.all_tc_actions)
104
100
 
105
101
  self.collapse_btn = QPushButton()
106
- self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
102
+ self.collapse_btn.setIcon(icon(MDI6.chevron_down, color="black"))
107
103
  self.collapse_btn.setIconSize(QSize(25, 25))
108
104
  self.collapse_btn.setStyleSheet(self.button_select_all)
109
105
  #self.grid.addWidget(self.collapse_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
110
106
 
111
- title_hbox.addWidget(self.select_all_btn, 5)
107
+ title_hbox.addWidget(QLabel(), 5) #self.select_all_btn
112
108
  title_hbox.addWidget(QLabel(), 85, alignment=Qt.AlignCenter)
113
109
  title_hbox.addWidget(self.help_pop_btn, 5)
114
110
  title_hbox.addWidget(self.collapse_btn, 5)
115
111
 
116
- self.grid.addLayout(title_hbox, 0,0,1,4)
112
+ self.grid.addLayout(title_hbox, 0, 0, 1, 4)
117
113
  self.populate_contents()
118
114
 
119
115
  self.grid.addWidget(self.ContentsFrame, 1, 0, 1, 4, alignment=Qt.AlignTop)
@@ -123,20 +119,19 @@ class ProcessPanel(QFrame, Styles):
123
119
 
124
120
  def collapse_advanced(self):
125
121
 
126
- effector_open = not self.parent_window.ProcessEffectors.ContentsFrame.isHidden()
127
- targets_open = not self.parent_window.ProcessTargets.ContentsFrame.isHidden()
122
+ panels_open = [not p.ContentsFrame.isHidden() for p in self.parent_window.ProcessPopulations]
128
123
  interactions_open = not self.parent_window.NeighPanel.ContentsFrame.isHidden()
129
124
  preprocessing_open = not self.parent_window.PreprocessingPanel.ContentsFrame.isHidden()
130
- is_open = np.array([effector_open, targets_open, interactions_open, preprocessing_open])
125
+ is_open = np.array(panels_open+[interactions_open, preprocessing_open])
131
126
 
132
127
  if self.ContentsFrame.isHidden():
133
- self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
128
+ self.collapse_btn.setIcon(icon(MDI6.chevron_down, color="black"))
134
129
  self.collapse_btn.setIconSize(QSize(20, 20))
135
130
  if len(is_open[is_open])==0:
136
131
  self.parent_window.scroll.setMinimumHeight(int(550))
137
132
  self.parent_window.adjustSize()
138
133
  else:
139
- self.collapse_btn.setIcon(icon(MDI6.chevron_up,color="black"))
134
+ self.collapse_btn.setIcon(icon(MDI6.chevron_up, color="black"))
140
135
  self.collapse_btn.setIconSize(QSize(20, 20))
141
136
  self.parent_window.scroll.setMinimumHeight(min(int(930), int(0.9*self.parent_window.screen_height)))
142
137
 
@@ -352,14 +347,14 @@ class ProcessPanel(QFrame, Styles):
352
347
  if returnValue == QMessageBox.No:
353
348
  return None
354
349
  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']))
350
+ if os.path.exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])):
351
+ os.remove(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.csv']))
352
+ if os.path.exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.pkl'])):
353
+ os.remove(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_{self.mode}.pkl']))
354
+ if os.path.exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'napari_{self.mode[:-1]}_trajectories.npy'])):
355
+ os.remove(os.sep.join([self.parent_window.pos, 'output', 'tables', f'napari_{self.mode[:-1]}_trajectories.npy']))
356
+ if os.path.exists(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_pairs.csv'])):
357
+ os.remove(os.sep.join([self.parent_window.pos, 'output', 'tables', f'trajectories_pairs.csv']))
363
358
  self.parent_window.update_position_options()
364
359
  else:
365
360
  return None
@@ -378,6 +373,15 @@ class ProcessPanel(QFrame, Styles):
378
373
  #self.to_disable.append(self.segment_action)
379
374
  grid_segment.addWidget(self.segment_action, 90)
380
375
 
376
+ self.flip_segment_btn = QPushButton()
377
+ self.flip_segment_btn.setIcon(icon(MDI6.camera_flip_outline,color="black"))
378
+ self.flip_segment_btn.setIconSize(QSize(20, 20))
379
+ self.flip_segment_btn.clicked.connect(self.flip_segmentation)
380
+ self.flip_segment_btn.setStyleSheet(self.button_select_all)
381
+ self.flip_segment_btn.setToolTip("Flip the order of the frames for segmentation.")
382
+ grid_segment.addWidget(self.flip_segment_btn, 5)
383
+
384
+
381
385
  self.check_seg_btn = QPushButton()
382
386
  self.check_seg_btn.setIcon(icon(MDI6.eye_check_outline,color="black"))
383
387
  self.check_seg_btn.setIconSize(QSize(20, 20))
@@ -430,13 +434,25 @@ class ProcessPanel(QFrame, Styles):
430
434
  self.seg_model_list.setEnabled(False)
431
435
  self.grid_contents.addLayout(seg_option_vbox, 2, 0, 1, 4)
432
436
 
437
+ def flip_segmentation(self):
438
+ if not self.flipSeg:
439
+ self.flipSeg = True
440
+ self.flip_segment_btn.setIcon(icon(MDI6.camera_flip,color=self.celldetective_blue))
441
+ self.flip_segment_btn.setIconSize(QSize(20, 20))
442
+ self.flip_segment_btn.setToolTip("Unflip the order of the frames for segmentation.")
443
+ else:
444
+ self.flipSeg = False
445
+ self.flip_segment_btn.setIcon(icon(MDI6.camera_flip_outline,color='black'))
446
+ self.flip_segment_btn.setIconSize(QSize(20, 20))
447
+ self.flip_segment_btn.setToolTip("Flip the order of the frames for segmentation.")
448
+
433
449
  def help_segmentation(self):
434
450
 
435
451
  """
436
452
  Widget with different decision helper decision trees.
437
453
  """
438
454
 
439
- self.help_w = QWidget()
455
+ self.help_w = CelldetectiveWidget()
440
456
  self.help_w.setWindowTitle('Helper')
441
457
  layout = QVBoxLayout()
442
458
  seg_strategy_btn = QPushButton('A guide to choose a segmentation strategy.')
@@ -552,16 +568,18 @@ class ProcessPanel(QFrame, Styles):
552
568
  #QApplication.setOverrideCursor(Qt.WaitCursor)
553
569
  test = self.parent_window.locate_selected_position()
554
570
  if test:
555
- print('Memory use: ', dict(psutil.virtual_memory()._asdict()))
571
+ #print('Memory use: ', dict(psutil.virtual_memory()._asdict()))
572
+ print(f"Loading images and labels into napari...")
556
573
  try:
557
574
  control_segmentation_napari(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode,flush_memory=True)
558
575
  except Exception as e:
576
+ print(f'Task unsuccessful... Exception {e}...')
559
577
  msgBox = QMessageBox()
560
578
  msgBox.setIcon(QMessageBox.Warning)
561
579
  msgBox.setText(str(e))
562
580
  msgBox.setWindowTitle("Warning")
563
581
  msgBox.setStandardButtons(QMessageBox.Ok)
564
- returnValue = msgBox.exec()
582
+ _ = msgBox.exec()
565
583
 
566
584
  msgBox = QMessageBox()
567
585
  msgBox.setIcon(QMessageBox.Question)
@@ -611,7 +629,8 @@ class ProcessPanel(QFrame, Styles):
611
629
  def init_seg_model_list(self):
612
630
 
613
631
  self.seg_model_list.clear()
614
- self.seg_models = get_segmentation_models_list(mode=self.mode, return_path=False)
632
+ self.seg_models_specific = get_segmentation_models_list(mode=self.mode, return_path=False)
633
+ self.seg_models = self.seg_models_specific.copy() #get_segmentation_models_list(mode=self.mode, return_path=False)
615
634
  thresh = 40
616
635
  self.models_truncated = [m[:thresh - 3]+'...' if len(m)>thresh else m for m in self.seg_models]
617
636
  #self.seg_model_list.addItems(models_truncated)
@@ -628,22 +647,22 @@ class ProcessPanel(QFrame, Styles):
628
647
 
629
648
  self.seg_model_list.insertSeparator(len(self.models_truncated))
630
649
 
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
650
+ # def tick_all_actions(self):
651
+ # self.switch_all_ticks_option()
652
+ # if self.all_ticked:
653
+ # self.select_all_btn.setIcon(icon(MDI6.checkbox_outline,color="black"))
654
+ # self.select_all_btn.setIconSize(QSize(20, 20))
655
+ # self.segment_action.setChecked(True)
656
+ # else:
657
+ # self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
658
+ # self.select_all_btn.setIconSize(QSize(20, 20))
659
+ # self.segment_action.setChecked(False)
660
+
661
+ # def switch_all_ticks_option(self):
662
+ # if self.all_ticked == True:
663
+ # self.all_ticked = False
664
+ # else:
665
+ # self.all_ticked = True
647
666
 
648
667
  def upload_segmentation_model(self):
649
668
  print('Load a segmentation model or pipeline...')
@@ -696,6 +715,10 @@ class ProcessPanel(QFrame, Styles):
696
715
  def reset_generalist_setup(self, index):
697
716
  self.cellpose_calibrated = False
698
717
  self.stardist_calibrated = False
718
+ self.segChannelsSet = False
719
+
720
+ def reset_signals(self):
721
+ self.signalChannelsSet = False
699
722
 
700
723
  def process_population(self):
701
724
 
@@ -721,10 +744,8 @@ class ProcessPanel(QFrame, Styles):
721
744
  # self.freeze()
722
745
  # QApplication.setOverrideCursor(Qt.WaitCursor)
723
746
 
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
747
+ idx = self.parent_window.populations.index(self.mode)
748
+ self.threshold_config = self.threshold_configs[idx]
728
749
 
729
750
  self.load_available_tables()
730
751
 
@@ -742,6 +763,8 @@ class ProcessPanel(QFrame, Styles):
742
763
  else:
743
764
  print('erase tabs!')
744
765
  tabs = [pos+os.sep.join(['output', 'tables', f'trajectories_{self.mode}.csv']) for pos in self.df_pos_info['pos_path'].unique()]
766
+ #tabs += [pos+os.sep.join(['output', 'tables', f'trajectories_pairs.csv']) for pos in self.df_pos_info['pos_path'].unique()]
767
+ tabs += [pos+os.sep.join(['output', 'tables', f'napari_{self.mode}_trajectories.npy']) for pos in self.df_pos_info['pos_path'].unique()]
745
768
  for t in tabs:
746
769
  if os.path.exists(t.replace('.csv','.pkl')):
747
770
  os.remove(t.replace('.csv','.pkl'))
@@ -763,7 +786,6 @@ class ProcessPanel(QFrame, Styles):
763
786
  self.model_name = self.seg_models[self.seg_model_list.currentIndex()-1]
764
787
  else:
765
788
  self.model_name = self.seg_models[self.seg_model_list.currentIndex()]
766
- print(self.model_name, self.seg_model_list.currentIndex())
767
789
 
768
790
  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
791
 
@@ -771,12 +793,24 @@ class ProcessPanel(QFrame, Styles):
771
793
  self.diamWidget.show()
772
794
  return None
773
795
 
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:
796
+ 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
797
 
776
798
  self.diamWidget = StarDistParamsWidget(self, model_name = self.model_name)
777
799
  self.diamWidget.show()
778
800
  return None
779
801
 
802
+ elif self.segment_action.isChecked() and self.model_name in self.seg_models_specific and not self.segChannelsSet:
803
+
804
+ self.segChannelWidget = SegModelParamsWidget(self, model_name = self.model_name)
805
+ self.segChannelWidget.show()
806
+ return None
807
+
808
+ if self.signal_analysis_action.isChecked() and not self.signalChannelsSet:
809
+ self.signalChannelWidget = SignalModelParamsWidget(self, model_name = self.signal_models_list.currentText())
810
+ self.signalChannelWidget.show()
811
+ return None
812
+
813
+
780
814
  self.movie_prefix = self.parent_window.movie_prefix
781
815
 
782
816
  for w_idx in self.well_index:
@@ -822,12 +856,13 @@ class ProcessPanel(QFrame, Styles):
822
856
  return None
823
857
  else:
824
858
  print(f"Segmentation from threshold config: {self.threshold_config}")
825
- process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "threshold_instructions": self.threshold_config, "use_gpu": self.use_gpu}
859
+ process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "threshold_instructions": self.threshold_config, "use_gpu": self.use_gpu, 'flip': self.flipSeg}
826
860
  self.job = ProgressWindow(SegmentCellThresholdProcess, parent_window=self, title="Segment", process_args = process_args)
827
861
  result = self.job.exec_()
828
862
  if result == QDialog.Accepted:
829
863
  pass
830
864
  elif result == QDialog.Rejected:
865
+ self.reset_generalist_setup(0)
831
866
  return None
832
867
  #segment_from_threshold_at_position(self.pos, self.mode, self.threshold_config, threads=self.parent_window.parent_window.n_threads)
833
868
  else:
@@ -836,12 +871,13 @@ class ProcessPanel(QFrame, Styles):
836
871
  # process = {"output_dir": self.output_dir, "file": self.model_name}
837
872
  # self.download_model_job = ProgressWindow(DownloadProcess, parent_window=self, title="Download", process_args = args)
838
873
 
839
- process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "model_name": self.model_name, "use_gpu": self.use_gpu}
874
+ process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "model_name": self.model_name, "use_gpu": self.use_gpu, 'flip': self.flipSeg}
840
875
  self.job = ProgressWindow(SegmentCellDLProcess, parent_window=self, title="Segment", process_args = process_args)
841
876
  result = self.job.exec_()
842
877
  if result == QDialog.Accepted:
843
878
  pass
844
879
  elif result == QDialog.Rejected:
880
+ self.reset_generalist_setup(0)
845
881
  return None
846
882
 
847
883
  if self.track_action.isChecked():
@@ -900,6 +936,7 @@ class ProcessPanel(QFrame, Styles):
900
936
  action.setChecked(False)
901
937
 
902
938
  self.reset_generalist_setup(0)
939
+ self.reset_signals()
903
940
 
904
941
  def open_napari_tracking(self):
905
942
  print(f'View the tracks before post-processing for position {self.parent_window.pos} in napari...')
@@ -938,8 +975,11 @@ class ProcessPanel(QFrame, Styles):
938
975
  self.position_option = self.parent_window.position_list.getSelectedIndices()
939
976
 
940
977
  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)
978
+ self.signals = []
979
+ if self.df is not None:
980
+ self.signals = list(self.df.columns)
941
981
  if self.df is None:
942
- print('No table could be found...')
982
+ print('No table could be found for the selected position(s)...')
943
983
 
944
984
  def set_cellpose_scale(self):
945
985
 
@@ -951,7 +991,6 @@ class ProcessPanel(QFrame, Styles):
951
991
  model_complete_path = locate_segmentation_model(self.model_name)
952
992
  input_config_path = model_complete_path+"config_input.json"
953
993
  new_channels = [self.diamWidget.cellpose_channel_cb[i].currentText() for i in range(2)]
954
- print(new_channels)
955
994
  with open(input_config_path) as config_file:
956
995
  input_config = json.load(config_file)
957
996
 
@@ -983,6 +1022,46 @@ class ProcessPanel(QFrame, Styles):
983
1022
  self.diamWidget.close()
984
1023
  self.process_population()
985
1024
 
1025
+ def set_selected_channels_for_segmentation(self):
1026
+
1027
+ model_complete_path = locate_segmentation_model(self.model_name)
1028
+ input_config_path = model_complete_path+"config_input.json"
1029
+ new_channels = [self.segChannelWidget.channel_cbs[i].currentText() for i in range(len(self.segChannelWidget.channel_cbs))]
1030
+ target_cell_size = None
1031
+ if hasattr(self.segChannelWidget, "diameter_le"):
1032
+ target_cell_size = float(self.segChannelWidget.diameter_le.get_threshold())
1033
+
1034
+ with open(input_config_path) as config_file:
1035
+ input_config = json.load(config_file)
1036
+
1037
+ input_config.update({'selected_channels': new_channels, 'target_cell_size_um': target_cell_size})
1038
+
1039
+ #input_config['channels'] = new_channels
1040
+ with open(input_config_path, 'w') as f:
1041
+ json.dump(input_config, f, indent=4)
1042
+
1043
+ self.segChannelsSet = True
1044
+ self.segChannelWidget.close()
1045
+ self.process_population()
1046
+
1047
+ def set_selected_signals_for_event_detection(self):
1048
+
1049
+ model_complete_path = locate_signal_model(self.signal_models_list.currentText())
1050
+ input_config_path = model_complete_path+"config_input.json"
1051
+ new_channels = [self.signalChannelWidget.channel_cbs[i].currentText() for i in range(len(self.signalChannelWidget.channel_cbs))]
1052
+ with open(input_config_path) as config_file:
1053
+ input_config = json.load(config_file)
1054
+
1055
+ input_config.update({'selected_channels': new_channels})
1056
+
1057
+ #input_config['channels'] = new_channels
1058
+ with open(input_config_path, 'w') as f:
1059
+ json.dump(input_config, f, indent=4)
1060
+
1061
+ self.signalChannelsSet = True
1062
+ self.signalChannelWidget.close()
1063
+ self.process_population()
1064
+
986
1065
 
987
1066
 
988
1067
  class NeighPanel(QFrame, Styles):
@@ -1034,11 +1113,10 @@ class NeighPanel(QFrame, Styles):
1034
1113
 
1035
1114
  def collapse_advanced(self):
1036
1115
 
1037
- effector_open = not self.parent_window.ProcessEffectors.ContentsFrame.isHidden()
1038
- targets_open = not self.parent_window.ProcessTargets.ContentsFrame.isHidden()
1116
+ panels_open = [not p.ContentsFrame.isHidden() for p in self.parent_window.ProcessPopulations]
1039
1117
  interactions_open = not self.parent_window.NeighPanel.ContentsFrame.isHidden()
1040
1118
  preprocessing_open = not self.parent_window.PreprocessingPanel.ContentsFrame.isHidden()
1041
- is_open = np.array([effector_open, targets_open, interactions_open, preprocessing_open])
1119
+ is_open = np.array(panels_open+[interactions_open, preprocessing_open])
1042
1120
 
1043
1121
  if self.ContentsFrame.isHidden():
1044
1122
  self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
@@ -1432,6 +1510,7 @@ class NeighPanel(QFrame, Styles):
1432
1510
  for pos_idx in pos_indices:
1433
1511
 
1434
1512
  self.pos = natsorted(glob(well+f"{os.path.split(well)[-1].replace('W','').replace(os.sep,'')}*{os.sep}"))[pos_idx]
1513
+ self.pos_name = extract_position_name(self.pos)
1435
1514
  print(f"Position {self.pos}...\nLoading stack movie...")
1436
1515
 
1437
1516
  if not os.path.exists(self.pos + 'output' + os.sep):
@@ -1442,39 +1521,27 @@ class NeighPanel(QFrame, Styles):
1442
1521
  if self.neigh_action.isChecked():
1443
1522
  for protocol in self.protocols:
1444
1523
 
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
- )
1524
+ 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
1525
+ self.job = ProgressWindow(NeighborhoodProcess, parent_window=self, title="Neighborhood",
1526
+ process_args=process_args)
1527
+ result = self.job.exec_()
1528
+ if result == QDialog.Accepted:
1529
+ pass
1530
+ elif result == QDialog.Rejected:
1531
+ return None
1532
+
1470
1533
  if self.measure_pairs_action.isChecked():
1471
1534
  rel_measure_at_position(self.pos)
1472
1535
 
1473
1536
  if self.signal_analysis_action.isChecked():
1474
1537
 
1475
- analyze_pair_signals_at_position(self.pos, self.pair_signal_models_list.currentText(), use_gpu=self.parent_window.parent_window.use_gpu)
1538
+ 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
1539
 
1477
1540
  self.parent_window.update_position_options()
1541
+ for action in [self.neigh_action, self.measure_pairs_action, self.signal_analysis_action]:
1542
+ if action.isChecked():
1543
+ action.setChecked(False)
1544
+
1478
1545
  print('Done.')
1479
1546
 
1480
1547
  def check_signals2(self):
@@ -1484,14 +1551,6 @@ class NeighPanel(QFrame, Styles):
1484
1551
  self.SignalAnnotator2 = SignalAnnotator2(self)
1485
1552
  self.SignalAnnotator2.show()
1486
1553
 
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
-
1494
-
1495
1554
 
1496
1555
  class PreprocessingPanel(QFrame, Styles):
1497
1556
 
@@ -1529,13 +1588,13 @@ class PreprocessingPanel(QFrame, Styles):
1529
1588
 
1530
1589
  self.grid.addWidget(panel_title, 0, 0, 1, 4, alignment=Qt.AlignCenter)
1531
1590
 
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)
1591
+ # self.select_all_btn = QPushButton()
1592
+ # self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
1593
+ # self.select_all_btn.setIconSize(QSize(20, 20))
1594
+ # self.all_ticked = False
1595
+ # #self.select_all_btn.clicked.connect(self.tick_all_actions)
1596
+ # self.select_all_btn.setStyleSheet(self.button_select_all)
1597
+ # self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
1539
1598
  #self.to_disable.append(self.all_tc_actions)
1540
1599
 
1541
1600
  self.collapse_btn = QPushButton()
@@ -1553,12 +1612,11 @@ class PreprocessingPanel(QFrame, Styles):
1553
1612
 
1554
1613
  def collapse_advanced(self):
1555
1614
 
1556
- effector_open = not self.parent_window.ProcessEffectors.ContentsFrame.isHidden()
1557
- targets_open = not self.parent_window.ProcessTargets.ContentsFrame.isHidden()
1615
+ panels_open = [not p.ContentsFrame.isHidden() for p in self.parent_window.ProcessPopulations]
1558
1616
  interactions_open = not self.parent_window.NeighPanel.ContentsFrame.isHidden()
1559
1617
  preprocessing_open = not self.parent_window.PreprocessingPanel.ContentsFrame.isHidden()
1560
- is_open = np.array([effector_open, targets_open, interactions_open, preprocessing_open])
1561
-
1618
+ is_open = np.array(panels_open+[interactions_open, preprocessing_open])
1619
+
1562
1620
  if self.ContentsFrame.isHidden():
1563
1621
  self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
1564
1622
  self.collapse_btn.setIconSize(QSize(20, 20))
@@ -1592,7 +1650,6 @@ class PreprocessingPanel(QFrame, Styles):
1592
1650
  self.help_background_btn.setToolTip("Help.")
1593
1651
 
1594
1652
  self.protocol_layout.title_layout.addWidget(self.help_background_btn, 5, alignment=Qt.AlignRight)
1595
-
1596
1653
 
1597
1654
  self.channel_offset_correction_layout = QVBoxLayout()
1598
1655