cellects 0.1.2__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 +390 -330
- cellects/gui/cellects.py +102 -91
- cellects/gui/custom_widgets.py +16 -33
- cellects/gui/first_window.py +226 -104
- cellects/gui/if_several_folders_window.py +117 -68
- cellects/gui/image_analysis_window.py +866 -454
- cellects/gui/required_output.py +104 -57
- cellects/gui/ui_strings.py +840 -0
- cellects/gui/video_analysis_window.py +333 -155
- 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 -109
- 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.2.dist-info/LICENSE.odt +0 -0
- cellects-0.1.2.dist-info/METADATA +0 -132
- cellects-0.1.2.dist-info/RECORD +0 -44
- {cellects-0.1.2.dist-info → cellects-0.2.6.dist-info}/WHEEL +0 -0
- {cellects-0.1.2.dist-info → cellects-0.2.6.dist-info}/entry_points.txt +0 -0
- {cellects-0.1.2.dist-info → cellects-0.2.6.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
"""Third main widget for Cellects GUI enabling video analysis configuration and execution.
|
|
3
|
+
|
|
4
|
+
This module implements a video tracking interface for analyzing cell movement through configurable parameters like
|
|
5
|
+
arena selection, segmentation methods, and smoothing thresholds. It provides interactive controls including spinboxes,
|
|
6
|
+
comboboxes, buttons for detection/post-processing, and an image display area with full-screen support. Threaded
|
|
7
|
+
operations (VideoReaderThread, OneArenaThread) handle background processing to maintain UI responsiveness.
|
|
8
|
+
|
|
9
|
+
Main Components
|
|
10
|
+
VideoAnalysisWindow : QWidget subclass implementing the video analysis interface with tab navigation, parameter
|
|
11
|
+
controls, and thread coordination
|
|
12
|
+
|
|
13
|
+
Notes
|
|
14
|
+
Uses QThread for background operations to maintain UI responsiveness.
|
|
6
15
|
"""
|
|
7
16
|
import logging
|
|
8
17
|
import numpy as np
|
|
9
|
-
import cv2
|
|
10
18
|
from PySide6 import QtWidgets, QtCore
|
|
11
19
|
|
|
12
20
|
from cellects.core.cellects_threads import (
|
|
@@ -15,13 +23,54 @@ from cellects.core.cellects_threads import (
|
|
|
15
23
|
from cellects.gui.custom_widgets import (
|
|
16
24
|
MainTabsType, InsertImage, FullScreenImage, PButton, Spinbox,
|
|
17
25
|
Combobox, Checkbox, FixedText)
|
|
26
|
+
from cellects.gui.ui_strings import FW, VAW
|
|
18
27
|
|
|
19
28
|
|
|
20
29
|
class VideoAnalysisWindow(MainTabsType):
|
|
21
30
|
def __init__(self, parent, night_mode):
|
|
31
|
+
"""
|
|
32
|
+
Initialize the VideoAnalysis window with a parent widget and night mode setting.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
parent : QWidget
|
|
37
|
+
The parent widget to which this window will be attached.
|
|
38
|
+
night_mode : bool
|
|
39
|
+
A boolean indicating whether the night mode should be enabled.
|
|
40
|
+
|
|
41
|
+
Examples
|
|
42
|
+
--------
|
|
43
|
+
>>> from PySide6 import QtWidgets
|
|
44
|
+
>>> from cellects.gui.cellects import CellectsMainWidget
|
|
45
|
+
>>> from cellects.gui.video_analysis_window import VideoAnalysisWindow
|
|
46
|
+
>>> import sys
|
|
47
|
+
>>> app = QtWidgets.QApplication([])
|
|
48
|
+
>>> parent = CellectsMainWidget()
|
|
49
|
+
>>> session = VideoAnalysisWindow(parent, False)
|
|
50
|
+
>>> session.true_init()
|
|
51
|
+
>>> parent.insertWidget(0, session)
|
|
52
|
+
>>> parent.show()
|
|
53
|
+
>>> sys.exit(app.exec())
|
|
54
|
+
"""
|
|
22
55
|
super().__init__(parent, night_mode)
|
|
23
56
|
logging.info("Initialize VideoAnalysisWindow")
|
|
24
57
|
self.setParent(parent)
|
|
58
|
+
self.true_init()
|
|
59
|
+
|
|
60
|
+
def true_init(self):
|
|
61
|
+
"""
|
|
62
|
+
Initialize the video tracking interface and set up its UI components.
|
|
63
|
+
|
|
64
|
+
Extended Description
|
|
65
|
+
--------------------
|
|
66
|
+
This method initializes various tabs, threads, and UI elements for the video tracking interface. It sets up
|
|
67
|
+
event handlers for tab clicks and configures layout components such as labels, buttons, spinboxes, and
|
|
68
|
+
comboboxes.
|
|
69
|
+
|
|
70
|
+
Notes
|
|
71
|
+
-----
|
|
72
|
+
This method assumes that the parent widget has a 'po' attribute with specific settings and variables.
|
|
73
|
+
"""
|
|
25
74
|
self.data_tab.set_not_in_use()
|
|
26
75
|
self.image_tab.set_not_usable()
|
|
27
76
|
self.video_tab.set_in_use()
|
|
@@ -33,21 +82,10 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
33
82
|
self.thread['ChangeOneRepResult'] = ChangeOneRepResultThread(self.parent())
|
|
34
83
|
self.thread['RunAll'] = RunAllThread(self.parent())
|
|
35
84
|
self.previous_arena = 0
|
|
36
|
-
|
|
37
|
-
self.layout = QtWidgets.QGridLayout()
|
|
38
|
-
self.grid_widget = QtWidgets.QWidget()
|
|
39
|
-
|
|
40
|
-
# self.title = FixedText('Video tracking', police=30, night_mode=self.parent().po.all['night_mode'])
|
|
41
|
-
# self.title.setAlignment(QtCore.Qt.AlignHCenter)
|
|
42
85
|
curr_row_main_layout = 0
|
|
43
86
|
ncol = 1
|
|
44
|
-
|
|
45
|
-
# curr_row_main_layout += 2
|
|
46
|
-
self.layout.addItem(self.vertical_space, curr_row_main_layout, 0, 1, ncol)
|
|
87
|
+
self.Vlayout.addItem(self.vertical_space)#, curr_row_main_layout, 0, 1, ncol)
|
|
47
88
|
curr_row_main_layout += 1
|
|
48
|
-
#
|
|
49
|
-
# self.layout.addWidget(self.arena_widget, curr_row_main_layout, 0)
|
|
50
|
-
# curr_row_main_layout += 1
|
|
51
89
|
|
|
52
90
|
# Open subtitle
|
|
53
91
|
self.general_step_widget = QtWidgets.QWidget()
|
|
@@ -61,7 +99,7 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
61
99
|
self.general_step_layout.addWidget(self.general_step_button)
|
|
62
100
|
self.general_step_layout.addItem(self.horizontal_space)
|
|
63
101
|
self.general_step_widget.setLayout(self.general_step_layout)
|
|
64
|
-
self.
|
|
102
|
+
self.Vlayout.addWidget(self.general_step_widget)#, curr_row_main_layout, 0, 1, ncol)
|
|
65
103
|
curr_row_main_layout += 1
|
|
66
104
|
|
|
67
105
|
# Open central widget
|
|
@@ -72,18 +110,12 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
72
110
|
self.left_options_widget = QtWidgets.QWidget()
|
|
73
111
|
self.left_options_layout = QtWidgets.QVBoxLayout()
|
|
74
112
|
self.left_options_widget.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
|
|
75
|
-
# self.left_options_widget.setFixedWidth(420)
|
|
76
113
|
|
|
77
114
|
self.arena_widget = QtWidgets.QWidget()
|
|
78
115
|
self.arena_layout = QtWidgets.QHBoxLayout()
|
|
79
|
-
self.arena_label = FixedText(
|
|
80
|
-
tip="
|
|
116
|
+
self.arena_label = FixedText(VAW["Arena_to_analyze"]["label"] + ':',
|
|
117
|
+
tip=VAW["Arena_to_analyze"]["tips"],
|
|
81
118
|
night_mode=self.parent().po.all['night_mode'])
|
|
82
|
-
# if isinstance(self.parent().po.all['sample_number_per_folder'], int):
|
|
83
|
-
# self.parent().po.all['folder_number'] = 1
|
|
84
|
-
# if self.parent().po.all['folder_number'] > 1:
|
|
85
|
-
# sample_size = self.parent().po.all['sample_number_per_folder']
|
|
86
|
-
# else:
|
|
87
119
|
sample_size = self.parent().po.all['sample_number_per_folder'][0]
|
|
88
120
|
if self.parent().po.all['arena'] > sample_size:
|
|
89
121
|
self.parent().po.all['arena'] = 1
|
|
@@ -92,12 +124,9 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
92
124
|
night_mode=self.parent().po.all['night_mode'])
|
|
93
125
|
self.arena.valueChanged.connect(self.arena_changed)
|
|
94
126
|
|
|
95
|
-
# self.arena_layout.addItem(self.horizontal_space)
|
|
96
127
|
self.arena_layout.addWidget(self.arena_label)
|
|
97
128
|
self.arena_layout.addWidget(self.arena)
|
|
98
|
-
# self.arena_layout.addItem(self.horizontal_space)
|
|
99
129
|
self.arena_widget.setLayout(self.arena_layout)
|
|
100
|
-
# self.arena_layout.setAlignment(QtCore.Qt.AlignHCenter)
|
|
101
130
|
self.left_options_layout.addWidget(self.arena_widget)
|
|
102
131
|
|
|
103
132
|
|
|
@@ -110,13 +139,12 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
110
139
|
self.parent().po.vars['repeat_video_smoothing'] = self.parent().po.vars['iterate_smoothing']
|
|
111
140
|
self.maximal_growth_factor = Spinbox(min=0, max=0.5, val=self.parent().po.vars['maximal_growth_factor'],
|
|
112
141
|
decimals=3, night_mode=self.parent().po.all['night_mode'])
|
|
113
|
-
self.maximal_growth_factor_label = FixedText(
|
|
114
|
-
tip="
|
|
142
|
+
self.maximal_growth_factor_label = FixedText(VAW["Maximal_growth_factor"]["label"] + ':',
|
|
143
|
+
tip=VAW["Maximal_growth_factor"]["tips"],
|
|
115
144
|
night_mode=self.parent().po.all['night_mode'])
|
|
116
145
|
self.maximal_growth_factor.valueChanged.connect(self.maximal_growth_factor_changed)
|
|
117
146
|
self.growth_per_frame_layout.addWidget(self.maximal_growth_factor_label)
|
|
118
147
|
self.growth_per_frame_layout.addWidget(self.maximal_growth_factor)
|
|
119
|
-
# self.growth_per_frame_layout.setAlignment(QtCore.Qt.AlignHCenter)
|
|
120
148
|
self.growth_per_frame_widget.setLayout(self.growth_per_frame_layout)
|
|
121
149
|
self.left_options_layout.addWidget(self.growth_per_frame_widget)
|
|
122
150
|
|
|
@@ -124,45 +152,36 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
124
152
|
self.iterate_layout = QtWidgets.QHBoxLayout()
|
|
125
153
|
self.repeat_video_smoothing = Spinbox(min=0, max=10, val=self.parent().po.vars['repeat_video_smoothing'],
|
|
126
154
|
night_mode=self.parent().po.all['night_mode'])
|
|
127
|
-
self.repeat_video_smoothing_label = FixedText(
|
|
128
|
-
tip="
|
|
155
|
+
self.repeat_video_smoothing_label = FixedText(VAW["Temporal_smoothing"]["label"] + ':',
|
|
156
|
+
tip=VAW["Temporal_smoothing"]["tips"],
|
|
129
157
|
night_mode=self.parent().po.all['night_mode'])
|
|
130
158
|
self.repeat_video_smoothing.valueChanged.connect(self.repeat_video_smoothing_changed)
|
|
131
159
|
self.iterate_layout.addWidget(self.repeat_video_smoothing_label)
|
|
132
160
|
self.iterate_layout.addWidget(self.repeat_video_smoothing)
|
|
133
|
-
# self.iterate_layout.setAlignment(QtCore.Qt.AlignHCenter)
|
|
134
161
|
self.iterate_widget.setLayout(self.iterate_layout)
|
|
135
162
|
self.left_options_layout.addWidget(self.iterate_widget)
|
|
136
163
|
|
|
137
164
|
|
|
138
|
-
self.select_option_label = FixedText('
|
|
139
|
-
tip=
|
|
165
|
+
self.select_option_label = FixedText(VAW["Segmentation_method"]["label"] + ':',
|
|
166
|
+
tip=VAW["Segmentation_method"]["tips"],
|
|
140
167
|
night_mode=self.parent().po.all['night_mode'])
|
|
141
168
|
self.select_option = Combobox([], night_mode=self.parent().po.all['night_mode'])
|
|
142
169
|
self.select_option_label.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
|
|
143
170
|
self.select_option.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
|
|
144
171
|
self.select_option.setFixedWidth(175)
|
|
145
|
-
# self.select_option_label.setFixedWidth(265)
|
|
146
|
-
# self.select_option.setFixedWidth(150)
|
|
147
172
|
self.select_option.addItem("1. Frame by frame")
|
|
148
173
|
self.select_option.addItem("2. Dynamical threshold")
|
|
149
174
|
self.select_option.addItem("3. Dynamical slope")
|
|
150
175
|
self.select_option.addItem("4. Threshold and Slope")
|
|
151
176
|
self.select_option.addItem("5. Threshold or Slope")
|
|
152
|
-
# for option in range(5):
|
|
153
|
-
# self.select_option.addItem(f"Option {option + 1}")
|
|
154
177
|
self.select_option.setCurrentIndex(self.parent().po.all['video_option'])
|
|
155
178
|
self.select_option.currentTextChanged.connect(self.option_changed)
|
|
156
|
-
# self.select_option_label.setVisible(self.parent().po.vars["color_number"] == 2)
|
|
157
|
-
# self.select_option.setVisible(self.parent().po.vars["color_number"] == 2)
|
|
158
179
|
|
|
159
180
|
# Open the choose best option row layout
|
|
160
181
|
self.options_row_widget = QtWidgets.QWidget()
|
|
161
182
|
self.options_row_layout = QtWidgets.QHBoxLayout()
|
|
162
|
-
# self.options_row_layout.addItem(self.horizontal_space)
|
|
163
183
|
self.options_row_layout.addWidget(self.select_option_label)
|
|
164
184
|
self.options_row_layout.addWidget(self.select_option)
|
|
165
|
-
# self.options_row_layout.addItem(self.horizontal_space)
|
|
166
185
|
self.options_row_layout.setAlignment(QtCore.Qt.AlignHCenter)
|
|
167
186
|
self.options_row_layout.setAlignment(QtCore.Qt.AlignVCenter)
|
|
168
187
|
self.options_row_widget.setLayout(self.options_row_layout)
|
|
@@ -184,13 +203,19 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
184
203
|
self.right_options_widget = QtWidgets.QWidget()
|
|
185
204
|
self.right_options_layout = QtWidgets.QVBoxLayout()
|
|
186
205
|
self.right_options_widget.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
|
|
187
|
-
# self.right_options_widget.setFixedWidth(420)
|
|
188
206
|
|
|
189
207
|
self.compute_all_options_label = FixedText('Compute all options',
|
|
190
|
-
tip=
|
|
208
|
+
tip=VAW["Segmentation_method"]["tips"],
|
|
191
209
|
night_mode=self.parent().po.all['night_mode'])
|
|
192
210
|
self.compute_all_options_cb = Checkbox(self.parent().po.all['compute_all_options'])
|
|
193
|
-
self.compute_all_options_cb.setStyleSheet("
|
|
211
|
+
self.compute_all_options_cb.setStyleSheet("QCheckBox::indicator {width: 12px;height: 12px;background-color: transparent;"
|
|
212
|
+
"border-radius: 5px;border-style: solid;border-width: 1px;"
|
|
213
|
+
"border-color: rgb(100,100,100);}"
|
|
214
|
+
"QCheckBox::indicator:checked {background-color: rgb(70,130,180);}"
|
|
215
|
+
"QCheckBox:checked, QCheckBox::indicator:checked {border-color: black black white white;}"
|
|
216
|
+
"QCheckBox:checked {background-color: transparent;}"
|
|
217
|
+
"QCheckBox:margin-left {0%}"
|
|
218
|
+
"QCheckBox:margin-right {0%}")
|
|
194
219
|
self.compute_all_options_cb.stateChanged.connect(self.compute_all_options_check)
|
|
195
220
|
self.all_options_row_widget = QtWidgets.QWidget()
|
|
196
221
|
self.all_options_row_layout = QtWidgets.QHBoxLayout()
|
|
@@ -201,11 +226,13 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
201
226
|
self.all_options_row_widget.setLayout(self.all_options_row_layout)
|
|
202
227
|
self.right_options_layout.addWidget(self.all_options_row_widget)
|
|
203
228
|
|
|
204
|
-
self.load_one_arena = PButton(
|
|
229
|
+
self.load_one_arena = PButton(VAW["Load_one_arena"]["label"], tip=VAW["Load_one_arena"]["tips"],
|
|
230
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
205
231
|
self.load_one_arena.clicked.connect(self.load_one_arena_is_clicked)
|
|
206
|
-
self.detection = PButton(
|
|
232
|
+
self.detection = PButton(VAW["Detection"]["label"], tip=VAW["Detection"]["tips"],
|
|
233
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
207
234
|
self.detection.clicked.connect(self.detection_is_clicked)
|
|
208
|
-
self.read = PButton(
|
|
235
|
+
self.read = PButton(VAW["Read"]["label"], tip=VAW["Read"]["tips"], night_mode=self.parent().po.all['night_mode'])
|
|
209
236
|
self.read.clicked.connect(self.read_is_clicked)
|
|
210
237
|
self.read.setVisible(False)
|
|
211
238
|
self.right_options_layout.addWidget(self.load_one_arena, alignment=QtCore.Qt.AlignCenter)
|
|
@@ -219,10 +246,7 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
219
246
|
self.video_display_layout.addItem(self.horizontal_space)
|
|
220
247
|
# Close central widget
|
|
221
248
|
self.video_display_widget.setLayout(self.video_display_layout)
|
|
222
|
-
|
|
223
|
-
# self.video_display_widget.setAlignment(QtCore.Qt.AlignHCenter)
|
|
224
|
-
# self.video_display_widget.setFixedHeight(500)
|
|
225
|
-
self.layout.addWidget(self.video_display_widget, curr_row_main_layout, 0)
|
|
249
|
+
self.Vlayout.addWidget(self.video_display_widget)#, curr_row_main_layout, 0)
|
|
226
250
|
curr_row_main_layout += 1
|
|
227
251
|
|
|
228
252
|
# Open Second step row
|
|
@@ -234,15 +258,21 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
234
258
|
self.fading_widget = QtWidgets.QWidget()
|
|
235
259
|
self.fading_layout = QtWidgets.QHBoxLayout()
|
|
236
260
|
self.do_fading = Checkbox(self.parent().po.vars['do_fading'])
|
|
237
|
-
self.do_fading.setStyleSheet("
|
|
261
|
+
self.do_fading.setStyleSheet("QCheckBox::indicator {width: 12px;height: 12px;background-color: transparent;"
|
|
262
|
+
"border-radius: 5px;border-style: solid;border-width: 1px;"
|
|
263
|
+
"border-color: rgb(100,100,100);}"
|
|
264
|
+
"QCheckBox::indicator:checked {background-color: rgb(70,130,180);}"
|
|
265
|
+
"QCheckBox:checked, QCheckBox::indicator:checked {border-color: black black white white;}"
|
|
266
|
+
"QCheckBox:checked {background-color: transparent;}"
|
|
267
|
+
"QCheckBox:margin-left {0%}"
|
|
268
|
+
"QCheckBox:margin-right {0%}")
|
|
238
269
|
self.do_fading.stateChanged.connect(self.do_fading_check)
|
|
239
270
|
self.fading = Spinbox(min=- 1, max=1, val=self.parent().po.vars['fading'], decimals=2,
|
|
240
271
|
night_mode=self.parent().po.all['night_mode'])
|
|
241
|
-
self.fading_label = FixedText(
|
|
242
|
-
tip="
|
|
272
|
+
self.fading_label = FixedText(VAW["Fading_detection"]["label"],
|
|
273
|
+
tip=VAW["Fading_detection"]["tips"],
|
|
243
274
|
night_mode=self.parent().po.all['night_mode'])
|
|
244
275
|
self.fading.valueChanged.connect(self.fading_changed)
|
|
245
|
-
# self.fading.setVisible(self.parent().po.vars['do_fading'])
|
|
246
276
|
self.fading_layout.addWidget(self.do_fading)
|
|
247
277
|
self.fading_layout.addWidget(self.fading_label)
|
|
248
278
|
self.fading_layout.addWidget(self.fading)
|
|
@@ -250,23 +280,23 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
250
280
|
self.fading_widget.setLayout(self.fading_layout)
|
|
251
281
|
self.second_step_layout.addWidget(self.fading_widget)
|
|
252
282
|
|
|
253
|
-
self.post_processing = PButton(
|
|
283
|
+
self.post_processing = PButton(VAW["Post_processing"]["label"], tip=VAW["Post_processing"]["tips"],
|
|
284
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
254
285
|
self.post_processing.clicked.connect(self.post_processing_is_clicked)
|
|
255
286
|
self.second_step_layout.addWidget(self.post_processing)
|
|
256
|
-
# self.second_step_layout.addWidget(self.post_processing, alignment=QtCore.Qt.AlignCenter)
|
|
257
287
|
|
|
258
|
-
self.save_one_result = PButton(
|
|
288
|
+
self.save_one_result = PButton(VAW["Save_one_result"]["label"], tip=VAW["Save_one_result"]["tips"],
|
|
289
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
259
290
|
self.save_one_result.clicked.connect(self.save_one_result_is_clicked)
|
|
260
291
|
self.second_step_layout.addWidget(self.save_one_result)
|
|
261
|
-
# self.second_step_layout.addWidget(self.save_one_result, alignment=QtCore.Qt.AlignCenter)
|
|
262
292
|
|
|
263
293
|
# Close Second step row
|
|
264
294
|
self.second_step_layout.setAlignment(QtCore.Qt.AlignHCenter)
|
|
265
295
|
self.second_step_layout.addItem(self.horizontal_space)
|
|
266
296
|
self.second_step_widget.setLayout(self.second_step_layout)
|
|
267
|
-
self.
|
|
297
|
+
self.Vlayout.addItem(self.vertical_space)#, curr_row_main_layout, 0, 1, ncol)
|
|
268
298
|
curr_row_main_layout += 1
|
|
269
|
-
self.
|
|
299
|
+
self.Vlayout.addWidget(self.second_step_widget)#, curr_row_main_layout, 0)
|
|
270
300
|
curr_row_main_layout += 1
|
|
271
301
|
|
|
272
302
|
# Open last options row widget
|
|
@@ -274,42 +304,27 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
274
304
|
self.last_options_layout = QtWidgets.QHBoxLayout()
|
|
275
305
|
self.last_options_layout.addItem(self.horizontal_space)
|
|
276
306
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
# self.advanced_mode_layout = QtWidgets.QHBoxLayout()
|
|
280
|
-
# advanced_mode = self.parent().po.all['expert_mode']
|
|
281
|
-
# self.advanced_mode_cb = Checkbox(self.parent().po.all['expert_mode'])
|
|
282
|
-
# self.advanced_mode_cb.setStyleSheet("margin-left:0%; margin-right:0%;")
|
|
283
|
-
# self.advanced_mode_cb.stateChanged.connect(self.advanced_mode_check)
|
|
284
|
-
# self.advanced_mode_label = FixedText('Go to step 2 directly', align='l',
|
|
285
|
-
# tip="Allow the user to try Post processing before having tuned the parameters related to Detection.",
|
|
286
|
-
# night_mode=self.parent().po.all['night_mode'])
|
|
287
|
-
# # self.advanced_mode_label.setAlignment(QtCore.Qt.AlignTop)
|
|
288
|
-
# self.advanced_mode_layout.addWidget(self.advanced_mode_cb)
|
|
289
|
-
# self.advanced_mode_layout.addWidget(self.advanced_mode_label)
|
|
290
|
-
# self.advanced_mode_layout.addItem(self.horizontal_space)
|
|
291
|
-
# self.advanced_mode_layout.setAlignment(QtCore.Qt.AlignHCenter)
|
|
292
|
-
# self.advanced_mode_widget.setLayout(self.advanced_mode_layout)
|
|
293
|
-
# self.last_options_layout.addWidget(self.advanced_mode_widget)
|
|
294
|
-
|
|
295
|
-
self.advanced_parameters = PButton('Advanced Parameters', night_mode=self.parent().po.all['night_mode'])
|
|
307
|
+
self.advanced_parameters = PButton(FW["Advanced_parameters"]["label"], tip=FW["Advanced_parameters"]["tips"],
|
|
308
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
296
309
|
self.advanced_parameters.clicked.connect(self.advanced_parameters_is_clicked)
|
|
297
310
|
self.last_options_layout.addWidget(self.advanced_parameters)
|
|
298
311
|
|
|
299
312
|
# Required Outputs widget
|
|
300
|
-
self.required_outputs = PButton(
|
|
313
|
+
self.required_outputs = PButton(FW["Required_outputs"]["label"], tip=FW["Required_outputs"]["tips"],
|
|
314
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
301
315
|
self.required_outputs.clicked.connect(self.required_outputs_is_clicked)
|
|
302
316
|
self.last_options_layout.addWidget(self.required_outputs)
|
|
303
317
|
|
|
304
318
|
# Save all choices widget
|
|
305
|
-
self.save_all_vars = PButton(
|
|
319
|
+
self.save_all_vars = PButton(VAW["Save_all_choices"]["label"], tip=VAW["Save_all_choices"]["tips"],
|
|
320
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
306
321
|
self.save_all_vars.clicked.connect(self.save_current_settings)
|
|
307
322
|
self.last_options_layout.addWidget(self.save_all_vars)
|
|
308
323
|
|
|
309
324
|
# Close last options widget
|
|
310
325
|
self.last_options_layout.addItem(self.horizontal_space)
|
|
311
326
|
self.last_options_widget.setLayout(self.last_options_layout)
|
|
312
|
-
self.
|
|
327
|
+
self.Vlayout.addWidget(self.last_options_widget)#, curr_row_main_layout, 0)
|
|
313
328
|
curr_row_main_layout += 1
|
|
314
329
|
|
|
315
330
|
self.message = QtWidgets.QLabel(self)
|
|
@@ -320,7 +335,8 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
320
335
|
self.previous = PButton('Previous', night_mode=self.parent().po.all['night_mode'])
|
|
321
336
|
self.previous.clicked.connect(self.previous_is_clicked)
|
|
322
337
|
|
|
323
|
-
self.run_all = PButton(
|
|
338
|
+
self.run_all = PButton(VAW["Run_All"]["label"], tip=VAW["Run_All"]["tips"],
|
|
339
|
+
night_mode=self.parent().po.all['night_mode'])
|
|
324
340
|
self.run_all.clicked.connect(self.run_all_is_clicked)
|
|
325
341
|
|
|
326
342
|
# Open last row widget
|
|
@@ -332,21 +348,32 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
332
348
|
self.last_row_layout.addWidget(self.run_all)
|
|
333
349
|
# Close last row widget
|
|
334
350
|
self.last_row_widget.setLayout(self.last_row_layout)
|
|
335
|
-
self.
|
|
336
|
-
self.
|
|
351
|
+
self.Vlayout.addItem(self.vertical_space)#, curr_row_main_layout, 0, 1, ncol)
|
|
352
|
+
self.Vlayout.addWidget(self.last_row_widget)#, curr_row_main_layout + 1, 0)
|
|
337
353
|
|
|
338
|
-
self.grid_widget.setLayout(self.layout)
|
|
339
|
-
self.Vlayout.addItem(self.vertical_space)
|
|
340
|
-
self.Vlayout.addWidget(self.grid_widget)
|
|
341
354
|
self.setLayout(self.Vlayout)
|
|
342
|
-
# self.advanced_mode_check()
|
|
343
355
|
|
|
344
356
|
def display_conditionally_visible_widgets(self):
|
|
357
|
+
"""
|
|
358
|
+
Display Conditionally Visible Widgets
|
|
359
|
+
"""
|
|
345
360
|
self.select_option_label.setVisible(self.parent().po.vars["color_number"] == 2)
|
|
346
361
|
self.select_option.setVisible(self.parent().po.vars["color_number"] == 2)
|
|
347
362
|
self.fading.setVisible(self.parent().po.vars['do_fading'])
|
|
348
363
|
|
|
349
364
|
def step_done_is_clicked(self):
|
|
365
|
+
"""
|
|
366
|
+
Step the analysis progress when 'Done' button is clicked.
|
|
367
|
+
|
|
368
|
+
Increments the current step and updates the UI accordingly based on the
|
|
369
|
+
new step value. Updates labels, tooltips, and visibility of widgets.
|
|
370
|
+
|
|
371
|
+
Notes
|
|
372
|
+
-----
|
|
373
|
+
This method is automatically called when the 'Done' button is clicked.
|
|
374
|
+
It updates the GUI elements to reflect progress in a multi-step
|
|
375
|
+
analysis process.
|
|
376
|
+
"""
|
|
350
377
|
self.current_step += 1
|
|
351
378
|
if self.current_step == 1:
|
|
352
379
|
self.general_step_label.setText('Step 2: Tune fading and advanced parameters to improve Post processing')
|
|
@@ -361,6 +388,9 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
361
388
|
self.general_step_button.setVisible(False)
|
|
362
389
|
|
|
363
390
|
def reset_general_step(self):
|
|
391
|
+
"""
|
|
392
|
+
Reset the general step counter and update UI labels.
|
|
393
|
+
"""
|
|
364
394
|
self.current_step = 0
|
|
365
395
|
self.general_step_label.setText('Step 1: Tune parameters to improve Detection')
|
|
366
396
|
self.general_step_label.setToolTip('Detection uses only the visible parameters and those\npreviously determined on the first or last image.')
|
|
@@ -368,13 +398,29 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
368
398
|
self.second_step_widget.setVisible(False)
|
|
369
399
|
|
|
370
400
|
def full_screen_display(self, event):
|
|
401
|
+
"""
|
|
402
|
+
Full-screen display of an image.
|
|
403
|
+
|
|
404
|
+
This method creates a full-screen image popup and displays it. The
|
|
405
|
+
full-screen image is initialized with the current image to display,
|
|
406
|
+
and its size is set to match the screen dimensions.
|
|
407
|
+
"""
|
|
371
408
|
self.popup_img = FullScreenImage(self.parent().image_to_display, self.parent().screen_width, self.parent().screen_height)
|
|
372
409
|
self.popup_img.show()
|
|
373
410
|
|
|
374
411
|
def option_changed(self):
|
|
375
412
|
"""
|
|
376
|
-
|
|
377
|
-
|
|
413
|
+
Handles the logic for changing video option settings and logging the appropriate actions.
|
|
414
|
+
|
|
415
|
+
This method is responsible for updating various flags and configuration variables
|
|
416
|
+
based on the selected video option. It also logs informational messages regarding
|
|
417
|
+
the behavior of the segmentation algorithms being enabled or disabled.
|
|
418
|
+
|
|
419
|
+
Notes
|
|
420
|
+
-----
|
|
421
|
+
This function updates the parent object's configuration variables and logs messages
|
|
422
|
+
based on the selected video option. The behavior changes depending on the number of
|
|
423
|
+
colors detected and the specific video option chosen.
|
|
378
424
|
"""
|
|
379
425
|
self.parent().po.all['video_option'] = self.select_option.currentIndex()
|
|
380
426
|
self.parent().po.vars['frame_by_frame_segmentation'] = False
|
|
@@ -401,26 +447,17 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
401
447
|
elif self.parent().po.all['video_option'] == 4:
|
|
402
448
|
logging.info(f"This option will detect cell(s) using the dynamic threshold OR slope algorithms with a maximal growth factor of {self.parent().po.vars['maximal_growth_factor']}")
|
|
403
449
|
self.parent().po.vars['true_if_use_light_AND_slope_else_OR'] = False
|
|
404
|
-
# self.parent().po.motion
|
|
405
|
-
|
|
406
|
-
# def advanced_mode_check(self):
|
|
407
|
-
# advanced_mode = self.advanced_mode_cb.isChecked()
|
|
408
|
-
# self.parent().po.all['expert_mode'] = advanced_mode
|
|
409
|
-
# self.second_step_widget.setVisible(advanced_mode or self.current_step > 0)
|
|
410
|
-
# if advanced_mode:
|
|
411
|
-
# if self.current_step == 0:
|
|
412
|
-
# self.current_step += 1
|
|
413
|
-
# self.general_step_label.setText('Step 2: Tune fading and advanced parameters to improve Post processing')
|
|
414
|
-
# self.save_one_result.setVisible(self.current_step == 2)
|
|
415
|
-
|
|
416
|
-
# self.maximal_growth_factor.setVisible(advanced_mode)
|
|
417
|
-
# self.maximal_growth_factor_label.setVisible(advanced_mode)
|
|
418
|
-
# self.fading.setVisible(advanced_mode)
|
|
419
|
-
# self.fading_label.setVisible(advanced_mode)
|
|
420
|
-
# self.repeat_video_smoothing.setVisible(advanced_mode)
|
|
421
|
-
# self.repeat_video_smoothing_label.setVisible(advanced_mode)
|
|
422
450
|
|
|
423
451
|
def data_tab_is_clicked(self):
|
|
452
|
+
"""
|
|
453
|
+
Handles the logic for when the "Data specifications" button is clicked in the interface,
|
|
454
|
+
leading to the FirstWindow.
|
|
455
|
+
|
|
456
|
+
Notes
|
|
457
|
+
-----
|
|
458
|
+
This function displays an error message when a thread relative to the current window is running.
|
|
459
|
+
This function also save the id of the following window for later use.
|
|
460
|
+
"""
|
|
424
461
|
if self.thread['VideoReader'].isRunning() or self.thread['OneArena'].isRunning() or self.thread['ChangeOneRepResult'].isRunning() or self.parent().firstwindow.thread["RunAll"].isRunning():
|
|
425
462
|
self.message.setText("Wait for the analysis to end, or restart Cellects")
|
|
426
463
|
else:
|
|
@@ -428,6 +465,15 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
428
465
|
self.parent().change_widget(0) # FirstWidget
|
|
429
466
|
|
|
430
467
|
def image_tab_is_clicked(self):
|
|
468
|
+
"""
|
|
469
|
+
Handles the logic for when the "Image analysis" button is clicked in the interface,
|
|
470
|
+
leading to the image analysis window.
|
|
471
|
+
|
|
472
|
+
Notes
|
|
473
|
+
-----
|
|
474
|
+
This function displays an error message when a thread relative to the current window is running.
|
|
475
|
+
This function also save the id of the following window for later use.
|
|
476
|
+
"""
|
|
431
477
|
if self.image_tab.state != "not_usable":
|
|
432
478
|
if self.thread['VideoReader'].isRunning() or self.thread['OneArena'].isRunning() or self.thread[
|
|
433
479
|
'ChangeOneRepResult'].isRunning() or self.parent().firstwindow.thread["RunAll"].isRunning():
|
|
@@ -438,27 +484,59 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
438
484
|
|
|
439
485
|
|
|
440
486
|
def required_outputs_is_clicked(self):
|
|
487
|
+
"""
|
|
488
|
+
Sets the required outputs flag and changes the widget to the "Required Output" window.
|
|
489
|
+
"""
|
|
441
490
|
self.parent().last_is_first = False
|
|
442
491
|
self.parent().change_widget(4) # RequiredOutput
|
|
443
492
|
|
|
444
493
|
def advanced_parameters_is_clicked(self):
|
|
494
|
+
"""
|
|
495
|
+
Modifies the interface to display advanced parameters.
|
|
496
|
+
"""
|
|
445
497
|
self.parent().last_is_first = False
|
|
446
498
|
self.parent().widget(5).update_csc_editing_display()
|
|
447
499
|
self.parent().change_widget(5) # AdvancedParameters
|
|
448
500
|
|
|
449
501
|
def previous_is_clicked(self):
|
|
502
|
+
"""
|
|
503
|
+
Transition to the previous tab based on current tab history.
|
|
504
|
+
|
|
505
|
+
This method handles the logic for navigating back through the
|
|
506
|
+
application's tabs when "previous" is clicked. It updates the current
|
|
507
|
+
tab to the one that was last visited, cycling through the predefined
|
|
508
|
+
order of tabs.
|
|
509
|
+
|
|
510
|
+
Notes
|
|
511
|
+
-----
|
|
512
|
+
This function is part of a state-machine-like navigation system that
|
|
513
|
+
tracks tab history. It assumes the parent widget has methods `last_tab`
|
|
514
|
+
and `change_widget` for managing the current view.
|
|
515
|
+
"""
|
|
450
516
|
if self.parent().last_tab == "data_specifications":
|
|
451
517
|
self.parent().change_widget(0) # FirstWidget
|
|
452
518
|
elif self.parent().last_tab == "image_analysis":
|
|
453
519
|
self.parent().change_widget(2) # ThirdWidget
|
|
454
520
|
self.parent().last_tab = "video_analysis"
|
|
455
|
-
# self.parent().change_widget(2) # SecondWidget
|
|
456
521
|
|
|
457
522
|
def save_all_vars_thread(self):
|
|
523
|
+
"""
|
|
524
|
+
Start the 'SaveAllVars' thread if it is not already running.
|
|
525
|
+
|
|
526
|
+
This method is used to ensure that variable saving operations are performed
|
|
527
|
+
in a separate thread to avoid blocking the main application.
|
|
528
|
+
"""
|
|
458
529
|
if not self.parent().thread['SaveAllVars'].isRunning():
|
|
459
530
|
self.parent().thread['SaveAllVars'].start() # SaveAllVarsThreadInThirdWidget
|
|
460
531
|
|
|
461
532
|
def save_current_settings(self):
|
|
533
|
+
"""
|
|
534
|
+
Saves the current settings from UI components to persistent storage.
|
|
535
|
+
|
|
536
|
+
This method captures the values of various UI components and stores
|
|
537
|
+
them in a persistent data structure to ensure settings are saved across
|
|
538
|
+
sessions.
|
|
539
|
+
"""
|
|
462
540
|
self.parent().po.vars['maximal_growth_factor'] = self.maximal_growth_factor.value()
|
|
463
541
|
self.parent().po.vars['repeat_video_smoothing'] = int(np.round(self.repeat_video_smoothing.value()))
|
|
464
542
|
self.parent().po.vars['do_fading'] = self.do_fading.isChecked()
|
|
@@ -468,24 +546,40 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
468
546
|
self.save_all_vars_thread()
|
|
469
547
|
|
|
470
548
|
def repeat_video_smoothing_changed(self):
|
|
549
|
+
"""
|
|
550
|
+
Save the repeat_video_smoothing spinbox value to set how many times the pixel intensity dynamics will be
|
|
551
|
+
smoothed.
|
|
552
|
+
"""
|
|
471
553
|
self.parent().po.vars['repeat_video_smoothing'] = int(np.round(self.repeat_video_smoothing.value()))
|
|
472
|
-
# self.save_all_vars_is_clicked()
|
|
473
554
|
|
|
474
555
|
def do_fading_check(self):
|
|
556
|
+
"""
|
|
557
|
+
Save the fading checkbox value to allow cases where pixels can be left by the specimen(s).
|
|
558
|
+
"""
|
|
475
559
|
self.parent().po.vars['do_fading'] = self.do_fading.isChecked()
|
|
476
560
|
self.fading.setVisible(self.parent().po.vars['do_fading'])
|
|
477
561
|
|
|
478
562
|
def fading_changed(self):
|
|
563
|
+
"""
|
|
564
|
+
Save the fading spinbox value to modify how intensity must decrease to detect a pixel left by the specimen(s).
|
|
565
|
+
"""
|
|
479
566
|
self.parent().po.vars['fading'] = self.fading.value()
|
|
480
|
-
# self.save_all_vars_is_clicked()
|
|
481
567
|
|
|
482
568
|
def maximal_growth_factor_changed(self):
|
|
569
|
+
"""
|
|
570
|
+
Save the maximal_growth_factor spinbox value to modulate the maximal growth between two frames.
|
|
571
|
+
"""
|
|
483
572
|
self.parent().po.vars['maximal_growth_factor'] = self.maximal_growth_factor.value()
|
|
484
|
-
# self.save_all_vars_is_clicked()
|
|
485
573
|
|
|
486
574
|
def arena_changed(self):
|
|
487
575
|
"""
|
|
488
|
-
|
|
576
|
+
Resets the loaded arena when its video and processing threads are not running.
|
|
577
|
+
|
|
578
|
+
Notes
|
|
579
|
+
-----
|
|
580
|
+
This function is part of a larger class responsible for managing video and
|
|
581
|
+
arena processing threads. It should be called when all relevant threads are not
|
|
582
|
+
running to ensure the arena's state is properly reset.
|
|
489
583
|
"""
|
|
490
584
|
if not self.thread['VideoReader'].isRunning() and not self.thread['OneArena'].isRunning() and not self.thread['ChangeOneRepResult'].isRunning():
|
|
491
585
|
self.parent().po.motion = None
|
|
@@ -494,31 +588,64 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
494
588
|
self.parent().po.all['arena'] = int(np.round(self.arena.value()))
|
|
495
589
|
|
|
496
590
|
def load_one_arena_is_clicked(self):
|
|
591
|
+
"""
|
|
592
|
+
Load one arena if clicked.
|
|
593
|
+
|
|
594
|
+
Resets the general step, sets `load_quick_full` to 0, and runs the arena in a separate thread.
|
|
595
|
+
"""
|
|
497
596
|
self.reset_general_step()
|
|
498
|
-
# self.save_all_vars_is_clicked()
|
|
499
597
|
self.parent().po.load_quick_full = 0
|
|
500
598
|
self.run_one_arena_thread()
|
|
501
599
|
|
|
502
600
|
def compute_all_options_check(self):
|
|
601
|
+
"""
|
|
602
|
+
Save the compute_all_options checkbox value to process every video segmentation algorithms during the next run.
|
|
603
|
+
"""
|
|
503
604
|
self.parent().po.all['compute_all_options'] = self.compute_all_options_cb.isChecked()
|
|
504
605
|
|
|
505
606
|
def detection_is_clicked(self):
|
|
607
|
+
"""
|
|
608
|
+
Trigger detection when a button is clicked.
|
|
609
|
+
|
|
610
|
+
This method handles the logic when the user clicks the "detection" button.
|
|
611
|
+
It resets certain states, sets a flag for quick full processing,
|
|
612
|
+
and starts a thread to run the detection in one arena.
|
|
613
|
+
|
|
614
|
+
Notes
|
|
615
|
+
-----
|
|
616
|
+
This method is part of a larger state machine for handling user interactions.
|
|
617
|
+
It assumes that the parent object has a `po` attribute with a `load_quick_full`
|
|
618
|
+
flag and a method to run an arena thread.
|
|
619
|
+
"""
|
|
506
620
|
self.reset_general_step()
|
|
507
|
-
# self.save_all_vars_is_clicked()
|
|
508
621
|
self.parent().po.load_quick_full = 1
|
|
509
622
|
self.run_one_arena_thread()
|
|
510
623
|
|
|
511
624
|
def post_processing_is_clicked(self):
|
|
512
|
-
|
|
625
|
+
"""
|
|
626
|
+
Trigger post-processing when a button is clicked.
|
|
627
|
+
|
|
628
|
+
Extended Description
|
|
629
|
+
-------------------
|
|
630
|
+
This function updates the parent object's load_quick_full attribute,
|
|
631
|
+
logs a specific variable value, and runs an arena thread.
|
|
632
|
+
"""
|
|
513
633
|
self.parent().po.load_quick_full = 2
|
|
514
634
|
logging.info(self.parent().po.vars['maximal_growth_factor'])
|
|
515
635
|
self.run_one_arena_thread()
|
|
516
636
|
|
|
517
637
|
def run_one_arena_thread(self):
|
|
518
638
|
"""
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
639
|
+
Run the OneArena thread for processing.
|
|
640
|
+
|
|
641
|
+
Executes the OneArena thread to load video, initialize analysis,
|
|
642
|
+
stop any running instance of the thread, save settings, and connect
|
|
643
|
+
signals for displaying messages, images, and handling completion events.
|
|
644
|
+
|
|
645
|
+
Notes
|
|
646
|
+
-----
|
|
647
|
+
Ensures that the previous arena settings are cleared and connects signals
|
|
648
|
+
to display messages and images during thread execution.
|
|
522
649
|
"""
|
|
523
650
|
if self.thread['OneArena']._isRunning:
|
|
524
651
|
self.thread['OneArena'].stop()
|
|
@@ -526,20 +653,23 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
526
653
|
if self.previous_arena != self.parent().po.all['arena']:
|
|
527
654
|
self.parent().po.motion = None
|
|
528
655
|
self.message.setText("Load the video and initialize analysis, wait...")
|
|
529
|
-
# if not self.parent().po.first_exp_ready_to_run:
|
|
530
|
-
# self.parent().po.use_data_to_run_cellects_quickly = True
|
|
531
656
|
self.thread['OneArena'].start() # OneArenaThreadInThirdWidget
|
|
532
657
|
self.thread['OneArena'].message_from_thread_starting.connect(self.display_message_from_thread)
|
|
533
658
|
self.thread['OneArena'].when_loading_finished.connect(self.when_loading_thread_finished)
|
|
534
659
|
self.thread['OneArena'].when_detection_finished.connect(self.when_detection_finished)
|
|
535
660
|
self.thread['OneArena'].image_from_thread.connect(self.display_image_during_thread)
|
|
536
661
|
|
|
537
|
-
def when_loading_thread_finished(self, save_loaded_video):
|
|
662
|
+
def when_loading_thread_finished(self, save_loaded_video: bool):
|
|
663
|
+
"""
|
|
664
|
+
Ends the loading thread process and handles post-loading actions.
|
|
665
|
+
|
|
666
|
+
Notes
|
|
667
|
+
----------
|
|
668
|
+
This method assumes that the parent object has a `po` attribute with an
|
|
669
|
+
'arena' key and a `load_quick_full` attribute. It also assumes that the
|
|
670
|
+
parent object has a 'thread' dictionary and a message UI component.
|
|
671
|
+
"""
|
|
538
672
|
self.previous_arena = self.parent().po.all['arena']
|
|
539
|
-
# if not self.parent().po.vars['already_greyscale']:
|
|
540
|
-
# self.parent().po.motion.analysis_instance = self.parent().po.motion.visu.copy()
|
|
541
|
-
# else:
|
|
542
|
-
# self.parent().po.motion.analysis_instance = self.parent().po.motion.converted_video.copy()
|
|
543
673
|
if save_loaded_video:
|
|
544
674
|
self.thread['WriteVideo'] = WriteVideoThread(self.parent())
|
|
545
675
|
self.thread['WriteVideo'].start()
|
|
@@ -547,13 +677,32 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
547
677
|
self.message.setText("Loading done, you can watch the video")
|
|
548
678
|
self.read.setVisible(True)
|
|
549
679
|
|
|
550
|
-
def when_detection_finished(self, message):
|
|
551
|
-
|
|
680
|
+
def when_detection_finished(self, message: str):
|
|
681
|
+
"""
|
|
682
|
+
Handles the completion of video detection and updates the UI accordingly.
|
|
683
|
+
|
|
684
|
+
When the video detection is finished, this function waits for the
|
|
685
|
+
VideoReader thread to complete if it's running. It then processes the
|
|
686
|
+
last frame of the video based on the configured visualization and motion
|
|
687
|
+
detection settings. Finally, it updates the UI with the processed image
|
|
688
|
+
and sets appropriate labels' visibility.
|
|
689
|
+
|
|
690
|
+
Parameters
|
|
691
|
+
----------
|
|
692
|
+
message : str
|
|
693
|
+
The message to display upon completion of detection.
|
|
694
|
+
This could be a status update or any relevant information.
|
|
695
|
+
|
|
696
|
+
Notes
|
|
697
|
+
-----
|
|
698
|
+
This function assumes that the parent object has attributes `po` and
|
|
699
|
+
`image_to_display`, and methods like `display_image.update_image`.
|
|
700
|
+
"""
|
|
552
701
|
self.previous_arena = self.parent().po.all['arena']
|
|
553
702
|
if self.thread['VideoReader'].isRunning(): # VideoReaderThreadInThirdWidget
|
|
554
703
|
self.thread['VideoReader'].wait()
|
|
555
704
|
if self.parent().po.load_quick_full > 0:
|
|
556
|
-
image = self.parent().po.motion.
|
|
705
|
+
image = self.parent().po.motion.segmented[-1, ...]
|
|
557
706
|
if self.parent().po.motion.visu is None:
|
|
558
707
|
image = self.parent().po.motion.converted_video[-1, ...] * (1 - image)
|
|
559
708
|
image = np.round(image).astype(np.uint8)
|
|
@@ -563,20 +712,34 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
563
712
|
image = self.parent().po.motion.visu[-1, ...] * (1 - image)
|
|
564
713
|
self.parent().image_to_display = image
|
|
565
714
|
self.display_image.update_image(image)
|
|
566
|
-
|
|
567
715
|
self.message.setText(message)
|
|
568
|
-
|
|
569
716
|
self.select_option_label.setVisible(self.parent().po.vars["color_number"] == 2)
|
|
570
717
|
self.select_option.setVisible(self.parent().po.vars["color_number"] == 2)
|
|
571
718
|
self.read.setVisible(True)
|
|
572
|
-
# self.save_one_result.setVisible(True)
|
|
573
719
|
|
|
574
|
-
def display_image_during_thread(self, dictionary):
|
|
720
|
+
def display_image_during_thread(self, dictionary: dict):
|
|
721
|
+
"""
|
|
722
|
+
Display an image and set a message during a thread operation.
|
|
723
|
+
|
|
724
|
+
Parameters
|
|
725
|
+
----------
|
|
726
|
+
dictionary : dict
|
|
727
|
+
A dictionary containing the 'message' and 'current_image'.
|
|
728
|
+
The message is a string to display.
|
|
729
|
+
The current_image is the image data that will be displayed.
|
|
730
|
+
"""
|
|
575
731
|
self.message.setText(dictionary['message'])
|
|
576
732
|
self.parent().image_to_display = dictionary['current_image']
|
|
577
733
|
self.display_image.update_image(dictionary['current_image'])
|
|
578
734
|
|
|
579
735
|
def save_one_result_is_clicked(self):
|
|
736
|
+
"""
|
|
737
|
+
Finalize one arena analysis and save the result if conditions are met.
|
|
738
|
+
|
|
739
|
+
This function checks various conditions before starting a thread to
|
|
740
|
+
finalize the analysis and save the result. It ensures that certain
|
|
741
|
+
threads are not running before proceeding.
|
|
742
|
+
"""
|
|
580
743
|
if self.parent().po.motion is not None:
|
|
581
744
|
if self.parent().po.load_quick_full == 2:
|
|
582
745
|
if not self.thread['OneArena'].isRunning() and not self.thread['ChangeOneRepResult'].isRunning():
|
|
@@ -592,56 +755,71 @@ class VideoAnalysisWindow(MainTabsType):
|
|
|
592
755
|
self.message.setText("Run Post processing first")
|
|
593
756
|
|
|
594
757
|
def read_is_clicked(self):
|
|
758
|
+
"""
|
|
759
|
+
Read a video corresponding to a numbered arena (numbered using natural sorting) in the image
|
|
760
|
+
|
|
761
|
+
This function checks if the detection has been run and if the video reader or analysis thread is running.
|
|
762
|
+
If both threads are idle, it starts the video reading process. Otherwise, it updates the message accordingly.
|
|
763
|
+
"""
|
|
595
764
|
if self.parent().po.motion is not None:
|
|
596
|
-
if self.parent().po.motion.
|
|
765
|
+
if self.parent().po.motion.segmented is not None:
|
|
597
766
|
if not self.thread['OneArena'].isRunning() and not self.thread['VideoReader'].isRunning():
|
|
598
767
|
self.thread['VideoReader'].start() # VideoReaderThreadInThirdWidget
|
|
599
768
|
self.thread['VideoReader'].message_from_thread.connect(self.display_image_during_thread)
|
|
600
|
-
# if self.parent().po.computed_video_options[self.parent().po.all['video_option']]:
|
|
601
|
-
# self.message.setText("Run detection to visualize analysis")
|
|
602
769
|
else:
|
|
603
770
|
self.message.setText("Wait for the analysis to end")
|
|
604
771
|
else:
|
|
605
772
|
self.message.setText("Run detection first")
|
|
606
773
|
else:
|
|
607
774
|
self.message.setText("Run detection first")
|
|
608
|
-
#
|
|
609
|
-
# def video_display(self, dictionary):
|
|
610
|
-
# self.drawn_image = dictionary['image']
|
|
611
|
-
# self.display_image.update_image(dictionary['image'], self.parent().po.vars['contour_color'])
|
|
612
|
-
# self.message.setText(dictionary['message'])
|
|
613
|
-
# # self.message.setText(f"Reading done, try to change parameters if necessary")
|
|
614
775
|
|
|
615
776
|
def run_all_is_clicked(self):
|
|
777
|
+
"""
|
|
778
|
+
Handle the click event to start the complete analysis.
|
|
779
|
+
|
|
780
|
+
This function checks if any threads are running and starts the
|
|
781
|
+
'RunAll' thread if none of them are active. It also updates
|
|
782
|
+
various attributes and messages related to the analysis process.
|
|
783
|
+
|
|
784
|
+
Notes
|
|
785
|
+
-----
|
|
786
|
+
This function will only start the analysis if no other threads
|
|
787
|
+
are running. It updates several attributes of the parent object.
|
|
788
|
+
"""
|
|
616
789
|
if self.thread['OneArena'].isRunning() or self.thread['ChangeOneRepResult'].isRunning():
|
|
617
790
|
self.message.setText("Wait for the current analysis to end")
|
|
618
791
|
else:
|
|
619
792
|
if self.thread['VideoReader'].isRunning():
|
|
620
793
|
self.thread['VideoReader'].wait()
|
|
621
|
-
# self.save_all_vars_is_clicked()
|
|
622
|
-
# self.save_current_settings()
|
|
623
794
|
if self.parent().firstwindow.thread["RunAll"].isRunning():
|
|
624
795
|
self.message.setText('Analysis has already begun in the first window.')
|
|
625
796
|
else:
|
|
626
797
|
if not self.thread['RunAll'].isRunning():
|
|
627
|
-
# self.save_all_vars_is_clicked()
|
|
628
798
|
self.save_current_settings()
|
|
629
799
|
self.parent().po.motion = None
|
|
630
800
|
self.parent().po.converted_video = None
|
|
631
801
|
self.parent().po.converted_video2 = None
|
|
632
802
|
self.parent().po.visu = None
|
|
633
803
|
self.message.setText("Complete analysis has started, wait...")
|
|
634
|
-
# if not self.parent().po.first_exp_ready_to_run:
|
|
635
|
-
# self.parent().po.use_data_to_run_cellects_quickly = True
|
|
636
804
|
self.thread['RunAll'].start() # RunAllThread
|
|
637
805
|
self.thread['RunAll'].message_from_thread.connect(self.display_message_from_thread)
|
|
638
806
|
self.thread['RunAll'].image_from_thread.connect(self.display_image_during_thread)
|
|
639
|
-
# self.parent().imageanalysiswindow.true_init()
|
|
640
807
|
|
|
641
|
-
def display_message_from_thread(self, text_from_thread):
|
|
808
|
+
def display_message_from_thread(self, text_from_thread: str):
|
|
809
|
+
"""
|
|
810
|
+
Updates the message displayed in the UI with text from a thread.
|
|
811
|
+
|
|
812
|
+
Parameters
|
|
813
|
+
----------
|
|
814
|
+
text_from_thread : str
|
|
815
|
+
The text to be displayed in the UI message.
|
|
816
|
+
"""
|
|
642
817
|
self.message.setText(text_from_thread)
|
|
643
818
|
|
|
644
819
|
def closeEvent(self, event):
|
|
820
|
+
"""
|
|
821
|
+
Handle the close event for a QWidget.
|
|
822
|
+
"""
|
|
645
823
|
event.accept
|
|
646
824
|
|
|
647
825
|
|