celldetective 1.4.2__py3-none-any.whl → 1.5.0b1__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.
- celldetective/__init__.py +25 -0
- celldetective/__main__.py +62 -43
- celldetective/_version.py +1 -1
- celldetective/extra_properties.py +477 -399
- celldetective/filters.py +192 -97
- celldetective/gui/InitWindow.py +541 -411
- celldetective/gui/__init__.py +0 -15
- celldetective/gui/about.py +44 -39
- celldetective/gui/analyze_block.py +120 -84
- celldetective/gui/base/__init__.py +0 -0
- celldetective/gui/base/channel_norm_generator.py +335 -0
- celldetective/gui/base/components.py +249 -0
- celldetective/gui/base/feature_choice.py +92 -0
- celldetective/gui/base/figure_canvas.py +52 -0
- celldetective/gui/base/list_widget.py +133 -0
- celldetective/gui/{styles.py → base/styles.py} +92 -36
- celldetective/gui/base/utils.py +33 -0
- celldetective/gui/base_annotator.py +900 -767
- celldetective/gui/classifier_widget.py +6 -22
- celldetective/gui/configure_new_exp.py +777 -671
- celldetective/gui/control_panel.py +635 -524
- celldetective/gui/dynamic_progress.py +449 -0
- celldetective/gui/event_annotator.py +2023 -1662
- celldetective/gui/generic_signal_plot.py +1292 -944
- celldetective/gui/gui_utils.py +899 -1289
- celldetective/gui/interactions_block.py +658 -0
- celldetective/gui/interactive_timeseries_viewer.py +447 -0
- celldetective/gui/json_readers.py +48 -15
- celldetective/gui/layouts/__init__.py +5 -0
- celldetective/gui/layouts/background_model_free_layout.py +537 -0
- celldetective/gui/layouts/channel_offset_layout.py +134 -0
- celldetective/gui/layouts/local_correction_layout.py +91 -0
- celldetective/gui/layouts/model_fit_layout.py +372 -0
- celldetective/gui/layouts/operation_layout.py +68 -0
- celldetective/gui/layouts/protocol_designer_layout.py +96 -0
- celldetective/gui/pair_event_annotator.py +3130 -2435
- celldetective/gui/plot_measurements.py +586 -267
- celldetective/gui/plot_signals_ui.py +724 -506
- celldetective/gui/preprocessing_block.py +395 -0
- celldetective/gui/process_block.py +1678 -1831
- celldetective/gui/seg_model_loader.py +580 -473
- celldetective/gui/settings/__init__.py +0 -7
- celldetective/gui/settings/_cellpose_model_params.py +181 -0
- celldetective/gui/settings/_event_detection_model_params.py +95 -0
- celldetective/gui/settings/_segmentation_model_params.py +159 -0
- celldetective/gui/settings/_settings_base.py +77 -65
- celldetective/gui/settings/_settings_event_model_training.py +752 -526
- celldetective/gui/settings/_settings_measurements.py +1133 -964
- celldetective/gui/settings/_settings_neighborhood.py +574 -488
- celldetective/gui/settings/_settings_segmentation_model_training.py +779 -564
- celldetective/gui/settings/_settings_signal_annotator.py +329 -305
- celldetective/gui/settings/_settings_tracking.py +1304 -1094
- celldetective/gui/settings/_stardist_model_params.py +98 -0
- celldetective/gui/survival_ui.py +422 -312
- celldetective/gui/tableUI.py +1665 -1701
- celldetective/gui/table_ops/_maths.py +295 -0
- celldetective/gui/table_ops/_merge_groups.py +140 -0
- celldetective/gui/table_ops/_merge_one_hot.py +95 -0
- celldetective/gui/table_ops/_query_table.py +43 -0
- celldetective/gui/table_ops/_rename_col.py +44 -0
- celldetective/gui/thresholds_gui.py +382 -179
- celldetective/gui/viewers/__init__.py +0 -0
- celldetective/gui/viewers/base_viewer.py +700 -0
- celldetective/gui/viewers/channel_offset_viewer.py +331 -0
- celldetective/gui/viewers/contour_viewer.py +394 -0
- celldetective/gui/viewers/size_viewer.py +153 -0
- celldetective/gui/viewers/spot_detection_viewer.py +341 -0
- celldetective/gui/viewers/threshold_viewer.py +309 -0
- celldetective/gui/workers.py +403 -126
- celldetective/log_manager.py +92 -0
- celldetective/measure.py +1895 -1478
- celldetective/napari/__init__.py +0 -0
- celldetective/napari/utils.py +1025 -0
- celldetective/neighborhood.py +1914 -1448
- celldetective/preprocessing.py +1620 -1220
- celldetective/processes/__init__.py +0 -0
- celldetective/processes/background_correction.py +271 -0
- celldetective/processes/compute_neighborhood.py +894 -0
- celldetective/processes/detect_events.py +246 -0
- celldetective/processes/downloader.py +137 -0
- celldetective/processes/measure_cells.py +565 -0
- celldetective/processes/segment_cells.py +760 -0
- celldetective/processes/track_cells.py +435 -0
- celldetective/processes/train_segmentation_model.py +694 -0
- celldetective/processes/train_signal_model.py +265 -0
- celldetective/processes/unified_process.py +292 -0
- celldetective/regionprops/_regionprops.py +358 -317
- celldetective/relative_measurements.py +987 -710
- celldetective/scripts/measure_cells.py +313 -212
- celldetective/scripts/measure_relative.py +90 -46
- celldetective/scripts/segment_cells.py +165 -104
- celldetective/scripts/segment_cells_thresholds.py +96 -68
- celldetective/scripts/track_cells.py +198 -149
- celldetective/scripts/train_segmentation_model.py +324 -201
- celldetective/scripts/train_signal_model.py +87 -45
- celldetective/segmentation.py +844 -749
- celldetective/signals.py +3514 -2861
- celldetective/tracking.py +30 -15
- celldetective/utils/__init__.py +0 -0
- celldetective/utils/cellpose_utils/__init__.py +133 -0
- celldetective/utils/color_mappings.py +42 -0
- celldetective/utils/data_cleaning.py +630 -0
- celldetective/utils/data_loaders.py +450 -0
- celldetective/utils/dataset_helpers.py +207 -0
- celldetective/utils/downloaders.py +235 -0
- celldetective/utils/event_detection/__init__.py +8 -0
- celldetective/utils/experiment.py +1782 -0
- celldetective/utils/image_augmenters.py +308 -0
- celldetective/utils/image_cleaning.py +74 -0
- celldetective/utils/image_loaders.py +926 -0
- celldetective/utils/image_transforms.py +335 -0
- celldetective/utils/io.py +62 -0
- celldetective/utils/mask_cleaning.py +348 -0
- celldetective/utils/mask_transforms.py +5 -0
- celldetective/utils/masks.py +184 -0
- celldetective/utils/maths.py +351 -0
- celldetective/utils/model_getters.py +325 -0
- celldetective/utils/model_loaders.py +296 -0
- celldetective/utils/normalization.py +380 -0
- celldetective/utils/parsing.py +465 -0
- celldetective/utils/plots/__init__.py +0 -0
- celldetective/utils/plots/regression.py +53 -0
- celldetective/utils/resources.py +34 -0
- celldetective/utils/stardist_utils/__init__.py +104 -0
- celldetective/utils/stats.py +90 -0
- celldetective/utils/types.py +21 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/METADATA +1 -1
- celldetective-1.5.0b1.dist-info/RECORD +187 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/WHEEL +1 -1
- tests/gui/test_new_project.py +129 -117
- tests/gui/test_project.py +127 -79
- tests/test_filters.py +39 -15
- tests/test_notebooks.py +8 -0
- tests/test_tracking.py +232 -13
- tests/test_utils.py +123 -77
- celldetective/gui/base_components.py +0 -23
- celldetective/gui/layouts.py +0 -1602
- celldetective/gui/processes/compute_neighborhood.py +0 -594
- celldetective/gui/processes/downloader.py +0 -111
- celldetective/gui/processes/measure_cells.py +0 -360
- celldetective/gui/processes/segment_cells.py +0 -499
- celldetective/gui/processes/track_cells.py +0 -303
- celldetective/gui/processes/train_segmentation_model.py +0 -270
- celldetective/gui/processes/train_signal_model.py +0 -108
- celldetective/gui/table_ops/merge_groups.py +0 -118
- celldetective/gui/viewers.py +0 -1354
- celldetective/io.py +0 -3663
- celldetective/utils.py +0 -3108
- celldetective-1.4.2.dist-info/RECORD +0 -123
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/licenses/LICENSE +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/top_level.txt +0 -0
celldetective/gui/InitWindow.py
CHANGED
|
@@ -1,425 +1,555 @@
|
|
|
1
1
|
import gc
|
|
2
2
|
import json
|
|
3
3
|
import os
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
4
6
|
from glob import glob
|
|
5
7
|
from subprocess import Popen, check_output
|
|
6
8
|
|
|
7
|
-
from PyQt5.QtCore import QUrl, Qt
|
|
9
|
+
from PyQt5.QtCore import QUrl, Qt, QThread
|
|
8
10
|
from PyQt5.QtGui import QDesktopServices, QIntValidator
|
|
9
|
-
from PyQt5.QtWidgets import
|
|
10
|
-
|
|
11
|
+
from PyQt5.QtWidgets import (
|
|
12
|
+
QAction,
|
|
13
|
+
QApplication,
|
|
14
|
+
QCheckBox,
|
|
15
|
+
QDialog,
|
|
16
|
+
QFileDialog,
|
|
17
|
+
QHBoxLayout,
|
|
18
|
+
QLabel,
|
|
19
|
+
QLineEdit,
|
|
20
|
+
QMenu,
|
|
21
|
+
QPushButton,
|
|
22
|
+
QMessageBox,
|
|
23
|
+
QVBoxLayout,
|
|
24
|
+
)
|
|
11
25
|
from fonticon_mdi6 import MDI6
|
|
12
26
|
from psutil import cpu_count
|
|
13
27
|
from superqt.fonticon import icon
|
|
28
|
+
from celldetective.gui.base.components import (
|
|
29
|
+
CelldetectiveWidget,
|
|
30
|
+
CelldetectiveMainWindow,
|
|
31
|
+
generic_message,
|
|
32
|
+
)
|
|
33
|
+
from celldetective.gui.base.utils import center_window, pretty_table
|
|
34
|
+
from celldetective.log_manager import get_logger
|
|
14
35
|
|
|
15
|
-
|
|
16
|
-
from celldetective.gui.about import AboutWidget
|
|
17
|
-
from celldetective.gui.gui_utils import center_window, generic_message
|
|
18
|
-
from celldetective.gui.processes.downloader import DownloadProcess
|
|
19
|
-
from celldetective.gui.workers import ProgressWindow
|
|
20
|
-
from celldetective.io import correct_annotation, extract_well_name_and_number
|
|
21
|
-
from celldetective.utils import download_zenodo_file, pretty_table
|
|
36
|
+
logger = get_logger("celldetective")
|
|
22
37
|
|
|
23
38
|
|
|
24
|
-
class
|
|
39
|
+
class BackgroundLoader(QThread):
|
|
40
|
+
def run(self):
|
|
41
|
+
logger.info("Loading background packages...")
|
|
42
|
+
try:
|
|
43
|
+
from celldetective.gui.control_panel import ControlPanel
|
|
44
|
+
|
|
45
|
+
self.ControlPanel = ControlPanel
|
|
46
|
+
from celldetective.gui.about import AboutWidget
|
|
47
|
+
|
|
48
|
+
self.AboutWidget = AboutWidget
|
|
49
|
+
from celldetective.processes.downloader import DownloadProcess
|
|
50
|
+
|
|
51
|
+
self.DownloadProcess = DownloadProcess
|
|
52
|
+
from celldetective.gui.configure_new_exp import ConfigNewExperiment
|
|
25
53
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
54
|
+
self.ConfigNewExperiment = ConfigNewExperiment
|
|
55
|
+
import pandas
|
|
56
|
+
import matplotlib.pyplot
|
|
57
|
+
import scipy.ndimage
|
|
58
|
+
import tifffile
|
|
59
|
+
import numpy
|
|
60
|
+
except Exception:
|
|
61
|
+
logger.error("Background packages not loaded...")
|
|
62
|
+
logger.info("Background packages loaded...")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class AppInitWindow(CelldetectiveMainWindow):
|
|
66
|
+
"""
|
|
67
|
+
Initial window to set the experiment folder or create a new one.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self, parent_window=None, software_location=None):
|
|
71
|
+
|
|
72
|
+
super().__init__()
|
|
73
|
+
|
|
74
|
+
self.parent_window = parent_window
|
|
75
|
+
self.setWindowTitle("celldetective")
|
|
76
|
+
|
|
77
|
+
self.n_threads = min([1, cpu_count()])
|
|
78
|
+
|
|
79
|
+
self.use_gpu = False
|
|
80
|
+
self.gpu_thread = threading.Thread(target=self.check_gpu)
|
|
81
|
+
self.gpu_thread.start()
|
|
82
|
+
|
|
83
|
+
self.soft_path = software_location
|
|
84
|
+
self.onlyInt = QIntValidator()
|
|
85
|
+
|
|
86
|
+
self._create_actions()
|
|
87
|
+
self._create_menu_bar()
|
|
88
|
+
|
|
89
|
+
app = QApplication.instance()
|
|
90
|
+
self.screen = app.primaryScreen()
|
|
91
|
+
self.geometry = self.screen.availableGeometry()
|
|
92
|
+
self.screen_width, self.screen_height = self.geometry.getRect()[-2:]
|
|
93
|
+
|
|
94
|
+
central_widget = CelldetectiveWidget()
|
|
95
|
+
self.vertical_layout = QVBoxLayout(central_widget)
|
|
96
|
+
self.vertical_layout.setContentsMargins(15, 15, 15, 15)
|
|
97
|
+
self.vertical_layout.addWidget(QLabel("Experiment folder:"))
|
|
98
|
+
self.create_locate_exp_hbox()
|
|
99
|
+
self.create_buttons_hbox()
|
|
100
|
+
self.setCentralWidget(central_widget)
|
|
101
|
+
self.reload_previous_gpu_threads()
|
|
102
|
+
self.adjustSize()
|
|
103
|
+
self.setFixedSize(self.size())
|
|
104
|
+
self.show()
|
|
105
|
+
|
|
106
|
+
self.bg_loader = BackgroundLoader()
|
|
107
|
+
self.bg_loader.start()
|
|
108
|
+
|
|
109
|
+
def closeEvent(self, event):
|
|
110
|
+
|
|
111
|
+
QApplication.closeAllWindows()
|
|
112
|
+
event.accept()
|
|
113
|
+
gc.collect()
|
|
114
|
+
|
|
115
|
+
def check_gpu(self):
|
|
116
|
+
try:
|
|
117
|
+
if os.name == "nt":
|
|
118
|
+
kwargs = {"creationflags": 0x08000000}
|
|
119
|
+
else:
|
|
120
|
+
kwargs = {}
|
|
121
|
+
check_output("nvidia-smi", **kwargs)
|
|
122
|
+
logger.info(
|
|
123
|
+
"NVIDIA GPU detected (activate or disable in Memory & Threads)..."
|
|
124
|
+
)
|
|
125
|
+
self.use_gpu = True
|
|
126
|
+
except Exception as e:
|
|
127
|
+
logger.info(f"No NVIDIA GPU detected: {e}...")
|
|
128
|
+
self.use_gpu = False
|
|
129
|
+
|
|
130
|
+
def create_locate_exp_hbox(self):
|
|
131
|
+
|
|
132
|
+
self.locate_exp_layout = QHBoxLayout()
|
|
133
|
+
self.locate_exp_layout.setContentsMargins(0, 5, 0, 0)
|
|
134
|
+
self.experiment_path_selection = QLineEdit()
|
|
135
|
+
self.experiment_path_selection.setAlignment(Qt.AlignLeft)
|
|
136
|
+
self.experiment_path_selection.setEnabled(True)
|
|
137
|
+
self.experiment_path_selection.setDragEnabled(True)
|
|
138
|
+
self.experiment_path_selection.setFixedWidth(430)
|
|
139
|
+
self.experiment_path_selection.textChanged[str].connect(
|
|
140
|
+
self.check_path_and_enable_opening
|
|
141
|
+
)
|
|
142
|
+
try:
|
|
143
|
+
self.folder_name = os.getcwd()
|
|
144
|
+
except FileNotFoundError as _:
|
|
145
|
+
self.folder_name = ""
|
|
146
|
+
self.experiment_path_selection.setPlaceholderText("/path/to/experiment/folder/")
|
|
147
|
+
self.locate_exp_layout.addWidget(self.experiment_path_selection, 90)
|
|
148
|
+
|
|
149
|
+
self.browse_button = QPushButton("Browse...")
|
|
150
|
+
self.browse_button.clicked.connect(self.browse_experiment_folder)
|
|
151
|
+
self.browse_button.setStyleSheet(self.button_style_sheet)
|
|
152
|
+
self.browse_button.setIcon(icon(MDI6.folder, color="white"))
|
|
153
|
+
self.locate_exp_layout.addWidget(self.browse_button, 10)
|
|
154
|
+
self.vertical_layout.addLayout(self.locate_exp_layout)
|
|
155
|
+
|
|
156
|
+
def _create_menu_bar(self):
|
|
157
|
+
|
|
158
|
+
menu_bar = self.menuBar()
|
|
159
|
+
menu_bar.clear()
|
|
160
|
+
# Creating menus using a QMenu object
|
|
161
|
+
|
|
162
|
+
file_menu = QMenu("File", self)
|
|
163
|
+
file_menu.clear()
|
|
164
|
+
file_menu.addAction(self.new_exp_action)
|
|
165
|
+
file_menu.addAction(self.open_action)
|
|
166
|
+
|
|
167
|
+
file_menu.addMenu(self.OpenRecentAction)
|
|
168
|
+
self.OpenRecentAction.clear()
|
|
169
|
+
if len(self.recent_file_acts) > 0:
|
|
170
|
+
for i in range(len(self.recent_file_acts)):
|
|
171
|
+
self.OpenRecentAction.addAction(self.recent_file_acts[i])
|
|
172
|
+
|
|
173
|
+
file_menu.addMenu(self.openDemo)
|
|
174
|
+
self.openDemo.addAction(self.open_spreading_assay_demo)
|
|
175
|
+
self.openDemo.addAction(self.open_cytotoxicity_assay_demo)
|
|
176
|
+
|
|
177
|
+
file_menu.addAction(self.open_models)
|
|
178
|
+
file_menu.addSeparator()
|
|
179
|
+
file_menu.addAction(self.exit_action)
|
|
180
|
+
menu_bar.addMenu(file_menu)
|
|
181
|
+
|
|
182
|
+
options_menu = QMenu("Options", self)
|
|
183
|
+
options_menu.addAction(self.memory_and_threads_action)
|
|
184
|
+
menu_bar.addMenu(options_menu)
|
|
185
|
+
|
|
186
|
+
plugins_menu = QMenu("Plugins", self)
|
|
187
|
+
plugins_menu.addAction(self.correct_annotation_action)
|
|
188
|
+
menu_bar.addMenu(plugins_menu)
|
|
189
|
+
|
|
190
|
+
help_menu = QMenu("Help", self)
|
|
191
|
+
help_menu.clear()
|
|
192
|
+
help_menu.addAction(self.documentation_action)
|
|
193
|
+
# help_menu.addAction(self.SoftwareAction)
|
|
194
|
+
help_menu.addSeparator()
|
|
195
|
+
help_menu.addAction(self.about_action)
|
|
196
|
+
menu_bar.addMenu(help_menu)
|
|
197
|
+
|
|
198
|
+
# editMenu = menuBar.addMenu("&Edit")
|
|
199
|
+
# help_menu = menuBar.addMenu("&Help")
|
|
200
|
+
|
|
201
|
+
def _create_actions(self):
|
|
202
|
+
# Creating action using the first constructor
|
|
203
|
+
# self.newAction = QAction(self)
|
|
204
|
+
# self.newAction.setText("&New")
|
|
205
|
+
# Creating actions using the second constructor
|
|
206
|
+
self.open_action = QAction("Open Project", self)
|
|
207
|
+
self.open_action.setShortcut("Ctrl+O")
|
|
208
|
+
self.open_action.setShortcutVisibleInContextMenu(True)
|
|
209
|
+
|
|
210
|
+
self.openDemo = QMenu("Open Demo")
|
|
211
|
+
self.open_spreading_assay_demo = QAction("Spreading Assay Demo", self)
|
|
212
|
+
self.open_cytotoxicity_assay_demo = QAction("Cytotoxicity Assay Demo", self)
|
|
213
|
+
|
|
214
|
+
self.memory_and_threads_action = QAction("Threads")
|
|
215
|
+
|
|
216
|
+
self.correct_annotation_action = QAction("Correct a segmentation annotation")
|
|
217
|
+
|
|
218
|
+
self.new_exp_action = QAction("New", self)
|
|
219
|
+
self.new_exp_action.setShortcut("Ctrl+N")
|
|
220
|
+
self.new_exp_action.setShortcutVisibleInContextMenu(True)
|
|
221
|
+
self.exit_action = QAction("Exit", self)
|
|
222
|
+
|
|
223
|
+
self.open_models = QAction("Open Models Location")
|
|
224
|
+
self.open_models.setShortcut("Ctrl+L")
|
|
225
|
+
self.open_models.setShortcutVisibleInContextMenu(True)
|
|
226
|
+
|
|
227
|
+
self.OpenRecentAction = QMenu("Open Recent Project")
|
|
228
|
+
self.reload_previous_experiments()
|
|
229
|
+
|
|
230
|
+
self.documentation_action = QAction("Documentation", self)
|
|
231
|
+
self.documentation_action.setShortcut("Ctrl+D")
|
|
232
|
+
self.documentation_action.setShortcutVisibleInContextMenu(True)
|
|
233
|
+
|
|
234
|
+
# self.SoftwareAction = QAction("Software", self) #1st arg icon(MDI6.information)
|
|
235
|
+
self.about_action = QAction("About celldetective", self)
|
|
236
|
+
|
|
237
|
+
# self.DocumentationAction.triggered.connect(self.load_previous_config)
|
|
238
|
+
self.open_action.triggered.connect(self.open_experiment)
|
|
239
|
+
self.new_exp_action.triggered.connect(self.create_new_experiment)
|
|
240
|
+
self.exit_action.triggered.connect(self.close)
|
|
241
|
+
self.open_models.triggered.connect(self.open_models_folder)
|
|
242
|
+
self.about_action.triggered.connect(self.open_about_window)
|
|
243
|
+
self.memory_and_threads_action.triggered.connect(self.set_memory_and_threads)
|
|
244
|
+
self.correct_annotation_action.triggered.connect(self.correct_seg_annotation)
|
|
245
|
+
self.documentation_action.triggered.connect(self.open_documentation)
|
|
246
|
+
|
|
247
|
+
self.open_spreading_assay_demo.triggered.connect(
|
|
248
|
+
self.download_spreading_assay_demo
|
|
249
|
+
)
|
|
250
|
+
self.open_cytotoxicity_assay_demo.triggered.connect(
|
|
251
|
+
self.download_cytotoxicity_assay_demo
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
def download_spreading_assay_demo(self):
|
|
255
|
+
if self.bg_loader.isFinished() and hasattr(self.bg_loader, "DownloadProcess"):
|
|
256
|
+
DownloadProcess = self.bg_loader.DownloadProcess
|
|
257
|
+
else:
|
|
258
|
+
from celldetective.processes.downloader import DownloadProcess
|
|
259
|
+
from celldetective.gui.workers import GenericProgressWindow
|
|
260
|
+
|
|
261
|
+
self.target_dir = str(
|
|
262
|
+
QFileDialog.getExistingDirectory(self, "Select Folder for Download")
|
|
263
|
+
)
|
|
264
|
+
if self.target_dir == "":
|
|
265
|
+
return None
|
|
266
|
+
|
|
267
|
+
if not os.path.exists(os.sep.join([self.target_dir, "demo_ricm"])):
|
|
268
|
+
self.output_dir = self.target_dir
|
|
269
|
+
self.file = "demo_ricm"
|
|
270
|
+
process_args = {"output_dir": self.output_dir, "file": self.file}
|
|
271
|
+
self.job = GenericProgressWindow(
|
|
272
|
+
DownloadProcess,
|
|
273
|
+
parent_window=self,
|
|
274
|
+
title="Download",
|
|
275
|
+
process_args=process_args,
|
|
276
|
+
label_text="Downloading demo_ricm...",
|
|
277
|
+
)
|
|
278
|
+
result = self.job.exec_()
|
|
279
|
+
if result == QDialog.Accepted:
|
|
280
|
+
pass
|
|
281
|
+
elif result == QDialog.Rejected:
|
|
282
|
+
return None
|
|
283
|
+
# download_zenodo_file('demo_ricm', self.target_dir)
|
|
284
|
+
self.experiment_path_selection.setText(
|
|
285
|
+
os.sep.join([self.target_dir, "demo_ricm"])
|
|
286
|
+
)
|
|
287
|
+
self.validate_button.click()
|
|
288
|
+
|
|
289
|
+
def download_cytotoxicity_assay_demo(self):
|
|
290
|
+
from celldetective.utils.downloaders import download_zenodo_file
|
|
291
|
+
|
|
292
|
+
self.target_dir = str(
|
|
293
|
+
QFileDialog.getExistingDirectory(self, "Select Folder for Download")
|
|
294
|
+
)
|
|
295
|
+
if self.target_dir == "":
|
|
296
|
+
return None
|
|
297
|
+
|
|
298
|
+
if not os.path.exists(os.sep.join([self.target_dir, "demo_adcc"])):
|
|
299
|
+
download_zenodo_file("demo_adcc", self.target_dir)
|
|
300
|
+
self.experiment_path_selection.setText(
|
|
301
|
+
os.sep.join([self.target_dir, "demo_adcc"])
|
|
302
|
+
)
|
|
303
|
+
self.validate_button.click()
|
|
304
|
+
|
|
305
|
+
def reload_previous_gpu_threads(self):
|
|
306
|
+
|
|
307
|
+
self.recent_file_acts = []
|
|
308
|
+
self.threads_config_path = os.sep.join(
|
|
309
|
+
[self.soft_path, "celldetective", "threads.json"]
|
|
310
|
+
)
|
|
311
|
+
logger.info("Reading previous Memory & Threads settings...")
|
|
312
|
+
if os.path.exists(self.threads_config_path):
|
|
313
|
+
with open(self.threads_config_path, "r") as f:
|
|
314
|
+
self.threads_config = json.load(f)
|
|
315
|
+
if "use_gpu" in self.threads_config:
|
|
316
|
+
self.use_gpu = bool(self.threads_config["use_gpu"])
|
|
317
|
+
logger.info(f"Use GPU: {self.use_gpu}...")
|
|
318
|
+
if "n_threads" in self.threads_config:
|
|
319
|
+
self.n_threads = int(self.threads_config["n_threads"])
|
|
320
|
+
logger.info(f"Number of threads: {self.n_threads}...")
|
|
321
|
+
|
|
322
|
+
def reload_previous_experiments(self):
|
|
323
|
+
|
|
324
|
+
self.recent_file_acts = []
|
|
325
|
+
if os.path.exists(os.sep.join([self.soft_path, "celldetective", "recent.txt"])):
|
|
326
|
+
recent_exps = open(
|
|
327
|
+
os.sep.join([self.soft_path, "celldetective", "recent.txt"]), "r"
|
|
328
|
+
)
|
|
329
|
+
recent_exps = recent_exps.readlines()
|
|
330
|
+
recent_exps = [r.strip() for r in recent_exps]
|
|
331
|
+
recent_exps.reverse()
|
|
332
|
+
recent_exps = list(dict.fromkeys(recent_exps))
|
|
333
|
+
self.recent_file_acts = [QAction(r, self) for r in recent_exps]
|
|
334
|
+
for r in self.recent_file_acts:
|
|
335
|
+
r.triggered.connect(
|
|
336
|
+
lambda checked, item=r: self.load_recent_exp(item.text())
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
def correct_seg_annotation(self):
|
|
340
|
+
from celldetective.napari.utils import correct_annotation
|
|
341
|
+
|
|
342
|
+
self.filename, _ = QFileDialog.getOpenFileName(
|
|
343
|
+
self, "Open Image", "/home/", "TIF Files (*.tif)"
|
|
344
|
+
)
|
|
345
|
+
if self.filename != "":
|
|
346
|
+
logger.info(f"Opening {self.filename} in napari...")
|
|
347
|
+
correct_annotation(self.filename)
|
|
348
|
+
else:
|
|
349
|
+
return None
|
|
350
|
+
|
|
351
|
+
def set_memory_and_threads(self):
|
|
352
|
+
|
|
353
|
+
logger.info("Opening Memory & Threads...")
|
|
354
|
+
|
|
355
|
+
self.threads_widget = CelldetectiveWidget()
|
|
356
|
+
self.threads_widget.setWindowTitle("Threads")
|
|
357
|
+
layout = QVBoxLayout()
|
|
358
|
+
self.threads_widget.setLayout(layout)
|
|
359
|
+
|
|
360
|
+
self.threads_le = QLineEdit(str(self.n_threads))
|
|
361
|
+
self.threads_le.setValidator(self.onlyInt)
|
|
362
|
+
|
|
363
|
+
hbox = QHBoxLayout()
|
|
364
|
+
hbox.addWidget(QLabel("Parallel threads: "), 33)
|
|
365
|
+
hbox.addWidget(self.threads_le, 66)
|
|
366
|
+
layout.addLayout(hbox)
|
|
367
|
+
|
|
368
|
+
self.use_gpu_checkbox = QCheckBox()
|
|
369
|
+
hbox2 = QHBoxLayout()
|
|
370
|
+
hbox2.addWidget(QLabel("Use GPU: "), 33)
|
|
371
|
+
hbox2.addWidget(self.use_gpu_checkbox, 66)
|
|
372
|
+
layout.addLayout(hbox2)
|
|
373
|
+
if self.use_gpu:
|
|
374
|
+
self.use_gpu_checkbox.setChecked(True)
|
|
375
|
+
|
|
376
|
+
self.validate_thread_btn = QPushButton("Submit")
|
|
377
|
+
self.validate_thread_btn.setStyleSheet(self.button_style_sheet)
|
|
378
|
+
self.validate_thread_btn.clicked.connect(self.set_threads)
|
|
379
|
+
layout.addWidget(self.validate_thread_btn)
|
|
380
|
+
self.threads_widget.show()
|
|
381
|
+
center_window(self.threads_widget)
|
|
382
|
+
|
|
383
|
+
def set_threads(self):
|
|
384
|
+
self.n_threads = int(self.threads_le.text())
|
|
385
|
+
self.use_gpu = bool(self.use_gpu_checkbox.isChecked())
|
|
386
|
+
dico = {"use_gpu": self.use_gpu, "n_threads": self.n_threads}
|
|
387
|
+
with open(self.threads_config_path, "w") as f:
|
|
388
|
+
json.dump(dico, f, indent=4)
|
|
389
|
+
self.threads_widget.close()
|
|
390
|
+
|
|
391
|
+
def open_experiment(self):
|
|
392
|
+
|
|
393
|
+
self.browse_experiment_folder()
|
|
394
|
+
if self.experiment_path_selection.text() != "":
|
|
395
|
+
self.open_directory()
|
|
396
|
+
|
|
397
|
+
def load_recent_exp(self, path):
|
|
398
|
+
|
|
399
|
+
self.experiment_path_selection.setText(path)
|
|
400
|
+
logger.info(f"Attempt to load experiment folder: {path}...")
|
|
401
|
+
self.open_directory()
|
|
402
|
+
|
|
403
|
+
def open_about_window(self):
|
|
404
|
+
if self.bg_loader.isFinished() and hasattr(self.bg_loader, "AboutWidget"):
|
|
405
|
+
AboutWidget = self.bg_loader.AboutWidget
|
|
406
|
+
else:
|
|
407
|
+
from celldetective.gui.about import AboutWidget
|
|
408
|
+
|
|
409
|
+
self.about_wdw = AboutWidget()
|
|
410
|
+
self.about_wdw.show()
|
|
411
|
+
|
|
412
|
+
@staticmethod
|
|
413
|
+
def open_documentation():
|
|
414
|
+
doc_url = QUrl("https://celldetective.readthedocs.io/")
|
|
415
|
+
QDesktopServices.openUrl(doc_url)
|
|
416
|
+
|
|
417
|
+
def open_models_folder(self):
|
|
418
|
+
|
|
419
|
+
path = os.sep.join([self.soft_path, "celldetective", "models", os.sep])
|
|
420
|
+
try:
|
|
421
|
+
Popen(f"explorer {os.path.realpath(path)}")
|
|
422
|
+
except Exception as e:
|
|
423
|
+
logger.warning(f"{e}")
|
|
424
|
+
try:
|
|
425
|
+
os.system('xdg-open "%s"' % path)
|
|
426
|
+
except Exception as e:
|
|
427
|
+
logger.error(f"Error {e}...")
|
|
428
|
+
return None
|
|
429
|
+
|
|
430
|
+
def create_buttons_hbox(self):
|
|
431
|
+
|
|
432
|
+
self.buttons_layout = QHBoxLayout()
|
|
433
|
+
self.buttons_layout.setContentsMargins(30, 15, 30, 5)
|
|
434
|
+
self.new_exp_button = QPushButton("New")
|
|
435
|
+
self.new_exp_button.clicked.connect(self.create_new_experiment)
|
|
436
|
+
self.new_exp_button.setStyleSheet(self.button_style_sheet_2)
|
|
437
|
+
self.buttons_layout.addWidget(self.new_exp_button, 50)
|
|
438
|
+
|
|
439
|
+
self.validate_button = QPushButton("Open")
|
|
440
|
+
self.validate_button.clicked.connect(self.open_directory)
|
|
441
|
+
self.validate_button.setStyleSheet(self.button_style_sheet)
|
|
442
|
+
self.validate_button.setEnabled(False)
|
|
443
|
+
self.validate_button.setShortcut("Return")
|
|
444
|
+
self.buttons_layout.addWidget(self.validate_button, 50)
|
|
445
|
+
self.vertical_layout.addLayout(self.buttons_layout)
|
|
446
|
+
|
|
447
|
+
def check_path_and_enable_opening(self):
|
|
448
|
+
"""
|
|
449
|
+
Enable 'Open' button if the text is a valid path.
|
|
450
|
+
"""
|
|
451
|
+
|
|
452
|
+
text = self.experiment_path_selection.text()
|
|
453
|
+
if (os.path.exists(text)) and os.path.exists(os.sep.join([text, "config.ini"])):
|
|
454
|
+
self.validate_button.setEnabled(True)
|
|
455
|
+
else:
|
|
456
|
+
self.validate_button.setEnabled(False)
|
|
457
|
+
|
|
458
|
+
def set_experiment_path(self, path):
|
|
459
|
+
self.experiment_path_selection.setText(path)
|
|
460
|
+
|
|
461
|
+
def create_new_experiment(self):
|
|
462
|
+
|
|
463
|
+
if self.bg_loader.isFinished() and hasattr(
|
|
464
|
+
self.bg_loader, "ConfigNewExperiment"
|
|
465
|
+
):
|
|
466
|
+
ConfigNewExperiment = self.bg_loader.ConfigNewExperiment
|
|
467
|
+
else:
|
|
468
|
+
from celldetective.gui.configure_new_exp import ConfigNewExperiment
|
|
469
|
+
|
|
470
|
+
logger.info("Configuring new experiment...")
|
|
471
|
+
self.new_exp_window = ConfigNewExperiment(self)
|
|
472
|
+
self.new_exp_window.show()
|
|
473
|
+
center_window(self.new_exp_window)
|
|
474
|
+
|
|
475
|
+
def open_directory(self):
|
|
476
|
+
self.t_ref = time.time()
|
|
477
|
+
|
|
478
|
+
from celldetective.utils.experiment import extract_well_name_and_number
|
|
479
|
+
|
|
480
|
+
self.exp_dir = self.experiment_path_selection.text().replace("/", os.sep)
|
|
481
|
+
logger.info(f"Setting current directory to {self.exp_dir}...")
|
|
482
|
+
|
|
483
|
+
wells = glob(os.sep.join([self.exp_dir, "W*"]))
|
|
484
|
+
self.number_of_wells = len(wells)
|
|
485
|
+
if self.number_of_wells == 0:
|
|
486
|
+
generic_message(
|
|
487
|
+
"No well was found in the experiment folder.\nPlease respect the W*/ nomenclature...",
|
|
488
|
+
msg_type="critical",
|
|
489
|
+
)
|
|
490
|
+
return None
|
|
491
|
+
else:
|
|
492
|
+
if self.number_of_wells == 1:
|
|
493
|
+
logger.info(f"Found {self.number_of_wells} well...")
|
|
494
|
+
elif self.number_of_wells > 1:
|
|
495
|
+
logger.info(f"Found {self.number_of_wells} wells...")
|
|
496
|
+
|
|
497
|
+
def log_position_stats(wells_list):
|
|
498
|
+
number_pos = {}
|
|
499
|
+
for w in wells_list:
|
|
500
|
+
well_name, well_nbr = extract_well_name_and_number(w)
|
|
501
|
+
position_folders = glob(os.sep.join([w, f"{well_nbr}*", os.sep]))
|
|
502
|
+
number_pos.update({well_name: len(position_folders)})
|
|
503
|
+
logger.info(f"Number of positions per well:")
|
|
504
|
+
pretty_table(number_pos)
|
|
505
|
+
|
|
506
|
+
with open(
|
|
507
|
+
os.sep.join([self.soft_path, "celldetective", "recent.txt"]), "a+"
|
|
508
|
+
) as f:
|
|
509
|
+
f.write(self.exp_dir + "\n")
|
|
510
|
+
|
|
511
|
+
threading.Thread(
|
|
512
|
+
target=log_position_stats, args=(wells,), daemon=True
|
|
513
|
+
).start()
|
|
514
|
+
|
|
515
|
+
if self.bg_loader.isFinished() and hasattr(self.bg_loader, "ControlPanel"):
|
|
516
|
+
ControlPanel = self.bg_loader.ControlPanel
|
|
517
|
+
else:
|
|
518
|
+
from celldetective.gui.control_panel import ControlPanel
|
|
519
|
+
|
|
520
|
+
try:
|
|
521
|
+
self.control_panel = ControlPanel(self, self.exp_dir)
|
|
522
|
+
self.control_panel.adjustSize()
|
|
523
|
+
self.control_panel.setFixedSize(self.control_panel.size())
|
|
524
|
+
self.control_panel.show()
|
|
525
|
+
center_window(self.control_panel)
|
|
526
|
+
except (AssertionError, FileNotFoundError) as e:
|
|
527
|
+
QMessageBox.critical(
|
|
528
|
+
self,
|
|
529
|
+
"Error Loading Experiment",
|
|
530
|
+
f"Could not load experiment configuration.\n\nError: {str(e)}\n\nPlease ensure 'config.ini' exists in the selected folder.",
|
|
531
|
+
)
|
|
532
|
+
return
|
|
533
|
+
|
|
534
|
+
self.reload_previous_experiments()
|
|
535
|
+
self._create_menu_bar()
|
|
536
|
+
|
|
537
|
+
def browse_experiment_folder(self):
|
|
538
|
+
"""
|
|
539
|
+
Locate an experiment folder. If no configuration file is in the experiment, display a warning.
|
|
540
|
+
"""
|
|
541
|
+
|
|
542
|
+
self.folder_name = str(
|
|
543
|
+
QFileDialog.getExistingDirectory(self, "Select directory")
|
|
544
|
+
)
|
|
545
|
+
if self.folder_name != "":
|
|
546
|
+
self.experiment_path_selection.setText(self.folder_name)
|
|
547
|
+
else:
|
|
548
|
+
return None
|
|
549
|
+
if not os.path.exists(os.sep.join([self.folder_name, "config.ini"])):
|
|
550
|
+
generic_message(
|
|
551
|
+
"No configuration can be found in the selected folder...",
|
|
552
|
+
msg_type="warning",
|
|
553
|
+
)
|
|
554
|
+
self.experiment_path_selection.setText("")
|
|
555
|
+
return None
|