celldetective 1.2.2__py3-none-any.whl → 1.2.2.post2__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 (29) hide show
  1. celldetective/__main__.py +2 -404
  2. celldetective/gui/InitWindow.py +400 -0
  3. celldetective/gui/help/DL-segmentation-strategy.json +41 -0
  4. celldetective/gui/help/Threshold-vs-DL.json +26 -0
  5. celldetective/gui/help/cell-populations.json +11 -0
  6. celldetective/gui/help/exp-structure.json +36 -0
  7. celldetective/gui/help/feature-btrack.json +11 -0
  8. celldetective/gui/help/neighborhood.json +16 -0
  9. celldetective/gui/help/prefilter-for-segmentation.json +16 -0
  10. celldetective/gui/help/preprocessing.json +51 -0
  11. celldetective/gui/help/propagate-classification.json +16 -0
  12. celldetective/gui/help/track-postprocessing.json +46 -0
  13. celldetective/gui/help/tracking.json +11 -0
  14. celldetective/io.py +60 -23
  15. celldetective/utils.py +5 -4
  16. celldetective-1.2.2.post2.dist-info/METADATA +214 -0
  17. {celldetective-1.2.2.dist-info → celldetective-1.2.2.post2.dist-info}/RECORD +22 -15
  18. tests/test_qt.py +101 -0
  19. celldetective/models/segmentation_effectors/primNK_cfse/config_input.json +0 -29
  20. celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer +0 -0
  21. celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json +0 -37
  22. celldetective/models/segmentation_effectors/ricm-bimodal/config_input.json +0 -130
  23. celldetective/models/segmentation_effectors/ricm-bimodal/ricm-bimodal +0 -0
  24. celldetective/models/segmentation_effectors/ricm-bimodal/training_instructions.json +0 -37
  25. celldetective-1.2.2.dist-info/METADATA +0 -312
  26. {celldetective-1.2.2.dist-info → celldetective-1.2.2.post2.dist-info}/LICENSE +0 -0
  27. {celldetective-1.2.2.dist-info → celldetective-1.2.2.post2.dist-info}/WHEEL +0 -0
  28. {celldetective-1.2.2.dist-info → celldetective-1.2.2.post2.dist-info}/entry_points.txt +0 -0
  29. {celldetective-1.2.2.dist-info → celldetective-1.2.2.post2.dist-info}/top_level.txt +0 -0
celldetective/__main__.py CHANGED
@@ -1,405 +1,18 @@
1
1
  #!/usr/bin/env python3
2
2
  import sys
3
- from PyQt5.QtWidgets import QApplication, QSplashScreen, QMainWindow
3
+ from PyQt5.QtWidgets import QApplication, QSplashScreen
4
4
  from PyQt5.QtGui import QPixmap
5
5
  from os import sep
6
6
  from celldetective.utils import get_software_location
7
- #from PyQt5.QtCore import QEventLoop
8
7
  from time import time, sleep
9
- import os
10
8
  #os.environ['QT_DEBUG_PLUGINS'] = '1'
11
9
 
12
- class AppInitWindow(QMainWindow):
13
-
14
- """
15
- Initial window to set the experiment folder or create a new one.
16
- """
17
-
18
- def __init__(self, parent_window=None):
19
- super().__init__()
20
-
21
- self.parent_window = parent_window
22
- self.Styles = Styles()
23
- self.init_styles()
24
- self.setWindowTitle("celldetective")
25
-
26
- self.n_threads = min([1,psutil.cpu_count()])
27
-
28
- try:
29
- subprocess.check_output('nvidia-smi')
30
- print('NVIDIA GPU detected (activate or disable in Memory & Threads)...')
31
- self.use_gpu = True
32
- except Exception: # this command not being found can raise quite a few different errors depending on the configuration
33
- print('No NVIDIA GPU detected...')
34
- self.use_gpu = False
35
-
36
- self.soft_path = get_software_location()
37
- self.onlyInt = QIntValidator()
38
- self.setWindowIcon(QIcon(os.sep.join([self.soft_path,'celldetective','icons','logo.png'])))
39
- center_window(self)
40
- self._createActions()
41
- self._createMenuBar()
42
-
43
- app = QApplication.instance()
44
- self.screen = app.primaryScreen()
45
- self.geometry = self.screen.availableGeometry()
46
- self.screen_width, self.screen_height = self.geometry.getRect()[-2:]
47
-
48
- central_widget = QWidget()
49
- self.vertical_layout = QVBoxLayout(central_widget)
50
- self.vertical_layout.setContentsMargins(15,15,15,15)
51
- self.vertical_layout.addWidget(QLabel("Experiment folder:"))
52
- self.create_locate_exp_hbox()
53
- self.create_buttons_hbox()
54
- self.setCentralWidget(central_widget)
55
- self.reload_previous_gpu_threads()
56
- self.show()
57
-
58
- def closeEvent(self, event):
59
- QApplication.closeAllWindows()
60
- event.accept()
61
- gc.collect()
62
-
63
- def create_locate_exp_hbox(self):
64
-
65
- self.locate_exp_layout = QHBoxLayout()
66
- self.locate_exp_layout.setContentsMargins(0,5,0,0)
67
- self.experiment_path_selection = QLineEdit()
68
- self.experiment_path_selection.setAlignment(Qt.AlignLeft)
69
- self.experiment_path_selection.setEnabled(True)
70
- self.experiment_path_selection.setDragEnabled(True)
71
- self.experiment_path_selection.setFixedWidth(430)
72
- self.experiment_path_selection.textChanged[str].connect(self.check_path_and_enable_opening)
73
- self.foldername = os.getcwd()
74
- self.experiment_path_selection.setPlaceholderText('/path/to/experiment/folder/')
75
- self.locate_exp_layout.addWidget(self.experiment_path_selection, 90)
76
-
77
- self.browse_button = QPushButton("Browse...")
78
- self.browse_button.clicked.connect(self.browse_experiment_folder)
79
- self.browse_button.setStyleSheet(self.button_style_sheet)
80
- self.browse_button.setIcon(icon(MDI6.folder, color="white"))
81
- self.locate_exp_layout.addWidget(self.browse_button, 10)
82
- self.vertical_layout.addLayout(self.locate_exp_layout)
83
-
84
-
85
- def _createMenuBar(self):
86
-
87
- menuBar = self.menuBar()
88
- menuBar.clear()
89
- # Creating menus using a QMenu object
90
-
91
- fileMenu = QMenu("File", self)
92
- fileMenu.clear()
93
- fileMenu.addAction(self.newExpAction)
94
- fileMenu.addAction(self.openAction)
95
-
96
- fileMenu.addMenu(self.OpenRecentAction)
97
- self.OpenRecentAction.clear()
98
- if len(self.recentFileActs)>0:
99
- for i in range(len(self.recentFileActs)):
100
- self.OpenRecentAction.addAction(self.recentFileActs[i])
101
-
102
- fileMenu.addAction(self.openModels)
103
- fileMenu.addSeparator()
104
- fileMenu.addAction(self.exitAction)
105
- menuBar.addMenu(fileMenu)
106
-
107
- OptionsMenu = QMenu("Options", self)
108
- OptionsMenu.addAction(self.MemoryAndThreadsAction)
109
- menuBar.addMenu(OptionsMenu)
110
-
111
- PluginsMenu = QMenu("Plugins", self)
112
- PluginsMenu.addAction(self.CorrectAnnotationAction)
113
- menuBar.addMenu(PluginsMenu)
114
-
115
- helpMenu = QMenu("Help", self)
116
- helpMenu.clear()
117
- helpMenu.addAction(self.DocumentationAction)
118
- helpMenu.addAction(self.SoftwareAction)
119
- helpMenu.addSeparator()
120
- helpMenu.addAction(self.AboutAction)
121
- menuBar.addMenu(helpMenu)
122
-
123
- #editMenu = menuBar.addMenu("&Edit")
124
- #helpMenu = menuBar.addMenu("&Help")
125
-
126
- def _createActions(self):
127
- # Creating action using the first constructor
128
- #self.newAction = QAction(self)
129
- #self.newAction.setText("&New")
130
- # Creating actions using the second constructor
131
- self.openAction = QAction('Open...', self)
132
- self.openAction.setShortcut("Ctrl+O")
133
- self.openAction.setShortcutVisibleInContextMenu(True)
134
-
135
- self.MemoryAndThreadsAction = QAction('Memory & Threads...')
136
-
137
- self.CorrectAnnotationAction = QAction('Correct a segmentation annotation...')
138
-
139
- self.newExpAction = QAction('New', self)
140
- self.newExpAction.setShortcut("Ctrl+N")
141
- self.newExpAction.setShortcutVisibleInContextMenu(True)
142
- self.exitAction = QAction('Exit', self)
143
-
144
- self.openModels = QAction('Open Models Location')
145
- self.openModels.setShortcut("Ctrl+L")
146
- self.openModels.setShortcutVisibleInContextMenu(True)
147
-
148
- self.OpenRecentAction = QMenu('Open Recent')
149
- self.reload_previous_experiments()
150
-
151
- self.DocumentationAction = QAction("Documentation", self)
152
- self.DocumentationAction.setShortcut("Ctrl+D")
153
- self.DocumentationAction.setShortcutVisibleInContextMenu(True)
154
-
155
- self.SoftwareAction = QAction("Software", self) #1st arg icon(MDI6.information)
156
- self.AboutAction = QAction("About celldetective", self)
157
-
158
- #self.DocumentationAction.triggered.connect(self.load_previous_config)
159
- self.openAction.triggered.connect(self.open_experiment)
160
- self.newExpAction.triggered.connect(self.create_new_experiment)
161
- self.exitAction.triggered.connect(self.close)
162
- self.openModels.triggered.connect(self.open_models_folder)
163
- self.AboutAction.triggered.connect(self.open_about_window)
164
- self.MemoryAndThreadsAction.triggered.connect(self.set_memory_and_threads)
165
- self.CorrectAnnotationAction.triggered.connect(self.correct_seg_annotation)
166
-
167
- self.DocumentationAction.triggered.connect(self.open_documentation)
168
-
169
- def reload_previous_gpu_threads(self):
170
-
171
- self.recentFileActs = []
172
- self.threads_config_path = os.sep.join([self.soft_path,'celldetective','threads.json'])
173
- print('Reading previous Memory & Threads settings...')
174
- if os.path.exists(self.threads_config_path):
175
- with open(self.threads_config_path, 'r') as f:
176
- self.threads_config = json.load(f)
177
- if 'use_gpu' in self.threads_config:
178
- self.use_gpu = bool(self.threads_config['use_gpu'])
179
- print(f'Use GPU: {self.use_gpu}...')
180
- if 'n_threads' in self.threads_config:
181
- self.n_threads = int(self.threads_config['n_threads'])
182
- print(f'Number of threads: {self.n_threads}...')
183
-
184
-
185
- def reload_previous_experiments(self):
186
-
187
- recentExps = []
188
- self.recentFileActs = []
189
- if os.path.exists(os.sep.join([self.soft_path,'celldetective','recent.txt'])):
190
- recentExps = open(os.sep.join([self.soft_path,'celldetective','recent.txt']), 'r')
191
- recentExps = recentExps.readlines()
192
- recentExps = [r.strip() for r in recentExps]
193
- recentExps.reverse()
194
- recentExps = list(dict.fromkeys(recentExps))
195
- self.recentFileActs = [QAction(r,self) for r in recentExps]
196
- for r in self.recentFileActs:
197
- r.triggered.connect(lambda checked, item=r: self.load_recent_exp(item.text()))
198
-
199
- def correct_seg_annotation(self):
200
-
201
- self.filename,_ = QFileDialog.getOpenFileName(self,"Open Image", "/home/", "TIF Files (*.tif)")
202
- if self.filename!='':
203
- print('Opening ',self.filename,' in napari...')
204
- correct_annotation(self.filename)
205
- else:
206
- return None
207
-
208
- def set_memory_and_threads(self):
209
-
210
- print('setting memory and threads')
211
-
212
- self.ThreadsWidget = QWidget()
213
- self.ThreadsWidget.setWindowTitle("Threads")
214
- layout = QVBoxLayout()
215
- self.ThreadsWidget.setLayout(layout)
216
-
217
- self.threads_le = QLineEdit(str(self.n_threads))
218
- self.threads_le.setValidator(self.onlyInt)
219
-
220
- hbox = QHBoxLayout()
221
- hbox.addWidget(QLabel('Parallel threads: '), 33)
222
- hbox.addWidget(self.threads_le, 66)
223
- layout.addLayout(hbox)
224
-
225
- self.use_gpu_checkbox = QCheckBox()
226
- hbox2 = QHBoxLayout()
227
- hbox2.addWidget(QLabel('Use GPU: '), 33)
228
- hbox2.addWidget(self.use_gpu_checkbox, 66)
229
- layout.addLayout(hbox2)
230
- if self.use_gpu:
231
- self.use_gpu_checkbox.setChecked(True)
232
-
233
- self.validateThreadBtn = QPushButton('Submit')
234
- self.validateThreadBtn.setStyleSheet(self.button_style_sheet)
235
- self.validateThreadBtn.clicked.connect(self.set_threads)
236
- layout.addWidget(self.validateThreadBtn)
237
- center_window(self.ThreadsWidget)
238
- self.ThreadsWidget.show()
239
-
240
- def set_threads(self):
241
- self.n_threads = int(self.threads_le.text())
242
- self.use_gpu = bool(self.use_gpu_checkbox.isChecked())
243
- dico = {"use_gpu": self.use_gpu, "n_threads": self.n_threads}
244
- with open(self.threads_config_path, 'w') as f:
245
- json.dump(dico, f, indent=4)
246
- self.ThreadsWidget.close()
247
-
248
-
249
- def open_experiment(self):
250
- print('ok')
251
- self.browse_experiment_folder()
252
- if self.experiment_path_selection.text()!='':
253
- self.open_directory()
254
-
255
- def load_recent_exp(self, path):
256
-
257
- self.experiment_path_selection.setText(path)
258
- print(f'Attempt to load experiment folder: {path}...')
259
- self.open_directory()
260
-
261
- def open_about_window(self):
262
- self.about_wdw = AboutWidget()
263
- self.about_wdw.show()
264
-
265
- def open_documentation(self):
266
- doc_url = QUrl('https://celldetective.readthedocs.io/')
267
- QDesktopServices.openUrl(doc_url)
268
-
269
- def open_models_folder(self):
270
- path = os.sep.join([self.soft_path,'celldetective','models',os.sep])
271
- try:
272
- subprocess.Popen(f'explorer {os.path.realpath(path)}')
273
- except:
274
-
275
- try:
276
- os.system('xdg-open "%s"' % path)
277
- except:
278
- return None
279
-
280
-
281
- #os.system(f'start {os.path.realpath(path)}')
282
-
283
- def create_buttons_hbox(self):
284
-
285
- self.buttons_layout = QHBoxLayout()
286
- self.buttons_layout.setContentsMargins(30,15,30,5)
287
- self.new_exp_button = QPushButton("New")
288
- self.new_exp_button.clicked.connect(self.create_new_experiment)
289
- self.new_exp_button.setStyleSheet(self.button_style_sheet_2)
290
- self.buttons_layout.addWidget(self.new_exp_button, 50)
291
-
292
- self.validate_button = QPushButton("Open")
293
- self.validate_button.clicked.connect(self.open_directory)
294
- self.validate_button.setStyleSheet(self.button_style_sheet)
295
- self.validate_button.setEnabled(False)
296
- self.validate_button.setShortcut("Return")
297
- self.buttons_layout.addWidget(self.validate_button, 50)
298
- self.vertical_layout.addLayout(self.buttons_layout)
299
-
300
- def check_path_and_enable_opening(self):
301
-
302
- """
303
- Enable 'Open' button if the text is a valid path.
304
- """
305
-
306
- text = self.experiment_path_selection.text()
307
- if (os.path.exists(text)) and os.path.exists(os.sep.join([text,"config.ini"])):
308
- self.validate_button.setEnabled(True)
309
- else:
310
- self.validate_button.setEnabled(False)
311
-
312
- def init_styles(self):
313
-
314
- """
315
- Initialize styles.
316
- """
317
-
318
- self.qtab_style = self.Styles.qtab_style
319
- self.button_style_sheet = self.Styles.button_style_sheet
320
- self.button_style_sheet_2 = self.Styles.button_style_sheet_2
321
- self.button_style_sheet_2_not_done = self.Styles.button_style_sheet_2_not_done
322
- self.button_style_sheet_3 = self.Styles.button_style_sheet_3
323
- self.button_select_all = self.Styles.button_select_all
324
-
325
- def set_experiment_path(self, path):
326
- self.experiment_path_selection.setText(path)
327
-
328
- def create_new_experiment(self):
329
-
330
- print("Configuring new experiment...")
331
- self.new_exp_window = ConfigNewExperiment(self)
332
- self.new_exp_window.show()
333
-
334
- def open_directory(self):
335
-
336
- self.exp_dir = self.experiment_path_selection.text().replace('/', os.sep)
337
- print(f"Setting current directory to {self.exp_dir}...")
338
-
339
- wells = glob(os.sep.join([self.exp_dir,"W*"]))
340
- self.number_of_wells = len(wells)
341
- if self.number_of_wells==0:
342
- msgBox = QMessageBox()
343
- msgBox.setIcon(QMessageBox.Critical)
344
- msgBox.setText("No well was found in the experiment folder.\nPlease respect the W*/ nomenclature...")
345
- msgBox.setWindowTitle("Error")
346
- msgBox.setStandardButtons(QMessageBox.Ok)
347
- returnValue = msgBox.exec()
348
- if returnValue == QMessageBox.Ok:
349
- return None
350
- else:
351
- if self.number_of_wells==1:
352
- print(f"Found {self.number_of_wells} well...")
353
- elif self.number_of_wells>1:
354
- print(f"Found {self.number_of_wells} wells...")
355
- number_pos = []
356
- for w in wells:
357
- position_folders = glob(os.sep.join([w,f"{w.split(os.sep)[-1][1]}*", os.sep]))
358
- number_pos.append(len(position_folders))
359
- print(f"Number of positions per well: {number_pos}")
360
-
361
- with open(os.sep.join([self.soft_path,'celldetective','recent.txt']), 'a+') as f:
362
- f.write(self.exp_dir+'\n')
363
-
364
- self.control_panel = ControlPanel(self, self.exp_dir)
365
- self.control_panel.show()
366
-
367
- self.reload_previous_experiments()
368
- self._createMenuBar()
369
-
370
-
371
- def browse_experiment_folder(self):
372
-
373
- """
374
- Locate an experiment folder. If no configuration file is in the experiment, display a warning.
375
- """
376
-
377
- self.foldername = str(QFileDialog.getExistingDirectory(self, 'Select directory'))
378
- if self.foldername!='':
379
- self.experiment_path_selection.setText(self.foldername)
380
- else:
381
- return None
382
- if not os.path.exists(self.foldername+"/config.ini"):
383
- msgBox = QMessageBox()
384
- msgBox.setIcon(QMessageBox.Warning)
385
- msgBox.setText("No configuration can be found in the selected folder...")
386
- msgBox.setWindowTitle("Warning")
387
- msgBox.setStandardButtons(QMessageBox.Ok)
388
- returnValue = msgBox.exec()
389
- if returnValue == QMessageBox.Ok:
390
- self.experiment_path_selection.setText('')
391
- return None
392
-
393
10
  if __name__ == "__main__":
394
11
 
395
- # import ctypes
396
- # myappid = 'mycompany.myproduct.subproduct.version' # arbitrary string
397
- # ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
398
12
  splash=True
399
13
  print('Loading the libraries...')
400
14
 
401
15
  App = QApplication(sys.argv)
402
- #App.setWindowIcon(QIcon(os.sep.join([get_software_location(),'celldetective','icons','mexican-hat.png'])))
403
16
  App.setStyle("Fusion")
404
17
 
405
18
  if splash:
@@ -414,23 +27,8 @@ if __name__ == "__main__":
414
27
  App.processEvents()
415
28
 
416
29
  from PyQt5.QtWidgets import QFileDialog, QWidget, QVBoxLayout, QCheckBox, QHBoxLayout, QLabel, QLineEdit, QPushButton, QMessageBox, QMenu, QAction
417
- from PyQt5.QtCore import Qt, QUrl
418
30
  from PyQt5.QtGui import QIcon, QDesktopServices, QIntValidator
419
- from glob import glob
420
- from superqt.fonticon import icon
421
- from fonticon_mdi6 import MDI6
422
- import gc
423
- from celldetective.gui import Styles, ControlPanel, ConfigNewExperiment
424
- from celldetective.gui.gui_utils import center_window
425
- import subprocess
426
- import os
427
- from celldetective.gui.about import AboutWidget
428
- from celldetective.io import correct_annotation
429
- import psutil
430
- import subprocess
431
- import json
432
- # import matplotlib
433
- # matplotlib.rcParams.update({'font.size': 8})
31
+ from celldetective.gui.InitWindow import AppInitWindow
434
32
 
435
33
  print('Libraries successfully loaded...')
436
34