supervisely 6.73.444__py3-none-any.whl → 6.73.468__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.
Potentially problematic release.
This version of supervisely might be problematic. Click here for more details.
- supervisely/__init__.py +24 -1
- supervisely/_utils.py +81 -0
- supervisely/annotation/json_geometries_map.py +2 -0
- supervisely/api/dataset_api.py +74 -12
- supervisely/api/entity_annotation/figure_api.py +8 -5
- supervisely/api/image_api.py +4 -0
- supervisely/api/video/video_annotation_api.py +4 -2
- supervisely/api/video/video_api.py +41 -1
- supervisely/app/__init__.py +1 -1
- supervisely/app/content.py +14 -6
- supervisely/app/fastapi/__init__.py +1 -0
- supervisely/app/fastapi/custom_static_files.py +1 -1
- supervisely/app/fastapi/multi_user.py +88 -0
- supervisely/app/fastapi/subapp.py +88 -42
- supervisely/app/fastapi/websocket.py +77 -9
- supervisely/app/singleton.py +21 -0
- supervisely/app/v1/app_service.py +18 -2
- supervisely/app/v1/constants.py +7 -1
- supervisely/app/widgets/card/card.py +20 -0
- supervisely/app/widgets/deploy_model/deploy_model.py +56 -35
- supervisely/app/widgets/dialog/dialog.py +12 -0
- supervisely/app/widgets/dialog/template.html +2 -1
- supervisely/app/widgets/experiment_selector/experiment_selector.py +8 -0
- supervisely/app/widgets/fast_table/fast_table.py +121 -31
- supervisely/app/widgets/fast_table/template.html +1 -1
- supervisely/app/widgets/radio_tabs/radio_tabs.py +18 -2
- supervisely/app/widgets/radio_tabs/template.html +1 -0
- supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +65 -7
- supervisely/app/widgets/table/table.py +68 -13
- supervisely/app/widgets/tree_select/tree_select.py +2 -0
- supervisely/convert/image/csv/csv_converter.py +24 -15
- supervisely/convert/video/video_converter.py +2 -2
- supervisely/geometry/polyline_3d.py +110 -0
- supervisely/io/env.py +76 -1
- supervisely/nn/inference/cache.py +37 -17
- supervisely/nn/inference/inference.py +667 -114
- supervisely/nn/inference/inference_request.py +15 -8
- supervisely/nn/inference/predict_app/gui/classes_selector.py +81 -12
- supervisely/nn/inference/predict_app/gui/gui.py +676 -488
- supervisely/nn/inference/predict_app/gui/input_selector.py +205 -26
- supervisely/nn/inference/predict_app/gui/model_selector.py +2 -4
- supervisely/nn/inference/predict_app/gui/output_selector.py +46 -6
- supervisely/nn/inference/predict_app/gui/settings_selector.py +756 -59
- supervisely/nn/inference/predict_app/gui/tags_selector.py +1 -1
- supervisely/nn/inference/predict_app/gui/utils.py +236 -119
- supervisely/nn/inference/predict_app/predict_app.py +2 -2
- supervisely/nn/inference/session.py +43 -35
- supervisely/nn/model/model_api.py +9 -0
- supervisely/nn/model/prediction_session.py +8 -7
- supervisely/nn/prediction_dto.py +7 -0
- supervisely/nn/tracker/base_tracker.py +11 -1
- supervisely/nn/tracker/botsort/botsort_config.yaml +0 -1
- supervisely/nn/tracker/botsort_tracker.py +14 -7
- supervisely/nn/tracker/visualize.py +70 -72
- supervisely/nn/training/gui/train_val_splits_selector.py +52 -31
- supervisely/nn/training/train_app.py +10 -5
- supervisely/project/project.py +9 -1
- supervisely/video/sampling.py +39 -20
- supervisely/video/video.py +41 -12
- supervisely/volume/stl_converter.py +2 -0
- supervisely/worker_api/agent_rpc.py +24 -1
- supervisely/worker_api/rpc_servicer.py +31 -7
- {supervisely-6.73.444.dist-info → supervisely-6.73.468.dist-info}/METADATA +14 -11
- {supervisely-6.73.444.dist-info → supervisely-6.73.468.dist-info}/RECORD +68 -66
- {supervisely-6.73.444.dist-info → supervisely-6.73.468.dist-info}/LICENSE +0 -0
- {supervisely-6.73.444.dist-info → supervisely-6.73.468.dist-info}/WHEEL +0 -0
- {supervisely-6.73.444.dist-info → supervisely-6.73.468.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.444.dist-info → supervisely-6.73.468.dist-info}/top_level.txt +0 -0
|
@@ -1,26 +1,33 @@
|
|
|
1
|
+
import threading
|
|
1
2
|
from typing import Any, Dict, List
|
|
2
3
|
|
|
4
|
+
from supervisely.api.api import Api
|
|
5
|
+
from supervisely.api.dataset_api import DatasetInfo
|
|
6
|
+
from supervisely.api.project_api import ProjectInfo
|
|
7
|
+
from supervisely.api.video.video_api import VideoInfo
|
|
3
8
|
from supervisely.app.widgets import (
|
|
4
9
|
Button,
|
|
5
10
|
Card,
|
|
6
11
|
Container,
|
|
12
|
+
FastTable,
|
|
7
13
|
OneOf,
|
|
8
14
|
RadioGroup,
|
|
9
|
-
RadioTable,
|
|
10
15
|
SelectDatasetTree,
|
|
11
16
|
Text,
|
|
12
17
|
)
|
|
18
|
+
from supervisely.app.widgets.widget import Widget
|
|
13
19
|
from supervisely.project.project import ProjectType
|
|
14
20
|
|
|
15
21
|
|
|
16
22
|
class InputSelector:
|
|
17
|
-
title = "
|
|
23
|
+
title = "Input data"
|
|
18
24
|
description = "Select input data on which to run model for prediction"
|
|
19
25
|
lock_message = None
|
|
20
26
|
|
|
21
|
-
def __init__(self, workspace_id: int):
|
|
27
|
+
def __init__(self, workspace_id: int, api: Api):
|
|
22
28
|
# Init Step
|
|
23
29
|
self.workspace_id = workspace_id
|
|
30
|
+
self.api = api
|
|
24
31
|
self.display_widgets: List[Any] = []
|
|
25
32
|
# -------------------------------- #
|
|
26
33
|
|
|
@@ -48,7 +55,7 @@ class InputSelector:
|
|
|
48
55
|
self.select_dataset_for_images = SelectDatasetTree(
|
|
49
56
|
multiselect=True,
|
|
50
57
|
flat=True,
|
|
51
|
-
select_all_datasets=
|
|
58
|
+
select_all_datasets=True,
|
|
52
59
|
allowed_project_types=[ProjectType.IMAGES],
|
|
53
60
|
always_open=False,
|
|
54
61
|
compact=False,
|
|
@@ -64,16 +71,31 @@ class InputSelector:
|
|
|
64
71
|
|
|
65
72
|
# Videos
|
|
66
73
|
self.select_dataset_for_video = SelectDatasetTree(
|
|
74
|
+
multiselect=True,
|
|
67
75
|
flat=True,
|
|
68
|
-
select_all_datasets=
|
|
76
|
+
select_all_datasets=True,
|
|
69
77
|
allowed_project_types=[ProjectType.VIDEOS],
|
|
70
78
|
always_open=False,
|
|
71
79
|
compact=False,
|
|
72
80
|
team_is_selectable=False,
|
|
73
81
|
workspace_is_selectable=False,
|
|
74
|
-
show_select_all_datasets_checkbox=
|
|
82
|
+
show_select_all_datasets_checkbox=True,
|
|
75
83
|
)
|
|
76
|
-
self.
|
|
84
|
+
self._video_table_columns = [
|
|
85
|
+
"Video id",
|
|
86
|
+
"Video name",
|
|
87
|
+
"Size",
|
|
88
|
+
"Duration",
|
|
89
|
+
"FPS",
|
|
90
|
+
"Frames count",
|
|
91
|
+
"Dataset name",
|
|
92
|
+
"Dataset id",
|
|
93
|
+
]
|
|
94
|
+
self.select_video = FastTable(
|
|
95
|
+
columns=self._video_table_columns,
|
|
96
|
+
is_selectable=True,
|
|
97
|
+
)
|
|
98
|
+
self.select_video.hide()
|
|
77
99
|
self.select_video_container = Container(
|
|
78
100
|
widgets=[self.select_dataset_for_video, self.select_video]
|
|
79
101
|
)
|
|
@@ -83,9 +105,9 @@ class InputSelector:
|
|
|
83
105
|
# -------------------------------- #
|
|
84
106
|
|
|
85
107
|
# Data type Radio Selector
|
|
86
|
-
|
|
87
|
-
self.radio = RadioGroup(items=[self._radio_item_images])
|
|
88
|
-
self.radio.hide()
|
|
108
|
+
self.radio = RadioGroup(items=[self._radio_item_images, self._radio_item_videos])
|
|
109
|
+
# self.radio = RadioGroup(items=[self._radio_item_images])
|
|
110
|
+
# self.radio.hide()
|
|
89
111
|
self.one_of = OneOf(conditional_widget=self.radio)
|
|
90
112
|
# Add widgets to display ------------ #
|
|
91
113
|
self.display_widgets.extend([self.radio, self.one_of])
|
|
@@ -109,12 +131,148 @@ class InputSelector:
|
|
|
109
131
|
)
|
|
110
132
|
# ----------------------------------- #
|
|
111
133
|
|
|
134
|
+
self._refresh_table_lock = threading.Lock()
|
|
135
|
+
self._refresh_table_thread: threading.Thread = None
|
|
136
|
+
self._refresh_called = False
|
|
137
|
+
|
|
138
|
+
@self.radio.value_changed
|
|
139
|
+
def input_selector_type_changed(value: str):
|
|
140
|
+
self.validator_text.hide()
|
|
141
|
+
|
|
142
|
+
@self.select_dataset_for_images.project_changed
|
|
143
|
+
def _images_project_changed(project_id):
|
|
144
|
+
self.validator_text.hide()
|
|
145
|
+
|
|
146
|
+
@self.select_dataset_for_images.value_changed
|
|
147
|
+
def _images_dataset_changed(dataset_ids):
|
|
148
|
+
self.validator_text.hide()
|
|
149
|
+
|
|
150
|
+
@self.select_dataset_for_video.project_changed
|
|
151
|
+
def _videos_project_changed(project_id: int):
|
|
152
|
+
self._refresh_video_table_called()
|
|
153
|
+
|
|
154
|
+
@self.select_dataset_for_video.value_changed
|
|
155
|
+
def _videos_dataset_changed(datasets_ids):
|
|
156
|
+
self._refresh_video_table_called()
|
|
157
|
+
|
|
158
|
+
def _refresh_video_table_called(self):
|
|
159
|
+
with self._refresh_table_lock:
|
|
160
|
+
self._refresh_called = True
|
|
161
|
+
if self._refresh_table_thread is None or not self._refresh_table_thread.is_alive():
|
|
162
|
+
self._refresh_table_thread = threading.Thread(target=self._refresh_video_table_loop)
|
|
163
|
+
if self._refresh_table_thread is not None and not self._refresh_table_thread.is_alive():
|
|
164
|
+
self._refresh_table_thread.start()
|
|
165
|
+
|
|
166
|
+
def _refresh_video_table_loop(self):
|
|
167
|
+
while self._refresh_called:
|
|
168
|
+
with self._refresh_table_lock:
|
|
169
|
+
self._refresh_called = False
|
|
170
|
+
self.select_video.loading = True
|
|
171
|
+
self._refresh_video_table()
|
|
172
|
+
if not self._refresh_called:
|
|
173
|
+
self.select_video.loading = False
|
|
174
|
+
|
|
175
|
+
def _refresh_video_table(self):
|
|
176
|
+
self.validator_text.hide()
|
|
177
|
+
self.select_video.clear()
|
|
178
|
+
selected_datasets = self.select_dataset_for_video.get_selected_ids()
|
|
179
|
+
if not selected_datasets:
|
|
180
|
+
self.select_video.hide()
|
|
181
|
+
else:
|
|
182
|
+
rows = []
|
|
183
|
+
self.select_video.show()
|
|
184
|
+
for dataset_id in selected_datasets:
|
|
185
|
+
dataset_info = self.api.dataset.get_info_by_id(dataset_id)
|
|
186
|
+
videos = self.api.video.get_list(dataset_id)
|
|
187
|
+
for video in videos:
|
|
188
|
+
size = f"{video.frame_height}x{video.frame_width}"
|
|
189
|
+
try:
|
|
190
|
+
frame_rate = int(video.frames_count / video.duration)
|
|
191
|
+
except:
|
|
192
|
+
frame_rate = "N/A"
|
|
193
|
+
rows.append(
|
|
194
|
+
[
|
|
195
|
+
video.id,
|
|
196
|
+
video.name,
|
|
197
|
+
size,
|
|
198
|
+
video.duration,
|
|
199
|
+
frame_rate,
|
|
200
|
+
video.frames_count,
|
|
201
|
+
dataset_info.name,
|
|
202
|
+
dataset_info.id,
|
|
203
|
+
]
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
self.select_video.add_rows(rows)
|
|
207
|
+
|
|
208
|
+
def select_project(self, project_id: int, project_info: ProjectInfo = None):
|
|
209
|
+
if project_info is None:
|
|
210
|
+
project_info = self.api.project.get_info_by_id(project_id)
|
|
211
|
+
if project_info.type == ProjectType.IMAGES.value:
|
|
212
|
+
self.select_dataset_for_images.set_project_id(project_id)
|
|
213
|
+
self.select_dataset_for_images.select_all()
|
|
214
|
+
self.radio.set_value(ProjectType.IMAGES.value)
|
|
215
|
+
elif project_info.type == ProjectType.VIDEOS.value:
|
|
216
|
+
self.select_dataset_for_video.set_project_id(project_id)
|
|
217
|
+
self.select_dataset_for_video.select_all()
|
|
218
|
+
self._refresh_video_table()
|
|
219
|
+
self.select_video.select_rows(list(range(len(self.select_video._rows_total))))
|
|
220
|
+
self.radio.set_value(ProjectType.VIDEOS.value)
|
|
221
|
+
else:
|
|
222
|
+
raise ValueError(f"Project of type {project_info.type} is not supported.")
|
|
223
|
+
|
|
224
|
+
def select_datasets(self, dataset_ids: List[int], dataset_infos: List[DatasetInfo] = None):
|
|
225
|
+
if dataset_infos is None:
|
|
226
|
+
dataset_infos = [self.api.dataset.get_info_by_id(ds_id) for ds_id in dataset_ids]
|
|
227
|
+
project_ids = set(ds.project_id for ds in dataset_infos)
|
|
228
|
+
if len(project_ids) > 1:
|
|
229
|
+
raise ValueError("Cannot select datasets from different projects")
|
|
230
|
+
project_id = project_ids.pop()
|
|
231
|
+
project_info = self.api.project.get_info_by_id(project_id)
|
|
232
|
+
if project_info.type == ProjectType.IMAGES.value:
|
|
233
|
+
self.select_dataset_for_images.set_project_id(project_id)
|
|
234
|
+
self.select_dataset_for_images.set_dataset_ids(dataset_ids)
|
|
235
|
+
self.radio.set_value(ProjectType.IMAGES.value)
|
|
236
|
+
elif project_info.type == ProjectType.VIDEOS.value:
|
|
237
|
+
self.select_dataset_for_video.set_project_id(project_id)
|
|
238
|
+
self.select_dataset_for_video.set_dataset_ids(dataset_ids)
|
|
239
|
+
self._refresh_video_table()
|
|
240
|
+
self.select_video.select_rows(list(range(self.select_video._rows_total)))
|
|
241
|
+
self.radio.set_value(ProjectType.VIDEOS.value)
|
|
242
|
+
else:
|
|
243
|
+
raise ValueError(f"Project of type {project_info.type} is not supported.")
|
|
244
|
+
|
|
245
|
+
def select_videos(self, video_ids: List[int], video_infos: List[VideoInfo] = None):
|
|
246
|
+
if video_infos is None:
|
|
247
|
+
video_infos = self.api.video.get_info_by_id_batch(video_ids)
|
|
248
|
+
project_id = video_infos[0].project_id
|
|
249
|
+
self.select_dataset_for_video.set_project_id(project_id)
|
|
250
|
+
self.select_dataset_for_video.select_all()
|
|
251
|
+
self._refresh_video_table()
|
|
252
|
+
self.select_video.select_row_by_value("id", video_ids)
|
|
253
|
+
self.radio.set_value(ProjectType.VIDEOS.value)
|
|
254
|
+
|
|
255
|
+
def disable(self):
|
|
256
|
+
for widget in self.widgets_to_disable:
|
|
257
|
+
widget.disable()
|
|
258
|
+
|
|
259
|
+
def enable(self):
|
|
260
|
+
for widget in self.widgets_to_disable:
|
|
261
|
+
widget.enable()
|
|
262
|
+
|
|
112
263
|
@property
|
|
113
|
-
def widgets_to_disable(self) ->
|
|
264
|
+
def widgets_to_disable(self) -> List[Widget]:
|
|
114
265
|
return [
|
|
266
|
+
# Images Selector
|
|
115
267
|
self.select_dataset_for_images,
|
|
268
|
+
self.select_dataset_for_images._select_project,
|
|
269
|
+
self.select_dataset_for_images._select_dataset,
|
|
270
|
+
# Videos Selector
|
|
116
271
|
self.select_dataset_for_video,
|
|
272
|
+
self.select_dataset_for_video._select_project,
|
|
273
|
+
self.select_dataset_for_video._select_dataset,
|
|
117
274
|
self.select_video,
|
|
275
|
+
# Controls
|
|
118
276
|
self.radio,
|
|
119
277
|
self.one_of,
|
|
120
278
|
]
|
|
@@ -126,39 +284,60 @@ class InputSelector:
|
|
|
126
284
|
"dataset_ids": self.select_dataset_for_images.get_selected_ids(),
|
|
127
285
|
}
|
|
128
286
|
if self.radio.get_value() == ProjectType.VIDEOS.value:
|
|
129
|
-
|
|
287
|
+
rows = self.select_video.get_selected_rows()
|
|
288
|
+
if rows:
|
|
289
|
+
video_ids = [row.row[0] for row in rows]
|
|
290
|
+
else:
|
|
291
|
+
video_ids = None
|
|
292
|
+
return {"video_ids": video_ids}
|
|
130
293
|
|
|
131
294
|
def load_from_json(self, data):
|
|
132
|
-
if "
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
295
|
+
if "video_ids" in data:
|
|
296
|
+
video_ids = data["video_ids"]
|
|
297
|
+
if not video_ids:
|
|
298
|
+
raise ValueError("Video ids cannot be empty")
|
|
299
|
+
video_infos = self.api.video.get_info_by_id_batch(video_ids)
|
|
300
|
+
if not video_infos:
|
|
301
|
+
raise ValueError(f"Videos with video ids {video_ids} are not found")
|
|
302
|
+
self.select_videos(video_ids, video_infos)
|
|
303
|
+
elif "dataset_ids" in data:
|
|
304
|
+
dataset_ids = data["dataset_ids"]
|
|
305
|
+
self.select_datasets(dataset_ids)
|
|
306
|
+
elif "project_id" in data:
|
|
307
|
+
project_id = data["project_id"]
|
|
308
|
+
self.select_project(project_id)
|
|
309
|
+
|
|
310
|
+
def get_project_id(self) -> int:
|
|
311
|
+
if self.radio.get_value() == ProjectType.IMAGES.value:
|
|
312
|
+
return self.select_dataset_for_images.project_id
|
|
313
|
+
if self.radio.get_value() == ProjectType.VIDEOS.value:
|
|
314
|
+
return self.select_dataset_for_video.project_id
|
|
315
|
+
return None
|
|
142
316
|
|
|
143
317
|
def validate_step(self) -> bool:
|
|
144
318
|
self.validator_text.hide()
|
|
145
319
|
if self.radio.get_value() == ProjectType.IMAGES.value:
|
|
146
|
-
|
|
320
|
+
selected_ids = self.select_dataset_for_images.get_selected_ids()
|
|
321
|
+
if selected_ids is None:
|
|
322
|
+
self.validator_text.set(text="Select a project", status="error")
|
|
323
|
+
self.validator_text.show()
|
|
324
|
+
return False
|
|
325
|
+
if len(selected_ids) == 0:
|
|
147
326
|
self.validator_text.set(text="Select at least one dataset", status="error")
|
|
148
327
|
self.validator_text.show()
|
|
149
328
|
return False
|
|
150
329
|
if self.radio.get_value() == ProjectType.VIDEOS.value:
|
|
151
|
-
if self.select_dataset_for_video.
|
|
330
|
+
if not self.select_dataset_for_video.get_selected_ids():
|
|
152
331
|
self.validator_text.set(text="Select a dataset", status="error")
|
|
153
332
|
self.validator_text.show()
|
|
154
333
|
return False
|
|
155
|
-
if
|
|
334
|
+
if self.select_video._rows_total == 0:
|
|
156
335
|
self.validator_text.set(
|
|
157
336
|
text="No videos found in the selected dataset", status="error"
|
|
158
337
|
)
|
|
159
338
|
self.validator_text.show()
|
|
160
339
|
return False
|
|
161
|
-
if self.select_video.
|
|
340
|
+
if self.select_video.get_selected_rows() == []:
|
|
162
341
|
self.validator_text.set(text="Select a video", status="error")
|
|
163
342
|
self.validator_text.show()
|
|
164
343
|
return False
|
|
@@ -5,7 +5,7 @@ from supervisely.app.widgets import Button, Card, Container, DeployModel, Text
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class ModelSelector:
|
|
8
|
-
title = "
|
|
8
|
+
title = "Model"
|
|
9
9
|
description = "Connect to deployed model or deploy new model"
|
|
10
10
|
lock_message = "Select previous step to unlock"
|
|
11
11
|
|
|
@@ -36,9 +36,8 @@ class ModelSelector:
|
|
|
36
36
|
# Base Widgets
|
|
37
37
|
self.validator_text = Text("")
|
|
38
38
|
self.validator_text.hide()
|
|
39
|
-
self.button = Button("Select")
|
|
40
39
|
# Add widgets to display ------------ #
|
|
41
|
-
self.display_widgets.extend([self.validator_text
|
|
40
|
+
self.display_widgets.extend([self.validator_text])
|
|
42
41
|
# ----------------------------------- #
|
|
43
42
|
|
|
44
43
|
# Card Layout
|
|
@@ -49,7 +48,6 @@ class ModelSelector:
|
|
|
49
48
|
content=self.container,
|
|
50
49
|
lock_message=self.lock_message,
|
|
51
50
|
)
|
|
52
|
-
self.card.lock()
|
|
53
51
|
# ----------------------------------- #
|
|
54
52
|
|
|
55
53
|
@property
|
|
@@ -9,14 +9,17 @@ from supervisely.app.widgets import (
|
|
|
9
9
|
Container,
|
|
10
10
|
Field,
|
|
11
11
|
Input,
|
|
12
|
+
OneOf,
|
|
12
13
|
Progress,
|
|
13
14
|
ProjectThumbnail,
|
|
15
|
+
RadioGroup,
|
|
14
16
|
Text,
|
|
15
17
|
)
|
|
18
|
+
from supervisely.project.project_meta import ProjectType
|
|
16
19
|
|
|
17
20
|
|
|
18
21
|
class OutputSelector:
|
|
19
|
-
title = "
|
|
22
|
+
title = "Result"
|
|
20
23
|
description = "Select the output mode"
|
|
21
24
|
lock_message = "Select previous step to unlock"
|
|
22
25
|
|
|
@@ -58,8 +61,18 @@ class OutputSelector:
|
|
|
58
61
|
title="New Project Name",
|
|
59
62
|
description="Name of the new project to create for the results. The created project will have the same dataset structure as the input project.",
|
|
60
63
|
)
|
|
64
|
+
self.skip_annotated_checkbox = Checkbox("Skip annotated items", False)
|
|
65
|
+
self._tab_names = ["Create New Project", "Update source project"]
|
|
66
|
+
self._tab_contents = [self.project_name_field, self.skip_annotated_checkbox]
|
|
67
|
+
self.tabs = RadioGroup(
|
|
68
|
+
items=[
|
|
69
|
+
RadioGroup.Item(tab_name, content=tab_content)
|
|
70
|
+
for tab_name, tab_content in zip(self._tab_names, self._tab_contents)
|
|
71
|
+
],
|
|
72
|
+
)
|
|
73
|
+
self.oneof = OneOf(self.tabs)
|
|
61
74
|
# Add widgets to display ------------ #
|
|
62
|
-
self.display_widgets.extend([self.
|
|
75
|
+
self.display_widgets.extend([self.tabs, self.oneof])
|
|
63
76
|
# ----------------------------------- #
|
|
64
77
|
|
|
65
78
|
# Base Widgets
|
|
@@ -74,8 +87,10 @@ class OutputSelector:
|
|
|
74
87
|
# Progress
|
|
75
88
|
self.progress = Progress(hide_on_finish=False)
|
|
76
89
|
self.progress.hide()
|
|
90
|
+
self.secondary_progress = Progress(hide_on_finish=False)
|
|
91
|
+
self.secondary_progress.hide()
|
|
77
92
|
# Add widgets to display ------------ #
|
|
78
|
-
self.display_widgets.extend([self.progress])
|
|
93
|
+
self.display_widgets.extend([self.progress, self.secondary_progress])
|
|
79
94
|
# ----------------------------------- #
|
|
80
95
|
|
|
81
96
|
# Result
|
|
@@ -93,9 +108,14 @@ class OutputSelector:
|
|
|
93
108
|
content=self.container,
|
|
94
109
|
lock_message=self.lock_message,
|
|
95
110
|
)
|
|
96
|
-
self.card.lock()
|
|
97
111
|
# ----------------------------------- #
|
|
98
112
|
|
|
113
|
+
def lock(self):
|
|
114
|
+
self.card.lock(self.lock_message)
|
|
115
|
+
|
|
116
|
+
def unlock(self):
|
|
117
|
+
self.card.unlock()
|
|
118
|
+
|
|
99
119
|
@property
|
|
100
120
|
def widgets_to_disable(self) -> list:
|
|
101
121
|
return [self.project_name_input]
|
|
@@ -111,7 +131,11 @@ class OutputSelector:
|
|
|
111
131
|
|
|
112
132
|
def get_settings(self) -> Dict[str, Any]:
|
|
113
133
|
settings = {}
|
|
114
|
-
|
|
134
|
+
if self.tabs.get_value() == self._tab_names[1]:
|
|
135
|
+
settings["upload_to_source_project"] = True
|
|
136
|
+
else:
|
|
137
|
+
settings["project_name"] = self.project_name_input.get_value()
|
|
138
|
+
settings["skip_annotated"] = self.skip_annotated_checkbox.is_checked()
|
|
115
139
|
return settings
|
|
116
140
|
|
|
117
141
|
def should_stop_serving_on_finish(self) -> bool:
|
|
@@ -128,12 +152,28 @@ class OutputSelector:
|
|
|
128
152
|
project_name = data.get("project_name", None)
|
|
129
153
|
if project_name:
|
|
130
154
|
self.project_name_input.set_value(project_name)
|
|
155
|
+
upload_to_source_project = data.get("upload_to_source_project", False)
|
|
156
|
+
if upload_to_source_project:
|
|
157
|
+
self.tabs.set_value(self._tab_names[1])
|
|
158
|
+
else:
|
|
159
|
+
self.tabs.set_value(self._tab_names[0])
|
|
131
160
|
|
|
132
161
|
def validate_step(self) -> bool:
|
|
133
162
|
self.validator_text.hide()
|
|
134
|
-
if
|
|
163
|
+
if (
|
|
164
|
+
self.tabs.get_value() == self._tab_names[0]
|
|
165
|
+
and self.project_name_input.get_value() == ""
|
|
166
|
+
):
|
|
135
167
|
self.validator_text.set(text="Project name is required", status="error")
|
|
136
168
|
self.validator_text.show()
|
|
137
169
|
return False
|
|
138
170
|
|
|
139
171
|
return True
|
|
172
|
+
|
|
173
|
+
def update_item_type(self, item_type: str):
|
|
174
|
+
if item_type == ProjectType.IMAGES.value:
|
|
175
|
+
self.skip_annotated_checkbox.show()
|
|
176
|
+
elif item_type == ProjectType.VIDEOS.value:
|
|
177
|
+
self.skip_annotated_checkbox.hide()
|
|
178
|
+
else:
|
|
179
|
+
raise ValueError(f"Unsupported item type: {item_type}")
|