celldetective 1.0.2.post1__py3-none-any.whl → 1.1.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 (63) hide show
  1. celldetective/__main__.py +7 -21
  2. celldetective/events.py +2 -44
  3. celldetective/extra_properties.py +62 -52
  4. celldetective/filters.py +4 -5
  5. celldetective/gui/__init__.py +1 -1
  6. celldetective/gui/analyze_block.py +37 -10
  7. celldetective/gui/btrack_options.py +24 -23
  8. celldetective/gui/classifier_widget.py +62 -19
  9. celldetective/gui/configure_new_exp.py +32 -35
  10. celldetective/gui/control_panel.py +120 -81
  11. celldetective/gui/gui_utils.py +674 -396
  12. celldetective/gui/json_readers.py +7 -6
  13. celldetective/gui/layouts.py +756 -0
  14. celldetective/gui/measurement_options.py +98 -513
  15. celldetective/gui/neighborhood_options.py +322 -270
  16. celldetective/gui/plot_measurements.py +1114 -0
  17. celldetective/gui/plot_signals_ui.py +21 -20
  18. celldetective/gui/process_block.py +449 -169
  19. celldetective/gui/retrain_segmentation_model_options.py +27 -26
  20. celldetective/gui/retrain_signal_model_options.py +25 -24
  21. celldetective/gui/seg_model_loader.py +31 -27
  22. celldetective/gui/signal_annotator.py +2326 -2295
  23. celldetective/gui/signal_annotator_options.py +18 -16
  24. celldetective/gui/styles.py +16 -1
  25. celldetective/gui/survival_ui.py +67 -39
  26. celldetective/gui/tableUI.py +337 -48
  27. celldetective/gui/thresholds_gui.py +75 -71
  28. celldetective/gui/viewers.py +743 -0
  29. celldetective/io.py +247 -27
  30. celldetective/measure.py +43 -263
  31. celldetective/models/segmentation_effectors/primNK_cfse/config_input.json +29 -0
  32. celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer +0 -0
  33. celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json +37 -0
  34. celldetective/neighborhood.py +498 -27
  35. celldetective/preprocessing.py +1023 -0
  36. celldetective/scripts/analyze_signals.py +7 -0
  37. celldetective/scripts/measure_cells.py +12 -0
  38. celldetective/scripts/segment_cells.py +20 -4
  39. celldetective/scripts/track_cells.py +11 -0
  40. celldetective/scripts/train_segmentation_model.py +35 -34
  41. celldetective/segmentation.py +14 -9
  42. celldetective/signals.py +234 -329
  43. celldetective/tracking.py +2 -2
  44. celldetective/utils.py +602 -49
  45. celldetective-1.1.1.dist-info/METADATA +305 -0
  46. celldetective-1.1.1.dist-info/RECORD +84 -0
  47. {celldetective-1.0.2.post1.dist-info → celldetective-1.1.1.dist-info}/top_level.txt +1 -0
  48. tests/__init__.py +0 -0
  49. tests/test_events.py +28 -0
  50. tests/test_filters.py +24 -0
  51. tests/test_io.py +70 -0
  52. tests/test_measure.py +141 -0
  53. tests/test_neighborhood.py +70 -0
  54. tests/test_preprocessing.py +37 -0
  55. tests/test_segmentation.py +93 -0
  56. tests/test_signals.py +135 -0
  57. tests/test_tracking.py +164 -0
  58. tests/test_utils.py +118 -0
  59. celldetective-1.0.2.post1.dist-info/METADATA +0 -221
  60. celldetective-1.0.2.post1.dist-info/RECORD +0 -66
  61. {celldetective-1.0.2.post1.dist-info → celldetective-1.1.1.dist-info}/LICENSE +0 -0
  62. {celldetective-1.0.2.post1.dist-info → celldetective-1.1.1.dist-info}/WHEEL +0 -0
  63. {celldetective-1.0.2.post1.dist-info → celldetective-1.1.1.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  from PyQt5.QtWidgets import QWidget, QLineEdit, QMessageBox, QHBoxLayout, QVBoxLayout, QPushButton, QLabel, QComboBox, \
2
- QCheckBox
2
+ QCheckBox, QRadioButton, QButtonGroup
3
3
  from celldetective.gui.gui_utils import FigureCanvas, center_window, color_from_class
4
4
  import numpy as np
5
5
  import matplotlib.pyplot as plt
@@ -10,25 +10,27 @@ from PyQt5.QtCore import Qt, QSize
10
10
  import os
11
11
  from sklearn.metrics import r2_score
12
12
  from scipy.optimize import curve_fit
13
+ from celldetective.gui import Styles
14
+ from math import ceil
13
15
 
14
16
  def step_function(t, t_shift, dt):
15
17
  return 1/(1+np.exp(-(t-t_shift)/dt))
16
18
 
17
- class ClassifierWidget(QWidget):
19
+ class ClassifierWidget(QWidget, Styles):
18
20
 
19
- def __init__(self, parent):
21
+ def __init__(self, parent_window):
20
22
 
21
23
  super().__init__()
22
24
 
23
- self.parent = parent
24
- self.screen_height = self.parent.parent.parent.screen_height
25
- self.screen_width = self.parent.parent.parent.screen_width
25
+ self.parent_window = parent_window
26
+ self.screen_height = self.parent_window.parent_window.parent_window.screen_height
27
+ self.screen_width = self.parent_window.parent_window.parent_window.screen_width
26
28
  self.currentAlpha = 1.0
27
29
 
28
30
  self.setWindowTitle("Custom classification")
29
31
 
30
- self.mode = self.parent.mode
31
- self.df = self.parent.df
32
+ self.mode = self.parent_window.mode
33
+ self.df = self.parent_window.df
32
34
 
33
35
  is_number = np.vectorize(lambda x: np.issubdtype(x, np.number))
34
36
  is_number_test = is_number(self.df.dtypes)
@@ -53,7 +55,7 @@ class ClassifierWidget(QWidget):
53
55
  fig_btn_hbox = QHBoxLayout()
54
56
  fig_btn_hbox.addWidget(QLabel(''), 95)
55
57
  self.project_times_btn = QPushButton('')
56
- self.project_times_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
58
+ self.project_times_btn.setStyleSheet(self.parent_window.parent_window.parent_window.button_select_all)
57
59
  self.project_times_btn.setIcon(icon(MDI6.math_integral,color="black"))
58
60
  self.project_times_btn.setToolTip("Project measurements at all times.")
59
61
  self.project_times_btn.setIconSize(QSize(20, 20))
@@ -111,7 +113,7 @@ class ClassifierWidget(QWidget):
111
113
  self.features_cb[i].setCurrentIndex(i)
112
114
 
113
115
  self.log_btns[i].setIcon(icon(MDI6.math_log,color="black"))
114
- self.log_btns[i].setStyleSheet(self.parent.parent.parent.button_select_all)
116
+ self.log_btns[i].setStyleSheet(self.button_select_all)
115
117
  self.log_btns[i].clicked.connect(lambda ch, i=i: self.switch_to_log(i))
116
118
 
117
119
  hbox_classify = QHBoxLayout()
@@ -124,19 +126,47 @@ class ClassifierWidget(QWidget):
124
126
  hbox_classify.addWidget(self.submit_query_btn, 20)
125
127
  layout.addLayout(hbox_classify)
126
128
 
127
- self.time_corr = QCheckBox('Time correlated event')
129
+ self.time_corr = QCheckBox('Time correlated')
130
+ self.time_corr.toggled.connect(self.activate_time_corr_options)
128
131
  if "TRACK_ID" in self.df.columns:
129
132
  self.time_corr.setEnabled(True)
130
133
  else:
131
134
  self.time_corr.setEnabled(False)
132
135
  layout.addWidget(self.time_corr,alignment=Qt.AlignCenter)
136
+
137
+ self.irreversible_event_btn = QRadioButton('irreversible event')
138
+ self.unique_state_btn = QRadioButton('unique state')
139
+ self.time_corr_options = [self.irreversible_event_btn, self.unique_state_btn]
140
+ time_corr_btn_group = QButtonGroup()
141
+ self.unique_state_btn.click()
142
+ for btn in self.time_corr_options:
143
+ time_corr_btn_group.addButton(btn)
144
+ btn.setEnabled(False)
145
+
146
+ time_corr_layout = QHBoxLayout()
147
+ time_corr_layout.addWidget(self.unique_state_btn, 50, alignment=Qt.AlignCenter)
148
+ time_corr_layout.addWidget(self.irreversible_event_btn, 50,alignment=Qt.AlignCenter)
149
+ layout.addLayout(time_corr_layout)
150
+
151
+ layout.addWidget(QLabel())
152
+
133
153
  self.submit_btn = QPushButton('apply')
154
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
134
155
  self.submit_btn.clicked.connect(self.submit_classification)
135
156
  layout.addWidget(self.submit_btn, 30)
136
157
 
137
158
  self.frame_slider.valueChanged.connect(self.set_frame)
138
159
  self.alpha_slider.valueChanged.connect(self.set_transparency)
139
160
 
161
+ def activate_time_corr_options(self):
162
+
163
+ if self.time_corr.isChecked():
164
+ for btn in self.time_corr_options:
165
+ btn.setEnabled(True)
166
+ else:
167
+ for btn in self.time_corr_options:
168
+ btn.setEnabled(False)
169
+
140
170
  def init_class(self):
141
171
 
142
172
  self.class_name = 'custom'
@@ -277,14 +307,27 @@ class ClassifierWidget(QWidget):
277
307
  for tid,track in self.df.groupby(['position','TRACK_ID']):
278
308
  indices = track[self.class_name_user].index
279
309
  status_values = track[stat_col].to_numpy()
280
- if np.all([s==0 for s in status_values]):
281
- self.df.loc[indices, self.class_name_user] = 1
282
- elif np.all([s==1 for s in status_values]):
283
- self.df.loc[indices, self.class_name_user] = 2
284
- self.df.loc[indices, self.class_name_user.replace('class','status')] = 2
285
- else:
286
- self.df.loc[indices, self.class_name_user] = 2
287
- self.estimate_time()
310
+ if self.irreversible_event_btn.isChecked():
311
+ if np.all([s==0 for s in status_values]):
312
+ self.df.loc[indices, self.class_name_user] = 1
313
+ elif np.all([s==1 for s in status_values]):
314
+ self.df.loc[indices, self.class_name_user] = 2
315
+ self.df.loc[indices, self.class_name_user.replace('class','status')] = 2
316
+ else:
317
+ self.df.loc[indices, self.class_name_user] = 2
318
+ elif self.unique_state_btn.isChecked():
319
+ frames = track['FRAME'].to_numpy()
320
+ t_first = track['t_firstdetection'].to_numpy()[0]
321
+ median_status = np.nanmedian(status_values[frames>=t_first])
322
+ c = ceil(median_status)
323
+ if c==0:
324
+ self.df.loc[indices, self.class_name_user] = 1
325
+ self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
326
+ elif c==1:
327
+ self.df.loc[indices, self.class_name_user] = 2
328
+ self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
329
+ if self.irreversible_event_btn.isChecked():
330
+ self.estimate_time()
288
331
  else:
289
332
  self.group_name_user = 'group_' + self.name_le.text()
290
333
  print(f'User defined characteristic group name: {self.group_name_user}.')
@@ -12,23 +12,20 @@ from shutil import copyfile
12
12
  import time
13
13
  from functools import partial
14
14
  import numpy as np
15
+ from celldetective.gui import Styles
15
16
 
16
- class ConfigNewExperiment(QMainWindow):
17
+ class ConfigNewExperiment(QMainWindow, Styles):
17
18
 
18
- def __init__(self, parent=None):
19
+ def __init__(self, parent_window=None):
19
20
 
20
21
  super().__init__()
21
- self.parent = parent
22
+ self.parent_window = parent_window
22
23
  self.setWindowTitle("New experiment")
23
24
  center_window(self)
24
25
  self.setFixedWidth(500)
25
- self.setMaximumHeight(int(0.8*self.parent.screen_height))
26
+ self.setMaximumHeight(int(0.8*self.parent_window.screen_height))
26
27
  self.onlyFloat = QDoubleValidator()
27
28
 
28
- self.Styles = Styles()
29
- self.button_style = self.Styles.button_add
30
- self.button_style_sheet = self.Styles.button_style_sheet
31
-
32
29
  self.newExpFolder = str(QFileDialog.getExistingDirectory(self, 'Select directory'))
33
30
  self.populate_widget()
34
31
 
@@ -37,25 +34,25 @@ class ConfigNewExperiment(QMainWindow):
37
34
  # Create button widget and layout
38
35
  self.scroll_area = QScrollArea(self)
39
36
  button_widget = QWidget()
40
- grid = QGridLayout()
41
- button_widget.setLayout(grid)
37
+ self.grid = QGridLayout()
38
+ button_widget.setLayout(self.grid)
42
39
 
43
- grid.setContentsMargins(30,30,30,30)
44
- grid.addWidget(QLabel("Folder:"), 0, 0, 1, 3)
40
+ self.grid.setContentsMargins(30,30,30,30)
41
+ self.grid.addWidget(QLabel("Folder:"), 0, 0, 1, 3)
45
42
  self.supFolder = QLineEdit()
46
43
  self.supFolder.setAlignment(Qt.AlignLeft)
47
44
  self.supFolder.setEnabled(True)
48
45
  self.supFolder.setText(self.newExpFolder)
49
- grid.addWidget(self.supFolder, 1, 0, 1, 1)
46
+ self.grid.addWidget(self.supFolder, 1, 0, 1, 1)
50
47
 
51
48
  self.browse_button = QPushButton("Browse...")
52
49
  self.browse_button.clicked.connect(self.browse_experiment_folder)
53
50
  self.browse_button.setIcon(icon(MDI6.folder, color="white"))
54
- self.browse_button.setStyleSheet(self.parent.button_style_sheet)
51
+ self.browse_button.setStyleSheet(self.button_style_sheet)
55
52
  #self.browse_button.setIcon(QIcon_from_svg(abs_path+f"/icons/browse.svg", color='white'))
56
- grid.addWidget(self.browse_button, 1, 1, 1, 1)
53
+ self.grid.addWidget(self.browse_button, 1, 1, 1, 1)
57
54
 
58
- grid.addWidget(QLabel("Experiment name:"), 2, 0, 1, 3)
55
+ self.grid.addWidget(QLabel("Experiment name:"), 2, 0, 1, 3)
59
56
 
60
57
  self.expName = QLineEdit()
61
58
  self.expName.setPlaceholderText('folder_name_for_the_experiment')
@@ -63,20 +60,20 @@ class ConfigNewExperiment(QMainWindow):
63
60
  self.expName.setEnabled(True)
64
61
  self.expName.setFixedWidth(400)
65
62
  self.expName.setText("Untitled_Experiment")
66
- grid.addWidget(self.expName, 3, 0, 1, 3)
63
+ self.grid.addWidget(self.expName, 3, 0, 1, 3)
67
64
 
68
65
  self.generate_movie_settings()
69
- grid.addLayout(self.ms_grid,29,0,1,3)
66
+ self.grid.addLayout(self.ms_grid,29,0,1,3)
70
67
 
71
68
  self.generate_channel_params_box()
72
- grid.addLayout(self.channel_grid,30,0,1,3)
69
+ self.grid.addLayout(self.channel_grid,30,0,1,3)
73
70
 
74
71
  self.validate_button = QPushButton("Submit")
75
72
  self.validate_button.clicked.connect(self.create_config)
76
- self.validate_button.setStyleSheet(self.parent.button_style_sheet)
73
+ self.validate_button.setStyleSheet(self.button_style_sheet)
77
74
  #self.validate_button.setIcon(QIcon_from_svg(abs_path+f"/icons/process.svg", color='white'))
78
75
 
79
- grid.addWidget(self.validate_button, 31, 0, 1, 3, alignment = Qt.AlignBottom)
76
+ self.grid.addWidget(self.validate_button, 31, 0, 1, 3, alignment = Qt.AlignBottom)
80
77
  button_widget.adjustSize()
81
78
 
82
79
  self.scroll_area.setAlignment(Qt.AlignCenter)
@@ -233,9 +230,9 @@ class ConfigNewExperiment(QMainWindow):
233
230
 
234
231
  # Add channel button
235
232
  self.addChannelBtn = QPushButton('Add channel')
236
- self.addChannelBtn.setIcon(icon(MDI6.plus,color="#000000"))
233
+ self.addChannelBtn.setIcon(icon(MDI6.plus,color="white"))
237
234
  self.addChannelBtn.setIconSize(QSize(25, 25))
238
- self.addChannelBtn.setStyleSheet(self.button_style)
235
+ self.addChannelBtn.setStyleSheet(self.button_style_sheet)
239
236
  self.addChannelBtn.clicked.connect(self.add_custom_channel)
240
237
  self.channel_grid.addWidget(self.addChannelBtn, 1000, 0, 1, 1)
241
238
 
@@ -427,14 +424,14 @@ class ConfigNewExperiment(QMainWindow):
427
424
  with open('config.ini', 'w') as configfile:
428
425
  config.write(configfile)
429
426
 
430
- self.parent.set_experiment_path(self.directory)
427
+ self.parent_window.set_experiment_path(self.directory)
431
428
  print(f'New experiment successfully configured in folder {self.directory}...')
432
429
  self.close()
433
430
 
434
- class SetupConditionLabels(QWidget):
435
- def __init__(self, parent, n_wells):
431
+ class SetupConditionLabels(QWidget, Styles):
432
+ def __init__(self, parent_window, n_wells):
436
433
  super().__init__()
437
- self.parent = parent
434
+ self.parent_window = parent_window
438
435
  self.n_wells = n_wells
439
436
  self.setWindowTitle("Well conditions")
440
437
  self.layout = QVBoxLayout()
@@ -477,12 +474,12 @@ class SetupConditionLabels(QWidget):
477
474
  btn_hbox = QHBoxLayout()
478
475
  btn_hbox.setContentsMargins(0,20,0,0)
479
476
  self.skip_btn = QPushButton('Skip')
480
- self.skip_btn.setStyleSheet(self.parent.parent.button_style_sheet_2)
477
+ self.skip_btn.setStyleSheet(self.button_style_sheet_2)
481
478
  self.skip_btn.clicked.connect(self.set_default_values)
482
479
  btn_hbox.addWidget(self.skip_btn)
483
480
 
484
481
  self.submit_btn = QPushButton('Submit')
485
- self.submit_btn.setStyleSheet(self.parent.parent.button_style_sheet)
482
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
486
483
  self.submit_btn.clicked.connect(self.set_user_values)
487
484
  btn_hbox.addWidget(self.submit_btn)
488
485
  self.layout.addLayout(btn_hbox)
@@ -495,7 +492,7 @@ class SetupConditionLabels(QWidget):
495
492
  self.concentrations_cbs[i].setText(str(i))
496
493
  self.pharmaceutical_agents_cbs[i].setText(str(i))
497
494
  self.set_attributes()
498
- self.parent.create_config_file()
495
+ self.parent_window.create_config_file()
499
496
  self.close()
500
497
 
501
498
  def set_user_values(self):
@@ -509,22 +506,22 @@ class SetupConditionLabels(QWidget):
509
506
  if self.pharmaceutical_agents_cbs[i].text()=='':
510
507
  self.pharmaceutical_agents_cbs[i].setText(str(i))
511
508
  self.set_attributes()
512
- self.parent.create_config_file()
509
+ self.parent_window.create_config_file()
513
510
  self.close()
514
511
 
515
512
  def set_attributes(self):
516
513
 
517
514
  cell_type_text = [c.text() for c in self.cell_type_cbs]
518
- self.parent.cell_types = ','.join(cell_type_text)
515
+ self.parent_window.cell_types = ','.join(cell_type_text)
519
516
 
520
517
  antibodies_text = [c.text() for c in self.antibodies_cbs]
521
- self.parent.antibodies = ','.join(antibodies_text)
518
+ self.parent_window.antibodies = ','.join(antibodies_text)
522
519
 
523
520
  concentrations_text = [c.text() for c in self.concentrations_cbs]
524
- self.parent.concentrations = ','.join(concentrations_text)
521
+ self.parent_window.concentrations = ','.join(concentrations_text)
525
522
 
526
523
  pharamaceutical_text = [c.text() for c in self.pharmaceutical_agents_cbs]
527
- self.parent.pharmaceutical_agents = ','.join(pharamaceutical_text)
524
+ self.parent_window.pharmaceutical_agents = ','.join(pharamaceutical_text)
528
525
 
529
526
 
530
527
 
@@ -4,7 +4,7 @@ from PyQt5.QtCore import Qt, QSize
4
4
  from PyQt5.QtGui import QIcon
5
5
  from celldetective.gui.gui_utils import center_window, QHSeperationLine
6
6
  from celldetective.utils import _extract_labels_from_config, ConfigSectionMap, extract_experiment_channels
7
- from celldetective.gui import ConfigEditor, ProcessPanel, AnalysisPanel, NeighPanel
7
+ from celldetective.gui import ConfigEditor, ProcessPanel, PreprocessingPanel, AnalysisPanel, NeighPanel
8
8
  from natsort import natsorted
9
9
  from glob import glob
10
10
  import os
@@ -13,18 +13,22 @@ from superqt.fonticon import icon
13
13
  from fonticon_mdi6 import MDI6
14
14
  import gc
15
15
  import subprocess
16
+ from celldetective.gui.viewers import StackVisualizer
17
+ from celldetective.utils import extract_experiment_channels
18
+ from celldetective.gui import Styles
16
19
 
17
- class ControlPanel(QMainWindow):
20
+ class ControlPanel(QMainWindow, Styles):
18
21
 
19
- def __init__(self, parent=None, exp_dir=""):
22
+ def __init__(self, parent_window=None, exp_dir=""):
20
23
 
21
24
  super().__init__()
25
+
22
26
  self.exp_dir = exp_dir
23
27
  if not self.exp_dir.endswith(os.sep):
24
28
  self.exp_dir = self.exp_dir+os.sep
25
29
  self.setWindowTitle("celldetective")
26
30
  self.setWindowIcon(QIcon(os.sep.join(['celldetective','icons','logo.png'])))
27
- self.parent = parent
31
+ self.parent_window = parent_window
28
32
  center_window(self)
29
33
 
30
34
  self.init_wells_and_positions()
@@ -33,24 +37,25 @@ class ControlPanel(QMainWindow):
33
37
  self.w = QWidget()
34
38
  self.grid = QGridLayout(self.w)
35
39
  self.grid.setSpacing(5)
36
- self.grid.setContentsMargins(20,20,20,20) #left top right bottom
40
+ self.grid.setContentsMargins(10,10,10,10) #left top right bottom
37
41
 
38
42
  self.to_disable = []
39
43
  self.generate_header()
40
44
  self.ProcessEffectors = ProcessPanel(self,'effectors')
41
45
  self.ProcessTargets = ProcessPanel(self,'targets')
42
46
  self.NeighPanel = NeighPanel(self)
47
+ self.PreprocessingPanel = PreprocessingPanel(self)
43
48
 
44
49
  ProcessFrame = QFrame()
45
50
  grid_process = QVBoxLayout(ProcessFrame)
46
- grid_process.setContentsMargins(20,50,20,20)
51
+ grid_process.setContentsMargins(15,30,15,15)
47
52
 
48
53
  AnalyzeFrame = QFrame()
49
54
  grid_analyze = QVBoxLayout(AnalyzeFrame)
50
- grid_analyze.setContentsMargins(20,50,20,20)
55
+ grid_analyze.setContentsMargins(15,30,15,15)
51
56
  self.SurvivalBlock = AnalysisPanel(self,title='Survival')
52
57
 
53
-
58
+ grid_process.addWidget(self.PreprocessingPanel)
54
59
  grid_process.addWidget(self.ProcessEffectors)
55
60
  grid_process.addWidget(self.ProcessTargets)
56
61
  grid_process.addWidget(self.NeighPanel)
@@ -61,7 +66,7 @@ class ControlPanel(QMainWindow):
61
66
  self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
62
67
  self.scroll.setWidgetResizable(True)
63
68
  desktop = QDesktopWidget()
64
- self.scroll.setMinimumHeight(500)
69
+ self.scroll.setMinimumHeight(550)
65
70
  #self.scroll.setMinimumHeight(int(0.4*screen_height))
66
71
 
67
72
  tabWidget = QTabWidget()
@@ -70,7 +75,7 @@ class ControlPanel(QMainWindow):
70
75
 
71
76
  tab_index_analyze = tabWidget.addTab(AnalyzeFrame, "Analyze")
72
77
  tabWidget.setTabIcon(tab_index_analyze, icon(MDI6.poll, color='black'))
73
- tabWidget.setStyleSheet(self.parent.qtab_style)
78
+ tabWidget.setStyleSheet(self.qtab_style)
74
79
 
75
80
  self.grid.addWidget(tabWidget, 7,0,1,3, alignment=Qt.AlignTop)
76
81
  self.grid.setSpacing(5)
@@ -84,7 +89,7 @@ class ControlPanel(QMainWindow):
84
89
  self.initial_width = self.size().width()
85
90
  self.screen_height = desktop.screenGeometry().height()
86
91
  self.screen_width = desktop.screenGeometry().width()
87
- self.scroll.setMinimumWidth(410)
92
+ self.scroll.setMinimumWidth(425)
88
93
 
89
94
  def init_wells_and_positions(self):
90
95
 
@@ -122,7 +127,7 @@ class ControlPanel(QMainWindow):
122
127
  self.folder_exp_btn.setIconSize(QSize(20, 20))
123
128
  self.folder_exp_btn.setToolTip("Experiment folder")
124
129
  self.folder_exp_btn.clicked.connect(self.open_experiment_folder)
125
- self.folder_exp_btn.setStyleSheet(self.parent.button_select_all)
130
+ self.folder_exp_btn.setStyleSheet(self.button_select_all)
126
131
 
127
132
 
128
133
  self.edit_config_button = QPushButton()
@@ -130,7 +135,7 @@ class ControlPanel(QMainWindow):
130
135
  self.edit_config_button.setIconSize(QSize(20, 20))
131
136
  self.edit_config_button.setToolTip("Configuration file")
132
137
  self.edit_config_button.clicked.connect(self.open_config_editor)
133
- self.edit_config_button.setStyleSheet(self.parent.button_select_all)
138
+ self.edit_config_button.setStyleSheet(self.button_select_all)
134
139
 
135
140
  self.exp_options_layout = QHBoxLayout()
136
141
  self.exp_options_layout.addWidget(experiment_label, 32, alignment=Qt.AlignRight)
@@ -139,8 +144,9 @@ class ControlPanel(QMainWindow):
139
144
  self.exp_options_layout.addWidget(self.edit_config_button, 5, alignment=Qt.AlignRight)
140
145
  self.grid.addLayout(self.exp_options_layout, 0,0,1,3)
141
146
 
147
+ well_layout = QHBoxLayout()
142
148
  self.well_list = QComboBox()
143
- thresh = 40
149
+ thresh = 32
144
150
  self.well_truncated = [w[:thresh - 3]+'...' if len(w)>thresh else w for w in self.well_labels]
145
151
  self.well_list.addItems(self.well_truncated) #self.well_labels
146
152
  for i in range(len(self.well_labels)):
@@ -149,6 +155,7 @@ class ControlPanel(QMainWindow):
149
155
  self.well_list.activated.connect(self.display_positions)
150
156
  self.to_disable.append(self.well_list)
151
157
 
158
+ position_layout = QHBoxLayout()
152
159
  self.position_list = QComboBox()
153
160
  self.position_list.addItems(["*"])
154
161
  self.position_list.addItems(self.positions[0])
@@ -156,16 +163,73 @@ class ControlPanel(QMainWindow):
156
163
  self.to_disable.append(self.position_list)
157
164
  #self.locate_selected_position()
158
165
 
159
- self.grid.addWidget(QLabel("Well:"), 1, 0, 1,1, alignment=Qt.AlignRight)
160
- self.grid.addWidget(self.well_list, 1, 1, 1, 2)
161
-
162
- self.grid.addWidget(QLabel("Position:"),2,0,1,1, alignment=Qt.AlignRight)
163
- self.grid.addWidget(self.position_list, 2,1,1,2)
166
+ self.view_stack_btn = QPushButton()
167
+ self.view_stack_btn.setStyleSheet(self.button_select_all)
168
+ self.view_stack_btn.setIcon(icon(MDI6.image_check, color="black"))
169
+ self.view_stack_btn.setToolTip("View stack.")
170
+ self.view_stack_btn.setIconSize(QSize(20, 20))
171
+ self.view_stack_btn.clicked.connect(self.view_current_stack)
172
+ self.view_stack_btn.setEnabled(False)
173
+
174
+ well_layout.setContentsMargins(0,0,0,0)
175
+ well_lbl = QLabel('Well: ')
176
+ well_lbl.setAlignment(Qt.AlignRight)
177
+ well_layout.addWidget(well_lbl, 32)
178
+ well_layout.addWidget(self.well_list, 68)
179
+ self.grid.addLayout(well_layout, 1, 0, 1, 3)
180
+
181
+ position_layout.setContentsMargins(0,0,0,0)
182
+ pos_lbl = QLabel('Position: ')
183
+ pos_lbl.setAlignment(Qt.AlignRight)
184
+ position_layout.addWidget(pos_lbl, 32)
185
+
186
+ subposition_layout = QHBoxLayout()
187
+ subposition_layout.addWidget(self.position_list, 95)
188
+ subposition_layout.addWidget(self.view_stack_btn, 5)
189
+ position_layout.addLayout(subposition_layout, 68)
190
+ self.grid.addLayout(position_layout, 2, 0, 1, 3)
164
191
 
165
192
 
166
193
  hsep = QHSeperationLine()
167
194
  self.grid.addWidget(hsep, 5, 0, 1, 3)
168
195
 
196
+ def locate_image(self):
197
+
198
+ """
199
+ Load the first frame of the first movie found in the experiment folder as a sample.
200
+ """
201
+
202
+ movies = glob(self.pos + os.sep.join(['movie', f"{self.movie_prefix}*.tif"]))
203
+
204
+ if len(movies) == 0:
205
+ msgBox = QMessageBox()
206
+ msgBox.setIcon(QMessageBox.Warning)
207
+ msgBox.setText("Please select a position containing a movie...")
208
+ msgBox.setWindowTitle("Warning")
209
+ msgBox.setStandardButtons(QMessageBox.Ok)
210
+ returnValue = msgBox.exec()
211
+ if returnValue == QMessageBox.Ok:
212
+ self.current_stack = None
213
+ return None
214
+ else:
215
+ self.current_stack = movies[0]
216
+
217
+ def view_current_stack(self):
218
+
219
+ self.locate_image()
220
+ if self.current_stack is not None:
221
+ self.viewer = StackVisualizer(
222
+ stack_path=self.current_stack,
223
+ window_title=f'Position {self.position_list.currentText()}',
224
+ frame_slider = True,
225
+ contrast_slider = True,
226
+ channel_cb = True,
227
+ channel_names = self.exp_channels,
228
+ n_channels = self.nbr_channels,
229
+ PxToUm = self.PxToUm,
230
+ )
231
+ self.viewer.show()
232
+
169
233
  def open_experiment_folder(self):
170
234
 
171
235
  try:
@@ -223,79 +287,50 @@ class ControlPanel(QMainWindow):
223
287
  print("Warning... pharmaceutical agents not found...")
224
288
  self.pharmaceutical_agents = [str(s) for s in np.linspace(0,number_of_wells-1,number_of_wells)]
225
289
 
226
-
227
290
  def closeEvent(self, event):
228
291
 
229
292
  """
230
293
  Close child windows if closed.
231
294
  """
232
295
 
296
+ for process_block in [self.ProcessTargets, self.ProcessEffectors]:
297
+ try:
298
+ if process_block.SegModelLoader:
299
+ process_block.SegModelLoader.close()
300
+ except:
301
+ pass
302
+ try:
303
+ if process_block.ConfigTracking:
304
+ process_block.ConfigTracking.close()
305
+ except:
306
+ pass
307
+ try:
308
+ if process_block.ConfigSignalTrain:
309
+ process_block.ConfigSignalTrain.close()
310
+ except:
311
+ pass
312
+ try:
313
+ if process_block.ConfigMeasurements:
314
+ process_block.ConfigMeasurements.close()
315
+ except:
316
+ pass
317
+ try:
318
+ if process_block.ConfigSignalAnnotator:
319
+ process_block.ConfigSignalAnnotator.close()
320
+ except:
321
+ pass
322
+ try:
323
+ if process_block.tab_ui:
324
+ process_block.tab_ui.close()
325
+ except:
326
+ pass
327
+
233
328
  try:
234
329
  if self.cfg_editor:
235
330
  self.cfg_editor.close()
236
331
  except:
237
332
  pass
238
333
 
239
- try:
240
- if self.ProcessTargets.SegModelLoader:
241
- self.ProcessTargets.SegModelLoader.close()
242
- except:
243
- pass
244
-
245
- try:
246
- if self.ProcessEffectors.SegModelLoader:
247
- self.ProcessEffectors.SegModelLoader.close()
248
- except:
249
- pass
250
-
251
- try:
252
- if self.ProcessTargets.ConfigTracking:
253
- self.ProcessTargets.ConfigTracking.close()
254
- except:
255
- pass
256
-
257
- try:
258
- if self.ProcessEffectors.ConfigTracking:
259
- self.ProcessEffectors.ConfigTracking.close()
260
- except:
261
- pass
262
-
263
- try:
264
- if self.ProcessTargets.ConfigSignalTrain:
265
- self.ProcessTargets.ConfigSignalTrain.close()
266
- except:
267
- pass
268
-
269
- try:
270
- if self.ProcessEffectors.ConfigSignalTrain:
271
- self.ProcessEffectors.ConfigSignalTrain.close()
272
- except:
273
- pass
274
-
275
- try:
276
- if self.ProcessTargets.ConfigMeasurements:
277
- self.ProcessTargets.ConfigMeasurements.close()
278
- except:
279
- pass
280
-
281
- try:
282
- if self.ProcessEffectors.ConfigMeasurements:
283
- self.ProcessEffectors.ConfigMeasurements.close()
284
- except:
285
- pass
286
-
287
- try:
288
- if self.ProcessTargets.ConfigSignalAnnotator:
289
- self.ProcessTargets.ConfigSignalAnnotator.close()
290
- except:
291
- pass
292
-
293
- try:
294
- if self.ProcessEffectors.ConfigSignalAnnotator:
295
- self.ProcessEffectors.ConfigSignalAnnotator.close()
296
- except:
297
- pass
298
-
299
334
  gc.collect()
300
335
 
301
336
 
@@ -316,6 +351,7 @@ class ControlPanel(QMainWindow):
316
351
  self.position_list.clear()
317
352
  self.position_list.addItems(["*"])
318
353
  self.position_list.addItems(self.positions[pos_index])
354
+ self.update_position_options()
319
355
 
320
356
  def open_config_editor(self):
321
357
  self.cfg_editor = ConfigEditor(self)
@@ -392,12 +428,15 @@ class ControlPanel(QMainWindow):
392
428
  #self.ProcessEffectors.signal_analysis_action.setEnabled(False)
393
429
  self.ProcessTargets.check_signals_btn.setEnabled(False)
394
430
  self.ProcessEffectors.check_signals_btn.setEnabled(False)
431
+ self.view_stack_btn.setEnabled(False)
395
432
  elif self.well_list.currentText()=='*':
396
433
  self.ProcessTargets.view_tab_btn.setEnabled(True)
397
- self.ProcessEffectors.view_tab_btn.setEnabled(True)
434
+ self.ProcessEffectors.view_tab_btn.setEnabled(True)
435
+ self.view_stack_btn.setEnabled(False)
398
436
  else:
399
437
  if not self.well_list.currentText()=="*":
400
438
  self.locate_selected_position()
439
+ self.view_stack_btn.setEnabled(True)
401
440
  # if os.path.exists(os.sep.join([self.pos,'labels_effectors', os.sep])):
402
441
  self.ProcessEffectors.check_seg_btn.setEnabled(True)
403
442
  # if os.path.exists(os.sep.join([self.pos,'labels_targets', os.sep])):