celldetective 1.0.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 (66) hide show
  1. celldetective/__init__.py +2 -0
  2. celldetective/__main__.py +432 -0
  3. celldetective/datasets/segmentation_annotations/blank +0 -0
  4. celldetective/datasets/signal_annotations/blank +0 -0
  5. celldetective/events.py +149 -0
  6. celldetective/extra_properties.py +100 -0
  7. celldetective/filters.py +89 -0
  8. celldetective/gui/__init__.py +20 -0
  9. celldetective/gui/about.py +44 -0
  10. celldetective/gui/analyze_block.py +563 -0
  11. celldetective/gui/btrack_options.py +898 -0
  12. celldetective/gui/classifier_widget.py +386 -0
  13. celldetective/gui/configure_new_exp.py +532 -0
  14. celldetective/gui/control_panel.py +438 -0
  15. celldetective/gui/gui_utils.py +495 -0
  16. celldetective/gui/json_readers.py +113 -0
  17. celldetective/gui/measurement_options.py +1425 -0
  18. celldetective/gui/neighborhood_options.py +452 -0
  19. celldetective/gui/plot_signals_ui.py +1042 -0
  20. celldetective/gui/process_block.py +1055 -0
  21. celldetective/gui/retrain_segmentation_model_options.py +706 -0
  22. celldetective/gui/retrain_signal_model_options.py +643 -0
  23. celldetective/gui/seg_model_loader.py +460 -0
  24. celldetective/gui/signal_annotator.py +2388 -0
  25. celldetective/gui/signal_annotator_options.py +340 -0
  26. celldetective/gui/styles.py +217 -0
  27. celldetective/gui/survival_ui.py +903 -0
  28. celldetective/gui/tableUI.py +608 -0
  29. celldetective/gui/thresholds_gui.py +1300 -0
  30. celldetective/icons/logo-large.png +0 -0
  31. celldetective/icons/logo.png +0 -0
  32. celldetective/icons/signals_icon.png +0 -0
  33. celldetective/icons/splash-test.png +0 -0
  34. celldetective/icons/splash.png +0 -0
  35. celldetective/icons/splash0.png +0 -0
  36. celldetective/icons/survival2.png +0 -0
  37. celldetective/icons/vignette_signals2.png +0 -0
  38. celldetective/icons/vignette_signals2.svg +114 -0
  39. celldetective/io.py +2050 -0
  40. celldetective/links/zenodo.json +561 -0
  41. celldetective/measure.py +1258 -0
  42. celldetective/models/segmentation_effectors/blank +0 -0
  43. celldetective/models/segmentation_generic/blank +0 -0
  44. celldetective/models/segmentation_targets/blank +0 -0
  45. celldetective/models/signal_detection/blank +0 -0
  46. celldetective/models/tracking_configs/mcf7.json +68 -0
  47. celldetective/models/tracking_configs/ricm.json +203 -0
  48. celldetective/models/tracking_configs/ricm2.json +203 -0
  49. celldetective/neighborhood.py +717 -0
  50. celldetective/scripts/analyze_signals.py +51 -0
  51. celldetective/scripts/measure_cells.py +275 -0
  52. celldetective/scripts/segment_cells.py +212 -0
  53. celldetective/scripts/segment_cells_thresholds.py +140 -0
  54. celldetective/scripts/track_cells.py +206 -0
  55. celldetective/scripts/train_segmentation_model.py +246 -0
  56. celldetective/scripts/train_signal_model.py +49 -0
  57. celldetective/segmentation.py +712 -0
  58. celldetective/signals.py +2826 -0
  59. celldetective/tracking.py +974 -0
  60. celldetective/utils.py +1681 -0
  61. celldetective-1.0.2.dist-info/LICENSE +674 -0
  62. celldetective-1.0.2.dist-info/METADATA +192 -0
  63. celldetective-1.0.2.dist-info/RECORD +66 -0
  64. celldetective-1.0.2.dist-info/WHEEL +5 -0
  65. celldetective-1.0.2.dist-info/entry_points.txt +2 -0
  66. celldetective-1.0.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,460 @@
1
+ from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel, QCheckBox, QLineEdit, QHBoxLayout, QRadioButton, QComboBox, QFileDialog, QPushButton, QMessageBox
2
+ from PyQt5.QtCore import Qt, QSize
3
+ from celldetective.gui.gui_utils import center_window
4
+ from celldetective.gui import ThresholdConfigWizard
5
+ from PyQt5.QtGui import QDoubleValidator
6
+ from superqt.fonticon import icon
7
+ from fonticon_mdi6 import MDI6
8
+ import numpy as np
9
+ from glob import glob
10
+ import os
11
+ import json
12
+ import shutil
13
+
14
+ class SegmentationModelLoader(QWidget):
15
+
16
+ """
17
+ Upload a segmentation model or define a Threshold pipeline.
18
+ """
19
+
20
+ def __init__(self, parent):
21
+
22
+ super().__init__()
23
+ self.parent = parent
24
+ self.mode = self.parent.mode
25
+ if self.mode=="targets":
26
+ self.target_folder = "segmentation_targets"
27
+ elif self.mode=="effectors":
28
+ self.target_folder = "segmentation_effectors"
29
+ self.setWindowTitle('Upload model')
30
+ self.generate_content()
31
+ center_window(self)
32
+
33
+ def generate_content(self):
34
+
35
+ self.layout = QGridLayout(self)
36
+ self.layout.addWidget(QLabel('Select:'), 0, 0, 1, 1)
37
+ self.layout.setSpacing(5)
38
+
39
+ option_layout = QHBoxLayout()
40
+ option_layout.setContentsMargins(10,10,10,10)
41
+
42
+ # Create radio buttons
43
+ self.stardist_button = QRadioButton('StarDist')
44
+ #self.stardist_button.setIcon(QIcon(abs_path+f"/icons/star.png"))
45
+ self.stardist_button.setChecked(True)
46
+ option_layout.addWidget(self.stardist_button)
47
+
48
+ self.cellpose_button = QRadioButton('Cellpose')
49
+ #self.cellpose_button.setIcon(QIcon(abs_path+f"/icons/cellpose.png"))
50
+ option_layout.addWidget(self.cellpose_button)
51
+
52
+ self.threshold_button = QRadioButton('Threshold')
53
+ option_layout.addWidget(self.threshold_button)
54
+
55
+ self.layout.addLayout(option_layout, 1,0,1,2, alignment=Qt.AlignCenter)
56
+ self.generate_base_block()
57
+ self.layout.addLayout(self.base_block, 2,0,1,2)
58
+ self.generate_stardist_specific_block()
59
+ self.layout.addLayout(self.stardist_block, 3,0,1,2)
60
+ self.combos = [self.combo_ch1, self.combo_ch2, self.combo_ch3, self.combo_ch4]
61
+
62
+ self.normalize_checkbox = QCheckBox()
63
+ self.normalize_checkbox.setChecked(True)
64
+ self.normalize_lbl = QLabel('normalize: ')
65
+ self.layout.addWidget(self.normalize_lbl, 8,0,1,1)
66
+ self.layout.addWidget(self.normalize_checkbox,8, 1,1,2, Qt.AlignLeft)
67
+
68
+ self.generate_cellpose_options()
69
+ self.layout.addLayout(self.cellpose_block, 3,0,1,2)
70
+ self.generate_threshold_options()
71
+
72
+ # Create file dialog
73
+ self.file_dialog = QFileDialog()
74
+
75
+ # Create button to open file dialog
76
+ self.open_dialog_button = QPushButton('Choose File')
77
+ self.open_dialog_button.clicked.connect(self.showDialog)
78
+ self.file_label = QLabel('No file chosen', self)
79
+ self.layout.addWidget(self.open_dialog_button, 9, 0, 1, 1)
80
+ self.layout.addWidget(self.file_label, 9, 1, 1, 1)
81
+
82
+ self.upload_button = QPushButton("Upload")
83
+ self.upload_button.clicked.connect(self.upload_model)
84
+ self.upload_button.setIcon(icon(MDI6.upload,color="white"))
85
+ self.upload_button.setIconSize(QSize(25, 25))
86
+ self.upload_button.setStyleSheet(self.parent.parent.parent.button_style_sheet)
87
+ self.upload_button.setEnabled(False)
88
+ self.layout.addWidget(self.upload_button, 10, 0, 1, 1)
89
+
90
+ self.base_block_options = [self.calibration_label, self.spatial_calib_le, self.ch_1_label, self.combo_ch1, self.ch_2_label, self.combo_ch2,
91
+ self.normalize_checkbox, self.normalize_lbl,
92
+ ]
93
+
94
+ self.stardist_button.toggled.connect(self.show_seg_options)
95
+ self.cellpose_button.toggled.connect(self.show_seg_options)
96
+ self.threshold_button.toggled.connect(self.show_seg_options)
97
+
98
+ for cb in self.combos:
99
+ cb.activated.connect(self.unlock_upload)
100
+
101
+ self.setLayout(self.layout)
102
+ self.show()
103
+
104
+ def unlock_upload(self):
105
+ if self.stardist_button.isChecked():
106
+ if np.any([c.currentText()!='--' for c in self.combos]):
107
+ self.upload_button.setEnabled(True)
108
+ else:
109
+ self.upload_button.setEnabled(False)
110
+ elif self.cellpose_button.isChecked():
111
+ if np.any([c.currentText()!='--' for c in self.combos[:2]]):
112
+ self.upload_button.setEnabled(True)
113
+ else:
114
+ self.upload_button.setEnabled(False)
115
+
116
+ def generate_base_block(self):
117
+
118
+ """
119
+ Create widgets common to StarDist and Cellpose.
120
+ """
121
+
122
+ self.base_block = QGridLayout()
123
+
124
+ self.calibration_label = QLabel("pixel calibration: ")
125
+ self.base_block.addWidget(self.calibration_label,0,0,1,1, alignment=Qt.AlignLeft)
126
+ self.spatial_calib_le = QLineEdit("0,1")
127
+ self.qdv = QDoubleValidator(0.0, np.amax([self.parent.parent.shape_x, self.parent.parent.shape_y]), 8, notation=QDoubleValidator.StandardNotation)
128
+ self.spatial_calib_le.setValidator(self.qdv)
129
+ self.base_block.addWidget(self.spatial_calib_le, 0,1,1,2,alignment=Qt.AlignRight)
130
+
131
+ self.channel_options = ["--","live_nuclei_channel", "dead_nuclei_channel", "effector_fluo_channel", "brightfield_channel", "adhesion_channel", "fluo_channel_1", "fluo_channel_2"]
132
+ exp_channels = self.parent.parent.exp_channels
133
+ for ec in exp_channels:
134
+ if ec not in self.channel_options:
135
+ self.channel_options.append(ec)
136
+ self.channel_options += ['None']
137
+
138
+ self.ch_1_label = QLabel("channel 1: ")
139
+ self.base_block.addWidget(self.ch_1_label, 1, 0, 1, 1, alignment=Qt.AlignLeft)
140
+ self.combo_ch1 = QComboBox()
141
+ self.combo_ch1.addItems(self.channel_options)
142
+ self.base_block.addWidget(self.combo_ch1, 1, 1, 1, 2, alignment=Qt.AlignRight)
143
+
144
+ self.ch_2_label = QLabel("channel 2: ")
145
+ self.base_block.addWidget(self.ch_2_label, 2, 0, 1, 1, alignment=Qt.AlignLeft)
146
+ self.combo_ch2 = QComboBox()
147
+ self.combo_ch2.addItems(self.channel_options)
148
+ self.base_block.addWidget(self.combo_ch2, 2, 1, 1, 2, alignment=Qt.AlignRight)
149
+
150
+ def generate_stardist_specific_block(self):
151
+
152
+ """
153
+ Create StarDist specific fields to use the model properly when calling it from the app.
154
+ """
155
+
156
+ self.stardist_block = QGridLayout()
157
+ self.ch_3_label = QLabel("channel 3: ")
158
+ self.stardist_block.addWidget(self.ch_3_label, 0, 0, 1, 1, alignment=Qt.AlignLeft)
159
+ self.combo_ch3 = QComboBox()
160
+ self.combo_ch3.addItems(self.channel_options)
161
+ self.stardist_block.addWidget(self.combo_ch3, 0, 1, 1, 2, alignment=Qt.AlignRight)
162
+
163
+ self.ch_4_label = QLabel("channel 4: ")
164
+ self.stardist_block.addWidget(self.ch_4_label, 1, 0, 1, 1, alignment=Qt.AlignLeft)
165
+ self.combo_ch4 = QComboBox()
166
+ self.combo_ch4.addItems(self.channel_options)
167
+ self.stardist_block.addWidget(self.combo_ch4, 1, 1, 1, 2, alignment=Qt.AlignRight)
168
+
169
+ self.stardist_options = [self.ch_3_label, self.ch_4_label, self.combo_ch3, self.combo_ch4]
170
+
171
+ def generate_cellpose_options(self):
172
+
173
+ """
174
+ Create Cellpose specific fields to use the model properly when calling it from the app.
175
+ """
176
+
177
+ self.cellpose_block = QGridLayout()
178
+ self.cp_diameter_label = QLabel('diameter: ')
179
+ self.cp_diameter_le = QLineEdit("30,0")
180
+ self.cp_diameter_le.setValidator(self.qdv)
181
+ self.cellpose_block.addWidget(self.cp_diameter_label, 0, 0, 1, 2)
182
+ self.cellpose_block.addWidget(self.cp_diameter_le, 0, 1, 1, 2)
183
+
184
+ qdv_prob = QDoubleValidator(-6, 6, 8, notation=QDoubleValidator.StandardNotation)
185
+ self.cp_cellprob_label = QLabel('cellprob\nthreshold: ')
186
+ self.cp_cellprob_le = QLineEdit('0,0')
187
+ self.cp_cellprob_le.setValidator(qdv_prob)
188
+ self.cellpose_block.addWidget(self.cp_cellprob_label, 1, 0, 1, 2)
189
+ self.cellpose_block.addWidget(self.cp_cellprob_le, 1, 1, 1, 2)
190
+
191
+ self.cp_flow_label = QLabel('flow threshold: ')
192
+ self.cp_flow_le = QLineEdit('0,4')
193
+ self.cp_flow_le.setValidator(qdv_prob)
194
+ self.cellpose_block.addWidget(self.cp_flow_label, 2, 0, 1, 2)
195
+ self.cellpose_block.addWidget(self.cp_flow_le, 2, 1, 1, 2)
196
+
197
+ self.cellpose_options = [self.cp_diameter_label,self.cp_diameter_le,self.cp_cellprob_label,
198
+ self.cp_cellprob_le, self.cp_flow_label, self.cp_flow_le]
199
+ for c in self.cellpose_options:
200
+ c.hide()
201
+
202
+ def generate_threshold_options(self):
203
+
204
+ """
205
+ Show the threshold config pipeline button.
206
+ """
207
+
208
+ self.threshold_config_button = QPushButton("Threshold Config Wizard")
209
+ self.threshold_config_button.setIcon(icon(MDI6.auto_fix,color="#1565c0"))
210
+ self.threshold_config_button.setIconSize(QSize(20, 20))
211
+ self.threshold_config_button.setVisible(False)
212
+ self.threshold_config_button.setStyleSheet(self.parent.parent.parent.button_style_sheet_2)
213
+ self.threshold_config_button.clicked.connect(self.open_threshold_config_wizard)
214
+ self.layout.addWidget(self.threshold_config_button,3,0,1,2)
215
+ self.threshold_config_button.hide()
216
+
217
+ def showDialog(self):
218
+
219
+ """
220
+ Import the model in the proper folder.
221
+ """
222
+
223
+ # Set the file dialog according to the option chosen
224
+ if self.stardist_button.isChecked():
225
+ self.seg_mode = "stardist"
226
+ self.file_dialog.setFileMode(QFileDialog.Directory)
227
+ elif self.cellpose_button.isChecked():
228
+ self.seg_mode = "cellpose"
229
+ self.file_dialog.setFileMode(QFileDialog.ExistingFile)
230
+ elif self.threshold_button.isChecked():
231
+ self.seg_mode = "threshold"
232
+ self.file_dialog.setFileMode(QFileDialog.ExistingFile)
233
+
234
+ # If accepted check validity of data
235
+ if self.file_dialog.exec_() == QFileDialog.Accepted:
236
+ self.filename = self.file_dialog.selectedFiles()[0]
237
+ if self.seg_mode=="stardist":
238
+ subfiles = glob(self.filename+"/*")
239
+ subfiles = [s.replace('\\','/') for s in subfiles]
240
+ if self.filename+"/thresholds.json" in subfiles:
241
+ self.file_label.setText(self.filename.split("/")[-1])
242
+ self.modelname = self.filename.split("/")[-1]
243
+ self.destination = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]+f"/models/{self.target_folder}/"+self.modelname
244
+ self.folder_dest = self.destination
245
+ else:
246
+ msgBox = QMessageBox()
247
+ msgBox.setIcon(QMessageBox.Warning)
248
+ msgBox.setText("StarDist model not recognized... Please ensure that it contains a thresholds.json file or that it is a valid StarDist model...")
249
+ msgBox.setWindowTitle("Warning")
250
+ msgBox.setStandardButtons(QMessageBox.Ok)
251
+ returnValue = msgBox.exec()
252
+ if returnValue == QMessageBox.Ok:
253
+ return None
254
+
255
+ if self.seg_mode=="cellpose":
256
+ self.file_label.setText(self.filename.split("/")[-1])
257
+ self.modelname = self.filename.split("/")[-1]
258
+ print(f"Transferring Cellpose model {self.filename}...")
259
+ self.folder_dest = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]+f"/models/{self.target_folder}/"+self.modelname
260
+ self.destination = self.folder_dest+f"/{self.modelname}"
261
+
262
+ if self.seg_mode=="threshold":
263
+ self.file_label.setText(self.filename.split("/")[-1])
264
+
265
+ def show_seg_options(self):
266
+
267
+ """
268
+ Show the relevant widgets, mask the others.
269
+ """
270
+
271
+ if self.cellpose_button.isChecked():
272
+ self.spatial_calib_le.setToolTip('Cellpose rescales the images such that the cells are 30.0 pixels. You can compute the scale from the training data as\n(pixel calibration [µm] in training images)*(cell diameter [px] in training images)/(30 [px]).\nIf you pass images with a different calibration to the model, they will be rescaled automatically.\nThe rescaling is ignored if you pass a diameter different from 30 px below.')
273
+ self.ch_1_label.setText('cyto: ')
274
+ self.ch_2_label.setText('nuclei: ')
275
+ for c in self.stardist_options+[self.threshold_config_button]:
276
+ c.hide()
277
+ for c in self.cellpose_options+self.base_block_options:
278
+ c.show()
279
+ self.unlock_upload()
280
+ elif self.stardist_button.isChecked():
281
+ self.spatial_calib_le.setToolTip('')
282
+ self.ch_1_label.setText('channel 1: ')
283
+ self.ch_2_label.setText('channel 2: ')
284
+ for c in self.stardist_options+self.base_block_options:
285
+ c.show()
286
+ for c in self.cellpose_options+[self.threshold_config_button]:
287
+ c.hide()
288
+ self.unlock_upload()
289
+ else:
290
+ for c in self.stardist_options+self.cellpose_options+self.base_block_options:
291
+ c.hide()
292
+ self.threshold_config_button.show()
293
+ self.upload_button.setEnabled(True)
294
+ self.adjustSize()
295
+
296
+ def upload_model(self):
297
+
298
+ """
299
+ Upload the model.
300
+ """
301
+
302
+ if self.file_label.text()=='No file chosen':
303
+ msgBox = QMessageBox()
304
+ msgBox.setIcon(QMessageBox.Warning)
305
+ msgBox.setText("Please select a model first.")
306
+ msgBox.setWindowTitle("Warning")
307
+ msgBox.setStandardButtons(QMessageBox.Ok)
308
+ returnValue = msgBox.exec()
309
+ if returnValue == QMessageBox.Ok:
310
+ return None
311
+
312
+ if not self.threshold_button.isChecked():
313
+
314
+ if self.stardist_button.isChecked():
315
+
316
+ try:
317
+ shutil.copytree(self.filename, self.destination)
318
+ except FileExistsError:
319
+ msgBox = QMessageBox()
320
+ msgBox.setIcon(QMessageBox.Warning)
321
+ msgBox.setText("A model with the same name already exists in the models folder. Please rename it.")
322
+ msgBox.setWindowTitle("Warning")
323
+ msgBox.setStandardButtons(QMessageBox.Ok)
324
+ returnValue = msgBox.exec()
325
+ if returnValue == QMessageBox.Ok:
326
+ return None
327
+
328
+ elif self.cellpose_button.isChecked():
329
+ try:
330
+ if not os.path.exists(self.folder_dest):
331
+ os.mkdir(self.folder_dest)
332
+ shutil.copy(self.filename, self.destination)
333
+ except FileExistsError:
334
+ msgBox = QMessageBox()
335
+ msgBox.setIcon(QMessageBox.Warning)
336
+ msgBox.setText("A model with the same name already exists in the models folder. Please rename it.")
337
+ msgBox.setWindowTitle("Warning")
338
+ msgBox.setStandardButtons(QMessageBox.Ok)
339
+ returnValue = msgBox.exec()
340
+ if returnValue == QMessageBox.Ok:
341
+ return None
342
+
343
+ self.generate_input_config()
344
+ self.parent.init_seg_model_list()
345
+ self.close()
346
+ else:
347
+ if self.mode=="targets":
348
+ self.parent.threshold_config_targets = self.filename
349
+ print('Path to threshold configuration successfully set in the software')
350
+ self.close()
351
+ elif self.mode=="effectors":
352
+ self.parent.threshold_config_effectors = self.filename
353
+ print('Path to threshold configuration successfully set in the software')
354
+ self.close()
355
+
356
+ def generate_input_config(self):
357
+
358
+ """
359
+ Check the ticked options and input parameters to create
360
+ a configuration to use the uploaded model properly.
361
+ """
362
+
363
+ dico = {}
364
+
365
+ # Check model option
366
+ if self.stardist_button.isChecked():
367
+
368
+ # Get channels
369
+ channels = []
370
+ for c in self.combos:
371
+ if c.currentText()!="--":
372
+ channels.append(c.currentText())
373
+ model_type = "stardist"
374
+ spatial_calib = float(self.spatial_calib_le.text().replace(',','.'))
375
+ normalize = self.normalize_checkbox.isChecked()
376
+ dico.update({"channels": channels,
377
+ "normalize": normalize,
378
+ "spatial_calibration": spatial_calib,
379
+ 'normalization_percentile': norm_percentile,
380
+ 'normalization_clip': norm_clip,
381
+ 'normalization_values': normalization_values,
382
+ })
383
+
384
+ elif self.cellpose_button.isChecked():
385
+
386
+ # Get channels (cyto and nucleus)
387
+ channels = []
388
+ for c in self.combos[:2]:
389
+ if c.currentText()!="--":
390
+ channels.append(c.currentText())
391
+
392
+ model_type = "cellpose"
393
+ # cellpose requires at least two channels
394
+ if len(channels)==1:
395
+ channels += ['None']
396
+
397
+ diameter = float(self.cp_diameter_le.text().replace(',','.'))
398
+ cellprob_threshold = float(self.cp_cellprob_le.text().replace(',','.'))
399
+ flow_threshold = float(self.cp_flow_le.text().replace(',','.'))
400
+ normalize = self.normalize_checkbox.isChecked()
401
+ spatial_calib = float(self.spatial_calib_le.text().replace(',','.'))
402
+ if normalize:
403
+ norm_percentile = [True]*len(channels)
404
+ norm_clip = [True]*len(channels)
405
+ else:
406
+ norm_percentile = [False]*len(channels)
407
+ norm_clip = [False]*len(channels)
408
+ normalization_values = [[1.0,99.0]]*len(channels)
409
+
410
+ dico.update({"channels": channels,
411
+ "diameter": diameter,
412
+ "cellprob_threshold": cellprob_threshold,
413
+ "flow_threshold": flow_threshold,
414
+ "normalize": normalize,
415
+ "spatial_calibration": spatial_calib,
416
+ 'normalization_percentile': norm_percentile,
417
+ 'normalization_clip': norm_clip,
418
+ 'normalization_values': normalization_values,
419
+ })
420
+
421
+ elif self.threshold_button.isChecked():
422
+ model_type = "threshold"
423
+ return None
424
+
425
+ dico.update({"model_type": model_type})
426
+ json_object = json.dumps(dico, indent=4)
427
+
428
+ # Writing to sample.json
429
+ # if os.path.exists(self.folder_dest):
430
+
431
+ # msgBox = QMessageBox()
432
+ # msgBox.setIcon(QMessageBox.Warning)
433
+ # msgBox.setText("A model with the same name already exists. Do you want to replace it?")
434
+ # msgBox.setWindowTitle("Confirm")
435
+ # msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
436
+ # returnValue = msgBox.exec()
437
+ # if returnValue == QMessageBox.No:
438
+ # return None
439
+ # elif returnValue == QMessageBox.Yes:
440
+ # shutil.rmtree(self.folder_dest)
441
+ #os.mkdir(self.folder_dest)
442
+
443
+ print("Configuration successfully written in ",self.folder_dest+"/config_input.json")
444
+ with open(self.folder_dest+"/config_input.json", "w") as outfile:
445
+ outfile.write(json_object)
446
+
447
+ def open_threshold_config_wizard(self):
448
+
449
+ if isinstance(self.parent.parent.pos, str):
450
+ self.ThreshWizard = ThresholdConfigWizard(self)
451
+ self.ThreshWizard.show()
452
+ else:
453
+ msgBox = QMessageBox()
454
+ msgBox.setIcon(QMessageBox.Warning)
455
+ msgBox.setText("Please select a unique position before launching the wizard...")
456
+ msgBox.setWindowTitle("Warning")
457
+ msgBox.setStandardButtons(QMessageBox.Ok)
458
+ returnValue = msgBox.exec()
459
+ if returnValue == QMessageBox.Ok:
460
+ return None