celldetective 1.3.3.post1__py3-none-any.whl → 1.3.4.post1__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 (35) hide show
  1. celldetective/__main__.py +30 -4
  2. celldetective/_version.py +1 -1
  3. celldetective/extra_properties.py +21 -0
  4. celldetective/filters.py +15 -2
  5. celldetective/gui/InitWindow.py +28 -34
  6. celldetective/gui/analyze_block.py +3 -498
  7. celldetective/gui/classifier_widget.py +1 -1
  8. celldetective/gui/control_panel.py +98 -27
  9. celldetective/gui/generic_signal_plot.py +35 -18
  10. celldetective/gui/gui_utils.py +143 -2
  11. celldetective/gui/layouts.py +7 -6
  12. celldetective/gui/measurement_options.py +3 -11
  13. celldetective/gui/plot_measurements.py +5 -13
  14. celldetective/gui/plot_signals_ui.py +30 -30
  15. celldetective/gui/process_block.py +61 -103
  16. celldetective/gui/signal_annotator.py +50 -32
  17. celldetective/gui/signal_annotator2.py +7 -4
  18. celldetective/gui/styles.py +13 -0
  19. celldetective/gui/survival_ui.py +8 -21
  20. celldetective/gui/tableUI.py +1 -2
  21. celldetective/gui/thresholds_gui.py +0 -6
  22. celldetective/gui/viewers.py +1 -5
  23. celldetective/io.py +31 -4
  24. celldetective/measure.py +8 -5
  25. celldetective/neighborhood.py +0 -2
  26. celldetective/scripts/measure_cells.py +21 -9
  27. celldetective/signals.py +78 -66
  28. celldetective/tracking.py +19 -13
  29. {celldetective-1.3.3.post1.dist-info → celldetective-1.3.4.post1.dist-info}/METADATA +2 -1
  30. {celldetective-1.3.3.post1.dist-info → celldetective-1.3.4.post1.dist-info}/RECORD +35 -35
  31. tests/test_qt.py +5 -3
  32. {celldetective-1.3.3.post1.dist-info → celldetective-1.3.4.post1.dist-info}/LICENSE +0 -0
  33. {celldetective-1.3.3.post1.dist-info → celldetective-1.3.4.post1.dist-info}/WHEEL +0 -0
  34. {celldetective-1.3.3.post1.dist-info → celldetective-1.3.4.post1.dist-info}/entry_points.txt +0 -0
  35. {celldetective-1.3.3.post1.dist-info → celldetective-1.3.4.post1.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
- from PyQt5.QtWidgets import QMainWindow, QComboBox, QPushButton, QHBoxLayout, QLabel, QWidget, QGridLayout, QFrame, \
1
+ from PyQt5.QtWidgets import QMainWindow, QPushButton, QHBoxLayout, QLabel, QWidget, QGridLayout, QFrame, \
2
2
  QTabWidget, QVBoxLayout, QMessageBox, QScrollArea, QDesktopWidget
3
3
  from PyQt5.QtCore import Qt, QSize
4
- from celldetective.gui.gui_utils import center_window, QHSeperationLine
4
+ from celldetective.gui.gui_utils import center_window, QHSeperationLine, QCheckableComboBox
5
5
  from celldetective.utils import _extract_labels_from_config, ConfigSectionMap, extract_experiment_channels, extract_identity_col
6
6
  from celldetective.gui import ConfigEditor, ProcessPanel, PreprocessingPanel, AnalysisPanel, NeighPanel
7
7
  from celldetective.io import get_experiment_wells, get_config, get_spatial_calibration, get_temporal_calibration, get_experiment_concentrations, get_experiment_cell_types, get_experiment_antibodies, get_experiment_pharmaceutical_agents
@@ -18,6 +18,7 @@ from celldetective.utils import extract_experiment_channels
18
18
  from celldetective.gui import Styles
19
19
  import pandas as pd
20
20
 
21
+
21
22
  class ControlPanel(QMainWindow, Styles):
22
23
 
23
24
  def __init__(self, parent_window=None, exp_dir=""):
@@ -90,7 +91,7 @@ class ControlPanel(QMainWindow, Styles):
90
91
  self.initial_width = self.size().width()
91
92
  self.screen_height = desktop.screenGeometry().height()
92
93
  self.screen_width = desktop.screenGeometry().width()
93
- self.scroll.setMinimumWidth(425)
94
+ self.scroll.setMinimumWidth(440)
94
95
 
95
96
  def init_wells_and_positions(self):
96
97
 
@@ -138,23 +139,23 @@ class ControlPanel(QMainWindow, Styles):
138
139
  self.edit_config_button.clicked.connect(self.open_config_editor)
139
140
  self.edit_config_button.setStyleSheet(self.button_select_all)
140
141
 
141
- self.well_list = QComboBox()
142
+ self.well_list = QCheckableComboBox(obj='well', parent_window=self)
142
143
  thresh = 32
143
- self.well_truncated = [w[:thresh - 3]+'...' if len(w)>thresh else w for w in self.well_labels]
144
- self.well_list.addItems(self.well_truncated) #self.well_labels
145
- for i in range(len(self.well_labels)):
146
- self.well_list.setItemData(i, self.well_labels[i], Qt.ToolTipRole)
147
- self.well_list.addItems(["*"])
148
- self.well_list.activated.connect(self.display_positions)
149
- self.to_disable.append(self.well_list)
144
+ self.well_truncated = [w[:thresh - 3]+'...' if len(w)>thresh else w for w in self.well_labels]
145
+ for i in range(len(self.well_truncated)):
146
+ self.well_list.addItem(self.well_truncated[i], tooltip=self.well_labels[i])
150
147
 
151
- self.position_list = QComboBox()
152
- self.position_list.addItems(["*"])
148
+ self.position_list = QCheckableComboBox(obj='position', parent_window=self)
153
149
  self.position_list.addItems(self.positions[0])
154
- self.position_list.activated.connect(self.update_position_options)
155
150
  self.to_disable.append(self.position_list)
156
151
  #self.locate_selected_position()
157
152
 
153
+ self.well_list.activated.connect(self.display_positions)
154
+ self.well_list.setCurrentIndex(0)
155
+
156
+ self.position_list.activated.connect(self.update_position_options)
157
+ self.position_list.setCurrentIndex(0)
158
+
158
159
  self.view_stack_btn = QPushButton()
159
160
  self.view_stack_btn.setStyleSheet(self.button_select_all)
160
161
  self.view_stack_btn.setIcon(icon(MDI6.image_check, color="black"))
@@ -163,6 +164,24 @@ class ControlPanel(QMainWindow, Styles):
163
164
  self.view_stack_btn.clicked.connect(self.view_current_stack)
164
165
  self.view_stack_btn.setEnabled(False)
165
166
 
167
+ self.select_all_wells_btn = QPushButton()
168
+ self.select_all_wells_btn.setIcon(icon(MDI6.select_all,color="black"))
169
+ self.select_all_wells_btn.setIconSize(QSize(20, 20))
170
+ self.select_all_wells_btn.setToolTip("Select all wells.")
171
+ self.select_all_wells_btn.clicked.connect(self.select_all_wells)
172
+ self.select_all_wells_btn.setStyleSheet(self.button_select_all)
173
+ self.select_all_wells_option = False
174
+
175
+
176
+ self.select_all_pos_btn = QPushButton()
177
+ self.select_all_pos_btn.setIcon(icon(MDI6.select_all,color="black"))
178
+ self.select_all_pos_btn.setIconSize(QSize(20, 20))
179
+ self.select_all_pos_btn.setToolTip("Select all positions.")
180
+ self.select_all_pos_btn.clicked.connect(self.select_all_positions)
181
+ self.select_all_pos_btn.setStyleSheet(self.button_select_all)
182
+ self.select_all_pos_option = False
183
+
184
+
166
185
  well_lbl = QLabel('Well: ')
167
186
  well_lbl.setAlignment(Qt.AlignRight)
168
187
 
@@ -196,20 +215,52 @@ class ControlPanel(QMainWindow, Styles):
196
215
  # Well row
197
216
  well_hbox = QHBoxLayout()
198
217
  well_hbox.addWidget(well_lbl, 25, alignment=Qt.AlignVCenter)
199
- well_hbox.addWidget(self.well_list, 75)
218
+ well_subhbox = QHBoxLayout()
219
+ well_subhbox.addWidget(self.well_list, 95)
220
+ well_subhbox.addWidget(self.select_all_wells_btn, 5)
221
+ well_hbox.addLayout(well_subhbox, 75)
200
222
  vbox.addLayout(well_hbox)
201
223
 
202
224
  # Position row
203
225
  position_hbox = QHBoxLayout()
204
226
  position_hbox.addWidget(pos_lbl, 25, alignment=Qt.AlignVCenter)
205
227
  pos_subhbox = QHBoxLayout()
206
- pos_subhbox.addWidget(self.position_list, 95)
228
+ pos_subhbox.addWidget(self.position_list, 90)
229
+ pos_subhbox.addWidget(self.select_all_pos_btn, 5)
207
230
  pos_subhbox.addWidget(self.view_stack_btn, 5)
208
231
  position_hbox.addLayout(pos_subhbox, 75)
209
232
  vbox.addLayout(position_hbox)
210
233
 
211
234
  vbox.addWidget(hsep)
212
235
 
236
+ def select_all_wells(self):
237
+
238
+ if not self.select_all_wells_option:
239
+ self.well_list.selectAll()
240
+ self.select_all_wells_option = True
241
+ self.select_all_wells_btn.setIcon(icon(MDI6.select_all,color=self.celldetective_blue))
242
+ self.select_all_wells_btn.setIconSize(QSize(20, 20))
243
+ self.display_positions()
244
+ else:
245
+ self.well_list.unselectAll()
246
+ self.select_all_wells_option = False
247
+ self.select_all_wells_btn.setIcon(icon(MDI6.select_all,color="black"))
248
+ self.select_all_wells_btn.setIconSize(QSize(20, 20))
249
+ self.display_positions()
250
+
251
+ def select_all_positions(self):
252
+
253
+ if not self.select_all_pos_option:
254
+ self.position_list.selectAll()
255
+ self.select_all_pos_option = True
256
+ self.select_all_pos_btn.setIcon(icon(MDI6.select_all,color=self.celldetective_blue))
257
+ self.select_all_pos_btn.setIconSize(QSize(20, 20))
258
+ else:
259
+ self.position_list.unselectAll()
260
+ self.select_all_pos_option = False
261
+ self.select_all_pos_btn.setIcon(icon(MDI6.select_all,color="black"))
262
+ self.select_all_pos_btn.setIconSize(QSize(20, 20))
263
+
213
264
  def locate_image(self):
214
265
 
215
266
  """
@@ -341,17 +392,30 @@ class ControlPanel(QMainWindow, Styles):
341
392
  Show the positions as the well is changed.
342
393
  """
343
394
 
344
- if self.well_list.currentText()=="*":
395
+ if self.well_list.isMultipleSelection():
396
+
345
397
  self.position_list.clear()
346
- self.position_list.addItems(["*"])
347
398
  position_linspace = np.linspace(0,len(self.positions[0])-1,len(self.positions[0]),dtype=int)
348
399
  position_linspace = [str(s) for s in position_linspace]
349
400
  self.position_list.addItems(position_linspace)
401
+ if self.select_all_pos_option:
402
+ self.select_all_pos_btn.click()
403
+ self.select_all_pos_btn.click()
404
+
405
+ elif not self.well_list.isAnySelected():
406
+
407
+ self.position_list.unselectAll()
408
+ if self.select_all_pos_option:
409
+ self.select_all_pos_btn.click()
410
+
350
411
  else:
351
- pos_index = self.well_list.currentIndex()
412
+ pos_index = self.well_list.getSelectedIndices()[0]
352
413
  self.position_list.clear()
353
- self.position_list.addItems(["*"])
354
414
  self.position_list.addItems(self.positions[pos_index])
415
+ if self.select_all_pos_option:
416
+ self.select_all_pos_btn.click()
417
+ self.position_list.setCurrentIndex(0)
418
+
355
419
  self.update_position_options()
356
420
 
357
421
  def open_config_editor(self):
@@ -366,7 +430,7 @@ class ControlPanel(QMainWindow, Styles):
366
430
 
367
431
  """
368
432
 
369
- if self.well_list.currentText()=="*":
433
+ if self.well_list.isMultipleSelection():
370
434
  msgBox = QMessageBox()
371
435
  msgBox.setIcon(QMessageBox.Critical)
372
436
  msgBox.setText("Please select a single well...")
@@ -376,12 +440,12 @@ class ControlPanel(QMainWindow, Styles):
376
440
  if returnValue == QMessageBox.Ok:
377
441
  return False
378
442
  else:
379
- self.well_index = [self.well_list.currentIndex()]
443
+ self.well_index = self.well_list.getSelectedIndices() #[self.well_list.currentIndex()]
380
444
 
381
445
  for w_idx in self.well_index:
382
446
 
383
447
  pos = self.positions[w_idx]
384
- if self.position_list.currentText()=="*":
448
+ if not self.position_list.isSingleSelection():
385
449
  msgBox = QMessageBox()
386
450
  msgBox.setIcon(QMessageBox.Critical)
387
451
  msgBox.setText("Please select a single position...")
@@ -391,11 +455,12 @@ class ControlPanel(QMainWindow, Styles):
391
455
  if returnValue == QMessageBox.Ok:
392
456
  return False
393
457
  else:
394
- pos_indices = natsorted([pos.index(self.position_list.currentText())])
458
+ pos_indices = self.position_list.getSelectedIndices()
395
459
 
396
460
  well = self.wells[w_idx]
397
461
 
398
462
  for pos_idx in pos_indices:
463
+
399
464
  self.pos = natsorted(glob(well+f"{os.path.split(well)[-1].replace('W','').replace(os.sep,'')}*{os.sep}"))[pos_idx]
400
465
  if not os.path.exists(self.pos + 'output'):
401
466
  os.mkdir(self.pos + 'output')
@@ -405,6 +470,7 @@ class ControlPanel(QMainWindow, Styles):
405
470
  return True
406
471
 
407
472
  def create_config_dir(self):
473
+
408
474
  self.config_folder = self.exp_dir+'configs'+os.sep
409
475
  if not os.path.exists(self.config_folder):
410
476
  os.mkdir(self.config_folder)
@@ -413,7 +479,8 @@ class ControlPanel(QMainWindow, Styles):
413
479
 
414
480
  self.pos = self.position_list.currentText()
415
481
  panels = [self.ProcessEffectors, self.ProcessTargets]
416
- if self.position_list.currentText()=="*":
482
+
483
+ if self.position_list.isMultipleSelection() or not self.position_list.isAnySelected():
417
484
 
418
485
  for p in panels:
419
486
  p.check_seg_btn.setEnabled(False)
@@ -443,7 +510,9 @@ class ControlPanel(QMainWindow, Styles):
443
510
  self.ProcessEffectors.delete_tracks_btn.hide()
444
511
 
445
512
  self.view_stack_btn.setEnabled(False)
446
- elif self.well_list.currentText()=='*':
513
+
514
+ elif self.well_list.isMultipleSelection():
515
+
447
516
  self.ProcessTargets.view_tab_btn.setEnabled(True)
448
517
  self.ProcessEffectors.view_tab_btn.setEnabled(True)
449
518
  self.NeighPanel.view_tab_btn.setEnabled(True)
@@ -455,7 +524,9 @@ class ControlPanel(QMainWindow, Styles):
455
524
  self.ProcessTargets.delete_tracks_btn.hide()
456
525
  self.ProcessEffectors.delete_tracks_btn.hide()
457
526
  else:
458
- if not self.well_list.currentText()=="*":
527
+
528
+ if self.well_list.isAnySelected() and self.position_list.isAnySelected():
529
+
459
530
  self.locate_selected_position()
460
531
  self.view_stack_btn.setEnabled(True)
461
532
  # if os.path.exists(os.sep.join([self.pos,'labels_effectors', os.sep])):
@@ -4,12 +4,12 @@ from PyQt5.QtWidgets import QMessageBox,QGridLayout, QButtonGroup, \
4
4
  from PyQt5.QtCore import Qt, QSize
5
5
  from PyQt5.QtGui import QDoubleValidator
6
6
 
7
- from celldetective.gui.gui_utils import center_window, FigureCanvas, ExportPlotBtn
7
+ from celldetective.gui.gui_utils import center_window, FigureCanvas, ExportPlotBtn, QuickSliderLayout
8
8
  from celldetective.gui.tableUI import TableUI
9
9
  from celldetective.io import collect_experiment_metadata
10
10
 
11
11
  from superqt.fonticon import icon
12
- from superqt import QLabeledSlider
12
+ from superqt import QLabeledSlider, QLabeledDoubleSlider
13
13
  from fonticon_mdi6 import MDI6
14
14
  import numpy as np
15
15
  import json
@@ -81,16 +81,21 @@ class GenericSignalPlotWidget(QWidget, Styles):
81
81
  radio_subhbox.addWidget(self.plot_options[i], 33, alignment=Qt.AlignCenter)
82
82
 
83
83
  if self.parent_window.position_indices is not None:
84
+ # at least a position is selected
84
85
  if len(self.parent_window.well_indices)>1 and len(self.parent_window.position_indices)==1:
86
+ # several wells but one position
85
87
  self.plot_btn_group.buttons()[0].click()
86
- for i in [1,2]:
87
- self.plot_options[i].setEnabled(False)
88
+ # for i in [1,2]:
89
+ # self.plot_options[i].setEnabled(False)
88
90
  elif len(self.parent_window.well_indices)>1:
89
91
  self.plot_btn_group.buttons()[0].click()
90
92
  elif len(self.parent_window.well_indices)==1 and len(self.parent_window.position_indices)==1:
91
93
  self.plot_btn_group.buttons()[1].click()
92
94
  for i in [0,2]:
93
95
  self.plot_options[i].setEnabled(False)
96
+ elif len(self.parent_window.well_indices)==1 and len(self.parent_window.position_indices)>1:
97
+ # one well, several positions
98
+ self.plot_btn_group.buttons()[2].click()
94
99
  else:
95
100
  if len(self.parent_window.well_indices)>1:
96
101
  self.plot_btn_group.buttons()[0].click()
@@ -212,17 +217,22 @@ class GenericSignalPlotWidget(QWidget, Styles):
212
217
  # Rescale
213
218
  self.cell_lines_alpha_wdg = QWidget()
214
219
  alpha_hbox = QHBoxLayout()
215
- self.cell_lines_alpha_wdg.setLayout(alpha_hbox)
216
220
 
217
- alpha_hbox.addWidget(QLabel('single-cell\nsignal alpha: '), 25)
218
- self.alpha_le = QLineEdit('0,8')
219
- self.alpha_le.setValidator(self.float_validator)
220
- alpha_hbox.addWidget(self.alpha_le, 65)
221
+ self.alpha_slider = QLabeledDoubleSlider()
222
+ alpha_hbox = QuickSliderLayout(label='single-cell\nsignal alpha: ',
223
+ slider=self.alpha_slider,
224
+ slider_initial_value=0.8,
225
+ slider_range=(0,1),
226
+ decimal_option=True,
227
+ precision=1.0E-05,
228
+ )
229
+ self.alpha_slider.valueChanged.connect(self.submit_alpha)
230
+ self.cell_lines_alpha_wdg.setLayout(alpha_hbox)
221
231
 
222
- self.submit_alpha_btn = QPushButton('submit')
223
- self.submit_alpha_btn.setStyleSheet(self.button_style_sheet_2)
224
- self.submit_alpha_btn.clicked.connect(self.submit_alpha)
225
- alpha_hbox.addWidget(self.submit_alpha_btn, 10)
232
+ # self.submit_alpha_btn = QPushButton('submit')
233
+ # self.submit_alpha_btn.setStyleSheet(self.button_style_sheet_2)
234
+ # self.submit_alpha_btn.clicked.connect(self.submit_alpha)
235
+ # alpha_hbox.addWidget(self.submit_alpha_btn, 10)
226
236
  self.layout.addWidget(self.cell_lines_alpha_wdg)
227
237
 
228
238
  self.select_option = [QRadioButton() for i in range(2)]
@@ -263,9 +273,9 @@ class GenericSignalPlotWidget(QWidget, Styles):
263
273
  self.generate_pos_selection_widget()
264
274
  self.select_btn_group.buttons()[0].click()
265
275
 
266
- def submit_alpha(self):
276
+ def submit_alpha(self, value):
267
277
 
268
- alpha = self.alpha_le.text().replace(',','.')
278
+ alpha = value
269
279
  try:
270
280
  alpha = float(alpha)
271
281
  except:
@@ -329,17 +339,21 @@ class GenericSignalPlotWidget(QWidget, Styles):
329
339
  if name+':' in lbl:
330
340
  self.usable_well_labels.append(lbl)
331
341
 
342
+ thresh = 20
343
+ self.well_name_truncated = [w[:thresh - 3]+'...' if len(w)>thresh else w for w in self.usable_well_labels]
344
+
332
345
  self.line_choice_widget = QWidget()
333
346
  self.line_check_vbox = QGridLayout()
334
347
  self.line_choice_widget.setLayout(self.line_check_vbox)
335
348
 
336
349
  if len(self.parent_window.well_indices)>1:
337
- self.well_display_options = [QCheckBox(self.usable_well_labels[i]) for i in range(len(self.usable_well_labels))]
350
+ self.well_display_options = [QCheckBox(self.well_name_truncated[i]) for i in range(len(self.well_name_truncated))]
338
351
  for i in range(len(self.well_names)):
339
- self.line_check_vbox.addWidget(self.well_display_options[i], i, 0, 1, 1, alignment=Qt.AlignLeft)
352
+ self.line_check_vbox.addWidget(self.well_display_options[i], i%4, i//4, 1, 1, alignment=Qt.AlignCenter)
340
353
  self.well_display_options[i].setChecked(True)
341
354
  self.well_display_options[i].setStyleSheet("font-size: 12px;")
342
355
  self.well_display_options[i].toggled.connect(self.select_lines)
356
+ self.well_display_options[i].setToolTip(self.usable_well_labels[i])
343
357
  else:
344
358
  self.pos_display_options = [QCheckBox(self.pos_names[i]) for i in range(len(self.pos_names))]
345
359
  for i in range(len(self.pos_names)):
@@ -648,7 +662,10 @@ class GenericSignalPlotWidget(QWidget, Styles):
648
662
  else:
649
663
  self.ci_btn.setIcon(icon(MDI6.arrow_expand_horizontal,color=self.help_color))
650
664
  self.show_ci = not self.show_ci
651
- self.plot_signals(0)
665
+ try:
666
+ self.plot_signals(0)
667
+ except Exception as e:
668
+ print(f"{e=}")
652
669
 
653
670
  def switch_cell_lines(self):
654
671
 
@@ -1,8 +1,8 @@
1
1
  import numpy as np
2
2
  from PyQt5.QtWidgets import QApplication, QMessageBox, QFrame, QSizePolicy, QWidget, QLineEdit, QListWidget, QVBoxLayout, QComboBox, \
3
- QPushButton, QLabel, QHBoxLayout, QCheckBox, QFileDialog
3
+ QPushButton, QLabel, QHBoxLayout, QCheckBox, QFileDialog, QToolButton, QMenu, QStylePainter, QStyleOptionComboBox, QStyle
4
4
  from PyQt5.QtCore import Qt, QSize, QAbstractTableModel
5
- from PyQt5.QtGui import QDoubleValidator, QIntValidator
5
+ from PyQt5.QtGui import QDoubleValidator, QIntValidator, QStandardItemModel, QPalette
6
6
 
7
7
  from celldetective.gui import Styles
8
8
  from superqt.fonticon import icon
@@ -16,6 +16,147 @@ from inspect import getmembers, isfunction
16
16
  from celldetective.filters import *
17
17
  from os import sep
18
18
 
19
+
20
+ class QCheckableComboBox(QComboBox):
21
+
22
+ """
23
+ adapted from https://stackoverflow.com/questions/22775095/pyqt-how-to-set-combobox-items-be-checkable
24
+ """
25
+
26
+ def __init__(self, obj='', parent_window=None, *args, **kwargs):
27
+
28
+ super().__init__(parent_window, *args, **kwargs)
29
+
30
+ self.setTitle('')
31
+ self.view().pressed.connect(self.handleItemPressed)
32
+ self.setModel(QStandardItemModel(self))
33
+ self.obj = obj
34
+ self.toolButton = QToolButton(parent_window)
35
+ self.toolButton.setText('')
36
+ self.toolMenu = QMenu(parent_window)
37
+ self.toolButton.setMenu(self.toolMenu)
38
+ self.toolButton.setPopupMode(QToolButton.InstantPopup)
39
+ self.anySelected = False
40
+
41
+ def clear(self):
42
+
43
+ self.unselectAll()
44
+ self.toolMenu.clear()
45
+ super().clear()
46
+
47
+
48
+ def handleItemPressed(self, index):
49
+
50
+ idx = index.row()
51
+ actions = self.toolMenu.actions()
52
+
53
+ item = self.model().itemFromIndex(index)
54
+ if item.checkState() == Qt.Checked:
55
+ item.setCheckState(Qt.Unchecked)
56
+ actions[idx].setChecked(False)
57
+ else:
58
+ item.setCheckState(Qt.Checked)
59
+ actions[idx].setChecked(True)
60
+ self.anySelected = True
61
+
62
+ options_checked = np.array([a.isChecked() for a in actions])
63
+ if len(options_checked[options_checked]) > 1:
64
+ self.setTitle(f'Multiple {self.obj+"s"} selected...')
65
+ elif len(options_checked[options_checked])==1:
66
+ idx_selected = np.where(options_checked)[0][0]
67
+ if idx_selected!=idx:
68
+ item = self.model().item(idx_selected)
69
+ self.setTitle(item.text())
70
+ elif len(options_checked[options_checked])==0:
71
+ self.setTitle(f"No {self.obj} selected...")
72
+ self.anySelected = False
73
+
74
+ def setCurrentIndex(self, index):
75
+
76
+ super().setCurrentIndex(index)
77
+
78
+ item = self.model().item(index)
79
+ modelIndex = self.model().indexFromItem(item)
80
+
81
+ self.handleItemPressed(modelIndex)
82
+
83
+ def selectAll(self):
84
+
85
+ actions = self.toolMenu.actions()
86
+ for i,a in enumerate(actions):
87
+ if not a.isChecked():
88
+ self.setCurrentIndex(i)
89
+ self.anySelected = True
90
+
91
+ def unselectAll(self):
92
+
93
+ actions = self.toolMenu.actions()
94
+ for i,a in enumerate(actions):
95
+ if a.isChecked():
96
+ self.setCurrentIndex(i)
97
+ self.anySelected = False
98
+
99
+ def title(self):
100
+ return self._title
101
+
102
+ def setTitle(self, title):
103
+ self._title = title
104
+ self.repaint()
105
+
106
+ def paintEvent(self, event):
107
+
108
+ painter = QStylePainter(self)
109
+ painter.setPen(self.palette().color(QPalette.Text))
110
+ opt = QStyleOptionComboBox()
111
+ self.initStyleOption(opt)
112
+ opt.currentText = self._title
113
+ painter.drawComplexControl(QStyle.CC_ComboBox, opt)
114
+ painter.drawControl(QStyle.CE_ComboBoxLabel, opt)
115
+
116
+ def addItem(self, item, tooltip=None):
117
+
118
+ super().addItem(item)
119
+ idx = self.findText(item)
120
+ if tooltip is not None:
121
+ self.setItemData(idx, tooltip, Qt.ToolTipRole)
122
+ item2 = self.model().item(idx, 0)
123
+ item2.setCheckState(Qt.Unchecked)
124
+ action = self.toolMenu.addAction(item)
125
+ action.setCheckable(True)
126
+
127
+ def addItems(self, items):
128
+
129
+ super().addItems(items)
130
+
131
+ for item in items:
132
+
133
+ idx = self.findText(item)
134
+ item2 = self.model().item(idx, 0)
135
+ item2.setCheckState(Qt.Unchecked)
136
+ action = self.toolMenu.addAction(item)
137
+ action.setCheckable(True)
138
+
139
+ def getSelectedIndices(self):
140
+
141
+ actions = self.toolMenu.actions()
142
+ options_checked = np.array([a.isChecked() for a in actions])
143
+ idx_selected = np.where(options_checked)[0]
144
+
145
+ return list(idx_selected)
146
+
147
+ def currentText(self):
148
+ return self.title()
149
+
150
+ def isMultipleSelection(self):
151
+ return self.currentText().startswith('Multiple')
152
+
153
+ def isSingleSelection(self):
154
+ return not self.currentText().startswith('Multiple') and not self.title().startswith('No')
155
+
156
+ def isAnySelected(self):
157
+ return not self.title().startswith('No')
158
+
159
+
19
160
  class PandasModel(QAbstractTableModel):
20
161
 
21
162
  """
@@ -650,7 +650,8 @@ class BackgroundFitCorrectionLayout(QGridLayout, Styles):
650
650
 
651
651
  def preview_correction(self):
652
652
 
653
- if self.attr_parent.well_list.currentText()=="*" or self.attr_parent.position_list.currentText()=="*":
653
+ if self.attr_parent.well_list.isMultipleSelection() or not self.attr_parent.well_list.isAnySelected() or self.attr_parent.position_list.isMultipleSelection() or not self.attr_parent.position_list.isAnySelected():
654
+
654
655
  msgBox = QMessageBox()
655
656
  msgBox.setIcon(QMessageBox.Critical)
656
657
  msgBox.setText("Please select a single position...")
@@ -672,8 +673,8 @@ class BackgroundFitCorrectionLayout(QGridLayout, Styles):
672
673
  clip = False
673
674
 
674
675
  corrected_stack = correct_background_model(self.attr_parent.exp_dir,
675
- well_option=self.attr_parent.well_list.currentIndex(), #+1 ??
676
- position_option=self.attr_parent.position_list.currentIndex()-1, #+1??
676
+ well_option=self.attr_parent.well_list.getSelectedIndices(), #+1 ??
677
+ position_option=self.attr_parent.position_list.getSelectedIndices(), #+1??
677
678
  target_channel=self.channels_cb.currentText(),
678
679
  model = self.models_cb.currentText(),
679
680
  threshold_on_std = self.threshold_le.get_threshold(),
@@ -1258,7 +1259,7 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
1258
1259
 
1259
1260
  def preview_correction(self):
1260
1261
 
1261
- if self.attr_parent.well_list.currentText()=="*" or self.attr_parent.position_list.currentText()=="*":
1262
+ if self.attr_parent.well_list.isMultipleSelection() or not self.attr_parent.well_list.isAnySelected() or self.attr_parent.position_list.isMultipleSelection() or not self.attr_parent.position_list.isAnySelected():
1262
1263
  msgBox = QMessageBox()
1263
1264
  msgBox.setIcon(QMessageBox.Warning)
1264
1265
  msgBox.setText("Please select a single position...")
@@ -1294,8 +1295,8 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
1294
1295
  clip = False
1295
1296
 
1296
1297
  corrected_stacks = correct_background_model_free(self.attr_parent.exp_dir,
1297
- well_option=self.attr_parent.well_list.currentIndex(), #+1 ??
1298
- position_option=self.attr_parent.position_list.currentIndex()-1, #+1??
1298
+ well_option=self.attr_parent.well_list.getSelectedIndices(), #+1 ??
1299
+ position_option=self.attr_parent.getSelectedIndices(), #+1??
1299
1300
  target_channel=self.channels_cb.currentText(),
1300
1301
  mode = mode,
1301
1302
  threshold_on_std = self.threshold_le.get_threshold(),
@@ -24,7 +24,6 @@ from glob import glob
24
24
  from natsort import natsorted
25
25
  from tifffile import imread
26
26
  from pathlib import Path
27
- import gc
28
27
 
29
28
  from celldetective.gui.viewers import CellEdgeVisualizer, SpotDetectionVisualizer
30
29
  from celldetective.gui.layouts import ProtocolDesignerLayout, BackgroundFitCorrectionLayout, LocalCorrectionLayout
@@ -726,18 +725,10 @@ class ConfigMeasurements(QMainWindow, Styles):
726
725
  else:
727
726
  self.current_stack = movies[0]
728
727
  self.stack_length = auto_load_number_of_frames(self.current_stack)
729
-
730
- if self.stack_length is None:
731
- stack = imread(self.current_stack)
732
- self.stack_length = len(stack)
733
- del stack
734
- gc.collect()
735
-
736
728
  self.mid_time = self.stack_length // 2
737
- indices = self.mid_time + np.arange(len(self.channel_names))
729
+ indices = len(self.channel_names) * self.mid_time + np.arange(len(self.channel_names))
738
730
  self.test_frame = load_frames(list(indices.astype(int)),self.current_stack, normalize_input=False)
739
731
 
740
-
741
732
  def control_haralick_digitalization(self):
742
733
 
743
734
  """
@@ -746,9 +737,10 @@ class ConfigMeasurements(QMainWindow, Styles):
746
737
 
747
738
  """
748
739
 
749
- self.locate_image()
740
+ self.locate_image() # pb here
750
741
  self.extract_haralick_options()
751
742
  if self.test_frame is not None:
743
+
752
744
  digitized_img = compute_haralick_features(self.test_frame, np.zeros(self.test_frame.shape[:2]),
753
745
  channels=self.channel_names, return_digit_image_only=True,
754
746
  **self.haralick_options
@@ -56,8 +56,8 @@ class ConfigMeasurementsPlot(QWidget,Styles):
56
56
 
57
57
  print('Parent wells: ', self.wells)
58
58
 
59
- self.well_option = self.parent_window.parent_window.well_list.currentIndex()
60
- self.position_option = self.parent_window.parent_window.position_list.currentIndex()
59
+ self.well_option = self.parent_window.parent_window.well_list.getSelectedIndices()
60
+ self.position_option = self.parent_window.parent_window.position_list.getSelectedIndices()
61
61
  self.interpret_pos_location()
62
62
  # self.load_available_tables()
63
63
  # self.config_path = self.exp_dir + self.config_name
@@ -526,18 +526,10 @@ class ConfigMeasurementsPlot(QWidget,Styles):
526
526
 
527
527
  """
528
528
 
529
- self.well_option = self.parent_window.parent_window.well_list.currentIndex()
530
- if self.well_option == len(self.wells):
531
- wo = '*'
532
- else:
533
- wo = self.well_option
534
- self.position_option = self.parent_window.parent_window.position_list.currentIndex()
535
- if self.position_option == 0:
536
- po = '*'
537
- else:
538
- po = self.position_option - 1
529
+ self.well_option = self.parent_window.parent_window.well_list.getSelectedIndices()
530
+ self.position_option = self.parent_window.parent_window.position_list.getSelectedIndices()
539
531
 
540
- self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=wo, position_option=po,
532
+ self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=self.well_option, position_option=self.position_option,
541
533
  population=self.cbs[0].currentText(), return_pos_info=True)
542
534
 
543
535
  if self.df is None: