cellects 0.1.3__py3-none-any.whl → 0.2.6__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.
- cellects/__main__.py +65 -25
- cellects/config/all_vars_dict.py +18 -17
- cellects/core/cellects_threads.py +1034 -396
- cellects/core/motion_analysis.py +1664 -2010
- cellects/core/one_image_analysis.py +1082 -1061
- cellects/core/program_organizer.py +1687 -1316
- cellects/core/script_based_run.py +80 -76
- cellects/gui/advanced_parameters.py +365 -326
- cellects/gui/cellects.py +102 -91
- cellects/gui/custom_widgets.py +4 -3
- cellects/gui/first_window.py +226 -104
- cellects/gui/if_several_folders_window.py +117 -68
- cellects/gui/image_analysis_window.py +841 -450
- cellects/gui/required_output.py +100 -56
- cellects/gui/ui_strings.py +840 -0
- cellects/gui/video_analysis_window.py +317 -135
- cellects/image_analysis/cell_leaving_detection.py +64 -4
- cellects/image_analysis/image_segmentation.py +451 -22
- cellects/image_analysis/morphological_operations.py +2166 -1635
- cellects/image_analysis/network_functions.py +616 -253
- cellects/image_analysis/one_image_analysis_threads.py +94 -153
- cellects/image_analysis/oscillations_functions.py +131 -0
- cellects/image_analysis/progressively_add_distant_shapes.py +2 -3
- cellects/image_analysis/shape_descriptors.py +517 -466
- cellects/utils/formulas.py +169 -6
- cellects/utils/load_display_save.py +362 -105
- cellects/utils/utilitarian.py +86 -9
- cellects-0.2.6.dist-info/LICENSE +675 -0
- cellects-0.2.6.dist-info/METADATA +829 -0
- cellects-0.2.6.dist-info/RECORD +44 -0
- cellects/core/one_video_per_blob.py +0 -540
- cellects/image_analysis/cluster_flux_study.py +0 -102
- cellects-0.1.3.dist-info/LICENSE.odt +0 -0
- cellects-0.1.3.dist-info/METADATA +0 -176
- cellects-0.1.3.dist-info/RECORD +0 -44
- {cellects-0.1.3.dist-info → cellects-0.2.6.dist-info}/WHEEL +0 -0
- {cellects-0.1.3.dist-info → cellects-0.2.6.dist-info}/entry_points.txt +0 -0
- {cellects-0.1.3.dist-info → cellects-0.2.6.dist-info}/top_level.txt +0 -0
cellects/gui/first_window.py
CHANGED
|
@@ -1,29 +1,78 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
2
|
+
"""First window of the Cellects graphical user interface (GUI).
|
|
3
|
+
|
|
4
|
+
This module implements the initial setup UI for Cellects data processing. It provides
|
|
5
|
+
widgets for selecting image/video inputs, configuring folder paths, arena numbers,
|
|
6
|
+
and prefixes/extensions. Threaded operations ensure UI responsiveness during background tasks.
|
|
7
|
+
|
|
8
|
+
Main Components
|
|
9
|
+
FirstWindow : QWidget subclass implementing the first GUI window with tabs and interactive widgets
|
|
10
|
+
"""
|
|
3
11
|
|
|
4
12
|
import os
|
|
5
13
|
import logging
|
|
6
14
|
from pathlib import Path
|
|
7
15
|
import numpy as np
|
|
8
|
-
import cv2
|
|
9
16
|
from PySide6 import QtWidgets, QtCore
|
|
10
|
-
|
|
11
17
|
from cellects.core.cellects_threads import (
|
|
12
18
|
GetFirstImThread, GetExifDataThread, RunAllThread, LookForDataThreadInFirstW, LoadDataToRunCellectsQuicklyThread)
|
|
13
19
|
from cellects.gui.custom_widgets import (
|
|
14
20
|
MainTabsType, InsertImage, FullScreenImage, PButton, Spinbox,
|
|
15
21
|
Combobox, FixedText, EditText, LineWidget)
|
|
22
|
+
from cellects.gui.ui_strings import FW
|
|
16
23
|
|
|
17
24
|
|
|
18
25
|
class FirstWindow(MainTabsType):
|
|
26
|
+
"""
|
|
27
|
+
First window of the Cellects GUI.
|
|
28
|
+
"""
|
|
19
29
|
def __init__(self, parent, night_mode):
|
|
30
|
+
"""
|
|
31
|
+
Initialize the First window with a parent widget and night mode setting.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
parent : QWidget
|
|
36
|
+
The parent widget to which this window will be attached.
|
|
37
|
+
night_mode : bool
|
|
38
|
+
A boolean indicating whether the night mode should be enabled.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
Examples
|
|
42
|
+
--------
|
|
43
|
+
>>> from PySide6 import QtWidgets
|
|
44
|
+
>>> from cellects.gui.cellects import CellectsMainWidget
|
|
45
|
+
>>> from cellects.gui.first_window import FirstWindow
|
|
46
|
+
>>> import sys
|
|
47
|
+
>>> app = QtWidgets.QApplication([])
|
|
48
|
+
>>> parent = CellectsMainWidget()
|
|
49
|
+
>>> session = FirstWindow(parent, False)
|
|
50
|
+
>>> session.true_init()
|
|
51
|
+
>>> parent.insertWidget(0, session)
|
|
52
|
+
>>> parent.show()
|
|
53
|
+
>>> sys.exit(app.exec())
|
|
54
|
+
"""
|
|
20
55
|
super().__init__(parent, night_mode)
|
|
21
56
|
logging.info("Initialize first window")
|
|
22
57
|
self.setParent(parent)
|
|
58
|
+
|
|
59
|
+
self.true_init()
|
|
60
|
+
|
|
61
|
+
def true_init(self):
|
|
62
|
+
"""
|
|
63
|
+
Initialize the FirstWindow components and setup its layout.
|
|
64
|
+
|
|
65
|
+
Sets up various widgets, layouts, and threading components for the Cellects GUI,
|
|
66
|
+
including image or video selection, folder path input, arena number management,
|
|
67
|
+
and display setup.
|
|
68
|
+
|
|
69
|
+
Notes
|
|
70
|
+
-----
|
|
71
|
+
This method assumes that the parent widget has a 'po' attribute with specific settings and variables.
|
|
72
|
+
"""
|
|
23
73
|
self.data_tab.set_in_use()
|
|
24
74
|
self.image_tab.set_not_usable()
|
|
25
75
|
self.video_tab.set_not_usable()
|
|
26
|
-
# self.night_mode_switch(False)
|
|
27
76
|
self.thread = {}
|
|
28
77
|
self.thread["LookForData"] = LookForDataThreadInFirstW(self.parent())
|
|
29
78
|
self.thread["RunAll"] = RunAllThread(self.parent())
|
|
@@ -31,15 +80,11 @@ class FirstWindow(MainTabsType):
|
|
|
31
80
|
self.thread["GetFirstIm"] = GetFirstImThread(self.parent())
|
|
32
81
|
self.thread["GetExifDataThread"] = GetExifDataThread(self.parent())
|
|
33
82
|
self.instantiate: bool = True
|
|
34
|
-
##
|
|
35
83
|
self.title_label = FixedText('Cellects', police=60, night_mode=self.parent().po.all['night_mode'])
|
|
36
84
|
self.title_label.setAlignment(QtCore.Qt.AlignHCenter)
|
|
37
|
-
# self.subtitle_label = FixedText('A Cell Expansion Computer Tracking Software', police=18, night_mode=self.parent().po.all['night_mode'])
|
|
38
|
-
# self.subtitle_label.setAlignment(QtCore.Qt.AlignHCenter)
|
|
39
85
|
self.subtitle_line = LineWidget(size=[1, 50], night_mode=self.parent().po.all['night_mode'])
|
|
40
86
|
|
|
41
87
|
self.Vlayout.addWidget(self.title_label)
|
|
42
|
-
# self.Vlayout.addWidget(self.subtitle_label)
|
|
43
88
|
self.Vlayout.addWidget(self.subtitle_line)
|
|
44
89
|
self.Vlayout.addItem(self.vertical_space)
|
|
45
90
|
|
|
@@ -47,7 +92,9 @@ class FirstWindow(MainTabsType):
|
|
|
47
92
|
# Open the layout:
|
|
48
93
|
self.second_row_widget = QtWidgets.QWidget()
|
|
49
94
|
self.second_row_layout = QtWidgets.QHBoxLayout()
|
|
50
|
-
self.im_or_vid_label = FixedText('
|
|
95
|
+
self.im_or_vid_label = FixedText(FW['Image_list_or_videos']['label'], tip=FW['Image_list_or_videos']['tips'],
|
|
96
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
97
|
+
# self.im_or_vid_label = FixedText('Image list or Videos:', tip="What type of data do(es) contain(s) folder(s)?", night_mode=self.parent().po.all['night_mode'])
|
|
51
98
|
self.im_or_vid = Combobox(["Image list", "Videos"], self.parent().po.all['im_or_vid'], night_mode=self.parent().po.all['night_mode'])
|
|
52
99
|
self.im_or_vid.setFixedWidth(150)
|
|
53
100
|
# Set their positions on layout
|
|
@@ -67,28 +114,28 @@ class FirstWindow(MainTabsType):
|
|
|
67
114
|
if self.parent().po.all['extension'] == '.mp4':
|
|
68
115
|
self.parent().po.all['radical'] = 'IMG_'
|
|
69
116
|
self.parent().po.all['extension'] = '.JPG'
|
|
70
|
-
self.arena_number_label = FixedText('Arena number per folder:',
|
|
117
|
+
self.arena_number_label = FixedText('Arena number per folder:',
|
|
118
|
+
tip=FW["Arena_number_per_folder"]["tips"] , #"If this number is not always the same (depending on the folder), it can be changed later",
|
|
71
119
|
night_mode=self.parent().po.all['night_mode'])
|
|
72
|
-
|
|
73
120
|
else:
|
|
74
121
|
if self.parent().po.all['extension'] == '.JPG':
|
|
75
122
|
self.parent().po.all['radical'] = ''
|
|
76
123
|
self.parent().po.all['extension'] = '.mp4'
|
|
77
124
|
self.arena_number_label = FixedText('Arena number per video:',
|
|
78
|
-
tip="If this number is not always the same (depending on the video), it can be changed later",
|
|
125
|
+
tip=FW["Arena_number_per_folder"]["tips"], #"If this number is not always the same (depending on the video), it can be changed later",
|
|
79
126
|
night_mode=self.parent().po.all['night_mode'])
|
|
80
127
|
what = 'Videos'
|
|
81
128
|
self.arena_number_label.setAlignment(QtCore.Qt.AlignVCenter)
|
|
82
129
|
self.arena_number = Spinbox(min=0, max=255, val=self.parent().po.all['first_folder_sample_number'],
|
|
83
130
|
decimals=0, night_mode=self.parent().po.all['night_mode'])
|
|
84
131
|
self.arena_number.valueChanged.connect(self.re_instantiate_widgets)
|
|
85
|
-
self.radical_label = FixedText(what + ' prefix:', tip="
|
|
132
|
+
self.radical_label = FixedText(what + ' prefix:', tip=FW["Image_prefix_and_extension"]["tips"], night_mode=self.parent().po.all['night_mode'])
|
|
86
133
|
self.radical_label.setAlignment(QtCore.Qt.AlignVCenter)
|
|
87
134
|
self.radical = EditText(self.parent().po.all['radical'],
|
|
88
135
|
night_mode=self.parent().po.all['night_mode'])
|
|
89
136
|
self.radical.textChanged.connect(self.re_instantiate_widgets)
|
|
90
137
|
|
|
91
|
-
self.extension_label = FixedText(what + ' extension:', tip="
|
|
138
|
+
self.extension_label = FixedText(what + ' extension:', tip=FW["Image_prefix_and_extension"]["tips"], night_mode=self.parent().po.all['night_mode'])
|
|
92
139
|
self.extension_label.setAlignment(QtCore.Qt.AlignVCenter)
|
|
93
140
|
self.extension = EditText(self.parent().po.all['extension'],
|
|
94
141
|
night_mode=self.parent().po.all['night_mode'])
|
|
@@ -112,15 +159,16 @@ class FirstWindow(MainTabsType):
|
|
|
112
159
|
self.first_row_widget = QtWidgets.QWidget()
|
|
113
160
|
self.first_row_layout = QtWidgets.QHBoxLayout()
|
|
114
161
|
|
|
115
|
-
self.folder_label = FixedText('
|
|
116
|
-
tip="Path to the folder containing images or videos\nThe selected folder may also contain several folders of data",
|
|
162
|
+
self.folder_label = FixedText(FW["Folder"]["label"] + ':',
|
|
163
|
+
tip=FW["Folder"]["tips"],#"Path to the folder containing images or videos\nThe selected folder may also contain several folders of data",
|
|
117
164
|
night_mode=self.parent().po.all['night_mode'])
|
|
118
165
|
self.folder_label.setAlignment(QtCore.Qt.AlignVCenter)
|
|
119
166
|
self.global_pathway = EditText(self.parent().po.all['global_pathway'],
|
|
120
167
|
night_mode=self.parent().po.all['night_mode'])
|
|
121
168
|
self.global_pathway.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
|
|
122
169
|
self.global_pathway.textChanged.connect(self.pathway_changed)
|
|
123
|
-
self.browse = PButton(
|
|
170
|
+
self.browse = PButton(FW["Browse"]["label"], tip=FW["Browse"]["tips"],
|
|
171
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
124
172
|
self.browse.clicked.connect(self.browse_is_clicked)
|
|
125
173
|
|
|
126
174
|
# Set their positions on layout
|
|
@@ -147,36 +195,29 @@ class FirstWindow(MainTabsType):
|
|
|
147
195
|
self.display_image.setVisible(False)
|
|
148
196
|
self.display_image.mousePressEvent = self.full_screen_display
|
|
149
197
|
|
|
150
|
-
# Add the display shortcuts option
|
|
151
|
-
#self.shortcut_cb = Checkbox(self.parent().po.all['display_shortcuts'])
|
|
152
|
-
#self.shortcut_cb.stateChanged.connect(self.display_shortcuts_checked)
|
|
153
|
-
#self.shortcut_label = FixedText('Display shortcuts', night_mode=self.parent().po.all['night_mode'])
|
|
154
|
-
#self.shortcut_label.setAlignment(QtCore.Qt.AlignVCenter)
|
|
155
|
-
|
|
156
198
|
# 4) Create the shortcuts row
|
|
157
199
|
self.shortcuts_widget = QtWidgets.QWidget()
|
|
158
200
|
self.shortcuts_layout = QtWidgets.QHBoxLayout()
|
|
159
201
|
# Add shortcuts: Video_analysis and Run directly
|
|
160
202
|
# Shortcut 1 : Advanced Parameters
|
|
161
|
-
self.advanced_parameters = PButton(
|
|
203
|
+
self.advanced_parameters = PButton(FW["Advanced_parameters"]["label"], tip=FW["Advanced_parameters"]["tips"],
|
|
204
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
162
205
|
self.advanced_parameters.clicked.connect(self.advanced_parameters_is_clicked)
|
|
163
206
|
# Shortcut 2 : Required Outputs
|
|
164
|
-
self.required_outputs = PButton(
|
|
207
|
+
self.required_outputs = PButton(FW["Required_outputs"]["label"], tip=FW["Required_outputs"]["tips"],
|
|
208
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
165
209
|
self.required_outputs.clicked.connect(self.required_outputs_is_clicked)
|
|
166
210
|
# Shortcut 3 :
|
|
167
|
-
# self.Video_analysis_window = PButton("Video tracking window", night_mode=self.parent().po.all['night_mode'])
|
|
168
|
-
# self.Video_analysis_window.clicked.connect(self.video_analysis_window_is_clicked)
|
|
169
211
|
self.video_tab.clicked.connect(self.video_analysis_window_is_clicked)
|
|
170
212
|
# Shortcut 4 :
|
|
171
|
-
self.Run_all_directly = PButton("
|
|
213
|
+
self.Run_all_directly = PButton(FW["Run_all_directly"]["label"], tip=FW["Run_all_directly"]["tips"],
|
|
214
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
172
215
|
self.Run_all_directly.clicked.connect(self.Run_all_directly_is_clicked)
|
|
173
|
-
# self.Video_analysis_window.setVisible(False)
|
|
174
216
|
self.Run_all_directly.setVisible(False)
|
|
175
217
|
|
|
176
218
|
self.shortcuts_layout.addItem(self.horizontal_space)
|
|
177
219
|
self.shortcuts_layout.addWidget(self.advanced_parameters)
|
|
178
220
|
self.shortcuts_layout.addWidget(self.required_outputs)
|
|
179
|
-
# self.shortcuts_layout.addWidget(self.Video_analysis_window)
|
|
180
221
|
self.shortcuts_layout.addWidget(self.Run_all_directly)
|
|
181
222
|
self.shortcuts_layout.addItem(self.horizontal_space)
|
|
182
223
|
self.shortcuts_widget.setLayout(self.shortcuts_layout)
|
|
@@ -190,12 +231,11 @@ class FirstWindow(MainTabsType):
|
|
|
190
231
|
self.message = FixedText('', halign='r', night_mode=self.parent().po.all['night_mode'])
|
|
191
232
|
self.message.setStyleSheet("color: rgb(230, 145, 18)")
|
|
192
233
|
# Next button
|
|
193
|
-
self.next = PButton('Next',
|
|
234
|
+
self.next = PButton(FW['Next']['label'], tip=FW['Next']['tips'],
|
|
235
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
194
236
|
self.image_tab.clicked.connect(self.next_is_clicked)
|
|
195
237
|
self.next.clicked.connect(self.next_is_clicked)
|
|
196
238
|
# Add widgets to the last_row_layout
|
|
197
|
-
#self.last_row_layout.addWidget(self.shortcut_cb)
|
|
198
|
-
#self.last_row_layout.addWidget(self.shortcut_label)
|
|
199
239
|
self.last_row_layout.addItem(self.horizontal_space)
|
|
200
240
|
self.last_row_layout.addWidget(self.message)
|
|
201
241
|
self.last_row_layout.addWidget(self.next)
|
|
@@ -208,10 +248,41 @@ class FirstWindow(MainTabsType):
|
|
|
208
248
|
self.pathway_changed()
|
|
209
249
|
|
|
210
250
|
def full_screen_display(self, event):
|
|
251
|
+
"""
|
|
252
|
+
Display an image in full screen.
|
|
253
|
+
|
|
254
|
+
Displays the current `image_to_display` of the parent window
|
|
255
|
+
in a separate full-screen window.
|
|
256
|
+
|
|
257
|
+
Parameters
|
|
258
|
+
----------
|
|
259
|
+
event : QEvent
|
|
260
|
+
The event that triggers the full-screen display.
|
|
261
|
+
|
|
262
|
+
Other Parameters
|
|
263
|
+
----------------
|
|
264
|
+
popup_img : FullScreenImage
|
|
265
|
+
The instance of `FullScreenImage` created to display the image.
|
|
266
|
+
|
|
267
|
+
Notes
|
|
268
|
+
-----
|
|
269
|
+
The method creates a new instance of `FullScreenImage` and displays it.
|
|
270
|
+
This is intended to provide a full-screen view of the image currently
|
|
271
|
+
displayed in the parent window.
|
|
272
|
+
"""
|
|
211
273
|
self.popup_img = FullScreenImage(self.parent().image_to_display, self.parent().screen_width, self.parent().screen_height)
|
|
212
274
|
self.popup_img.show()
|
|
213
275
|
|
|
214
276
|
def browse_is_clicked(self):
|
|
277
|
+
"""
|
|
278
|
+
Handles the logic for when a "Browse" button is clicked in the interface.
|
|
279
|
+
|
|
280
|
+
Opens a file dialog to select a directory and updates the global pathway.
|
|
281
|
+
|
|
282
|
+
Notes
|
|
283
|
+
-----
|
|
284
|
+
This function assumes that `self.parent().po.all` is a dictionary with a key `'global_pathway'`.
|
|
285
|
+
"""
|
|
215
286
|
dialog = QtWidgets.QFileDialog()
|
|
216
287
|
dialog.setDirectory(str(self.parent().po.all['global_pathway']))
|
|
217
288
|
self.parent().po.all['global_pathway'] = dialog.getExistingDirectory(self,
|
|
@@ -219,6 +290,9 @@ class FirstWindow(MainTabsType):
|
|
|
219
290
|
self.global_pathway.setText(self.parent().po.all['global_pathway'])
|
|
220
291
|
|
|
221
292
|
def im2vid(self):
|
|
293
|
+
"""
|
|
294
|
+
Toggle between processing images or videos based on UI selection.
|
|
295
|
+
"""
|
|
222
296
|
if self.im_or_vid.currentText() == "Image list":
|
|
223
297
|
what = 'Images'
|
|
224
298
|
if self.parent().po.all['extension'] == '.mp4':
|
|
@@ -234,20 +308,43 @@ class FirstWindow(MainTabsType):
|
|
|
234
308
|
self.radical.setText(self.parent().po.all['radical'])
|
|
235
309
|
self.extension.setText(self.parent().po.all['extension'])
|
|
236
310
|
|
|
237
|
-
def display_message_from_thread(self, text_from_thread):
|
|
311
|
+
def display_message_from_thread(self, text_from_thread: str):
|
|
312
|
+
"""
|
|
313
|
+
Updates the message displayed in the UI with text from a thread.
|
|
314
|
+
|
|
315
|
+
Parameters
|
|
316
|
+
----------
|
|
317
|
+
text_from_thread : str
|
|
318
|
+
The text to be displayed in the UI message.
|
|
319
|
+
"""
|
|
238
320
|
self.message.setText(text_from_thread)
|
|
239
321
|
|
|
240
|
-
def display_image_during_thread(self, dictionary):
|
|
322
|
+
def display_image_during_thread(self, dictionary: dict):
|
|
323
|
+
"""
|
|
324
|
+
Display an image and set a message during a thread operation.
|
|
325
|
+
|
|
326
|
+
Parameters
|
|
327
|
+
----------
|
|
328
|
+
dictionary : dict
|
|
329
|
+
A dictionary containing the 'message' and 'current_image'.
|
|
330
|
+
The message is a string to display.
|
|
331
|
+
The current_image is the image data that will be displayed.
|
|
332
|
+
"""
|
|
241
333
|
self.message.setText(dictionary['message'])
|
|
242
334
|
self.parent().image_to_display = dictionary['current_image']
|
|
243
335
|
self.display_image.update_image(dictionary['current_image'])
|
|
244
336
|
|
|
245
337
|
def next_is_clicked(self):
|
|
338
|
+
"""
|
|
339
|
+
Handles the logic for when a "Next" button is clicked in the interface.
|
|
340
|
+
|
|
341
|
+
Checks if certain threads are running, updates parent object's attributes,
|
|
342
|
+
and starts a data-looking thread if conditions are met.
|
|
343
|
+
"""
|
|
246
344
|
if not self.thread["LookForData"].isRunning() and not self.thread["RunAll"].isRunning():
|
|
247
345
|
self.parent().po.all['im_or_vid'] = self.im_or_vid.currentIndex()
|
|
248
346
|
self.parent().po.all['radical'] = self.radical.text()
|
|
249
347
|
self.parent().po.all['extension'] = self.extension.text()
|
|
250
|
-
#self.parent().po.all['display_shortcuts'] = self.shortcut_cb.isChecked()
|
|
251
348
|
self.parent().po.sample_number = int(self.arena_number.value())
|
|
252
349
|
self.parent().po.all['first_folder_sample_number'] = self.parent().po.sample_number
|
|
253
350
|
self.parent().po.all['sample_number_per_folder'] = [self.parent().po.sample_number]
|
|
@@ -260,11 +357,6 @@ class FirstWindow(MainTabsType):
|
|
|
260
357
|
self.message.setText('The folder selected is not valid')
|
|
261
358
|
else:
|
|
262
359
|
self.message.setText('')
|
|
263
|
-
# self.parent().po.all['im_or_vid'] = self.im_or_vid.currentIndex()
|
|
264
|
-
# self.parent().po.all['radical'] = self.radical.text()
|
|
265
|
-
# self.parent().po.all['extension'] = self.extension.text()
|
|
266
|
-
# self.parent().po.all['display_shortcuts'] = self.shortcut_cb.isChecked()
|
|
267
|
-
|
|
268
360
|
self.message.setText(f"Looking for {self.parent().po.all['radical']}***{self.parent().po.all['extension']} Wait...")
|
|
269
361
|
self.message.setStyleSheet("color: rgb(230, 145, 18)")
|
|
270
362
|
self.thread["LookForData"].start()
|
|
@@ -273,27 +365,23 @@ class FirstWindow(MainTabsType):
|
|
|
273
365
|
self.message.setText('Analysis has already begun, wait or restart Cellects.')
|
|
274
366
|
|
|
275
367
|
def when_look_for_data_finished(self):
|
|
368
|
+
"""
|
|
369
|
+
Check if there are any data items left in the selected folder and its sub-folders.
|
|
370
|
+
Display appropriate error messages or proceed with further actions based on the data availability.
|
|
371
|
+
|
|
372
|
+
Notes
|
|
373
|
+
-----
|
|
374
|
+
This function checks if there are any data items (images or videos) left in the selected folder and its sub-folders.
|
|
375
|
+
If no data is found, it displays an error message. Otherwise, it proceeds with instantiating widgets or starting a thread.
|
|
376
|
+
"""
|
|
276
377
|
if len(self.parent().po.all['folder_list']) == 0 and len(self.parent().po.data_list) == 0:
|
|
277
378
|
if self.parent().po.all['im_or_vid'] == 1:
|
|
278
379
|
error_message = f"There is no videos ({self.parent().po.all['extension']})in the selected folder and its sub-folders"
|
|
279
380
|
else:
|
|
280
381
|
error_message = f"There is no images ({self.parent().po.all['extension']}) in the selected folder and its sub-folders"
|
|
281
382
|
self.message.setText(error_message)
|
|
282
|
-
# = FixedText(error_message, align='r')
|
|
283
|
-
# self.message.setStyleSheet("color: rgb(230, 145, 18)")
|
|
284
|
-
# self.layout.addWidget(self.message, 12, 0, 12, 3)
|
|
285
383
|
else:
|
|
286
384
|
self.message.setText('')
|
|
287
|
-
# if len(self.parent().po.all['folder_list']) > 0:
|
|
288
|
-
# self.parent().po.update_folder_id(self.parent().po.all['first_folder_sample_number'],
|
|
289
|
-
# self.parent().po.all['folder_list'][0])
|
|
290
|
-
# if self.instantiate: # not self.parent().imageanalysiswindow.initialized:
|
|
291
|
-
# self.thread["GetFirstIm"].start()
|
|
292
|
-
# self.thread["GetFirstIm"].message_when_thread_finished.connect(self.first_im_read)
|
|
293
|
-
# else:
|
|
294
|
-
# self.first_im_read(True)
|
|
295
|
-
# if isinstance(self.parent().po.all['sample_number_per_folder'], int):
|
|
296
|
-
# self.parent().po.all['folder_number'] = 1
|
|
297
385
|
if self.parent().po.all['folder_number'] > 1:
|
|
298
386
|
self.parent().instantiate_widgets()
|
|
299
387
|
self.parent().ifseveralfolderswindow.true_init()
|
|
@@ -304,10 +392,18 @@ class FirstWindow(MainTabsType):
|
|
|
304
392
|
self.thread["GetFirstIm"].message_when_thread_finished.connect(self.first_im_read)
|
|
305
393
|
|
|
306
394
|
def first_im_read(self, greyscale):
|
|
395
|
+
"""
|
|
396
|
+
Initialize the image analysis window and prepare for reading images.
|
|
397
|
+
|
|
398
|
+
Notes
|
|
399
|
+
-----
|
|
400
|
+
This function prepares the image analysis window and sets it to be ready for
|
|
401
|
+
reading images. It also ensures that certain tabs are set as not in use.
|
|
402
|
+
"""
|
|
307
403
|
self.parent().instantiate_widgets()
|
|
308
404
|
self.parent().imageanalysiswindow.true_init()
|
|
309
405
|
self.instantiate = False
|
|
310
|
-
if self.parent().po.first_exp_ready_to_run:
|
|
406
|
+
if self.parent().po.first_exp_ready_to_run and (self.parent().po.all["im_or_vid"] == 1 or len(self.parent().po.data_list) > 1):
|
|
311
407
|
self.parent().imageanalysiswindow.video_tab.set_not_in_use()
|
|
312
408
|
self.parent().change_widget(2) # imageanalysiswindow
|
|
313
409
|
# From now on, image analysis will be available from video analysis:
|
|
@@ -315,10 +411,27 @@ class FirstWindow(MainTabsType):
|
|
|
315
411
|
self.thread["GetExifDataThread"].start()
|
|
316
412
|
|
|
317
413
|
def required_outputs_is_clicked(self):
|
|
414
|
+
"""
|
|
415
|
+
Handle the click event for switching to required outputs.
|
|
416
|
+
|
|
417
|
+
This function sets the `last_is_first` attribute of the parent to True
|
|
418
|
+
and changes the widget to the Required Outputs view.
|
|
419
|
+
"""
|
|
318
420
|
self.parent().last_is_first = True
|
|
319
421
|
self.parent().change_widget(4) # RequiredOutput
|
|
320
422
|
|
|
321
423
|
def advanced_parameters_is_clicked(self):
|
|
424
|
+
"""
|
|
425
|
+
Handle the click event for switching to advanced parameters.
|
|
426
|
+
|
|
427
|
+
Checks if an Exif data reading thread is running and acts accordingly.
|
|
428
|
+
If not, it updates the display for advanced parameters.
|
|
429
|
+
|
|
430
|
+
Notes
|
|
431
|
+
-----
|
|
432
|
+
This function updates the display for advanced parameters only if no Exif data reading thread is running.
|
|
433
|
+
If a thread is active, it informs the user to wait or restart Cellects.
|
|
434
|
+
"""
|
|
322
435
|
if self.thread["GetExifDataThread"].isRunning():
|
|
323
436
|
self.message.setText("Reading data, wait or restart Cellects")
|
|
324
437
|
else:
|
|
@@ -327,26 +440,61 @@ class FirstWindow(MainTabsType):
|
|
|
327
440
|
self.parent().change_widget(5) # AdvancedParameters
|
|
328
441
|
|
|
329
442
|
def video_analysis_window_is_clicked(self):
|
|
443
|
+
"""
|
|
444
|
+
Handles the logic for when the "Video tracking" button is clicked in the interface,
|
|
445
|
+
leading to the video analysis window.
|
|
446
|
+
|
|
447
|
+
Notes
|
|
448
|
+
-----
|
|
449
|
+
This function displays an error message when a thread relative to the current window is running.
|
|
450
|
+
This function also save the id of the following window for later use.
|
|
451
|
+
"""
|
|
330
452
|
if self.video_tab.state != "not_usable":
|
|
331
453
|
if self.thread["LookForData"].isRunning() or self.thread["LoadDataToRunCellectsQuickly"].isRunning() or self.thread["GetFirstIm"].isRunning() or self.thread["RunAll"].isRunning():
|
|
332
454
|
self.message.setText("Wait for the analysis to end, or restart Cellects")
|
|
333
455
|
else:
|
|
334
456
|
self.parent().last_tab = "data_specifications"
|
|
335
|
-
# self.parent().po.first_exp_ready_to_run = False
|
|
336
457
|
self.parent().change_widget(3) # Should be VideoAnalysisW
|
|
337
458
|
|
|
338
459
|
def Run_all_directly_is_clicked(self):
|
|
460
|
+
"""
|
|
461
|
+
Run_all_directly_is_clicked
|
|
462
|
+
|
|
463
|
+
This method initiates a complete analysis process by starting the `RunAll` thread
|
|
464
|
+
after ensuring no other relevant threads are currently running.
|
|
465
|
+
|
|
466
|
+
Notes
|
|
467
|
+
-----
|
|
468
|
+
- This method ensures that the `LookForData` and `RunAll` threads are not running
|
|
469
|
+
before initiating a new analysis.
|
|
470
|
+
- The method updates the UI to indicate that an analysis has started and displays
|
|
471
|
+
progress messages.
|
|
472
|
+
"""
|
|
339
473
|
if not self.thread["LookForData"].isRunning() and not self.thread["RunAll"].isRunning():
|
|
340
474
|
self.parent().po.motion = None
|
|
341
475
|
self.message.setText("Complete analysis has started, wait until this message disappear...")
|
|
342
|
-
# if not self.parent().po.first_exp_ready_to_run:
|
|
343
|
-
# self.parent().po.use_data_to_run_cellects_quickly = True
|
|
344
476
|
self.thread["RunAll"].start()
|
|
345
477
|
self.thread["RunAll"].message_from_thread.connect(self.display_message_from_thread)
|
|
346
478
|
self.thread["RunAll"].image_from_thread.connect(self.display_image_during_thread)
|
|
347
479
|
self.display_image.setVisible(True)
|
|
348
480
|
|
|
349
481
|
def pathway_changed(self):
|
|
482
|
+
"""
|
|
483
|
+
Method for handling pathway changes in the application.
|
|
484
|
+
|
|
485
|
+
This method performs several operations when a new global pathway is set:
|
|
486
|
+
1. Waits for any running thread to complete.
|
|
487
|
+
2. Updates the global pathway if a valid directory is found.
|
|
488
|
+
3. Changes the current working directory to the new global pathway.
|
|
489
|
+
4. Hides various widgets associated with advanced options and outputs.
|
|
490
|
+
5. Starts a background thread to load data quickly.
|
|
491
|
+
6. If the provided pathway is invalid, it hides relevant tabs and outputs an error message.
|
|
492
|
+
|
|
493
|
+
Notes
|
|
494
|
+
-----
|
|
495
|
+
This method performs actions to prepare the application for loading data from a new pathway.
|
|
496
|
+
It ensures that certain widgets are hidden and starts necessary background processes.
|
|
497
|
+
"""
|
|
350
498
|
if self.thread["LoadDataToRunCellectsQuickly"].isRunning():
|
|
351
499
|
self.thread["LoadDataToRunCellectsQuickly"].wait()
|
|
352
500
|
if os.path.isdir(Path(self.global_pathway.text())):
|
|
@@ -360,7 +508,6 @@ class FirstWindow(MainTabsType):
|
|
|
360
508
|
self.im_or_vid.setVisible(False)
|
|
361
509
|
self.advanced_parameters.setVisible(False)
|
|
362
510
|
self.required_outputs.setVisible(False)
|
|
363
|
-
# self.Video_analysis_window.setVisible(False)
|
|
364
511
|
self.Run_all_directly.setVisible(False)
|
|
365
512
|
self.next.setVisible(False)
|
|
366
513
|
# 2) Load the dict
|
|
@@ -368,13 +515,25 @@ class FirstWindow(MainTabsType):
|
|
|
368
515
|
self.thread["LoadDataToRunCellectsQuickly"].message_from_thread.connect(self.load_data_quickly_finished)
|
|
369
516
|
# 3) go to another func to change, put visible and re_instantiate
|
|
370
517
|
else:
|
|
371
|
-
# self.Video_analysis_window.setVisible(False)
|
|
372
518
|
self.Run_all_directly.setVisible(False)
|
|
373
519
|
self.image_tab.set_not_usable()
|
|
374
520
|
self.video_tab.set_not_usable()
|
|
375
521
|
self.message.setText("Please, enter a valid path")
|
|
376
522
|
|
|
377
|
-
def load_data_quickly_finished(self, message):
|
|
523
|
+
def load_data_quickly_finished(self, message: str):
|
|
524
|
+
"""
|
|
525
|
+
Set up the UI components for a new experiment.
|
|
526
|
+
|
|
527
|
+
Parameters
|
|
528
|
+
----------
|
|
529
|
+
message : str
|
|
530
|
+
The message to be displayed on the UI component.
|
|
531
|
+
|
|
532
|
+
Notes
|
|
533
|
+
-----
|
|
534
|
+
This function sets several visibility flags and values for UI components
|
|
535
|
+
in preparation for starting an experiment.
|
|
536
|
+
"""
|
|
378
537
|
self.image_tab.set_not_in_use()
|
|
379
538
|
self.message.setText(message)
|
|
380
539
|
self.radical.setVisible(True)
|
|
@@ -392,58 +551,21 @@ class FirstWindow(MainTabsType):
|
|
|
392
551
|
self.im_or_vid.setCurrentIndex(self.parent().po.all['im_or_vid'])
|
|
393
552
|
self.radical.setText(self.parent().po.all['radical'])
|
|
394
553
|
self.extension.setText(self.parent().po.all['extension'])
|
|
395
|
-
#self.shortcut_cb.setChecked(self.parent().po.all['display_shortcuts'])
|
|
396
|
-
#self.display_shortcuts_checked()
|
|
397
|
-
# self.Video_analysis_window.setVisible(True)
|
|
398
554
|
self.Run_all_directly.setVisible(True)
|
|
399
|
-
self.
|
|
555
|
+
if self.parent().po.all["im_or_vid"] == 1 or len(self.parent().po.data_list) > 1:
|
|
556
|
+
self.video_tab.set_not_in_use()
|
|
400
557
|
|
|
401
558
|
|
|
402
559
|
def re_instantiate_widgets(self):
|
|
403
560
|
"""
|
|
404
|
-
|
|
405
|
-
:return:
|
|
561
|
+
Reinstantiate the videoanalysis window from the parent of the current window.
|
|
406
562
|
"""
|
|
407
563
|
self.instantiate = True
|
|
408
564
|
# Since we re-instantiate everything, image analysis will no longer be available from video analysis:
|
|
409
565
|
self.parent().videoanalysiswindow.image_tab.set_not_usable()
|
|
410
566
|
|
|
411
|
-
# self.parent().po.all['radical'] = self.radical.text()
|
|
412
|
-
# self.parent().po.all['extension'] = self.extension.text()
|
|
413
|
-
# self.parent().po.sample_number = int(self.arena_number.value())
|
|
414
|
-
# self.parent().po.all['first_folder_sample_number'] = self.parent().po.sample_number
|
|
415
|
-
# self.parent().po.all['sample_number_per_folder'] = [self.parent().po.sample_number]
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
# Mettre ça en thread ? PB : conflict entre all et vars
|
|
419
|
-
# if os.path.isfile('Data to run Cellects quickly.pkl'):
|
|
420
|
-
# try:
|
|
421
|
-
# with open('Data to run Cellects quickly.pkl', 'rb') as fileopen:
|
|
422
|
-
# data_to_run_cellects_quickly = pickle.load(fileopen)
|
|
423
|
-
# if 'vars' in data_to_run_cellects_quickly:
|
|
424
|
-
# self.vars = data_to_run_cellects_quickly['vars']
|
|
425
|
-
# except EOFError:
|
|
426
|
-
# print("Pickle error: could not load vars from the data folder")
|
|
427
|
-
# self.instantiate = True
|
|
428
|
-
# else:
|
|
429
|
-
# self.instantiate = True
|
|
430
|
-
# if self.instantiate:
|
|
431
|
-
# self.parent().po.all['radical'] = self.radical.text()
|
|
432
|
-
# self.parent().po.all['extension'] = self.extension.text()
|
|
433
|
-
# self.parent().po.sample_number = int(self.arena_number.value())
|
|
434
|
-
# self.parent().po.all['first_folder_sample_number'] = self.parent().po.sample_number
|
|
435
|
-
# self.parent().po.all['sample_number_per_folder'] = [self.parent().po.sample_number]
|
|
436
|
-
|
|
437
567
|
def closeEvent(self, event):
|
|
568
|
+
"""
|
|
569
|
+
Handle the close event for a QWidget.
|
|
570
|
+
"""
|
|
438
571
|
event.accept
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
# if __name__ == "__main__":
|
|
442
|
-
# from cellects.gui.cellects import CellectsMainWidget
|
|
443
|
-
# import sys
|
|
444
|
-
# app = QtWidgets.QApplication([])
|
|
445
|
-
# parent = CellectsMainWidget()
|
|
446
|
-
# session = FirstWindow(parent, False)
|
|
447
|
-
# parent.insertWidget(0, session)
|
|
448
|
-
# parent.show()
|
|
449
|
-
# sys.exit(app.exec())
|