supervisely 6.73.375__py3-none-any.whl → 6.73.377__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. supervisely/__init__.py +1 -0
  2. supervisely/_utils.py +11 -0
  3. supervisely/api/nn/neural_network_api.py +1 -1
  4. supervisely/app/fastapi/index.html +20 -0
  5. supervisely/app/widgets/__init__.py +1 -0
  6. supervisely/app/widgets/agent_selector/agent_selector.py +6 -0
  7. supervisely/app/widgets/agent_selector/template.html +2 -0
  8. supervisely/app/widgets/button/button.py +28 -1
  9. supervisely/app/widgets/button/template.html +1 -1
  10. supervisely/app/widgets/card/card.py +4 -0
  11. supervisely/app/widgets/card/template.html +1 -1
  12. supervisely/app/widgets/classes_table/classes_table.py +3 -1
  13. supervisely/app/widgets/fast_table/fast_table.py +16 -0
  14. supervisely/app/widgets/fast_table/script.js +6 -2
  15. supervisely/app/widgets/fast_table/template.html +1 -0
  16. supervisely/app/widgets/flexbox/flexbox.py +4 -2
  17. supervisely/app/widgets/flexbox/template.html +5 -9
  18. supervisely/app/widgets/input_number/input_number.py +2 -0
  19. supervisely/app/widgets/input_number/template.html +3 -0
  20. supervisely/app/widgets/random_splits_table/random_splits_table.py +2 -0
  21. supervisely/app/widgets/sampling/__init__.py +0 -0
  22. supervisely/app/widgets/sampling/sampling.py +550 -0
  23. supervisely/app/widgets/select_dataset/select_dataset.py +15 -5
  24. supervisely/app/widgets/sly_tqdm/sly_tqdm.py +9 -0
  25. supervisely/nn/training/train_app.py +10 -14
  26. supervisely/video/sampling.py +550 -0
  27. {supervisely-6.73.375.dist-info → supervisely-6.73.377.dist-info}/METADATA +1 -1
  28. {supervisely-6.73.375.dist-info → supervisely-6.73.377.dist-info}/RECORD +32 -29
  29. {supervisely-6.73.375.dist-info → supervisely-6.73.377.dist-info}/LICENSE +0 -0
  30. {supervisely-6.73.375.dist-info → supervisely-6.73.377.dist-info}/WHEEL +0 -0
  31. {supervisely-6.73.375.dist-info → supervisely-6.73.377.dist-info}/entry_points.txt +0 -0
  32. {supervisely-6.73.375.dist-info → supervisely-6.73.377.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,550 @@
1
+ from typing import List, Tuple, Union
2
+
3
+ from supervisely.api.api import Api
4
+ from supervisely.api.dataset_api import DatasetInfo
5
+ from supervisely.app.widgets import Progress
6
+ from supervisely.app.widgets.button.button import Button
7
+ from supervisely.app.widgets.checkbox.checkbox import Checkbox
8
+ from supervisely.app.widgets.container.container import Container
9
+ from supervisely.app.widgets.empty.empty import Empty
10
+ from supervisely.app.widgets.flexbox.flexbox import Flexbox
11
+ from supervisely.app.widgets.input_number.input_number import InputNumber
12
+ from supervisely.app.widgets.notification_box.notification_box import NotificationBox
13
+ from supervisely.app.widgets.one_of.one_of import OneOf
14
+ from supervisely.app.widgets.project_thumbnail.project_thumbnail import ProjectThumbnail
15
+ from supervisely.app.widgets.radio_group.radio_group import RadioGroup
16
+ from supervisely.app.widgets.select_dataset.select_dataset import SelectDataset
17
+ from supervisely.app.widgets.select_project.select_project import SelectProject
18
+ from supervisely.app.widgets.text.text import Text
19
+ from supervisely.app.widgets.widget import Widget
20
+ from supervisely.project.project import ProjectType
21
+ from supervisely.video.sampling import SamplingSettings, sample_video_project
22
+
23
+
24
+ class Sampling(Widget):
25
+ def __init__(
26
+ self,
27
+ project_id: int = None,
28
+ input_selectable: bool = True,
29
+ datasets_ids: List[int] = None,
30
+ output_project_id: int = None,
31
+ output_project_selectable: bool = True,
32
+ widgth: int = 370,
33
+ widget_id: str = None,
34
+ file_path: str = __file__,
35
+ ):
36
+ super().__init__(widget_id, file_path)
37
+ if not input_selectable and project_id is None:
38
+ raise ValueError(
39
+ "Either 'project_id' must be provided or 'project_selectable' must be True."
40
+ )
41
+ if project_id is None and not input_selectable:
42
+ input_selectable = True
43
+ if not output_project_selectable and output_project_id is None:
44
+ raise ValueError(
45
+ "Either 'output_project_id' must be provided or 'output_project_selectable' must be True."
46
+ )
47
+ if output_project_id is None and not output_project_selectable:
48
+ output_project_selectable = True
49
+ self._api = Api()
50
+ self.project_id = project_id
51
+ self.project_selectable = input_selectable
52
+ self.datasets_ids = datasets_ids
53
+ self.output_project_id = output_project_id
54
+ self.output_project_selectable = output_project_selectable
55
+ self.widgth = widgth
56
+ self.project_info = (
57
+ self._api.project.get_info_by_id(self.project_id) if self.project_id else None
58
+ )
59
+ self.all_datasets = (
60
+ self._api.dataset.get_list(self.project_id, recursive=True) if self.project_id else []
61
+ )
62
+ self.items_count = self._count_items(self.all_datasets, datasets_ids, with_children=True)
63
+ self._init_gui()
64
+
65
+ def _init_input_gui(self):
66
+ self.input_datasets_select = SelectDataset(
67
+ default_id=self.datasets_ids,
68
+ project_id=self.project_id,
69
+ allowed_project_types=[ProjectType.VIDEOS],
70
+ size="mini",
71
+ multiselect=True,
72
+ select_all_datasets=self.datasets_ids is None,
73
+ )
74
+ self.project_preview = ProjectThumbnail()
75
+ self.project_preview.hide()
76
+ if not self.project_selectable:
77
+ self.input_datasets_select.hide()
78
+ project_info = self._api.project.get_info_by_id(self.project_id)
79
+ self.project_preview.set(project_info)
80
+ self.project_preview.show()
81
+ self.nested_datasets_checkbox = Checkbox(
82
+ "Include nested datasets",
83
+ checked=True,
84
+ )
85
+ self.input_project_container = Container(
86
+ widgets=[
87
+ self.input_datasets_select,
88
+ self.nested_datasets_checkbox,
89
+ self.project_preview,
90
+ ],
91
+ style="padding-left: 21px; padding-top: 10px;",
92
+ )
93
+ self.input_field = Container(
94
+ widgets=[
95
+ Text(
96
+ '<i class="zmdi zmdi-collection-video" style="padding-right: 10px; color: rgb(0, 154, 255);"></i><b>Input project</b>'
97
+ ),
98
+ self.input_project_container,
99
+ ],
100
+ gap=0,
101
+ )
102
+
103
+ def _init_settings_gui(self):
104
+ self.only_annotated_checkbox = Checkbox(Text("Only annotated frames", font_size=13))
105
+ self.only_annotated_row = Flexbox(widgets=[self.only_annotated_checkbox])
106
+ self.step_label = Text("Step:", font_size=13)
107
+ self.step_input = InputNumber(value=1, min=1, step=1, size="mini", width=160)
108
+ self.step_row = Container(
109
+ widgets=[self.step_label, self.step_input],
110
+ direction="horizontal",
111
+ style="width: 202px; align-items: center;",
112
+ widgets_style="flex: 1; display: flex;",
113
+ )
114
+ self.resize_checkbox = Checkbox(Text("Resize frames", font_size=13))
115
+ self.resize_input_h_label = Text("Height:", font_size=13)
116
+ self.resize_input_h = InputNumber(value=224, min=1, step=1, size="mini", controls=False)
117
+ self.resize_input_w_label = Text("Width:", font_size=13)
118
+ self.resize_input_w = InputNumber(value=224, min=1, step=1, size="mini", controls=False)
119
+ self.resize_h_row = Flexbox(widgets=[self.resize_input_h_label, self.resize_input_h])
120
+ self.resize_w_row = Flexbox(
121
+ widgets=[self.resize_input_w_label, self.resize_input_w], gap=15
122
+ )
123
+ self.resize_row = Flexbox(widgets=[self.resize_checkbox])
124
+ self.resize_hw_container = Container(
125
+ widgets=[Empty(), self.resize_h_row, self.resize_w_row]
126
+ )
127
+ self.resize_hw_container.hide()
128
+ self.resize_container = Container(
129
+ widgets=[self.resize_row, self.resize_hw_container], gap=0
130
+ )
131
+ self.copy_annotations_checkbox = Checkbox(
132
+ Text("Copy annotations from source project", font_size=13)
133
+ )
134
+ self.copy_annotations_row = Flexbox(widgets=[self.copy_annotations_checkbox])
135
+
136
+ @self.resize_checkbox.value_changed
137
+ def on_resize_checkbox_change(checked: bool):
138
+ if checked:
139
+ self.resize_hw_container.show()
140
+ else:
141
+ self.resize_hw_container.hide()
142
+
143
+ self.settings_container = Container(
144
+ widgets=[
145
+ Empty(),
146
+ self.step_row,
147
+ self.only_annotated_row,
148
+ self.resize_container,
149
+ self.copy_annotations_row,
150
+ Empty(),
151
+ ],
152
+ style="padding-left: 21px; padding-top: 10px;",
153
+ )
154
+ self.settings_field = Container(
155
+ widgets=[
156
+ Text(
157
+ '<i class="zmdi zmdi-settings" style="padding-right: 10px; color: rgb(0, 154, 255);"></i><b>Sampling settings</b>'
158
+ ),
159
+ self.settings_container,
160
+ ],
161
+ gap=0,
162
+ )
163
+
164
+ def _datasets_to_process(
165
+ self, all_datasets: List[DatasetInfo], datasets_ids: List[int], with_children: bool
166
+ ) -> List[DatasetInfo]:
167
+ if datasets_ids is None:
168
+ return all_datasets.copy()
169
+ datasets = []
170
+ for ds in [ds for ds in all_datasets if ds.id in datasets_ids]:
171
+ datasets.append(ds)
172
+ if with_children:
173
+ children = [child for child in all_datasets if child.parent_id == ds.id]
174
+ if children:
175
+ datasets.extend(
176
+ self._datasets_to_process(
177
+ all_datasets, [child.id for child in children], True
178
+ )
179
+ )
180
+ return datasets
181
+
182
+ def _count_items(
183
+ self, all_datasets: List[DatasetInfo], datasets_ids: List[int], with_children: bool
184
+ ) -> int:
185
+ return sum(
186
+ ds.items_count
187
+ for ds in self._datasets_to_process(
188
+ all_datasets, datasets_ids, with_children=with_children
189
+ )
190
+ )
191
+
192
+ def _selected_text(self, datasets_ids: List[int] = None, with_children: bool = True) -> str:
193
+ red = "#FF6458;"
194
+ blue = "rgb(0, 154, 255);"
195
+ datasets = self._datasets_to_process(
196
+ self.all_datasets, datasets_ids, with_children=with_children
197
+ )
198
+ color = blue if self.items_count > 0 else red
199
+ ds_num = len(datasets)
200
+ return f'Selected <b style="color: {color};">{ds_num}</b> dataset{"s" if ds_num % 10 != 1 else ""} with <b style="color: {color};">{self.items_count}</b> videos'
201
+
202
+ def _update_preview(self):
203
+ with_children = self.nested_datasets_checkbox.is_checked()
204
+ self.selected_items_text.text = self._selected_text(
205
+ self.datasets_ids, with_children=with_children
206
+ )
207
+ if self.items_count > 0:
208
+ self.run_button.enable()
209
+ else:
210
+ self.run_button.disable()
211
+
212
+ def _datasets_changed(self, datasets_ids: List[int]):
213
+ self.preview_container.loading = True
214
+ self.datasets_ids = datasets_ids
215
+ project_id = self.input_datasets_select.get_selected_project_id()
216
+ with_children = self.nested_datasets_checkbox.is_checked()
217
+ if self.project_id != project_id:
218
+ if project_id is None:
219
+ self.project_id = None
220
+ self.project_info = None
221
+ self.all_datasets = []
222
+ else:
223
+ self.project_id = project_id
224
+ self._api.project.get_info_by_id(project_id)
225
+ self.all_datasets = self._api.dataset.get_list(project_id, recursive=True)
226
+ self.items_count = self._count_items(
227
+ self.all_datasets, datasets_ids, with_children=with_children
228
+ )
229
+ self._update_preview()
230
+ self.preview_container.loading = False
231
+
232
+ def _init_peview_gui(self):
233
+ self.selected_items_text = Text("", font_size=13)
234
+ self.run_button = Button("Run", icon="zmdi zmdi-play", button_size="mini")
235
+ self.run_button.disable()
236
+
237
+ self.preview_text = Text(
238
+ '<i class="zmdi zmdi-eye" style="padding-right: 10px; color: rgb(0, 154, 255);"></i><b style="font-size: 14px">Preview</b>',
239
+ font_size=13,
240
+ )
241
+ self.preview_field = Container(
242
+ widgets=[
243
+ self.preview_text,
244
+ Container(widgets=[self.selected_items_text], style="padding-left: 21px;"),
245
+ ],
246
+ style="padding-top: 10px;",
247
+ )
248
+ self.run_button_container = Container(
249
+ widgets=[self.run_button],
250
+ direction="horizontal",
251
+ overflow="wrap",
252
+ style="display: flex; justify-content: flex-end;",
253
+ widgets_style="display: flex; flex: none;",
254
+ )
255
+ self.preview_container = Container(
256
+ widgets=[self.preview_field, self.run_button_container],
257
+ )
258
+ self._update_preview()
259
+
260
+ @self.input_datasets_select.value_changed
261
+ def on_input_datasets_select_change(datasets_ids: List[int]):
262
+ self._datasets_changed(datasets_ids)
263
+
264
+ @self.nested_datasets_checkbox.value_changed
265
+ def on_nested_datasets_checkbox_change(is_checked: bool):
266
+ self._datasets_changed(self.input_datasets_select.get_selected_ids())
267
+
268
+ @self.run_button.click
269
+ def on_run_button_click():
270
+ self.run()
271
+
272
+ def _init_output_gui(self):
273
+ self.output_project_select = SelectProject(
274
+ default_id=self.output_project_id, allowed_types=[ProjectType.IMAGES], size="mini"
275
+ )
276
+ self.output_project_preview = ProjectThumbnail()
277
+ self.output_project_preview.hide()
278
+ self.output_mode_radio = RadioGroup(
279
+ items=[
280
+ RadioGroup.Item("create", "Create new project", content=Empty()),
281
+ RadioGroup.Item(
282
+ "merge",
283
+ "Merge with existing project",
284
+ content=self.output_project_select,
285
+ ),
286
+ ]
287
+ )
288
+ self.output_merge_project_oneof = OneOf(self.output_mode_radio)
289
+ self.output_project_container = Container(
290
+ widgets=[self.output_mode_radio, self.output_merge_project_oneof],
291
+ )
292
+
293
+ self.output_container = Container(
294
+ widgets=[self.output_project_preview, self.output_project_container],
295
+ gap=0,
296
+ style="padding-left: 21px; padding-top: 10px;",
297
+ )
298
+ if not self.output_project_selectable:
299
+ self.output_project_container.hide()
300
+ project_info = self._api.project.get_info_by_id(self.output_project_id)
301
+ self.output_project_preview.set(project_info)
302
+ self.output_project_preview.show()
303
+ if self.output_project_id is not None:
304
+ self.output_mode_radio.set_value("merge")
305
+
306
+ self.output_field = Container(
307
+ widgets=[
308
+ Text(
309
+ '<i class="zmdi zmdi-collection-folder-image" style="padding-right: 10px; color: rgb(0, 154, 255);"></i><b>Output project</b>'
310
+ ),
311
+ self.output_container,
312
+ ],
313
+ gap=0,
314
+ )
315
+
316
+ def _init_progress_gui(self):
317
+ self.items_progress = Progress(hide_on_finish=False)
318
+ self.frames_progress = Progress(hide_on_finish=False)
319
+ self.error_notification = NotificationBox(title="Error", description="", box_type="error")
320
+ self.error_notification.hide()
321
+ self.progress_container = Container(widgets=[self.items_progress, self.frames_progress])
322
+ self.progress_container.hide()
323
+ self.result_project_preview = ProjectThumbnail()
324
+ self.result_project_preview_field = Container(
325
+ widgets=[
326
+ Text(
327
+ '<i class="zmdi zmdi-check-square" style="padding-right: 10px; color: rgb(0, 154, 255);"></i><b>Result project</b>'
328
+ ),
329
+ Container(
330
+ widgets=[self.result_project_preview],
331
+ style="padding-left: 21px;",
332
+ ),
333
+ ]
334
+ )
335
+ self.result_project_preview_field.hide()
336
+ self.result_container = Container(
337
+ widgets=[
338
+ self.progress_container,
339
+ self.error_notification,
340
+ self.result_project_preview_field,
341
+ ],
342
+ gap=0,
343
+ )
344
+
345
+ def _init_gui(self):
346
+ self._init_input_gui()
347
+ self._init_settings_gui()
348
+ self._init_output_gui()
349
+ self._init_peview_gui()
350
+ self._init_progress_gui()
351
+
352
+ self.content = Container(
353
+ widgets=[
354
+ self.input_field,
355
+ self.settings_field,
356
+ self.output_field,
357
+ self.preview_container,
358
+ self.result_container,
359
+ ],
360
+ style=f"width: {self.widgth}px;",
361
+ )
362
+
363
+ def _get_settings(self) -> dict:
364
+ settings = {
365
+ SamplingSettings.ONLY_ANNOTATED: self.only_annotated_checkbox.is_checked(),
366
+ SamplingSettings.STEP: self.step_input.get_value(),
367
+ SamplingSettings.RESIZE: None,
368
+ SamplingSettings.COPY_ANNOTATIONS: self.copy_annotations_checkbox.is_checked(),
369
+ }
370
+ if self.resize_checkbox.is_checked():
371
+ settings[SamplingSettings.RESIZE] = [
372
+ self.resize_input_h.get_value(),
373
+ self.resize_input_w.get_value(),
374
+ ]
375
+
376
+ return settings
377
+
378
+ def _get_dst_project_id(self) -> int:
379
+ if self.output_mode_radio.get_value() == "create":
380
+ return None
381
+ else:
382
+ return self.output_project_select.get_selected_id()
383
+
384
+ def run(self):
385
+ self.progress_container.show()
386
+ self.error_notification.hide()
387
+ self.result_project_preview_field.hide()
388
+ try:
389
+ project_id = self.input_datasets_select.get_selected_project_id()
390
+ datasets_ids = self.input_datasets_select.get_selected_ids()
391
+ with_children = self.nested_datasets_checkbox.is_checked()
392
+ selected_datasets_with_children = self._datasets_to_process(
393
+ all_datasets=self.all_datasets,
394
+ datasets_ids=datasets_ids,
395
+ with_children=with_children,
396
+ )
397
+ total_items = sum(ds.items_count for ds in selected_datasets_with_children)
398
+ datasets_ids = [ds.id for ds in selected_datasets_with_children]
399
+ project_info = self.project_info
400
+
401
+ if project_info.type != str(ProjectType.VIDEOS):
402
+ raise ValueError(
403
+ f"Project with ID {self.input_datasets_select.get_selected_id()} is not a video project."
404
+ )
405
+ frames_pbar = self.frames_progress()
406
+ with self.items_progress(
407
+ message=f"Videos progress...",
408
+ total=total_items,
409
+ ) as pbar:
410
+ self.progress_container.show()
411
+ dst_project_info = sample_video_project(
412
+ api=self._api,
413
+ project_id=project_id,
414
+ settings=self._get_settings(),
415
+ dst_project_id=self._get_dst_project_id(),
416
+ datasets_ids=datasets_ids,
417
+ items_progress_cb=pbar.update,
418
+ video_progress=frames_pbar,
419
+ )
420
+ except Exception as e:
421
+ self.error_notification.set(title="Error", description=str(e))
422
+ self.error_notification.show()
423
+ raise e
424
+ else:
425
+ if self.output_project_selectable:
426
+ dst_project_info = self._api.project.get_info_by_id(dst_project_info.id)
427
+ self.result_project_preview.set(dst_project_info)
428
+ self.result_project_preview_field.show()
429
+ else:
430
+ dst_project_info = self._api.project.get_info_by_id(self.output_project_id)
431
+ self.output_project_preview.set(dst_project_info)
432
+ finally:
433
+ self.progress_container.hide()
434
+
435
+ @property
436
+ def selected_project_id(self) -> int:
437
+ if not self.project_selectable:
438
+ return self.project_id
439
+ return self.input_datasets_select.get_selected_project_id()
440
+
441
+ @selected_project_id.setter
442
+ def selected_project_id(self, value: int):
443
+ if not self.project_selectable:
444
+ raise ValueError("Project is not selectable.")
445
+ self.input_datasets_select.set_project_id(value)
446
+ self._datasets_changed(self.input_datasets_select.get_selected_ids())
447
+
448
+ @property
449
+ def selected_all_datasets(self) -> bool:
450
+ return self.input_datasets_select._all_datasets_checkbox.is_checked()
451
+
452
+ @selected_all_datasets.setter
453
+ def selected_all_datasets(self, value: bool):
454
+ if value:
455
+ self.input_datasets_select._all_datasets_checkbox.check()
456
+ else:
457
+ self.input_datasets_select._all_datasets_checkbox.uncheck()
458
+ self._datasets_changed(self.input_datasets_select.get_selected_ids())
459
+
460
+ @property
461
+ def selected_datasets_ids(self) -> List[int]:
462
+ return self.input_datasets_select.get_selected_ids()
463
+
464
+ @selected_datasets_ids.setter
465
+ def selected_datasets_ids(self, value: List[int]):
466
+ self.input_datasets_select.set_dataset_ids(value)
467
+
468
+ @property
469
+ def include_nested_datasets(self) -> bool:
470
+ return self.nested_datasets_checkbox.is_checked()
471
+
472
+ @include_nested_datasets.setter
473
+ def include_nested_datasets(self, value: bool):
474
+ if value:
475
+ self.nested_datasets_checkbox.check()
476
+ else:
477
+ self.nested_datasets_checkbox.uncheck()
478
+ self._datasets_changed(self.input_datasets_select.get_selected_ids())
479
+
480
+ @property
481
+ def step(self) -> int:
482
+ return self.step_input.get_value()
483
+
484
+ @step.setter
485
+ def step(self, value: int):
486
+ self.step_input.value = value
487
+
488
+ @property
489
+ def only_annotated(self) -> bool:
490
+ return self.only_annotated_checkbox.is_checked()
491
+
492
+ @only_annotated.setter
493
+ def only_annotated(self, value: bool):
494
+ if value:
495
+ self.only_annotated_checkbox.check()
496
+ else:
497
+ self.only_annotated_checkbox.uncheck()
498
+
499
+ @property
500
+ def resize(self) -> Union[Tuple[int, int], None]:
501
+ if self.resize_checkbox.is_checked():
502
+ return (self.resize_input_h.get_value(), self.resize_input_w.get_value())
503
+ return None
504
+
505
+ @resize.setter
506
+ def resize(self, value: Union[Tuple[int, int], None]):
507
+ if value is None:
508
+ self.resize_checkbox.uncheck()
509
+ else:
510
+ self.resize_checkbox.check()
511
+ self.resize_input_h.value = value[0]
512
+ self.resize_input_w.value = value[1]
513
+
514
+ @property
515
+ def copy_annotations(self) -> bool:
516
+ return self.copy_annotations_checkbox.is_checked()
517
+
518
+ @copy_annotations.setter
519
+ def copy_annotations(self, value: bool):
520
+ if value:
521
+ self.copy_annotations_checkbox.check()
522
+ else:
523
+ self.copy_annotations_checkbox.uncheck()
524
+
525
+ @property
526
+ def selected_output_project_id(self) -> int:
527
+ if not self.output_project_selectable:
528
+ return self.output_project_id
529
+ if self.output_mode_radio.get_value() == "create":
530
+ return None
531
+ return self.output_project_select.get_selected_id()
532
+
533
+ @selected_output_project_id.setter
534
+ def selected_output_project_id(self, value: int):
535
+ if not self.output_project_selectable:
536
+ raise ValueError("Output project is not selectable.")
537
+ self.output_project_select.set_project_id(value)
538
+ if value is not None:
539
+ self.output_mode_radio.set_value("merge")
540
+ else:
541
+ self.output_mode_radio.set_value("create")
542
+
543
+ def get_json_data(self) -> dict:
544
+ return {}
545
+
546
+ def get_json_state(self) -> dict:
547
+ return {}
548
+
549
+ def to_html(self):
550
+ return self.content.to_html()
@@ -138,11 +138,18 @@ class SelectDataset(Widget):
138
138
 
139
139
  def set_project_id(self, id: int):
140
140
  self._project_id = id
141
- if self._compact is True:
142
- DataJson()[self.widget_id]["projectId"] = self._project_id
143
- DataJson().send_changes()
141
+ self._project_selector.set_project_id(self._project_id)
142
+
143
+ def set_select_all_datasets(self, is_checked: bool):
144
+ if self._multiselect is False:
145
+ raise ValueError(
146
+ "Multiselect is disabled. Use another method 'set_dataset_id' instead of 'set_select_all_datasets'"
147
+ )
148
+ if is_checked:
149
+ self._all_datasets_checkbox.check()
144
150
  else:
145
- StateJson()[self.widget_id]["projectId"] = self._project_id
151
+ self._all_datasets_checkbox.uncheck()
152
+ StateJson()[self.widget_id]["datasets"] = []
146
153
  StateJson().send_changes()
147
154
 
148
155
  def set_dataset_id(self, id: int):
@@ -202,6 +209,7 @@ class SelectDataset(Widget):
202
209
  _process()
203
210
 
204
211
  if self._compact is False:
212
+
205
213
  @self._project_selector.value_changed
206
214
  def _update_datasets(project_id):
207
215
  if self._multiselect is True:
@@ -217,7 +225,7 @@ class SelectDataset(Widget):
217
225
  self._cb_called = True
218
226
  _process()
219
227
 
220
- @server.post(route_path)
228
+ @server.post(route_path)
221
229
  def _click():
222
230
  if self._cb_called is False or self._multiselect is False:
223
231
  _process()
@@ -256,4 +264,6 @@ class SelectDataset(Widget):
256
264
  info = self._api.dataset.get_info_by_id(self._default_id[0], raise_error=True)
257
265
  elif isinstance(self._default_id, int):
258
266
  info = self._api.dataset.get_info_by_id(self._default_id, raise_error=True)
267
+ else:
268
+ return
259
269
  self._project_id = info.project_id
@@ -171,6 +171,15 @@ class CustomTqdm(tqdm):
171
171
  **kwargs,
172
172
  )
173
173
 
174
+ def __setattr__(self, name, value):
175
+ if name == "total":
176
+ if hasattr(self, "fp"):
177
+ self.fp.total = value
178
+ if name == "message":
179
+ if hasattr(self, "fp"):
180
+ self.fp.progress["message"] = value
181
+ return super().__setattr__(name, value)
182
+
174
183
  def refresh(self, *args, **kwargs):
175
184
  if self.fp is not None:
176
185
  self.fp._n = self.n
@@ -44,7 +44,7 @@ from supervisely import (
44
44
  )
45
45
  from supervisely._utils import abs_url, get_filename_from_headers
46
46
  from supervisely.api.file_api import FileInfo
47
- from supervisely.app import get_synced_data_dir
47
+ from supervisely.app import get_synced_data_dir, show_dialog
48
48
  from supervisely.app.widgets import Progress
49
49
  from supervisely.nn.benchmark import (
50
50
  InstanceSegmentationBenchmark,
@@ -2602,9 +2602,8 @@ class TrainApp:
2602
2602
  except Exception as e:
2603
2603
  message = f"Error occurred during training initialization. {check_logs_text}"
2604
2604
  self._show_error(message, e)
2605
- self._restore_train_widgets_state_on_error()
2606
2605
  self._set_ws_progress_status("reset")
2607
- return
2606
+ raise e
2608
2607
 
2609
2608
  try:
2610
2609
  self._set_text_status("preparing")
@@ -2613,9 +2612,8 @@ class TrainApp:
2613
2612
  except Exception as e:
2614
2613
  message = f"Error occurred during data preparation. {check_logs_text}"
2615
2614
  self._show_error(message, e)
2616
- self._restore_train_widgets_state_on_error()
2617
2615
  self._set_ws_progress_status("reset")
2618
- return
2616
+ raise e
2619
2617
 
2620
2618
  try:
2621
2619
  self._set_text_status("training")
@@ -2629,15 +2627,13 @@ class TrainApp:
2629
2627
  "Please check input data and hyperparameters."
2630
2628
  )
2631
2629
  self._show_error(message, e)
2632
- self._restore_train_widgets_state_on_error()
2633
2630
  self._set_ws_progress_status("reset")
2634
2631
  return
2635
2632
  except Exception as e:
2636
2633
  message = f"Error occurred during training. {check_logs_text}"
2637
2634
  self._show_error(message, e)
2638
- self._restore_train_widgets_state_on_error()
2639
2635
  self._set_ws_progress_status("reset")
2640
- return
2636
+ raise e
2641
2637
 
2642
2638
  try:
2643
2639
  self._set_text_status("finalizing")
@@ -2645,18 +2641,17 @@ class TrainApp:
2645
2641
  self._finalize(experiment_info)
2646
2642
  self.gui.training_process.start_button.loading = False
2647
2643
 
2648
- # Shutdown the app after training is finished
2644
+ if is_production() and self.gui.training_logs.tensorboard_offline_button is not None:
2645
+ self.gui.training_logs.tensorboard_button.hide()
2646
+ self.gui.training_logs.tensorboard_offline_button.show()
2649
2647
 
2650
- self.gui.training_logs.tensorboard_button.hide()
2651
- self.gui.training_logs.tensorboard_offline_button.show()
2652
- sleep(1) # wait for the button to be shown
2648
+ sleep(1)
2653
2649
  self.app.shutdown()
2654
2650
  except Exception as e:
2655
2651
  message = f"Error occurred during finalizing and uploading training artifacts. {check_logs_text}"
2656
2652
  self._show_error(message, e)
2657
- self._restore_train_widgets_state_on_error()
2658
2653
  self._set_ws_progress_status("reset")
2659
- return
2654
+ raise e
2660
2655
 
2661
2656
  def _show_error(self, message: str, e=None):
2662
2657
  if e is not None:
@@ -2667,6 +2662,7 @@ class TrainApp:
2667
2662
  self.gui.training_process.validator_text.show()
2668
2663
  self.gui.training_process.start_button.loading = False
2669
2664
  self._restore_train_widgets_state_on_error()
2665
+ show_dialog(title="Error", description=message, status="error")
2670
2666
 
2671
2667
  def _set_train_widgets_state_on_start(self):
2672
2668
  self.gui.disable_select_buttons()