celldetective 1.2.1__py3-none-any.whl → 1.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. celldetective/__main__.py +12 -5
  2. celldetective/events.py +28 -2
  3. celldetective/gui/about.py +0 -1
  4. celldetective/gui/analyze_block.py +3 -18
  5. celldetective/gui/btrack_options.py +126 -21
  6. celldetective/gui/classifier_widget.py +67 -111
  7. celldetective/gui/configure_new_exp.py +37 -4
  8. celldetective/gui/control_panel.py +14 -30
  9. celldetective/gui/generic_signal_plot.py +793 -0
  10. celldetective/gui/gui_utils.py +401 -226
  11. celldetective/gui/json_readers.py +0 -2
  12. celldetective/gui/layouts.py +269 -25
  13. celldetective/gui/measurement_options.py +14 -23
  14. celldetective/gui/neighborhood_options.py +3 -15
  15. celldetective/gui/plot_measurements.py +10 -23
  16. celldetective/gui/plot_signals_ui.py +53 -687
  17. celldetective/gui/process_block.py +320 -186
  18. celldetective/gui/retrain_segmentation_model_options.py +30 -47
  19. celldetective/gui/retrain_signal_model_options.py +5 -14
  20. celldetective/gui/seg_model_loader.py +129 -113
  21. celldetective/gui/signal_annotator.py +89 -99
  22. celldetective/gui/signal_annotator2.py +5 -9
  23. celldetective/gui/styles.py +32 -0
  24. celldetective/gui/survival_ui.py +49 -712
  25. celldetective/gui/tableUI.py +0 -1
  26. celldetective/gui/thresholds_gui.py +38 -11
  27. celldetective/gui/viewers.py +6 -7
  28. celldetective/io.py +60 -82
  29. celldetective/measure.py +374 -15
  30. celldetective/neighborhood.py +1 -7
  31. celldetective/preprocessing.py +2 -4
  32. celldetective/relative_measurements.py +0 -3
  33. celldetective/scripts/analyze_signals.py +0 -1
  34. celldetective/scripts/measure_cells.py +1 -3
  35. celldetective/scripts/measure_relative.py +1 -2
  36. celldetective/scripts/segment_cells.py +16 -12
  37. celldetective/scripts/segment_cells_thresholds.py +17 -10
  38. celldetective/scripts/track_cells.py +18 -18
  39. celldetective/scripts/train_segmentation_model.py +1 -2
  40. celldetective/scripts/train_signal_model.py +0 -3
  41. celldetective/segmentation.py +1 -1
  42. celldetective/signals.py +17 -8
  43. celldetective/tracking.py +2 -1
  44. celldetective/utils.py +42 -2
  45. {celldetective-1.2.1.dist-info → celldetective-1.2.2.dist-info}/METADATA +19 -12
  46. celldetective-1.2.2.dist-info/RECORD +92 -0
  47. {celldetective-1.2.1.dist-info → celldetective-1.2.2.dist-info}/WHEEL +1 -1
  48. celldetective-1.2.1.dist-info/RECORD +0 -91
  49. {celldetective-1.2.1.dist-info → celldetective-1.2.2.dist-info}/LICENSE +0 -0
  50. {celldetective-1.2.1.dist-info → celldetective-1.2.2.dist-info}/entry_points.txt +0 -0
  51. {celldetective-1.2.1.dist-info → celldetective-1.2.2.dist-info}/top_level.txt +0 -0
@@ -1,46 +1,41 @@
1
- from PyQt5.QtWidgets import QFrame, QGridLayout, QRadioButton, QButtonGroup, QGroupBox, QComboBox,QTabWidget,QSizePolicy,QListWidget, QDialog, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, \
2
- QMessageBox, QWidget, QLineEdit, QScrollArea, QSpacerItem, QLayout, QSizePolicy
1
+ from PyQt5.QtWidgets import QFrame, QGridLayout, QComboBox, QListWidget, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, \
2
+ QMessageBox, QWidget
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
- from PyQt5.QtGui import QIcon, QDoubleValidator, QIntValidator
7
+ from PyQt5.QtGui import QDoubleValidator, QIntValidator
8
8
 
9
9
  from celldetective.gui.signal_annotator import MeasureAnnotator
10
10
  from celldetective.gui.signal_annotator2 import SignalAnnotator2
11
11
  from celldetective.io import get_segmentation_models_list, control_segmentation_napari, get_signal_models_list, \
12
- control_tracking_btrack, load_experiment_tables, get_pair_signal_models_list, get_position_pickle, get_position_table
13
- from celldetective.io import locate_segmentation_model, auto_load_number_of_frames, load_frames, locate_signal_model
12
+ control_tracking_btrack, load_experiment_tables, get_pair_signal_models_list
13
+ from celldetective.io import locate_segmentation_model, fix_missing_labels, auto_load_number_of_frames, load_frames, locate_signal_model
14
14
  from celldetective.gui import SegmentationModelLoader, ClassifierWidget, ConfigNeighborhoods, ConfigSegmentationModelTraining, ConfigTracking, SignalAnnotator, ConfigSignalModelTraining, ConfigMeasurements, ConfigSignalAnnotator, TableUI
15
15
  from celldetective.gui.gui_utils import QHSeperationLine
16
16
  from celldetective.relative_measurements import rel_measure_at_position
17
17
  from celldetective.segmentation import segment_at_position, segment_from_threshold_at_position
18
18
  from celldetective.tracking import track_at_position
19
19
  from celldetective.measure import measure_at_position
20
- from celldetective.signals import analyze_signals_at_position, analyze_pair_signals, analyze_pair_signals_at_position
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
23
23
  from glob import glob
24
24
  from natsort import natsorted
25
- from superqt import QLabeledDoubleSlider, QLabeledSlider, QLabeledRangeSlider, QLabeledSlider, QLabeledDoubleRangeSlider
26
25
  import os
27
26
  import pandas as pd
28
- from tqdm import tqdm
29
27
  from celldetective.gui.gui_utils import center_window
30
28
  from tifffile import imwrite
31
29
  import json
32
30
  import psutil
33
31
  from celldetective.neighborhood import compute_neighborhood_at_position, compute_contact_neighborhood_at_position
34
32
  from celldetective.gui.gui_utils import FigureCanvas
35
- import matplotlib.pyplot as plt
36
- from celldetective.filters import std_filter, median_filter, gauss_filter
37
- from stardist import fill_label_holes
38
- from celldetective.preprocessing import correct_background_model_free, estimate_background_per_condition, correct_background_model
33
+ from celldetective.preprocessing import correct_background_model_free, correct_background_model
39
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
40
- from celldetective.gui.gui_utils import ThresholdLineEdit, QuickSliderLayout
41
- from celldetective.gui.viewers import StackVisualizer, CellSizeViewer, ThresholdedStackVisualizer
42
- from celldetective.gui.layouts import BackgroundModelFreeCorrectionLayout, ProtocolDesignerLayout, BackgroundFitCorrectionLayout, OperationLayout
35
+ from celldetective.gui.gui_utils import ThresholdLineEdit, QuickSliderLayout, help_generic
36
+ from celldetective.gui.layouts import CellposeParamsWidget, StarDistParamsWidget, BackgroundModelFreeCorrectionLayout, ProtocolDesignerLayout, BackgroundFitCorrectionLayout
43
37
  from celldetective.gui import Styles
38
+ from celldetective.utils import get_software_location
44
39
 
45
40
  class ProcessPanel(QFrame, Styles):
46
41
  def __init__(self, parent_window, mode):
@@ -70,29 +65,45 @@ class ProcessPanel(QFrame, Styles):
70
65
 
71
66
  """
72
67
 
73
- panel_title = QLabel(f"PROCESS {self.mode.upper()}")
68
+ panel_title = QLabel(f"PROCESS {self.mode.upper()} ")
74
69
  panel_title.setStyleSheet("""
75
70
  font-weight: bold;
76
71
  padding: 0px;
77
72
  """)
78
73
 
74
+ title_hbox = QHBoxLayout()
79
75
  self.grid.addWidget(panel_title, 0, 0, 1, 4, alignment=Qt.AlignCenter)
80
76
 
77
+ self.help_pop_btn = QPushButton()
78
+ self.help_pop_btn.setIcon(icon(MDI6.help_circle,color=self.help_color))
79
+ self.help_pop_btn.setIconSize(QSize(20, 20))
80
+ self.help_pop_btn.clicked.connect(self.help_population)
81
+ self.help_pop_btn.setStyleSheet(self.button_select_all)
82
+ self.help_pop_btn.setToolTip("Help.")
83
+ #self.grid.addWidget(self.help_pop_btn, 0, 0, 1, 3, alignment=Qt.AlignRight)
84
+
85
+
81
86
  self.select_all_btn = QPushButton()
82
87
  self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
83
88
  self.select_all_btn.setIconSize(QSize(20, 20))
84
89
  self.all_ticked = False
85
90
  self.select_all_btn.clicked.connect(self.tick_all_actions)
86
91
  self.select_all_btn.setStyleSheet(self.button_select_all)
87
- self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
92
+ #self.grid.addWidget(self.select_all_btn, 0, 0, 1, 4, alignment=Qt.AlignLeft)
88
93
  #self.to_disable.append(self.all_tc_actions)
89
94
 
90
95
  self.collapse_btn = QPushButton()
91
96
  self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
92
97
  self.collapse_btn.setIconSize(QSize(25, 25))
93
98
  self.collapse_btn.setStyleSheet(self.button_select_all)
94
- self.grid.addWidget(self.collapse_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
99
+ #self.grid.addWidget(self.collapse_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
100
+
101
+ title_hbox.addWidget(self.select_all_btn, 5)
102
+ title_hbox.addWidget(QLabel(), 85, alignment=Qt.AlignCenter)
103
+ title_hbox.addWidget(self.help_pop_btn, 5)
104
+ title_hbox.addWidget(self.collapse_btn, 5)
95
105
 
106
+ self.grid.addLayout(title_hbox, 0,0,1,4)
96
107
  self.populate_contents()
97
108
 
98
109
  self.grid.addWidget(self.ContentsFrame, 1, 0, 1, 4, alignment=Qt.AlignTop)
@@ -101,22 +112,48 @@ class ProcessPanel(QFrame, Styles):
101
112
  self.ContentsFrame.hide()
102
113
 
103
114
  def collapse_advanced(self):
115
+
116
+ effector_open = not self.parent_window.ProcessEffectors.ContentsFrame.isHidden()
117
+ targets_open = not self.parent_window.ProcessTargets.ContentsFrame.isHidden()
118
+ interactions_open = not self.parent_window.NeighPanel.ContentsFrame.isHidden()
119
+ preprocessing_open = not self.parent_window.PreprocessingPanel.ContentsFrame.isHidden()
120
+ is_open = np.array([effector_open, targets_open, interactions_open, preprocessing_open])
121
+
104
122
  if self.ContentsFrame.isHidden():
105
123
  self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
106
124
  self.collapse_btn.setIconSize(QSize(20, 20))
107
- self.parent_window.scroll.setMinimumHeight(int(550))
108
- #self.parent.w.adjustSize()
109
- self.parent_window.adjustSize()
110
- #self.parent.scroll.adjustSize()
125
+ if len(is_open[is_open])==0:
126
+ self.parent_window.scroll.setMinimumHeight(int(550))
127
+ self.parent_window.adjustSize()
111
128
  else:
112
129
  self.collapse_btn.setIcon(icon(MDI6.chevron_up,color="black"))
113
130
  self.collapse_btn.setIconSize(QSize(20, 20))
114
- #self.parent.w.adjustSize()
115
- #self.parent.adjustSize()
116
131
  self.parent_window.scroll.setMinimumHeight(min(int(930), int(0.9*self.parent_window.screen_height)))
117
- self.parent_window.scroll.setMinimumWidth(425)
118
132
 
119
- #self.parent.scroll.adjustSize()
133
+
134
+ def help_population(self):
135
+
136
+ """
137
+ Helper to choose a proper cell population structure.
138
+ """
139
+
140
+ dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','cell-populations.json'])
141
+
142
+ with open(dict_path) as f:
143
+ d = json.load(f)
144
+
145
+ suggestion = help_generic(d)
146
+ if isinstance(suggestion, str):
147
+ print(f"{suggestion=}")
148
+ msgBox = QMessageBox()
149
+ msgBox.setIcon(QMessageBox.Information)
150
+ msgBox.setTextFormat(Qt.RichText)
151
+ msgBox.setText(suggestion)
152
+ msgBox.setWindowTitle("Info")
153
+ msgBox.setStandardButtons(QMessageBox.Ok)
154
+ returnValue = msgBox.exec()
155
+ if returnValue == QMessageBox.Ok:
156
+ return None
120
157
 
121
158
  def populate_contents(self):
122
159
  self.ContentsFrame = QFrame()
@@ -265,7 +302,7 @@ class ProcessPanel(QFrame, Styles):
265
302
  padding-left: 10px;
266
303
  padding-top: 5px;
267
304
  """)
268
- grid_track.addWidget(self.track_action, 80)
305
+ grid_track.addWidget(self.track_action, 75)
269
306
  #self.to_disable.append(self.track_action_tc)
270
307
 
271
308
  # self.show_track_table_btn = QPushButton()
@@ -304,6 +341,14 @@ class ProcessPanel(QFrame, Styles):
304
341
  self.track_config_btn.clicked.connect(self.open_tracking_configuration_ui)
305
342
  grid_track.addWidget(self.track_config_btn, 6) #4,2,1,1, alignment=Qt.AlignRight
306
343
 
344
+ self.help_track_btn = QPushButton()
345
+ self.help_track_btn.setIcon(icon(MDI6.help_circle,color=self.help_color))
346
+ self.help_track_btn.setIconSize(QSize(20, 20))
347
+ self.help_track_btn.clicked.connect(self.help_tracking)
348
+ self.help_track_btn.setStyleSheet(self.button_select_all)
349
+ self.help_track_btn.setToolTip("Help.")
350
+ grid_track.addWidget(self.help_track_btn, 6) #4,2,1,1, alignment=Qt.AlignRight
351
+
307
352
  self.grid_contents.addLayout(grid_track, 4, 0, 1,4)
308
353
 
309
354
  def delete_tracks(self):
@@ -352,9 +397,15 @@ class ProcessPanel(QFrame, Styles):
352
397
  self.check_seg_btn.clicked.connect(self.check_segmentation)
353
398
  self.check_seg_btn.setStyleSheet(self.button_select_all)
354
399
  self.check_seg_btn.setToolTip("View segmentation output in napari.")
355
- #self.check_seg_btn.setEnabled(False)
356
- #self.to_disable.append(self.control_target_seg)
357
- grid_segment.addWidget(self.check_seg_btn, 10)
400
+ grid_segment.addWidget(self.check_seg_btn, 5)
401
+
402
+ self.help_seg_btn = QPushButton()
403
+ self.help_seg_btn.setIcon(icon(MDI6.help_circle,color=self.help_color))
404
+ self.help_seg_btn.setIconSize(QSize(20, 20))
405
+ self.help_seg_btn.clicked.connect(self.help_segmentation)
406
+ self.help_seg_btn.setStyleSheet(self.button_select_all)
407
+ self.help_seg_btn.setToolTip("Help.")
408
+ grid_segment.addWidget(self.help_seg_btn, 5)
358
409
  self.grid_contents.addLayout(grid_segment, 0,0,1,4)
359
410
 
360
411
  seg_option_vbox = QVBoxLayout()
@@ -391,6 +442,107 @@ class ProcessPanel(QFrame, Styles):
391
442
  self.seg_model_list.setEnabled(False)
392
443
  self.grid_contents.addLayout(seg_option_vbox, 2, 0, 1, 4)
393
444
 
445
+ def help_segmentation(self):
446
+
447
+ """
448
+ Widget with different decision helper decision trees.
449
+ """
450
+
451
+ self.help_w = QWidget()
452
+ self.help_w.setWindowTitle('Helper')
453
+ layout = QVBoxLayout()
454
+ seg_strategy_btn = QPushButton('A guide to choose a segmentation strategy.')
455
+ seg_strategy_btn.setIcon(icon(MDI6.help_circle,color=self.celldetective_blue))
456
+ seg_strategy_btn.setIconSize(QSize(40, 40))
457
+ seg_strategy_btn.setStyleSheet(self.button_style_sheet_5)
458
+ seg_strategy_btn.clicked.connect(self.help_seg_strategy)
459
+
460
+ dl_strategy_btn = QPushButton('A guide to choose your Deep learning segmentation strategy.')
461
+ dl_strategy_btn.setIcon(icon(MDI6.help_circle,color=self.celldetective_blue))
462
+ dl_strategy_btn.setIconSize(QSize(40, 40))
463
+ dl_strategy_btn.setStyleSheet(self.button_style_sheet_5)
464
+ dl_strategy_btn.clicked.connect(self.help_seg_dl_strategy)
465
+
466
+ layout.addWidget(seg_strategy_btn)
467
+ layout.addWidget(dl_strategy_btn)
468
+
469
+ self.help_w.setLayout(layout)
470
+ center_window(self.help_w)
471
+ self.help_w.show()
472
+
473
+ return None
474
+
475
+ def help_seg_strategy(self):
476
+
477
+ """
478
+ Helper for segmentation strategy between threshold-based and Deep learning.
479
+ """
480
+
481
+ dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','Threshold-vs-DL.json'])
482
+
483
+ with open(dict_path) as f:
484
+ d = json.load(f)
485
+
486
+ suggestion = help_generic(d)
487
+ if isinstance(suggestion, str):
488
+ print(f"{suggestion=}")
489
+ msgBox = QMessageBox()
490
+ msgBox.setIcon(QMessageBox.Information)
491
+ msgBox.setTextFormat(Qt.RichText)
492
+ msgBox.setText(f"The suggested technique is {suggestion}.\nSee a tutorial <a href='https://celldetective.readthedocs.io/en/latest/segment.html'>here</a>.")
493
+ msgBox.setWindowTitle("Info")
494
+ msgBox.setStandardButtons(QMessageBox.Ok)
495
+ returnValue = msgBox.exec()
496
+ if returnValue == QMessageBox.Ok:
497
+ return None
498
+
499
+ def help_seg_dl_strategy(self):
500
+
501
+ """
502
+ Helper for DL segmentation strategy, between pretrained models and custom models.
503
+ """
504
+
505
+ dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','DL-segmentation-strategy.json'])
506
+
507
+ with open(dict_path) as f:
508
+ d = json.load(f)
509
+
510
+ suggestion = help_generic(d)
511
+ if isinstance(suggestion, str):
512
+ print(f"{suggestion=}")
513
+ msgBox = QMessageBox()
514
+ msgBox.setIcon(QMessageBox.Information)
515
+ msgBox.setText(f"The suggested technique is {suggestion}.")
516
+ msgBox.setWindowTitle("Info")
517
+ msgBox.setStandardButtons(QMessageBox.Ok)
518
+ returnValue = msgBox.exec()
519
+ if returnValue == QMessageBox.Ok:
520
+ return None
521
+
522
+ def help_tracking(self):
523
+
524
+ """
525
+ Helper for segmentation strategy between threshold-based and Deep learning.
526
+ """
527
+
528
+ dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','tracking.json'])
529
+
530
+ with open(dict_path) as f:
531
+ d = json.load(f)
532
+
533
+ suggestion = help_generic(d)
534
+ if isinstance(suggestion, str):
535
+ print(f"{suggestion=}")
536
+ msgBox = QMessageBox()
537
+ msgBox.setIcon(QMessageBox.Information)
538
+ msgBox.setTextFormat(Qt.RichText)
539
+ msgBox.setText(f"{suggestion}")
540
+ msgBox.setWindowTitle("Info")
541
+ msgBox.setStandardButtons(QMessageBox.Ok)
542
+ returnValue = msgBox.exec()
543
+ if returnValue == QMessageBox.Ok:
544
+ return None
545
+
394
546
  def check_segmentation(self):
395
547
 
396
548
  if not os.path.exists(os.sep.join([self.parent_window.pos,f'labels_{self.mode}', os.sep])):
@@ -422,6 +574,24 @@ class ProcessPanel(QFrame, Styles):
422
574
  msgBox.setWindowTitle("Warning")
423
575
  msgBox.setStandardButtons(QMessageBox.Ok)
424
576
  returnValue = msgBox.exec()
577
+
578
+ msgBox = QMessageBox()
579
+ msgBox.setIcon(QMessageBox.Question)
580
+ msgBox.setText("Would you like to pass empty frames to fix the asymmetry?")
581
+ msgBox.setWindowTitle("Question")
582
+ msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
583
+ returnValue = msgBox.exec()
584
+ if returnValue == QMessageBox.Yes:
585
+ print('Fixing the missing labels...')
586
+ fix_missing_labels(self.parent_window.pos, prefix=self.parent_window.movie_prefix,population=self.mode)
587
+ try:
588
+ control_segmentation_napari(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode,flush_memory=True)
589
+ except Exception as e:
590
+ print(f'Error {e}')
591
+ return None
592
+ else:
593
+ return None
594
+
425
595
  gc.collect()
426
596
 
427
597
  def check_signals(self):
@@ -507,27 +677,27 @@ class ProcessPanel(QFrame, Styles):
507
677
  self.all_ticked = True
508
678
 
509
679
  def upload_segmentation_model(self):
510
-
680
+ print('Load a segmentation model or pipeline...')
511
681
  self.SegModelLoader = SegmentationModelLoader(self)
512
682
  self.SegModelLoader.show()
513
683
 
514
684
  def open_tracking_configuration_ui(self):
515
-
685
+ print('Set the tracking parameters...')
516
686
  self.ConfigTracking = ConfigTracking(self)
517
687
  self.ConfigTracking.show()
518
688
 
519
689
  def open_signal_model_config_ui(self):
520
-
690
+ print('Set the training parameters for new signal models...')
521
691
  self.ConfigSignalTrain = ConfigSignalModelTraining(self)
522
692
  self.ConfigSignalTrain.show()
523
693
 
524
694
  def open_segmentation_model_config_ui(self):
525
-
695
+ print('Set the training parameters for a new segmentation model...')
526
696
  self.ConfigSegmentationTrain = ConfigSegmentationModelTraining(self)
527
697
  self.ConfigSegmentationTrain.show()
528
698
 
529
699
  def open_measurement_configuration_ui(self):
530
-
700
+ print('Set the measurements to be performed...')
531
701
  self.ConfigMeasurements = ConfigMeasurements(self)
532
702
  self.ConfigMeasurements.show()
533
703
 
@@ -611,120 +781,14 @@ class ProcessPanel(QFrame, Styles):
611
781
 
612
782
  if self.model_name.startswith('CP') and self.model_name in self.seg_models_generic and not self.cellpose_calibrated:
613
783
 
614
- self.diamWidget = QWidget()
615
- self.diamWidget.setWindowTitle('Estimate diameter')
616
-
617
- layout = QVBoxLayout()
618
- self.diamWidget.setLayout(layout)
619
-
620
- self.view_diameter_btn = QPushButton()
621
- self.view_diameter_btn.setStyleSheet(self.button_select_all)
622
- self.view_diameter_btn.setIcon(icon(MDI6.image_check, color="black"))
623
- self.view_diameter_btn.setToolTip("View stack.")
624
- self.view_diameter_btn.setIconSize(QSize(20, 20))
625
- self.view_diameter_btn.clicked.connect(self.view_current_stack_with_scale_bar)
626
-
627
- self.diameter_le = ThresholdLineEdit(init_value=40, connected_buttons=[self.view_diameter_btn],placeholder='cell diameter in pixels', value_type='float')
628
-
629
- self.cellpose_channel_cb = [QComboBox() for i in range(2)]
630
- self.cellpose_channel_template = ['brightfield_channel', 'live_nuclei_channel']
631
- if self.model_name=="CP_nuclei":
632
- self.cellpose_channel_template = ['live_nuclei_channel', 'None']
633
-
634
-
635
- for k in range(2):
636
- hbox_channel = QHBoxLayout()
637
- hbox_channel.addWidget(QLabel(f'channel {k+1}: '))
638
- hbox_channel.addWidget(self.cellpose_channel_cb[k])
639
- if k==1:
640
- self.cellpose_channel_cb[k].addItems(list(self.exp_channels)+['None'])
641
- else:
642
- self.cellpose_channel_cb[k].addItems(list(self.exp_channels))
643
- idx = self.cellpose_channel_cb[k].findText(self.cellpose_channel_template[k])
644
- if idx>0:
645
- self.cellpose_channel_cb[k].setCurrentIndex(idx)
646
- else:
647
- self.cellpose_channel_cb[k].setCurrentIndex(0)
648
-
649
- if k==1:
650
- idx = self.cellpose_channel_cb[k].findText('None')
651
- self.cellpose_channel_cb[k].setCurrentIndex(idx)
652
-
653
- layout.addLayout(hbox_channel)
654
-
655
- hbox = QHBoxLayout()
656
- hbox.addWidget(QLabel('diameter [px]: '), 33)
657
- hbox.addWidget(self.diameter_le, 61)
658
- hbox.addWidget(self.view_diameter_btn)
659
- layout.addLayout(hbox)
660
-
661
- self.flow_slider = QLabeledDoubleSlider()
662
- self.flow_slider.setOrientation(1)
663
- self.flow_slider.setRange(-6,6)
664
- self.flow_slider.setValue(0.4)
665
-
666
- hbox = QHBoxLayout()
667
- hbox.addWidget(QLabel('flow threshold: '), 33)
668
- hbox.addWidget(self.flow_slider, 66)
669
- layout.addLayout(hbox)
670
-
671
- self.cellprob_slider = QLabeledDoubleSlider()
672
- self.cellprob_slider.setOrientation(1)
673
- self.cellprob_slider.setRange(-6,6)
674
- self.cellprob_slider.setValue(0.)
675
-
676
- hbox = QHBoxLayout()
677
- hbox.addWidget(QLabel('cellprob threshold: '), 33)
678
- hbox.addWidget(self.cellprob_slider, 66)
679
- layout.addLayout(hbox)
680
-
681
- self.set_cellpose_scale_btn = QPushButton('set')
682
- self.set_cellpose_scale_btn.clicked.connect(self.set_cellpose_scale)
683
- layout.addWidget(self.set_cellpose_scale_btn)
684
-
784
+ self.diamWidget = CellposeParamsWidget(self, model_name=self.model_name)
685
785
  self.diamWidget.show()
686
- center_window(self.diamWidget)
687
786
  return None
688
787
 
689
-
690
788
  if self.model_name.startswith('SD') and self.model_name in self.seg_models_generic and not self.stardist_calibrated:
691
789
 
692
- self.diamWidget = QWidget()
693
- self.diamWidget.setWindowTitle('Channels')
694
-
695
- layout = QVBoxLayout()
696
- self.diamWidget.setLayout(layout)
697
-
698
- self.stardist_channel_cb = [QComboBox() for i in range(1)]
699
- self.stardist_channel_template = ['live_nuclei_channel']
700
- max_i = 1
701
- if self.model_name=="SD_versatile_he":
702
- self.stardist_channel_template = ["H&E_1","H&E_2","H&E_3"]
703
- self.stardist_channel_cb = [QComboBox() for i in range(3)]
704
- max_i = 3
705
-
706
- for k in range(max_i):
707
- hbox_channel = QHBoxLayout()
708
- hbox_channel.addWidget(QLabel(f'channel {k+1}: '))
709
- hbox_channel.addWidget(self.stardist_channel_cb[k])
710
- if k==1:
711
- self.stardist_channel_cb[k].addItems(list(self.exp_channels)+['None'])
712
- else:
713
- self.stardist_channel_cb[k].addItems(list(self.exp_channels))
714
- idx = self.stardist_channel_cb[k].findText(self.stardist_channel_template[k])
715
- if idx>0:
716
- self.stardist_channel_cb[k].setCurrentIndex(idx)
717
- else:
718
- self.stardist_channel_cb[k].setCurrentIndex(0)
719
-
720
- layout.addLayout(hbox_channel)
721
-
722
- self.set_stardist_scale_btn = QPushButton('set')
723
- self.set_stardist_scale_btn.clicked.connect(self.set_stardist_scale)
724
- layout.addWidget(self.set_stardist_scale_btn)
725
-
790
+ self.diamWidget = StarDistParamsWidget(self, model_name = self.model_name)
726
791
  self.diamWidget.show()
727
- center_window(self.diamWidget)
728
792
  return None
729
793
 
730
794
 
@@ -823,27 +887,28 @@ class ProcessPanel(QFrame, Styles):
823
887
  # QApplication.restoreOverrideCursor()
824
888
  # self.unfreeze()
825
889
 
826
- def view_current_stack_with_scale_bar(self):
890
+ # def view_current_stack_with_scale_bar(self):
827
891
 
828
- self.parent_window.locate_image()
829
- if self.parent_window.current_stack is not None:
830
- self.viewer = CellSizeViewer(
831
- initial_diameter = float(self.diameter_le.text().replace(',', '.')),
832
- parent_le = self.diameter_le,
833
- stack_path=self.parent_window.current_stack,
834
- window_title=f'Position {self.parent_window.position_list.currentText()}',
835
- frame_slider = True,
836
- contrast_slider = True,
837
- channel_cb = True,
838
- channel_names = self.parent_window.exp_channels,
839
- n_channels = self.parent_window.nbr_channels,
840
- PxToUm = 1,
841
- )
842
- self.viewer.show()
892
+ # self.parent_window.locate_image()
893
+ # if self.parent_window.current_stack is not None:
894
+ # self.viewer = CellSizeViewer(
895
+ # initial_diameter = float(self.diameter_le.text().replace(',', '.')),
896
+ # parent_le = self.diameter_le,
897
+ # stack_path=self.parent_window.current_stack,
898
+ # window_title=f'Position {self.parent_window.position_list.currentText()}',
899
+ # frame_slider = True,
900
+ # contrast_slider = True,
901
+ # channel_cb = True,
902
+ # channel_names = self.parent_window.exp_channels,
903
+ # n_channels = self.parent_window.nbr_channels,
904
+ # PxToUm = 1,
905
+ # )
906
+ # self.viewer.show()
843
907
 
844
908
 
845
909
 
846
910
  def open_napari_tracking(self):
911
+ print(f'View the tracks before post-processing for position {self.parent_window.pos} in napari...')
847
912
  control_tracking_btrack(self.parent_window.pos, prefix=self.parent_window.movie_prefix, population=self.mode, threads=self.parent_window.parent_window.n_threads)
848
913
 
849
914
  def view_table_ui(self):
@@ -910,14 +975,14 @@ class ProcessPanel(QFrame, Styles):
910
975
 
911
976
  def set_cellpose_scale(self):
912
977
 
913
- scale = self.parent_window.PxToUm * float(self.diameter_le.get_threshold()) / 30.0
978
+ scale = self.parent_window.PxToUm * float(self.diamWidget.diameter_le.get_threshold()) / 30.0
914
979
  if self.model_name=="CP_nuclei":
915
- scale = self.parent_window.PxToUm * float(self.diameter_le.get_threshold()) / 17.0
916
- flow_thresh = self.flow_slider.value()
917
- cellprob_thresh = self.cellprob_slider.value()
980
+ scale = self.parent_window.PxToUm * float(self.diamWidget.diameter_le.get_threshold()) / 17.0
981
+ flow_thresh = self.diamWidget.flow_slider.value()
982
+ cellprob_thresh = self.diamWidget.cellprob_slider.value()
918
983
  model_complete_path = locate_segmentation_model(self.model_name)
919
984
  input_config_path = model_complete_path+"config_input.json"
920
- new_channels = [self.cellpose_channel_cb[i].currentText() for i in range(2)]
985
+ new_channels = [self.diamWidget.cellpose_channel_cb[i].currentText() for i in range(2)]
921
986
  print(new_channels)
922
987
  with open(input_config_path) as config_file:
923
988
  input_config = json.load(config_file)
@@ -936,21 +1001,13 @@ class ProcessPanel(QFrame, Styles):
936
1001
 
937
1002
  def set_stardist_scale(self):
938
1003
 
939
- # scale = self.parent.PxToUm * float(self.diameter_le.text()) / 30.0
940
- # if self.model_name=="CP_nuclei":
941
- # scale = self.parent.PxToUm * float(self.diameter_le.text()) / 17.0
942
- # flow_thresh = self.flow_slider.value()
943
- # cellprob_thresh = self.cellprob_slider.value()
944
1004
  model_complete_path = locate_segmentation_model(self.model_name)
945
1005
  input_config_path = model_complete_path+"config_input.json"
946
- new_channels = [self.stardist_channel_cb[i].currentText() for i in range(len(self.stardist_channel_cb))]
1006
+ new_channels = [self.diamWidget.stardist_channel_cb[i].currentText() for i in range(len(self.diamWidget.stardist_channel_cb))]
947
1007
  with open(input_config_path) as config_file:
948
1008
  input_config = json.load(config_file)
949
1009
 
950
- # input_config['spatial_calibration'] = scale
951
1010
  input_config['channels'] = new_channels
952
- # input_config['flow_threshold'] = flow_thresh
953
- # input_config['cellprob_threshold'] = cellprob_thresh
954
1011
  with open(input_config_path, 'w') as f:
955
1012
  json.dump(input_config, f, indent=4)
956
1013
 
@@ -1008,19 +1065,22 @@ class NeighPanel(QFrame, Styles):
1008
1065
 
1009
1066
  def collapse_advanced(self):
1010
1067
 
1068
+ effector_open = not self.parent_window.ProcessEffectors.ContentsFrame.isHidden()
1069
+ targets_open = not self.parent_window.ProcessTargets.ContentsFrame.isHidden()
1070
+ interactions_open = not self.parent_window.NeighPanel.ContentsFrame.isHidden()
1071
+ preprocessing_open = not self.parent_window.PreprocessingPanel.ContentsFrame.isHidden()
1072
+ is_open = np.array([effector_open, targets_open, interactions_open, preprocessing_open])
1073
+
1011
1074
  if self.ContentsFrame.isHidden():
1012
1075
  self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
1013
1076
  self.collapse_btn.setIconSize(QSize(20, 20))
1014
- self.parent_window.scroll.setMinimumHeight(int(550))
1015
- #self.parent.w.adjustSize()
1016
- self.parent_window.adjustSize()
1077
+ if len(is_open[is_open])==0:
1078
+ self.parent_window.scroll.setMinimumHeight(int(550))
1079
+ self.parent_window.adjustSize()
1017
1080
  else:
1018
1081
  self.collapse_btn.setIcon(icon(MDI6.chevron_up,color="black"))
1019
1082
  self.collapse_btn.setIconSize(QSize(20, 20))
1020
- #self.parent.w.adjustSize()
1021
- #self.parent.adjustSize()
1022
1083
  self.parent_window.scroll.setMinimumHeight(min(int(1000), int(0.9*self.parent_window.screen_height)))
1023
- self.parent_window.scroll.setMinimumWidth(425)
1024
1084
 
1025
1085
 
1026
1086
  def populate_contents(self):
@@ -1042,9 +1102,21 @@ class NeighPanel(QFrame, Styles):
1042
1102
  #self.neigh_action.setIconSize(QSize(20, 20))
1043
1103
  self.neigh_action.setToolTip(
1044
1104
  "Compute neighborhoods in list below.")
1105
+
1045
1106
  neigh_option_hbox.addWidget(self.neigh_action,90)
1107
+
1108
+
1109
+ self.help_neigh_btn = QPushButton()
1110
+ self.help_neigh_btn.setIcon(icon(MDI6.help_circle,color=self.help_color))
1111
+ self.help_neigh_btn.setIconSize(QSize(20, 20))
1112
+ self.help_neigh_btn.clicked.connect(self.help_neighborhood)
1113
+ self.help_neigh_btn.setStyleSheet(self.button_select_all)
1114
+ self.help_neigh_btn.setToolTip("Help.")
1115
+ neigh_option_hbox.addWidget(self.help_neigh_btn,5,alignment=Qt.AlignRight)
1116
+
1046
1117
  self.grid_contents.addLayout(neigh_option_hbox, 1,0,1,4)
1047
1118
 
1119
+
1048
1120
  neigh_options_layout = QVBoxLayout()
1049
1121
 
1050
1122
  neigh_options_vbox = QVBoxLayout()
@@ -1233,6 +1305,31 @@ class NeighPanel(QFrame, Styles):
1233
1305
  self.neigh_action.setChecked(False)
1234
1306
 
1235
1307
 
1308
+ def help_neighborhood(self):
1309
+
1310
+ """
1311
+ Helper for neighborhood strategy.
1312
+ """
1313
+
1314
+ dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','neighborhood.json'])
1315
+
1316
+ with open(dict_path) as f:
1317
+ d = json.load(f)
1318
+
1319
+ suggestion = help_generic(d)
1320
+ if isinstance(suggestion, str):
1321
+ print(f"{suggestion=}")
1322
+ msgBox = QMessageBox()
1323
+ msgBox.setIcon(QMessageBox.Information)
1324
+ msgBox.setTextFormat(Qt.RichText)
1325
+ msgBox.setText(f"{suggestion}\nSee a tutorial <a href='https://celldetective.readthedocs.io/en/latest/interactions.html#neighborhood'>here</a>.")
1326
+ msgBox.setWindowTitle("Info")
1327
+ msgBox.setStandardButtons(QMessageBox.Ok)
1328
+ returnValue = msgBox.exec()
1329
+ if returnValue == QMessageBox.Ok:
1330
+ return None
1331
+
1332
+
1236
1333
  def load_available_tables(self):
1237
1334
 
1238
1335
  """
@@ -1520,19 +1617,22 @@ class PreprocessingPanel(QFrame, Styles):
1520
1617
 
1521
1618
  def collapse_advanced(self):
1522
1619
 
1620
+ effector_open = not self.parent_window.ProcessEffectors.ContentsFrame.isHidden()
1621
+ targets_open = not self.parent_window.ProcessTargets.ContentsFrame.isHidden()
1622
+ interactions_open = not self.parent_window.NeighPanel.ContentsFrame.isHidden()
1623
+ preprocessing_open = not self.parent_window.PreprocessingPanel.ContentsFrame.isHidden()
1624
+ is_open = np.array([effector_open, targets_open, interactions_open, preprocessing_open])
1625
+
1523
1626
  if self.ContentsFrame.isHidden():
1524
1627
  self.collapse_btn.setIcon(icon(MDI6.chevron_down,color="black"))
1525
1628
  self.collapse_btn.setIconSize(QSize(20, 20))
1526
- self.parent_window.scroll.setMinimumHeight(int(550))
1527
- #self.parent.w.adjustSize()
1528
- self.parent_window.adjustSize()
1629
+ if len(is_open[is_open])==0:
1630
+ self.parent_window.scroll.setMinimumHeight(int(550))
1631
+ self.parent_window.adjustSize()
1529
1632
  else:
1530
1633
  self.collapse_btn.setIcon(icon(MDI6.chevron_up,color="black"))
1531
1634
  self.collapse_btn.setIconSize(QSize(20, 20))
1532
- #self.parent.w.adjustSize()
1533
- #self.parent.adjustSize()
1534
1635
  self.parent_window.scroll.setMinimumHeight(min(int(930), int(0.9*self.parent_window.screen_height)))
1535
- self.parent_window.scroll.setMinimumWidth(425)
1536
1636
 
1537
1637
  def populate_contents(self):
1538
1638
 
@@ -1547,6 +1647,16 @@ class PreprocessingPanel(QFrame, Styles):
1547
1647
  tab_names=['Fit', 'Model-free'],
1548
1648
  title='BACKGROUND CORRECTION',
1549
1649
  list_title='Corrections to apply:')
1650
+
1651
+ self.help_background_btn = QPushButton()
1652
+ self.help_background_btn.setIcon(icon(MDI6.help_circle,color=self.help_color))
1653
+ self.help_background_btn.setIconSize(QSize(20, 20))
1654
+ self.help_background_btn.clicked.connect(self.help_background)
1655
+ self.help_background_btn.setStyleSheet(self.button_select_all)
1656
+ self.help_background_btn.setToolTip("Help.")
1657
+
1658
+ self.protocol_layout.title_layout.addWidget(self.help_background_btn, 5, alignment=Qt.AlignRight)
1659
+
1550
1660
  self.grid_contents.addLayout(self.protocol_layout,0,0,1,4)
1551
1661
  self.submit_preprocessing_btn = QPushButton("Submit")
1552
1662
  self.submit_preprocessing_btn.setStyleSheet(self.button_style_sheet_2)
@@ -1650,4 +1760,28 @@ class PreprocessingPanel(QFrame, Styles):
1650
1760
  self.current_stack = None
1651
1761
  return None
1652
1762
  else:
1653
- self.current_stack = movies[0]
1763
+ self.current_stack = movies[0]
1764
+
1765
+ def help_background(self):
1766
+
1767
+ """
1768
+ Helper to choose a proper cell population structure.
1769
+ """
1770
+
1771
+ dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','preprocessing.json'])
1772
+
1773
+ with open(dict_path) as f:
1774
+ d = json.load(f)
1775
+
1776
+ suggestion = help_generic(d)
1777
+ if isinstance(suggestion, str):
1778
+ print(f"{suggestion=}")
1779
+ msgBox = QMessageBox()
1780
+ msgBox.setIcon(QMessageBox.Information)
1781
+ msgBox.setTextFormat(Qt.RichText)
1782
+ msgBox.setText(suggestion)
1783
+ msgBox.setWindowTitle("Info")
1784
+ msgBox.setStandardButtons(QMessageBox.Ok)
1785
+ returnValue = msgBox.exec()
1786
+ if returnValue == QMessageBox.Ok:
1787
+ return None