supervisely 6.73.410__py3-none-any.whl → 6.73.470__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.

Files changed (190) hide show
  1. supervisely/__init__.py +136 -1
  2. supervisely/_utils.py +81 -0
  3. supervisely/annotation/json_geometries_map.py +2 -0
  4. supervisely/annotation/label.py +80 -3
  5. supervisely/api/annotation_api.py +9 -9
  6. supervisely/api/api.py +67 -43
  7. supervisely/api/app_api.py +72 -5
  8. supervisely/api/dataset_api.py +108 -33
  9. supervisely/api/entity_annotation/figure_api.py +113 -49
  10. supervisely/api/image_api.py +82 -0
  11. supervisely/api/module_api.py +10 -0
  12. supervisely/api/nn/deploy_api.py +15 -9
  13. supervisely/api/nn/ecosystem_models_api.py +201 -0
  14. supervisely/api/nn/neural_network_api.py +12 -3
  15. supervisely/api/pointcloud/pointcloud_api.py +38 -0
  16. supervisely/api/pointcloud/pointcloud_episode_annotation_api.py +3 -0
  17. supervisely/api/project_api.py +213 -6
  18. supervisely/api/task_api.py +11 -1
  19. supervisely/api/video/video_annotation_api.py +4 -2
  20. supervisely/api/video/video_api.py +79 -1
  21. supervisely/api/video/video_figure_api.py +24 -11
  22. supervisely/api/volume/volume_api.py +38 -0
  23. supervisely/app/__init__.py +1 -1
  24. supervisely/app/content.py +14 -6
  25. supervisely/app/fastapi/__init__.py +1 -0
  26. supervisely/app/fastapi/custom_static_files.py +1 -1
  27. supervisely/app/fastapi/multi_user.py +88 -0
  28. supervisely/app/fastapi/subapp.py +175 -42
  29. supervisely/app/fastapi/templating.py +1 -1
  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 +11 -1
  35. supervisely/app/widgets/agent_selector/template.html +1 -0
  36. supervisely/app/widgets/card/card.py +20 -0
  37. supervisely/app/widgets/dataset_thumbnail/dataset_thumbnail.py +11 -2
  38. supervisely/app/widgets/dataset_thumbnail/template.html +3 -1
  39. supervisely/app/widgets/deploy_model/deploy_model.py +750 -0
  40. supervisely/app/widgets/dialog/dialog.py +12 -0
  41. supervisely/app/widgets/dialog/template.html +2 -1
  42. supervisely/app/widgets/dropdown_checkbox_selector/__init__.py +0 -0
  43. supervisely/app/widgets/dropdown_checkbox_selector/dropdown_checkbox_selector.py +87 -0
  44. supervisely/app/widgets/dropdown_checkbox_selector/template.html +12 -0
  45. supervisely/app/widgets/ecosystem_model_selector/__init__.py +0 -0
  46. supervisely/app/widgets/ecosystem_model_selector/ecosystem_model_selector.py +195 -0
  47. supervisely/app/widgets/experiment_selector/experiment_selector.py +454 -263
  48. supervisely/app/widgets/fast_table/fast_table.py +713 -126
  49. supervisely/app/widgets/fast_table/script.js +492 -95
  50. supervisely/app/widgets/fast_table/style.css +54 -0
  51. supervisely/app/widgets/fast_table/template.html +45 -5
  52. supervisely/app/widgets/heatmap/__init__.py +0 -0
  53. supervisely/app/widgets/heatmap/heatmap.py +523 -0
  54. supervisely/app/widgets/heatmap/script.js +378 -0
  55. supervisely/app/widgets/heatmap/style.css +227 -0
  56. supervisely/app/widgets/heatmap/template.html +21 -0
  57. supervisely/app/widgets/input_tag/input_tag.py +102 -15
  58. supervisely/app/widgets/input_tag_list/__init__.py +0 -0
  59. supervisely/app/widgets/input_tag_list/input_tag_list.py +274 -0
  60. supervisely/app/widgets/input_tag_list/template.html +70 -0
  61. supervisely/app/widgets/radio_table/radio_table.py +10 -2
  62. supervisely/app/widgets/radio_tabs/radio_tabs.py +18 -2
  63. supervisely/app/widgets/radio_tabs/template.html +1 -0
  64. supervisely/app/widgets/select/select.py +6 -4
  65. supervisely/app/widgets/select_dataset/select_dataset.py +6 -0
  66. supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +83 -7
  67. supervisely/app/widgets/table/table.py +68 -13
  68. supervisely/app/widgets/tabs/tabs.py +22 -6
  69. supervisely/app/widgets/tabs/template.html +5 -1
  70. supervisely/app/widgets/transfer/style.css +3 -0
  71. supervisely/app/widgets/transfer/template.html +3 -1
  72. supervisely/app/widgets/transfer/transfer.py +48 -45
  73. supervisely/app/widgets/tree_select/tree_select.py +2 -0
  74. supervisely/convert/image/csv/csv_converter.py +24 -15
  75. supervisely/convert/pointcloud/nuscenes_conv/nuscenes_converter.py +43 -41
  76. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py +75 -51
  77. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py +137 -124
  78. supervisely/convert/video/video_converter.py +2 -2
  79. supervisely/geometry/polyline_3d.py +110 -0
  80. supervisely/io/env.py +161 -1
  81. supervisely/nn/artifacts/__init__.py +1 -1
  82. supervisely/nn/artifacts/artifacts.py +10 -2
  83. supervisely/nn/artifacts/detectron2.py +1 -0
  84. supervisely/nn/artifacts/hrda.py +1 -0
  85. supervisely/nn/artifacts/mmclassification.py +20 -0
  86. supervisely/nn/artifacts/mmdetection.py +5 -3
  87. supervisely/nn/artifacts/mmsegmentation.py +1 -0
  88. supervisely/nn/artifacts/ritm.py +1 -0
  89. supervisely/nn/artifacts/rtdetr.py +1 -0
  90. supervisely/nn/artifacts/unet.py +1 -0
  91. supervisely/nn/artifacts/utils.py +3 -0
  92. supervisely/nn/artifacts/yolov5.py +2 -0
  93. supervisely/nn/artifacts/yolov8.py +1 -0
  94. supervisely/nn/benchmark/semantic_segmentation/metric_provider.py +18 -18
  95. supervisely/nn/experiments.py +9 -0
  96. supervisely/nn/inference/cache.py +37 -17
  97. supervisely/nn/inference/gui/serving_gui_template.py +39 -13
  98. supervisely/nn/inference/inference.py +953 -211
  99. supervisely/nn/inference/inference_request.py +15 -8
  100. supervisely/nn/inference/instance_segmentation/instance_segmentation.py +1 -0
  101. supervisely/nn/inference/object_detection/object_detection.py +1 -0
  102. supervisely/nn/inference/predict_app/__init__.py +0 -0
  103. supervisely/nn/inference/predict_app/gui/__init__.py +0 -0
  104. supervisely/nn/inference/predict_app/gui/classes_selector.py +160 -0
  105. supervisely/nn/inference/predict_app/gui/gui.py +915 -0
  106. supervisely/nn/inference/predict_app/gui/input_selector.py +344 -0
  107. supervisely/nn/inference/predict_app/gui/model_selector.py +77 -0
  108. supervisely/nn/inference/predict_app/gui/output_selector.py +179 -0
  109. supervisely/nn/inference/predict_app/gui/preview.py +93 -0
  110. supervisely/nn/inference/predict_app/gui/settings_selector.py +881 -0
  111. supervisely/nn/inference/predict_app/gui/tags_selector.py +110 -0
  112. supervisely/nn/inference/predict_app/gui/utils.py +399 -0
  113. supervisely/nn/inference/predict_app/predict_app.py +176 -0
  114. supervisely/nn/inference/session.py +47 -39
  115. supervisely/nn/inference/tracking/bbox_tracking.py +5 -1
  116. supervisely/nn/inference/tracking/point_tracking.py +5 -1
  117. supervisely/nn/inference/tracking/tracker_interface.py +4 -0
  118. supervisely/nn/inference/uploader.py +9 -5
  119. supervisely/nn/model/model_api.py +44 -22
  120. supervisely/nn/model/prediction.py +15 -1
  121. supervisely/nn/model/prediction_session.py +70 -14
  122. supervisely/nn/prediction_dto.py +7 -0
  123. supervisely/nn/tracker/__init__.py +6 -8
  124. supervisely/nn/tracker/base_tracker.py +54 -0
  125. supervisely/nn/tracker/botsort/__init__.py +1 -0
  126. supervisely/nn/tracker/botsort/botsort_config.yaml +30 -0
  127. supervisely/nn/tracker/botsort/osnet_reid/__init__.py +0 -0
  128. supervisely/nn/tracker/botsort/osnet_reid/osnet.py +566 -0
  129. supervisely/nn/tracker/botsort/osnet_reid/osnet_reid_interface.py +88 -0
  130. supervisely/nn/tracker/botsort/tracker/__init__.py +0 -0
  131. supervisely/nn/tracker/{bot_sort → botsort/tracker}/basetrack.py +1 -2
  132. supervisely/nn/tracker/{utils → botsort/tracker}/gmc.py +51 -59
  133. supervisely/nn/tracker/{deep_sort/deep_sort → botsort/tracker}/kalman_filter.py +71 -33
  134. supervisely/nn/tracker/botsort/tracker/matching.py +202 -0
  135. supervisely/nn/tracker/{bot_sort/bot_sort.py → botsort/tracker/mc_bot_sort.py} +68 -81
  136. supervisely/nn/tracker/botsort_tracker.py +273 -0
  137. supervisely/nn/tracker/calculate_metrics.py +264 -0
  138. supervisely/nn/tracker/utils.py +273 -0
  139. supervisely/nn/tracker/visualize.py +520 -0
  140. supervisely/nn/training/gui/gui.py +152 -49
  141. supervisely/nn/training/gui/hyperparameters_selector.py +1 -1
  142. supervisely/nn/training/gui/model_selector.py +8 -6
  143. supervisely/nn/training/gui/train_val_splits_selector.py +144 -71
  144. supervisely/nn/training/gui/training_artifacts.py +3 -1
  145. supervisely/nn/training/train_app.py +225 -46
  146. supervisely/project/pointcloud_episode_project.py +12 -8
  147. supervisely/project/pointcloud_project.py +12 -8
  148. supervisely/project/project.py +221 -75
  149. supervisely/template/experiment/experiment.html.jinja +105 -55
  150. supervisely/template/experiment/experiment_generator.py +258 -112
  151. supervisely/template/experiment/header.html.jinja +31 -13
  152. supervisely/template/experiment/sly-style.css +7 -2
  153. supervisely/versions.json +3 -1
  154. supervisely/video/sampling.py +42 -20
  155. supervisely/video/video.py +41 -12
  156. supervisely/video_annotation/video_figure.py +38 -4
  157. supervisely/volume/stl_converter.py +2 -0
  158. supervisely/worker_api/agent_rpc.py +24 -1
  159. supervisely/worker_api/rpc_servicer.py +31 -7
  160. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/METADATA +22 -14
  161. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/RECORD +167 -148
  162. supervisely_lib/__init__.py +6 -1
  163. supervisely/app/widgets/experiment_selector/style.css +0 -27
  164. supervisely/app/widgets/experiment_selector/template.html +0 -61
  165. supervisely/nn/tracker/bot_sort/__init__.py +0 -21
  166. supervisely/nn/tracker/bot_sort/fast_reid_interface.py +0 -152
  167. supervisely/nn/tracker/bot_sort/matching.py +0 -127
  168. supervisely/nn/tracker/bot_sort/sly_tracker.py +0 -401
  169. supervisely/nn/tracker/deep_sort/__init__.py +0 -6
  170. supervisely/nn/tracker/deep_sort/deep_sort/__init__.py +0 -1
  171. supervisely/nn/tracker/deep_sort/deep_sort/detection.py +0 -49
  172. supervisely/nn/tracker/deep_sort/deep_sort/iou_matching.py +0 -81
  173. supervisely/nn/tracker/deep_sort/deep_sort/linear_assignment.py +0 -202
  174. supervisely/nn/tracker/deep_sort/deep_sort/nn_matching.py +0 -176
  175. supervisely/nn/tracker/deep_sort/deep_sort/track.py +0 -166
  176. supervisely/nn/tracker/deep_sort/deep_sort/tracker.py +0 -145
  177. supervisely/nn/tracker/deep_sort/deep_sort.py +0 -301
  178. supervisely/nn/tracker/deep_sort/generate_clip_detections.py +0 -90
  179. supervisely/nn/tracker/deep_sort/preprocessing.py +0 -70
  180. supervisely/nn/tracker/deep_sort/sly_tracker.py +0 -273
  181. supervisely/nn/tracker/tracker.py +0 -285
  182. supervisely/nn/tracker/utils/kalman_filter.py +0 -492
  183. supervisely/nn/tracking/__init__.py +0 -1
  184. supervisely/nn/tracking/boxmot.py +0 -114
  185. supervisely/nn/tracking/tracking.py +0 -24
  186. /supervisely/{nn/tracker/utils → app/widgets/deploy_model}/__init__.py +0 -0
  187. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/LICENSE +0 -0
  188. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/WHEEL +0 -0
  189. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/entry_points.txt +0 -0
  190. {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- from typing import Dict, Union
1
+ from typing import Dict, Union, Callable
2
2
 
3
3
  from supervisely.annotation.tag import Tag
4
4
  from supervisely.annotation.tag_meta import TagMeta, TagValueType
@@ -30,6 +30,18 @@ VALUE_TYPES = [
30
30
 
31
31
 
32
32
  class InputTag(Widget):
33
+ """Widget for inputting a single tag value based on its TagMeta information. Accepts various input types depending on the tag's value type. Returns the tag value when requested.
34
+
35
+ :param tag_meta: Tag metadata
36
+ :type tag_meta: TagMeta
37
+ :param max_width: Maximum width of the widget in pixels, defaults to 300
38
+ :type max_width: int
39
+ :param hide_switch: Whether to hide the activation switch, defaults to False
40
+ :type hide_switch: bool
41
+ :param widget_id: Unique identifier for the widget, defaults to None
42
+ :type widget_id: int
43
+ """
44
+
33
45
  def __init__(
34
46
  self,
35
47
  tag_meta: TagMeta,
@@ -74,35 +86,79 @@ class InputTag(Widget):
74
86
  self._input_widgets[str(TagValueType.ANY_STRING)] = Input(type="textarea")
75
87
  self._input_widgets[str(TagValueType.ONEOF_STRING)] = RadioGroup(items=[])
76
88
 
77
- def _get_max_width(self, value):
89
+ def _get_max_width(self, value) -> str:
90
+ """Get the maximum width for the widget.
91
+ Ensures the width is at least 150 pixels.
92
+
93
+ :param value: Desired maximum width in pixels.
94
+ :type value: int
95
+ :return: Maximum width for the widget
96
+ :rtype: str
97
+ """
78
98
  if value < 150:
79
99
  value = 150
80
100
  return f"{value}px"
81
101
 
82
- def get_tag_meta(self):
102
+ def get_tag_meta(self) -> TagMeta:
103
+ """Get the tag metadata.
104
+
105
+ :return: Tag metadata
106
+ :rtype: TagMeta
107
+ """
83
108
  return self._tag_meta
84
109
 
85
- def activate(self):
110
+ def activate(self) -> None:
111
+ """Activate the widget."""
86
112
  self._activation_widget.on()
87
113
 
88
- def deactivate(self):
114
+ def deactivate(self) -> None:
115
+ """Deactivate the widget."""
89
116
  self._activation_widget.off()
90
117
 
91
- def is_active(self):
118
+ def is_active(self) -> bool:
119
+ """Check if the widget is active.
120
+
121
+ :return: True if the widget is active, False otherwise
122
+ :rtype: bool
123
+ """
92
124
  return self._activation_widget.is_switched()
93
125
 
94
126
  @property
95
- def value(self):
127
+ def value(self) -> Union[str, int, None]:
128
+ """Get the current value of the tag.
129
+
130
+ :return: Current value of the tag
131
+ :rtype: Union[str, int, None]
132
+ """
96
133
  return self._get_value()
97
134
 
98
135
  @value.setter
99
- def value(self, value):
136
+ def value(self, value: Union[str, int, None]) -> None:
137
+ """Set the current value of the tag.
138
+
139
+ :param value: Current value of the tag
140
+ :type value: Union[str, int, None]
141
+ :return: None
142
+ """
100
143
  self._set_value(value)
101
144
 
102
- def is_valid_value(self, value):
145
+ def is_valid_value(self, value: Union[str, int, None]) -> bool:
146
+ """Check if the value is valid for the tag.
147
+
148
+ :param value: Value to check
149
+ :type value: Union[str, int, None]
150
+ :return: True if the value is valid, False otherwise
151
+ :rtype: bool
152
+ """
103
153
  return self._tag_meta.is_valid_value(value)
104
154
 
105
- def set(self, tag: Union[Tag, None]):
155
+ def set(self, tag: Union[Tag, None]) -> None:
156
+ """Set the tag value.
157
+
158
+ :param tag: Tag to set
159
+ :type tag: Union[Tag, None]
160
+ :return: None
161
+ """
106
162
  if tag is None:
107
163
  self._set_default_value()
108
164
  self.deactivate()
@@ -110,13 +166,23 @@ class InputTag(Widget):
110
166
  self._set_value(tag.value)
111
167
  self.activate()
112
168
 
113
- def get_tag(self):
169
+ def get_tag(self) -> Union[Tag, None]:
170
+ """Get the current tag.
171
+
172
+ :return: Current tag
173
+ :rtype: Union[Tag, None]
174
+ """
114
175
  if not self._hide_switch and not self.is_active():
115
176
  return None
116
177
  tag_value = self._get_value()
117
178
  return Tag(self._tag_meta, tag_value)
118
179
 
119
- def _get_value(self):
180
+ def _get_value(self) -> Union[str, int, None]:
181
+ """Get the current value of the tag.
182
+
183
+ :return: Current value of the tag
184
+ :rtype: Union[str, int, None]
185
+ """
120
186
  input_widget = self._input_widgets[self._tag_meta.value_type]
121
187
  if isinstance(input_widget, Empty):
122
188
  return None
@@ -143,7 +209,12 @@ class InputTag(Widget):
143
209
  if isinstance(input_widget, RadioGroup):
144
210
  input_widget.set_value(None)
145
211
 
146
- def get_json_data(self):
212
+ def get_json_data(self) -> Dict:
213
+ """Get the JSON representation of the tag.
214
+
215
+ :return: JSON representation of the tag
216
+ :rtype: Dict
217
+ """
147
218
  return {
148
219
  "name": self._name,
149
220
  "valueType": self._value_type_name,
@@ -151,9 +222,19 @@ class InputTag(Widget):
151
222
  }
152
223
 
153
224
  def get_json_state(self) -> Dict:
225
+ """Get the JSON representation of the tag state.
226
+
227
+ :return: JSON representation of the tag state
228
+ :rtype: Dict
229
+ """
154
230
  return None
155
231
 
156
- def value_changed(self, func):
232
+ def value_changed(self, func) -> Callable:
233
+ """Decorator to register a callback function for selection changes.
234
+
235
+ :param func: Callback function
236
+ :type func: Callable
237
+ """
157
238
  for value_type, input_widget in self._input_widgets.items():
158
239
  if isinstance(input_widget, Empty):
159
240
  self._value_changed_callbacks[value_type] = func
@@ -168,7 +249,13 @@ class InputTag(Widget):
168
249
  def selection_changed(self, func):
169
250
  return self._activation_widget.value_changed(func)
170
251
 
171
- def set_tag_meta(self, tag_meta: TagMeta):
252
+ def set_tag_meta(self, tag_meta: TagMeta) -> None:
253
+ """Set the tag metadata.
254
+
255
+ :param tag_meta: Tag metadata to set
256
+ :type tag_meta: TagMeta
257
+ :return: None
258
+ """
172
259
  self._tag_meta = tag_meta
173
260
  self._value_type_name = VALUE_TYPE_NAME[self._tag_meta.value_type]
174
261
  self._name = f"<b>{self._tag_meta.name}</b>"
File without changes
@@ -0,0 +1,274 @@
1
+ from typing import List, Union, Dict, Callable
2
+
3
+ from supervisely.app.widgets import Widget
4
+ from supervisely import TagMeta, TagMetaCollection, Tag
5
+ from supervisely.annotation.tag_meta import TagValueType
6
+ from supervisely.app.content import DataJson, StateJson
7
+ from supervisely.imaging.color import rgb2hex
8
+
9
+
10
+ class InputTagList(Widget):
11
+ """Store and manage a list of input tags. Class accepts a list of TagMeta objects and provides methods to interact with them.
12
+
13
+ :param tag_metas: List of TagMeta objects or a TagMetaCollection, defaults to an empty list
14
+ :type tag_metas: Union[List[TagMeta], TagMetaCollection], optional
15
+ :param max_width: Maximum width of the widget in pixels, defaults to 300
16
+ :type max_width: int, optional
17
+ :param max_height: Maximum height of the widget in pixels, defaults to 50
18
+ :type max_height: int, optional
19
+ :param multiple: Whether to allow multiple tags to be selected, defaults to False
20
+ :type multiple: bool, optional
21
+ :param widget_id: Unique identifier for the widget, defaults to None
22
+ :type widget_id: int, optional
23
+ """
24
+
25
+ class VALUE_TYPES:
26
+ """Value types for input tags. Classifies the different types of values that tags can have."""
27
+
28
+ none = str(TagValueType.NONE)
29
+ any_string = str(TagValueType.ANY_STRING)
30
+ one_of = str(TagValueType.ONEOF_STRING)
31
+ number = str(TagValueType.ANY_NUMBER)
32
+
33
+ VALUE_TYPE_NAME = {
34
+ str(TagValueType.NONE): "NONE",
35
+ str(TagValueType.ANY_STRING): "TEXT",
36
+ str(TagValueType.ONEOF_STRING): "ONE OF",
37
+ str(TagValueType.ANY_NUMBER): "NUMBER",
38
+ }
39
+
40
+ def get_default_value(self, tag_meta: TagMeta) -> Union[str, int, None]:
41
+ """Get default value for the tag based on its meta information.
42
+ If the tag has a predefined set of possible values (ONEOF_STRING), return the first one.
43
+ For other types, return a standard default value:
44
+ 1. NONE: None
45
+ 2. ANY_STRING: ""
46
+ 3. ANY_NUMBER: 0
47
+
48
+ :param tag_meta: Tag metadata
49
+ :type tag_meta: TagMeta
50
+ :return: Default value for the tag
51
+ :rtype: Union[str, int, None]
52
+ """
53
+ DEFAULT_VALUES = {
54
+ str(TagValueType.NONE): None,
55
+ str(TagValueType.ANY_STRING): "",
56
+ str(TagValueType.ANY_NUMBER): 0,
57
+ }
58
+ if tag_meta.value_type == str(TagValueType.ONEOF_STRING):
59
+ return tag_meta.possible_values[0]
60
+ else:
61
+ return DEFAULT_VALUES[tag_meta.value_type]
62
+
63
+ class Routes:
64
+ """Routes for the widget events. Classifies the different types of events that can occur within the widget."""
65
+
66
+ CHECKBOX_CHANGED = "checkbox_cb"
67
+
68
+ def __init__(
69
+ self,
70
+ tag_metas: Union[List[TagMeta], TagMetaCollection] = [],
71
+ max_width: int = 300,
72
+ max_height: int = 50,
73
+ multiple: bool = False,
74
+ widget_id: int = None,
75
+ ):
76
+ self._tag_metas = tag_metas
77
+ self._max_width = self._get_max_width(max_width)
78
+ self._max_height = self._get_max_height(max_height)
79
+ self._multiple = multiple
80
+
81
+ super().__init__(widget_id=widget_id, file_path=__file__)
82
+
83
+ def _get_max_width(self, value) -> str:
84
+ """Get the maximum width for the widget.
85
+ Ensures the width is at least 150 pixels.
86
+
87
+ :param value: Desired maximum width in pixels.
88
+ :type value: int
89
+ :return: Maximum width for the widget
90
+ :rtype: str
91
+ """
92
+ if value < 150:
93
+ value = 150
94
+ return f"{value}px"
95
+
96
+ def _get_max_height(self, value) -> str:
97
+ """Get the maximum height for the widget.
98
+ Ensures the height is at least 100 pixels.
99
+
100
+ :param value: Desired maximum height in pixels.
101
+ :type value: int
102
+ :return: Maximum height for the widget
103
+ :rtype: str
104
+ """
105
+ if value < 100:
106
+ value = 100
107
+ return f"{value}px"
108
+
109
+ def get_json_data(self) -> Dict:
110
+ """Get JSON data for the widget.
111
+
112
+ :return: JSON data for the widget
113
+ :rtype: Dict
114
+ """
115
+ return {
116
+ "maxWidth": self._max_width,
117
+ "maxHeight": self._max_height,
118
+ "tags": [
119
+ {
120
+ "name": tag_meta.name,
121
+ "valueType": tag_meta.value_type,
122
+ "valueTypeText": self.VALUE_TYPE_NAME[tag_meta.value_type],
123
+ "color": rgb2hex(tag_meta.color),
124
+ "possible_values": tag_meta.possible_values,
125
+ }
126
+ for tag_meta in self._tag_metas
127
+ ],
128
+ }
129
+
130
+ def get_json_state(self) -> Dict:
131
+ """Get JSON state for the widget.
132
+
133
+ :return: JSON state for the widget
134
+ :rtype: Dict
135
+ """
136
+ return {
137
+ "selected": [False for _ in self._tag_metas],
138
+ "values": [self.get_default_value(tm) for tm in self._tag_metas],
139
+ }
140
+
141
+ def get_selected_tag_metas(self) -> List[TagMeta]:
142
+ """Get selected tag metas for the widget.
143
+
144
+ :return: List of selected tag metas
145
+ :rtype: List[TagMeta]
146
+ """
147
+ return [
148
+ tm
149
+ for selected, tm in zip(StateJson()[self.widget_id]["selected"], self._tag_metas)
150
+ if selected
151
+ ]
152
+
153
+ def get_selected_tags(self) -> List[Tag]:
154
+ """Get selected tags for the widget.
155
+
156
+ :return: List of selected tags
157
+ :rtype: List[Tag]
158
+ """
159
+ return [
160
+ Tag(meta=tm, value=value)
161
+ for selected, value, tm in zip(
162
+ StateJson()[self.widget_id]["selected"],
163
+ StateJson()[self.widget_id]["values"],
164
+ self._tag_metas,
165
+ )
166
+ if selected
167
+ ]
168
+
169
+ def get_all_tags(self) -> Union[List[TagMeta], TagMetaCollection]:
170
+ """Get all tags for the widget.
171
+
172
+ :return: List of all tag metas
173
+ :rtype: Union[List[TagMeta], TagMetaCollection]
174
+ """
175
+ return [
176
+ Tag(meta=tm, value=value)
177
+ for value, tm in zip(
178
+ StateJson()[self.widget_id]["values"],
179
+ self._tag_metas,
180
+ )
181
+ ]
182
+
183
+ def set(self, tag_metas: Union[List[TagMeta], TagMetaCollection]) -> None:
184
+ """Set tag metas for the widget.
185
+
186
+ :param tag_metas: Tag metas to set
187
+ :type tag_metas: Union[List[TagMeta], TagMetaCollection]
188
+ :return: None
189
+ """
190
+ self._tag_metas = tag_metas
191
+ DataJson()[self.widget_id] = self.get_json_data()
192
+ DataJson().send_changes()
193
+ StateJson()[self.widget_id] = self.get_json_state()
194
+ StateJson().send_changes()
195
+
196
+ def set_values(self, values_dict: Dict) -> None:
197
+ """Set values for the widget.
198
+
199
+ :param values_dict: Dictionary of values to set
200
+ :type values_dict: Dict
201
+ :return: None
202
+ """
203
+ current_values = StateJson()[self.widget_id]["values"]
204
+ values = [
205
+ values_dict.get(tm.name, current_values[idx]) for idx, tm in enumerate(self._tag_metas)
206
+ ]
207
+ StateJson()[self.widget_id]["values"] = values
208
+ StateJson().send_changes()
209
+
210
+ def select_all(self) -> None:
211
+ """Select all tags for the widget.
212
+
213
+ :return: None
214
+ """
215
+ StateJson()[self.widget_id]["selected"] = [True for _ in self._tag_metas]
216
+ StateJson().send_changes()
217
+
218
+ def deselect_all(self) -> None:
219
+ """Deselect all tags for the widget.
220
+
221
+ :return: None
222
+ """
223
+ StateJson()[self.widget_id]["selected"] = [False for _ in self._tag_metas]
224
+ StateJson().send_changes()
225
+
226
+ def select(self, names: List[str]) -> None:
227
+ """Select tags for the widget.
228
+
229
+ :param names: List of tag names to select
230
+ :type names: List[str]
231
+ :return: None
232
+ """
233
+ selected = [tm.name in names for tm in self._tag_metas]
234
+ StateJson()[self.widget_id]["selected"] = selected
235
+ StateJson().send_changes()
236
+
237
+ def deselect(self, names: List[str]) -> None:
238
+ """Deselect tags for the widget.
239
+
240
+ :param names: List of tag names to deselect
241
+ :type names: List[str]
242
+ :return: None
243
+ """
244
+ selected = StateJson()[self.widget_id]["selected"]
245
+ for idx, tm in enumerate(self._tag_metas):
246
+ if tm.name in names:
247
+ selected[idx] = False
248
+ StateJson()[self.widget_id]["selected"] = selected
249
+ StateJson().send_changes()
250
+
251
+ def get_all_tag_metas(self) -> List[TagMeta]:
252
+ """Get all tag metas for the widget.
253
+
254
+ :return: List of all tag metas
255
+ :rtype: List[TagMeta]
256
+ """
257
+ return self._tag_metas
258
+
259
+ def selection_changed(self, func: Callable) -> Callable:
260
+ """Decorator to register a callback function for selection changes.
261
+
262
+ :param func: Callback function
263
+ :type func: Callable
264
+ """
265
+ route_path = self.get_route_path(InputTagList.Routes.CHECKBOX_CHANGED)
266
+ server = self._sly_app.get_server()
267
+ self._checkboxes_handled = True
268
+
269
+ @server.post(route_path)
270
+ def _click():
271
+ selected = self.get_selected_tag_metas()
272
+ func(selected)
273
+
274
+ return _click
@@ -0,0 +1,70 @@
1
+ <div :style="{
2
+ 'display': 'flex',
3
+ 'flex-direction': 'column',
4
+ 'max-width': data.{{{widget.widget_id}}}.maxWidth,
5
+ 'max-height': data.{{{widget.widget_id}}}.maxHeight,
6
+ }">
7
+ {% if widget._multiple %}
8
+ <div>
9
+ <el-button style="margin-right: 10px" type="text" {% if widget._checkboxes_handled %} @click="{
10
+ state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map(() => true);
11
+ post('/{{{widget.widget_id}}}/checkbox_cb');
12
+ }" {% else %}
13
+ @click="state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map(() => true)" {% endif
14
+ %}>
15
+ <i class="zmdi zmdi-check-all"></i>
16
+ Select all
17
+ </el-button>
18
+ <el-button style="margin-right: 10px" type="text" {% if widget._checkboxes_handled %} @click="{
19
+ state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map(() => false);
20
+ post('/{{{widget.widget_id}}}/checkbox_cb');
21
+ }" {% else %}
22
+ @click="state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map(() => false)" {% endif
23
+ %}>
24
+ <i class="zmdi zmdi-square-o"></i>
25
+ Deselect all
26
+ </el-button>
27
+ </div>
28
+ {% endif %}
29
+ <div style="
30
+ display: flex;
31
+ flex-direction: column;
32
+ gap: 2px;
33
+ overflow-y: auto;
34
+ overflow-x: hidden;
35
+ ">
36
+ <div v-for="(tag, idx) in data.{{{widget.widget_id}}}.tags" class="fflex" style="gap: 4px">
37
+ <el-checkbox style="flex: none" v-model="state.{{{widget.widget_id}}}.selected[idx]" {% if
38
+ widget._checkboxes_handled %} {% if not widget._multiple %} @change="() => {
39
+ if ($event.target.checked) {
40
+ state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map((_, i) => i == idx);
41
+ }
42
+ post('/{{{widget.widget_id}}}/checkbox_cb');
43
+ }" {% else %} @change="() => {
44
+ post('/{{{widget.widget_id}}}/checkbox_cb');
45
+ }" {% endif %} {% else %} {% if not widget._multiple %}
46
+ @change="if ($event.target.checked) {state.{{{widget.widget_id}}}.selected = state.{{{widget.widget_id}}}.selected.map((_, i) => i == idx);}"
47
+ {% endif %} {% endif %}>
48
+ </el-checkbox>
49
+ <i class="zmdi zmdi-label" :style="{color: tag.color}" style="flex: none"></i>
50
+ <div class="fflex" style="flex: 1; overflow: hidden; text-overflow: ellipsis">
51
+ <b style="width: 100%; overflow: hidden; text-overflow: ellipsis">{{tag.name}}</b>
52
+ </div>
53
+ <div v-if="state.{{{widget.widget_id}}}.selected[idx]">
54
+ <div v-if="tag.valueType == '{{{widget.VALUE_TYPES.any_string}}}'">
55
+ <el-input v-model="state.{{{widget.widget_id}}}.values[idx]" size="mini" style="width: 140px"></el-input>
56
+ </div>
57
+ <div v-if="tag.valueType == '{{{widget.VALUE_TYPES.one_of}}}'">
58
+ <el-select v-model="state.{{{widget.widget_id}}}.values[idx]" size="mini" style="width: 140px">
59
+ <el-option v-for="item in tag.possible_values" :key="item" :label="item" :value="item">
60
+ </el-option>
61
+ </el-select>
62
+ </div>
63
+ <div v-if="tag.valueType == '{{{widget.VALUE_TYPES.number}}}'">
64
+ <el-input-number :controls="false" v-model="state.{{{widget.widget_id}}}.values[idx]" size="mini"
65
+ style="width: 140px"></el-input-number>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
@@ -1,6 +1,7 @@
1
- from typing import List, Dict, Union
2
- from supervisely.app.jinja2 import create_env
1
+ from typing import Dict, List, Union
2
+
3
3
  from supervisely.app.content import DataJson, StateJson
4
+ from supervisely.app.jinja2 import create_env
4
5
  from supervisely.app.widgets import Widget
5
6
 
6
7
 
@@ -154,3 +155,10 @@ class RadioTable(Widget):
154
155
  raise ValueError(f'Row with index "{row_index}" does not exist')
155
156
  StateJson()[self.widget_id]["selectedRow"] = row_index
156
157
  StateJson().send_changes()
158
+
159
+ def select_row_by_value(self, column, value):
160
+ for idx, row in enumerate(self._rows):
161
+ if row[self._columns.index(column)] == value:
162
+ self.select_row(idx)
163
+ return
164
+ raise ValueError(f'Value "{value}" not found in column "{column}"')
@@ -1,5 +1,9 @@
1
- from typing import List, Optional, Dict
1
+ import traceback
2
+ from typing import Dict, List, Optional
3
+
4
+ from supervisely._utils import logger
2
5
  from supervisely.app import StateJson
6
+ from supervisely.app.content import DataJson
3
7
  from supervisely.app.widgets import Widget
4
8
 
5
9
 
@@ -65,7 +69,7 @@ class RadioTabs(Widget):
65
69
  return _value_changed
66
70
 
67
71
  def get_json_data(self) -> Dict:
68
- return {}
72
+ return {"tabsOptions": {item.name: {"disabled": False} for item in self._items}}
69
73
 
70
74
  def get_json_state(self) -> Dict:
71
75
  return {"value": self._value}
@@ -77,3 +81,15 @@ class RadioTabs(Widget):
77
81
 
78
82
  def get_active_tab(self) -> str:
79
83
  return StateJson()[self.widget_id]["value"]
84
+
85
+ def disable_tab(self, tab_name: str):
86
+ if tab_name not in [item.name for item in self._items]:
87
+ raise ValueError(f"Tab with name '{tab_name}' does not exist.")
88
+ DataJson()[self.widget_id]["tabsOptions"][tab_name]["disabled"] = True
89
+ DataJson().send_changes()
90
+
91
+ def enable_tab(self, tab_name: str):
92
+ if tab_name not in [item.name for item in self._items]:
93
+ raise ValueError(f"Tab with name '{tab_name}' does not exist.")
94
+ DataJson()[self.widget_id]["tabsOptions"][tab_name]["disabled"] = False
95
+ DataJson().send_changes()
@@ -20,6 +20,7 @@
20
20
  {% for tab_pane in widget._items %}
21
21
  <el-tab-pane
22
22
  name="{{{tab_pane.name}}}"
23
+ :disabled="data.{{{widget.widget_id}}}.tabsOptions['{{{tab_pane.name}}}'].disabled"
23
24
  >
24
25
  <el-radio
25
26
  slot="label"
@@ -1,7 +1,9 @@
1
1
  from __future__ import annotations
2
- from supervisely.app import StateJson, DataJson
3
- from supervisely.app.widgets import Widget, ConditionalWidget
4
- from typing import List, Dict, Optional
2
+
3
+ from typing import Dict, List, Optional
4
+
5
+ from supervisely.app import DataJson, StateJson
6
+ from supervisely.app.widgets import ConditionalWidget, Widget
5
7
 
6
8
  try:
7
9
  from typing import Literal
@@ -183,7 +185,7 @@ class Select(ConditionalWidget):
183
185
  self._changes_handled = True
184
186
 
185
187
  @server.post(route_path)
186
- async def _click():
188
+ def _click():
187
189
  res = self.get_value()
188
190
  func(res)
189
191
 
@@ -138,6 +138,12 @@ 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()
144
+ else:
145
+ StateJson()[self.widget_id]["projectId"] = self._project_id
146
+ StateJson().send_changes()
141
147
  self._project_selector.set_project_id(self._project_id)
142
148
 
143
149
  def set_select_all_datasets(self, is_checked: bool):