supervisely 6.73.452__py3-none-any.whl → 6.73.513__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 (189) hide show
  1. supervisely/__init__.py +25 -1
  2. supervisely/annotation/annotation.py +8 -2
  3. supervisely/annotation/json_geometries_map.py +13 -12
  4. supervisely/api/annotation_api.py +6 -3
  5. supervisely/api/api.py +2 -0
  6. supervisely/api/app_api.py +10 -1
  7. supervisely/api/dataset_api.py +74 -12
  8. supervisely/api/entities_collection_api.py +10 -0
  9. supervisely/api/entity_annotation/figure_api.py +28 -0
  10. supervisely/api/entity_annotation/object_api.py +3 -3
  11. supervisely/api/entity_annotation/tag_api.py +63 -12
  12. supervisely/api/guides_api.py +210 -0
  13. supervisely/api/image_api.py +4 -0
  14. supervisely/api/labeling_job_api.py +83 -1
  15. supervisely/api/labeling_queue_api.py +33 -7
  16. supervisely/api/module_api.py +5 -0
  17. supervisely/api/project_api.py +71 -26
  18. supervisely/api/storage_api.py +3 -1
  19. supervisely/api/task_api.py +13 -2
  20. supervisely/api/team_api.py +4 -3
  21. supervisely/api/video/video_annotation_api.py +119 -3
  22. supervisely/api/video/video_api.py +65 -14
  23. supervisely/app/__init__.py +1 -1
  24. supervisely/app/content.py +23 -7
  25. supervisely/app/development/development.py +18 -2
  26. supervisely/app/fastapi/__init__.py +1 -0
  27. supervisely/app/fastapi/custom_static_files.py +1 -1
  28. supervisely/app/fastapi/multi_user.py +105 -0
  29. supervisely/app/fastapi/subapp.py +88 -42
  30. supervisely/app/fastapi/websocket.py +77 -9
  31. supervisely/app/singleton.py +21 -0
  32. supervisely/app/v1/app_service.py +18 -2
  33. supervisely/app/v1/constants.py +7 -1
  34. supervisely/app/widgets/__init__.py +6 -0
  35. supervisely/app/widgets/activity_feed/__init__.py +0 -0
  36. supervisely/app/widgets/activity_feed/activity_feed.py +239 -0
  37. supervisely/app/widgets/activity_feed/style.css +78 -0
  38. supervisely/app/widgets/activity_feed/template.html +22 -0
  39. supervisely/app/widgets/card/card.py +20 -0
  40. supervisely/app/widgets/classes_list_selector/classes_list_selector.py +121 -9
  41. supervisely/app/widgets/classes_list_selector/template.html +60 -93
  42. supervisely/app/widgets/classes_mapping/classes_mapping.py +13 -12
  43. supervisely/app/widgets/classes_table/classes_table.py +1 -0
  44. supervisely/app/widgets/deploy_model/deploy_model.py +56 -35
  45. supervisely/app/widgets/ecosystem_model_selector/ecosystem_model_selector.py +1 -1
  46. supervisely/app/widgets/experiment_selector/experiment_selector.py +8 -0
  47. supervisely/app/widgets/fast_table/fast_table.py +184 -60
  48. supervisely/app/widgets/fast_table/template.html +1 -1
  49. supervisely/app/widgets/heatmap/__init__.py +0 -0
  50. supervisely/app/widgets/heatmap/heatmap.py +564 -0
  51. supervisely/app/widgets/heatmap/script.js +533 -0
  52. supervisely/app/widgets/heatmap/style.css +233 -0
  53. supervisely/app/widgets/heatmap/template.html +21 -0
  54. supervisely/app/widgets/modal/__init__.py +0 -0
  55. supervisely/app/widgets/modal/modal.py +198 -0
  56. supervisely/app/widgets/modal/template.html +10 -0
  57. supervisely/app/widgets/object_class_view/object_class_view.py +3 -0
  58. supervisely/app/widgets/radio_tabs/radio_tabs.py +18 -2
  59. supervisely/app/widgets/radio_tabs/template.html +1 -0
  60. supervisely/app/widgets/select/select.py +6 -3
  61. supervisely/app/widgets/select_class/__init__.py +0 -0
  62. supervisely/app/widgets/select_class/select_class.py +363 -0
  63. supervisely/app/widgets/select_class/template.html +50 -0
  64. supervisely/app/widgets/select_cuda/select_cuda.py +22 -0
  65. supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +65 -7
  66. supervisely/app/widgets/select_tag/__init__.py +0 -0
  67. supervisely/app/widgets/select_tag/select_tag.py +352 -0
  68. supervisely/app/widgets/select_tag/template.html +64 -0
  69. supervisely/app/widgets/select_team/select_team.py +37 -4
  70. supervisely/app/widgets/select_team/template.html +4 -5
  71. supervisely/app/widgets/select_user/__init__.py +0 -0
  72. supervisely/app/widgets/select_user/select_user.py +270 -0
  73. supervisely/app/widgets/select_user/template.html +13 -0
  74. supervisely/app/widgets/select_workspace/select_workspace.py +59 -10
  75. supervisely/app/widgets/select_workspace/template.html +9 -12
  76. supervisely/app/widgets/table/table.py +68 -13
  77. supervisely/app/widgets/tree_select/tree_select.py +2 -0
  78. supervisely/aug/aug.py +6 -2
  79. supervisely/convert/base_converter.py +1 -0
  80. supervisely/convert/converter.py +2 -2
  81. supervisely/convert/image/image_converter.py +3 -1
  82. supervisely/convert/image/image_helper.py +48 -4
  83. supervisely/convert/image/label_studio/label_studio_converter.py +2 -0
  84. supervisely/convert/image/medical2d/medical2d_helper.py +2 -24
  85. supervisely/convert/image/multispectral/multispectral_converter.py +6 -0
  86. supervisely/convert/image/pascal_voc/pascal_voc_converter.py +8 -5
  87. supervisely/convert/image/pascal_voc/pascal_voc_helper.py +7 -0
  88. supervisely/convert/pointcloud/kitti_3d/kitti_3d_converter.py +33 -3
  89. supervisely/convert/pointcloud/kitti_3d/kitti_3d_helper.py +12 -5
  90. supervisely/convert/pointcloud/las/las_converter.py +13 -1
  91. supervisely/convert/pointcloud/las/las_helper.py +110 -11
  92. supervisely/convert/pointcloud/nuscenes_conv/nuscenes_converter.py +27 -16
  93. supervisely/convert/pointcloud/pointcloud_converter.py +91 -3
  94. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py +58 -22
  95. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py +21 -47
  96. supervisely/convert/video/__init__.py +1 -0
  97. supervisely/convert/video/multi_view/__init__.py +0 -0
  98. supervisely/convert/video/multi_view/multi_view.py +543 -0
  99. supervisely/convert/video/sly/sly_video_converter.py +359 -3
  100. supervisely/convert/video/video_converter.py +22 -2
  101. supervisely/convert/volume/dicom/dicom_converter.py +13 -5
  102. supervisely/convert/volume/dicom/dicom_helper.py +30 -18
  103. supervisely/geometry/constants.py +1 -0
  104. supervisely/geometry/geometry.py +4 -0
  105. supervisely/geometry/helpers.py +5 -1
  106. supervisely/geometry/oriented_bbox.py +676 -0
  107. supervisely/geometry/rectangle.py +2 -1
  108. supervisely/io/env.py +76 -1
  109. supervisely/io/fs.py +21 -0
  110. supervisely/nn/benchmark/base_evaluator.py +104 -11
  111. supervisely/nn/benchmark/instance_segmentation/evaluator.py +1 -8
  112. supervisely/nn/benchmark/object_detection/evaluator.py +20 -4
  113. supervisely/nn/benchmark/object_detection/vis_metrics/pr_curve.py +10 -5
  114. supervisely/nn/benchmark/semantic_segmentation/evaluator.py +34 -16
  115. supervisely/nn/benchmark/semantic_segmentation/vis_metrics/confusion_matrix.py +1 -1
  116. supervisely/nn/benchmark/semantic_segmentation/vis_metrics/frequently_confused.py +1 -1
  117. supervisely/nn/benchmark/semantic_segmentation/vis_metrics/overview.py +1 -1
  118. supervisely/nn/benchmark/visualization/evaluation_result.py +66 -4
  119. supervisely/nn/inference/cache.py +43 -18
  120. supervisely/nn/inference/gui/serving_gui_template.py +5 -2
  121. supervisely/nn/inference/inference.py +795 -199
  122. supervisely/nn/inference/inference_request.py +42 -9
  123. supervisely/nn/inference/predict_app/gui/classes_selector.py +83 -12
  124. supervisely/nn/inference/predict_app/gui/gui.py +676 -488
  125. supervisely/nn/inference/predict_app/gui/input_selector.py +205 -26
  126. supervisely/nn/inference/predict_app/gui/model_selector.py +2 -4
  127. supervisely/nn/inference/predict_app/gui/output_selector.py +46 -6
  128. supervisely/nn/inference/predict_app/gui/settings_selector.py +756 -59
  129. supervisely/nn/inference/predict_app/gui/tags_selector.py +1 -1
  130. supervisely/nn/inference/predict_app/gui/utils.py +236 -119
  131. supervisely/nn/inference/predict_app/predict_app.py +2 -2
  132. supervisely/nn/inference/session.py +43 -35
  133. supervisely/nn/inference/tracking/bbox_tracking.py +113 -34
  134. supervisely/nn/inference/tracking/tracker_interface.py +7 -2
  135. supervisely/nn/inference/uploader.py +139 -12
  136. supervisely/nn/live_training/__init__.py +7 -0
  137. supervisely/nn/live_training/api_server.py +111 -0
  138. supervisely/nn/live_training/artifacts_utils.py +243 -0
  139. supervisely/nn/live_training/checkpoint_utils.py +229 -0
  140. supervisely/nn/live_training/dynamic_sampler.py +44 -0
  141. supervisely/nn/live_training/helpers.py +14 -0
  142. supervisely/nn/live_training/incremental_dataset.py +146 -0
  143. supervisely/nn/live_training/live_training.py +497 -0
  144. supervisely/nn/live_training/loss_plateau_detector.py +111 -0
  145. supervisely/nn/live_training/request_queue.py +52 -0
  146. supervisely/nn/model/model_api.py +9 -0
  147. supervisely/nn/prediction_dto.py +12 -1
  148. supervisely/nn/tracker/base_tracker.py +11 -1
  149. supervisely/nn/tracker/botsort/botsort_config.yaml +0 -1
  150. supervisely/nn/tracker/botsort/tracker/mc_bot_sort.py +7 -4
  151. supervisely/nn/tracker/botsort_tracker.py +94 -65
  152. supervisely/nn/tracker/visualize.py +87 -90
  153. supervisely/nn/training/gui/classes_selector.py +16 -1
  154. supervisely/nn/training/train_app.py +28 -29
  155. supervisely/project/data_version.py +115 -51
  156. supervisely/project/download.py +1 -1
  157. supervisely/project/pointcloud_episode_project.py +37 -8
  158. supervisely/project/pointcloud_project.py +30 -2
  159. supervisely/project/project.py +14 -2
  160. supervisely/project/project_meta.py +27 -1
  161. supervisely/project/project_settings.py +32 -18
  162. supervisely/project/versioning/__init__.py +1 -0
  163. supervisely/project/versioning/common.py +20 -0
  164. supervisely/project/versioning/schema_fields.py +35 -0
  165. supervisely/project/versioning/video_schema.py +221 -0
  166. supervisely/project/versioning/volume_schema.py +87 -0
  167. supervisely/project/video_project.py +717 -15
  168. supervisely/project/volume_project.py +623 -5
  169. supervisely/template/experiment/experiment.html.jinja +4 -4
  170. supervisely/template/experiment/experiment_generator.py +14 -21
  171. supervisely/template/live_training/__init__.py +0 -0
  172. supervisely/template/live_training/header.html.jinja +96 -0
  173. supervisely/template/live_training/live_training.html.jinja +51 -0
  174. supervisely/template/live_training/live_training_generator.py +464 -0
  175. supervisely/template/live_training/sly-style.css +402 -0
  176. supervisely/template/live_training/template.html.jinja +18 -0
  177. supervisely/versions.json +28 -26
  178. supervisely/video/sampling.py +39 -20
  179. supervisely/video/video.py +40 -11
  180. supervisely/video_annotation/video_object.py +29 -4
  181. supervisely/volume/stl_converter.py +2 -0
  182. supervisely/worker_api/agent_rpc.py +24 -1
  183. supervisely/worker_api/rpc_servicer.py +31 -7
  184. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/METADATA +56 -39
  185. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/RECORD +189 -142
  186. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/WHEEL +1 -1
  187. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/entry_points.txt +0 -0
  188. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info/licenses}/LICENSE +0 -0
  189. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/top_level.txt +0 -0
@@ -1,131 +1,98 @@
1
- <link
2
- rel="stylesheet"
3
- href="./sly/css/app/widgets/classes_list_selector/style.css"
4
- />
1
+ <link rel="stylesheet" href="./sly/css/app/widgets/classes_list_selector/style.css" />
5
2
 
6
3
  <div v-show="data.{{{widget.widget_id}}}.classes.length == 0">
7
4
  {{{widget.empty_notification}}}
8
5
  </div>
9
6
  <div v-show="data.{{{widget.widget_id}}}.classes.length != 0">
10
- <link
11
- rel="stylesheet"
12
- href="./sly/css/app/widgets/object_class_view/style.css"
13
- />
7
+ <link rel="stylesheet" href="./sly/css/app/widgets/object_class_view/style.css" />
14
8
  {% if widget._multiple %}
15
9
  <div>
16
- <el-button
17
- style="margin-right: 10px"
18
- type="text"
19
- {%
20
- if
21
- widget._checkboxes_handled
22
- %}
23
- @click="{
10
+ <el-button style="margin-right: 10px" type="text" {% if widget._checkboxes_handled %} @click="{
24
11
  state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map(() => true);
25
12
  post('/{{{widget.widget_id}}}/checkbox_cb');
26
- }"
27
- {%
28
- else
29
- %}
30
- @click="state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map(() => true)"
31
- {%
32
- endif
33
- %}
34
- >
13
+ }" {% else %}
14
+ @click="state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map(() => true)" {%
15
+ endif %}>
35
16
  <i class="zmdi zmdi-check-all"></i>
36
17
  Select all
37
18
  </el-button>
38
- <el-button
39
- style="margin-right: 10px"
40
- type="text"
41
- {%
42
- if
43
- widget._checkboxes_handled
44
- %}
45
- @click="{
19
+ <el-button style="margin-right: 10px" type="text" {% if widget._checkboxes_handled %} @click="{
46
20
  state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map(() => false);
47
21
  post('/{{{widget.widget_id}}}/checkbox_cb');
48
- }"
49
- {%
50
- else
51
- %}
52
- @click="state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map(() => false)"
53
- {%
54
- endif
55
- %}
56
- >
22
+ }" {% else %}
23
+ @click="state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map(() => false)" {%
24
+ endif %}>
57
25
  <i class="zmdi zmdi-square-o"></i>
58
26
  Deselect all
59
27
  </el-button>
60
28
  </div>
61
29
  {% endif %}
62
- <div
63
- class="fflex"
64
- v-for="(obj_class, idx) in data.{{{widget.widget_id}}}.classes"
65
- >
66
- <el-checkbox
67
- v-model="state.{{{widget.widget_id}}}.selected[idx]"
68
- style="margin-right: 10px"
69
- {%
70
- if
71
- widget._checkboxes_handled
72
- %}
73
- {%
74
- if
75
- not
76
- widget._multiple
77
- %}
78
- @change="() => {
30
+ {% if widget._allow_new_classes %}
31
+ <div style="margin-bottom: 10px; margin-top: 10px">
32
+ <el-button type="primary" size="small" icon="el-icon-plus"
33
+ @click="state.{{{widget.widget_id}}}.createClassDialog.visible = true">
34
+ Add new class
35
+ </el-button>
36
+ </div>
37
+ {% endif %}
38
+ <div class="fflex" v-for="(obj_class, idx) in data.{{{widget.widget_id}}}.classes">
39
+ <el-checkbox v-model="state.{{{widget.widget_id}}}.selected[idx]" style="margin-right: 10px" {% if
40
+ widget._checkboxes_handled %} {% if not widget._multiple %} @change="() => {
79
41
  if ($event.target.checked) {
80
42
  state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map((_, i) => i == idx);
81
43
  }
82
44
  post('/{{{widget.widget_id}}}/checkbox_cb');
83
- }"
84
- {%
85
- else
86
- %}
87
- @change="() => {
45
+ }" {% else %} @change="() => {
88
46
  post('/{{{widget.widget_id}}}/checkbox_cb');
89
- }"
90
- {%
91
- endif
92
- %}
93
- {%
94
- else
95
- %}
96
- {%
97
- if
98
- not
99
- widget._multiple
100
- %}
47
+ }" {% endif %} {% else %} {% if not widget._multiple %}
101
48
  @change="if ($event.target.checked) {state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map((_, i) => i == idx);}"
102
- {%
103
- endif
104
- %}
105
- {%
106
- endif
107
- %}
108
- >
49
+ {% endif %} {% endif %}>
109
50
  </el-checkbox>
110
51
  <span class="icon-text-line classes-list-preview-sidebar">
111
- <i
112
- class="zmdi zmdi-circle"
113
- style="margin-right: 5px"
114
- :style="{color: obj_class.color}"
115
- ></i>
52
+ <i class="zmdi zmdi-circle" style="margin-right: 5px" :style="{color: obj_class.color}"></i>
116
53
  <b style="font-size: 16">{{obj_class.title}}</b>
117
- <span
118
- v-if="obj_class.shape_text"
119
- style="
54
+ <span v-if="obj_class.shape_text" style="
120
55
  flex: none;
121
56
  margin-left: 5px;
122
57
  font-size: 12px;
123
58
  color: #8492a6;
124
59
  line-height: initial;
125
- "
126
- >
60
+ ">
127
61
  {{obj_class.shape_text}}
128
62
  </span>
129
63
  </span>
130
64
  </div>
131
65
  </div>
66
+
67
+ {% if widget._allow_new_classes %}
68
+ <!-- Dialog for creating new class -->
69
+ <el-dialog title="Create New Class" :visible.sync="state.{{{widget.widget_id}}}.createClassDialog.visible" width="450px"
70
+ :close-on-click-modal="false" @open="state.{{{widget.widget_id}}}.createClassDialog.showError = false">
71
+ <el-form label-width="120px" label-position="left">
72
+ <el-form-item label="Class Name">
73
+ <el-input v-model="state.{{{widget.widget_id}}}.createClassDialog.className" placeholder="Enter class name"
74
+ @input="state.{{{widget.widget_id}}}.createClassDialog.showError = false"
75
+ @keyup.enter.native="post('/{{{widget.widget_id}}}/class_created_cb')"></el-input>
76
+ </el-form-item>
77
+ <el-form-item label="Geometry Type">
78
+ <el-select v-model="state.{{{widget.widget_id}}}.createClassDialog.geometryType"
79
+ placeholder="Select geometry type" style="width: 100%">
80
+ <el-option v-for="geom in data.{{{widget.widget_id}}}.availableGeometryTypes" :key="geom.value"
81
+ :label="geom.label" :value="geom.value">
82
+ </el-option>
83
+ </el-select>
84
+ </el-form-item>
85
+ <div v-show="state.{{{widget.widget_id}}}.createClassDialog.showError" style="margin-top: 10px;">
86
+ {{{widget._error_message}}}
87
+ </div>
88
+ </el-form>
89
+ <span slot="footer" class="dialog-footer">
90
+ <el-button @click="state.{{{widget.widget_id}}}.createClassDialog.visible = false">
91
+ Cancel
92
+ </el-button>
93
+ <el-button type="primary" @click="post('/{{{widget.widget_id}}}/class_created_cb')">
94
+ Create
95
+ </el-button>
96
+ </span>
97
+ </el-dialog>
98
+ {% endif %}
@@ -1,23 +1,23 @@
1
- from typing import Optional, Union, List
2
- from supervisely.app.widgets import Widget, NotificationBox, Button, generate_id
1
+ from typing import List, Optional, Union
2
+
3
3
  from supervisely import ObjClass, ObjClassCollection
4
4
  from supervisely.app import DataJson, StateJson
5
-
6
- from supervisely.geometry.bitmap import Bitmap
5
+ from supervisely.app.widgets import Button, NotificationBox, Widget, generate_id
7
6
  from supervisely.geometry.alpha_mask import AlphaMask
7
+ from supervisely.geometry.any_geometry import AnyGeometry
8
+ from supervisely.geometry.bitmap import Bitmap
9
+ from supervisely.geometry.closed_surface_mesh import ClosedSurfaceMesh
8
10
  from supervisely.geometry.cuboid import Cuboid
11
+ from supervisely.geometry.cuboid_3d import Cuboid3d
12
+ from supervisely.geometry.graph import GraphNodes
13
+ from supervisely.geometry.multichannel_bitmap import MultichannelBitmap
14
+ from supervisely.geometry.oriented_bbox import OrientedBBox
9
15
  from supervisely.geometry.point import Point
16
+ from supervisely.geometry.point_3d import Point3d
17
+ from supervisely.geometry.pointcloud import Pointcloud
10
18
  from supervisely.geometry.polygon import Polygon
11
19
  from supervisely.geometry.polyline import Polyline
12
20
  from supervisely.geometry.rectangle import Rectangle
13
- from supervisely.geometry.graph import GraphNodes
14
- from supervisely.geometry.any_geometry import AnyGeometry
15
- from supervisely.geometry.cuboid_3d import Cuboid3d
16
- from supervisely.geometry.pointcloud import Pointcloud
17
- from supervisely.geometry.point_3d import Point3d
18
- from supervisely.geometry.multichannel_bitmap import MultichannelBitmap
19
- from supervisely.geometry.closed_surface_mesh import ClosedSurfaceMesh
20
-
21
21
 
22
22
  type_to_shape_text = {
23
23
  AnyGeometry: "any shape",
@@ -34,6 +34,7 @@ type_to_shape_text = {
34
34
  Point3d: "point 3d", # "zmdi zmdi-select-all"
35
35
  GraphNodes: "keypoints",
36
36
  ClosedSurfaceMesh: "volume (3d mask)",
37
+ OrientedBBox: "oriented bbox",
37
38
  }
38
39
 
39
40
 
@@ -24,6 +24,7 @@ type_to_zmdi_icon = {
24
24
  Pointcloud: "zmdi zmdi-cloud-outline", # "zmdi zmdi-border-clear"
25
25
  sly.MultichannelBitmap: "zmdi zmdi-layers", # "zmdi zmdi-collection-item"
26
26
  Point3d: "zmdi zmdi-filter-center-focus", # "zmdi zmdi-select-all"
27
+ sly.OrientedBBox: "zmdi zmdi-rotate-cw",
27
28
  }
28
29
 
29
30
 
@@ -1,10 +1,10 @@
1
1
  import datetime
2
2
  import tempfile
3
+ import threading
3
4
  from pathlib import Path
4
5
  from typing import Any, Dict, List, Literal
5
6
 
6
7
  import pandas as pd
7
- import yaml
8
8
 
9
9
  from supervisely._utils import logger
10
10
  from supervisely.api.api import Api
@@ -12,8 +12,6 @@ from supervisely.api.app_api import ModuleInfo
12
12
  from supervisely.app.widgets.agent_selector.agent_selector import AgentSelector
13
13
  from supervisely.app.widgets.button.button import Button
14
14
  from supervisely.app.widgets.container.container import Container
15
- from supervisely.app.widgets.card.card import Card
16
- from supervisely.app.widgets.model_info.model_info import ModelInfo
17
15
  from supervisely.app.widgets.ecosystem_model_selector.ecosystem_model_selector import (
18
16
  EcosystemModelSelector,
19
17
  )
@@ -23,7 +21,8 @@ from supervisely.app.widgets.experiment_selector.experiment_selector import (
23
21
  from supervisely.app.widgets.fast_table.fast_table import FastTable
24
22
  from supervisely.app.widgets.field.field import Field
25
23
  from supervisely.app.widgets.flexbox.flexbox import Flexbox
26
- from supervisely.app.widgets.tabs.tabs import Tabs
24
+ from supervisely.app.widgets.model_info.model_info import ModelInfo
25
+ from supervisely.app.widgets.radio_tabs.radio_tabs import RadioTabs
27
26
  from supervisely.app.widgets.text.text import Text
28
27
  from supervisely.app.widgets.widget import Widget
29
28
  from supervisely.io import env
@@ -211,24 +210,31 @@ class DeployModel(Widget):
211
210
  return self._layout
212
211
 
213
212
  def _create_layout(self) -> Container:
214
- frameworks = self.deploy_model.get_frameworks()
215
- experiment_infos = []
216
- for framework_name in frameworks:
217
- experiment_infos.extend(
218
- get_experiment_infos(self.api, self.team_id, framework_name=framework_name)
219
- )
220
213
  self.experiment_table = ExperimentSelector(
221
- experiment_infos=experiment_infos,
222
- team_id=self.team_id,
223
214
  api=self.api,
215
+ team_id=self.team_id,
224
216
  )
225
217
 
226
218
  @self.experiment_table.checkpoint_changed
227
219
  def _checkpoint_changed(row: ExperimentSelector.ModelRow, checkpoint_value: str):
228
220
  print(f"Checkpoint changed for {row._experiment_info.task_id}: {checkpoint_value}")
229
221
 
222
+ threading.Thread(target=self.refresh_experiments, daemon=True).start()
223
+
230
224
  return self.experiment_table
231
225
 
226
+ def refresh_experiments(self):
227
+ self.experiment_table.loading = True
228
+ frameworks = self.deploy_model.get_frameworks()
229
+ experiment_infos = []
230
+ for framework_name in frameworks:
231
+ experiment_infos.extend(
232
+ get_experiment_infos(self.api, self.team_id, framework_name=framework_name)
233
+ )
234
+
235
+ self.experiment_table.set_experiment_infos(experiment_infos)
236
+ self.experiment_table.loading = False
237
+
232
238
  def get_deploy_parameters(self) -> Dict[str, Any]:
233
239
  experiment_info = self.experiment_table.get_selected_experiment_info()
234
240
  return {
@@ -267,8 +273,8 @@ class DeployModel(Widget):
267
273
  MODES = [str(MODE.CONNECT), str(MODE.PRETRAINED), str(MODE.CUSTOM)]
268
274
  MODE_TO_CLASS = {
269
275
  str(MODE.CONNECT): Connect,
270
- str(MODE.PRETRAINED): Pretrained,
271
276
  str(MODE.CUSTOM): Custom,
277
+ str(MODE.PRETRAINED): Pretrained,
272
278
  }
273
279
 
274
280
  def __init__(
@@ -295,6 +301,11 @@ class DeployModel(Widget):
295
301
  self.MODE.PRETRAINED: "Pretrained",
296
302
  self.MODE.CUSTOM: "Custom",
297
303
  }
304
+ self.modes_descriptions = {
305
+ self.MODE.CONNECT: "Connect to an already deployed model",
306
+ self.MODE.PRETRAINED: "Deploy a pretrained model from the ecosystem",
307
+ self.MODE.CUSTOM: "Deploy a custom model from your experiments",
308
+ }
298
309
 
299
310
  # GUI
300
311
  self.layout: Widget = None
@@ -444,31 +455,41 @@ class DeployModel(Widget):
444
455
 
445
456
  self._init_modes(modes)
446
457
  _labels = []
458
+ _descriptions = []
447
459
  _contents = []
460
+ self.statuses_widgets = Container(
461
+ widgets=[
462
+ self.sesson_link,
463
+ self._model_info_container,
464
+ ],
465
+ gap=20,
466
+ )
467
+ self.statuses_widgets.hide()
448
468
  for mode_name, mode in self.modes.items():
449
469
  label = self.modes_labels[mode_name]
470
+ description = self.modes_descriptions[mode_name]
450
471
  if mode_name == str(self.MODE.CONNECT):
451
472
  widgets = [
452
473
  mode.layout,
453
- self._model_info_card,
454
- self.connect_stop_buttons,
455
474
  self.status,
456
- self.sesson_link,
475
+ self.statuses_widgets,
476
+ self.connect_stop_buttons,
457
477
  ]
458
478
  else:
459
479
  widgets = [
460
480
  mode.layout,
461
- self._model_info_card,
462
481
  self.select_agent_field,
463
- self.deploy_stop_buttons,
464
482
  self.status,
465
- self.sesson_link,
483
+ self.statuses_widgets,
484
+ self.deploy_stop_buttons,
466
485
  ]
486
+
467
487
  content = Container(widgets=widgets, gap=20)
468
488
  _labels.append(label)
489
+ _descriptions.append(description)
469
490
  _contents.append(content)
470
491
 
471
- self.tabs = Tabs(labels=_labels, contents=_contents)
492
+ self.tabs = RadioTabs(titles=_labels, descriptions=_descriptions, contents=_contents)
472
493
  if len(self.modes) == 1:
473
494
  self.layout = _contents[0]
474
495
  else:
@@ -490,7 +511,7 @@ class DeployModel(Widget):
490
511
  def _disconnect_button_clicked():
491
512
  self.disconnect()
492
513
 
493
- @self.tabs.click
514
+ @self.tabs.value_changed
494
515
  def _active_tab_changed(tab_name: str):
495
516
  self.set_model_message_by_tab(tab_name)
496
517
 
@@ -573,6 +594,7 @@ class DeployModel(Widget):
573
594
  f"Model {framework}: {model_name} deployed with session ID {model_api.task_id}."
574
595
  )
575
596
  self.model_api = model_api
597
+ self.statuses_widgets.show()
576
598
  self.set_model_status("connected")
577
599
  self.set_session_info(task_info)
578
600
  self.set_model_info(model_api.task_id)
@@ -603,12 +625,14 @@ class DeployModel(Widget):
603
625
  self.set_session_info(task_info)
604
626
  self.set_model_info(model_api.task_id)
605
627
  self.show_stop()
628
+ self.statuses_widgets.show()
606
629
  except Exception as e:
607
630
  logger.error(f"Failed to deploy model: {e}", exc_info=True)
608
631
  self.set_model_status("error", str(e))
609
632
  self.set_session_info(None)
610
633
  self.reset_model_info()
611
634
  self.show_deploy_button()
635
+ self.statuses_widgets.hide()
612
636
  self.enable_modes()
613
637
  else:
614
638
  if str(self.MODE.CONNECT) in self.modes:
@@ -634,6 +658,7 @@ class DeployModel(Widget):
634
658
  self.enable_modes()
635
659
  self.reset_model_info()
636
660
  self.show_deploy_button()
661
+ self.statuses_widgets.hide()
637
662
  if str(self.MODE.CONNECT) in self.modes:
638
663
  self.modes[str(self.MODE.CONNECT)]._update_sessions()
639
664
 
@@ -645,6 +670,7 @@ class DeployModel(Widget):
645
670
  self.set_session_info(None)
646
671
  self.reset_model_info()
647
672
  self.show_deploy_button()
673
+ self.statuses_widgets.hide()
648
674
  self.enable_modes()
649
675
 
650
676
  def load_from_json(self, data: Dict[str, Any]) -> None:
@@ -690,29 +716,24 @@ class DeployModel(Widget):
690
716
  title="Model Info",
691
717
  description="Information about the deployed model",
692
718
  )
693
-
694
- self._model_info_container = Container([self._model_info_widget_field])
695
- self._model_info_container.hide()
696
719
  self._model_info_message = Text("Connect to model to see the session information.")
697
-
698
- self._model_info_card = Card(
699
- title="Session Info",
700
- description="Model parameters and classes",
701
- collapsable=True,
702
- content=Container([self._model_info_container, self._model_info_message]),
720
+ self._model_info_container = Container(
721
+ [self._model_info_widget_field, self._model_info_message], gap=0
703
722
  )
704
- self._model_info_card.collapse()
723
+ self._model_info_widget_field.hide()
724
+
725
+ self._model_info_container.hide()
705
726
 
706
727
  def set_model_info(self, session_id):
707
- self._model_info_widget.set_model_info(session_id)
728
+ self._model_info_widget.set_session_id(session_id)
708
729
 
709
730
  self._model_info_message.hide()
731
+ self._model_info_widget_field.show()
710
732
  self._model_info_container.show()
711
- self._model_info_card.uncollapse()
712
733
 
713
734
  def reset_model_info(self):
714
- self._model_info_card.collapse()
715
735
  self._model_info_container.hide()
736
+ self._model_info_widget_field.hide()
716
737
  self._model_info_message.show()
717
738
 
718
739
  def set_model_message_by_tab(self, tab_name: str):
@@ -724,6 +745,6 @@ class DeployModel(Widget):
724
745
  self._model_info_message.set(
725
746
  "Deploy model to see the session information.", status="text"
726
747
  )
727
- self._model_info_card.collapse()
748
+ self._model_info_widget_field.hide()
728
749
 
729
750
  # ------------------------------------------------------------ #
@@ -95,7 +95,7 @@ class EcosystemModelSelector(Widget):
95
95
  for model in models:
96
96
  if frameworks and model["framework"] not in frameworks:
97
97
  continue
98
- if task_types and model["model"]["task"] not in task_types:
98
+ if task_types and model["task"] not in task_types:
99
99
  continue
100
100
  filtered_models.append(model)
101
101
  return filtered_models
@@ -721,6 +721,14 @@ class ExperimentSelector(Widget):
721
721
  def enable(self):
722
722
  return self.table.enable()
723
723
 
724
+ @property
725
+ def loading(self):
726
+ return self.table.loading
727
+
728
+ @loading.setter
729
+ def loading(self, value: bool):
730
+ self.table.loading = value
731
+
724
732
  def get_json_data(self):
725
733
  return {}
726
734