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.
- supervisely/__init__.py +136 -1
- supervisely/_utils.py +81 -0
- supervisely/annotation/json_geometries_map.py +2 -0
- supervisely/annotation/label.py +80 -3
- supervisely/api/annotation_api.py +9 -9
- supervisely/api/api.py +67 -43
- supervisely/api/app_api.py +72 -5
- supervisely/api/dataset_api.py +108 -33
- supervisely/api/entity_annotation/figure_api.py +113 -49
- supervisely/api/image_api.py +82 -0
- supervisely/api/module_api.py +10 -0
- supervisely/api/nn/deploy_api.py +15 -9
- supervisely/api/nn/ecosystem_models_api.py +201 -0
- supervisely/api/nn/neural_network_api.py +12 -3
- supervisely/api/pointcloud/pointcloud_api.py +38 -0
- supervisely/api/pointcloud/pointcloud_episode_annotation_api.py +3 -0
- supervisely/api/project_api.py +213 -6
- supervisely/api/task_api.py +11 -1
- supervisely/api/video/video_annotation_api.py +4 -2
- supervisely/api/video/video_api.py +79 -1
- supervisely/api/video/video_figure_api.py +24 -11
- supervisely/api/volume/volume_api.py +38 -0
- supervisely/app/__init__.py +1 -1
- supervisely/app/content.py +14 -6
- supervisely/app/fastapi/__init__.py +1 -0
- supervisely/app/fastapi/custom_static_files.py +1 -1
- supervisely/app/fastapi/multi_user.py +88 -0
- supervisely/app/fastapi/subapp.py +175 -42
- supervisely/app/fastapi/templating.py +1 -1
- supervisely/app/fastapi/websocket.py +77 -9
- supervisely/app/singleton.py +21 -0
- supervisely/app/v1/app_service.py +18 -2
- supervisely/app/v1/constants.py +7 -1
- supervisely/app/widgets/__init__.py +11 -1
- supervisely/app/widgets/agent_selector/template.html +1 -0
- supervisely/app/widgets/card/card.py +20 -0
- supervisely/app/widgets/dataset_thumbnail/dataset_thumbnail.py +11 -2
- supervisely/app/widgets/dataset_thumbnail/template.html +3 -1
- supervisely/app/widgets/deploy_model/deploy_model.py +750 -0
- supervisely/app/widgets/dialog/dialog.py +12 -0
- supervisely/app/widgets/dialog/template.html +2 -1
- supervisely/app/widgets/dropdown_checkbox_selector/__init__.py +0 -0
- supervisely/app/widgets/dropdown_checkbox_selector/dropdown_checkbox_selector.py +87 -0
- supervisely/app/widgets/dropdown_checkbox_selector/template.html +12 -0
- supervisely/app/widgets/ecosystem_model_selector/__init__.py +0 -0
- supervisely/app/widgets/ecosystem_model_selector/ecosystem_model_selector.py +195 -0
- supervisely/app/widgets/experiment_selector/experiment_selector.py +454 -263
- supervisely/app/widgets/fast_table/fast_table.py +713 -126
- supervisely/app/widgets/fast_table/script.js +492 -95
- supervisely/app/widgets/fast_table/style.css +54 -0
- supervisely/app/widgets/fast_table/template.html +45 -5
- supervisely/app/widgets/heatmap/__init__.py +0 -0
- supervisely/app/widgets/heatmap/heatmap.py +523 -0
- supervisely/app/widgets/heatmap/script.js +378 -0
- supervisely/app/widgets/heatmap/style.css +227 -0
- supervisely/app/widgets/heatmap/template.html +21 -0
- supervisely/app/widgets/input_tag/input_tag.py +102 -15
- supervisely/app/widgets/input_tag_list/__init__.py +0 -0
- supervisely/app/widgets/input_tag_list/input_tag_list.py +274 -0
- supervisely/app/widgets/input_tag_list/template.html +70 -0
- supervisely/app/widgets/radio_table/radio_table.py +10 -2
- supervisely/app/widgets/radio_tabs/radio_tabs.py +18 -2
- supervisely/app/widgets/radio_tabs/template.html +1 -0
- supervisely/app/widgets/select/select.py +6 -4
- supervisely/app/widgets/select_dataset/select_dataset.py +6 -0
- supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +83 -7
- supervisely/app/widgets/table/table.py +68 -13
- supervisely/app/widgets/tabs/tabs.py +22 -6
- supervisely/app/widgets/tabs/template.html +5 -1
- supervisely/app/widgets/transfer/style.css +3 -0
- supervisely/app/widgets/transfer/template.html +3 -1
- supervisely/app/widgets/transfer/transfer.py +48 -45
- supervisely/app/widgets/tree_select/tree_select.py +2 -0
- supervisely/convert/image/csv/csv_converter.py +24 -15
- supervisely/convert/pointcloud/nuscenes_conv/nuscenes_converter.py +43 -41
- supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py +75 -51
- supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py +137 -124
- supervisely/convert/video/video_converter.py +2 -2
- supervisely/geometry/polyline_3d.py +110 -0
- supervisely/io/env.py +161 -1
- supervisely/nn/artifacts/__init__.py +1 -1
- supervisely/nn/artifacts/artifacts.py +10 -2
- supervisely/nn/artifacts/detectron2.py +1 -0
- supervisely/nn/artifacts/hrda.py +1 -0
- supervisely/nn/artifacts/mmclassification.py +20 -0
- supervisely/nn/artifacts/mmdetection.py +5 -3
- supervisely/nn/artifacts/mmsegmentation.py +1 -0
- supervisely/nn/artifacts/ritm.py +1 -0
- supervisely/nn/artifacts/rtdetr.py +1 -0
- supervisely/nn/artifacts/unet.py +1 -0
- supervisely/nn/artifacts/utils.py +3 -0
- supervisely/nn/artifacts/yolov5.py +2 -0
- supervisely/nn/artifacts/yolov8.py +1 -0
- supervisely/nn/benchmark/semantic_segmentation/metric_provider.py +18 -18
- supervisely/nn/experiments.py +9 -0
- supervisely/nn/inference/cache.py +37 -17
- supervisely/nn/inference/gui/serving_gui_template.py +39 -13
- supervisely/nn/inference/inference.py +953 -211
- supervisely/nn/inference/inference_request.py +15 -8
- supervisely/nn/inference/instance_segmentation/instance_segmentation.py +1 -0
- supervisely/nn/inference/object_detection/object_detection.py +1 -0
- supervisely/nn/inference/predict_app/__init__.py +0 -0
- supervisely/nn/inference/predict_app/gui/__init__.py +0 -0
- supervisely/nn/inference/predict_app/gui/classes_selector.py +160 -0
- supervisely/nn/inference/predict_app/gui/gui.py +915 -0
- supervisely/nn/inference/predict_app/gui/input_selector.py +344 -0
- supervisely/nn/inference/predict_app/gui/model_selector.py +77 -0
- supervisely/nn/inference/predict_app/gui/output_selector.py +179 -0
- supervisely/nn/inference/predict_app/gui/preview.py +93 -0
- supervisely/nn/inference/predict_app/gui/settings_selector.py +881 -0
- supervisely/nn/inference/predict_app/gui/tags_selector.py +110 -0
- supervisely/nn/inference/predict_app/gui/utils.py +399 -0
- supervisely/nn/inference/predict_app/predict_app.py +176 -0
- supervisely/nn/inference/session.py +47 -39
- supervisely/nn/inference/tracking/bbox_tracking.py +5 -1
- supervisely/nn/inference/tracking/point_tracking.py +5 -1
- supervisely/nn/inference/tracking/tracker_interface.py +4 -0
- supervisely/nn/inference/uploader.py +9 -5
- supervisely/nn/model/model_api.py +44 -22
- supervisely/nn/model/prediction.py +15 -1
- supervisely/nn/model/prediction_session.py +70 -14
- supervisely/nn/prediction_dto.py +7 -0
- supervisely/nn/tracker/__init__.py +6 -8
- supervisely/nn/tracker/base_tracker.py +54 -0
- supervisely/nn/tracker/botsort/__init__.py +1 -0
- supervisely/nn/tracker/botsort/botsort_config.yaml +30 -0
- supervisely/nn/tracker/botsort/osnet_reid/__init__.py +0 -0
- supervisely/nn/tracker/botsort/osnet_reid/osnet.py +566 -0
- supervisely/nn/tracker/botsort/osnet_reid/osnet_reid_interface.py +88 -0
- supervisely/nn/tracker/botsort/tracker/__init__.py +0 -0
- supervisely/nn/tracker/{bot_sort → botsort/tracker}/basetrack.py +1 -2
- supervisely/nn/tracker/{utils → botsort/tracker}/gmc.py +51 -59
- supervisely/nn/tracker/{deep_sort/deep_sort → botsort/tracker}/kalman_filter.py +71 -33
- supervisely/nn/tracker/botsort/tracker/matching.py +202 -0
- supervisely/nn/tracker/{bot_sort/bot_sort.py → botsort/tracker/mc_bot_sort.py} +68 -81
- supervisely/nn/tracker/botsort_tracker.py +273 -0
- supervisely/nn/tracker/calculate_metrics.py +264 -0
- supervisely/nn/tracker/utils.py +273 -0
- supervisely/nn/tracker/visualize.py +520 -0
- supervisely/nn/training/gui/gui.py +152 -49
- supervisely/nn/training/gui/hyperparameters_selector.py +1 -1
- supervisely/nn/training/gui/model_selector.py +8 -6
- supervisely/nn/training/gui/train_val_splits_selector.py +144 -71
- supervisely/nn/training/gui/training_artifacts.py +3 -1
- supervisely/nn/training/train_app.py +225 -46
- supervisely/project/pointcloud_episode_project.py +12 -8
- supervisely/project/pointcloud_project.py +12 -8
- supervisely/project/project.py +221 -75
- supervisely/template/experiment/experiment.html.jinja +105 -55
- supervisely/template/experiment/experiment_generator.py +258 -112
- supervisely/template/experiment/header.html.jinja +31 -13
- supervisely/template/experiment/sly-style.css +7 -2
- supervisely/versions.json +3 -1
- supervisely/video/sampling.py +42 -20
- supervisely/video/video.py +41 -12
- supervisely/video_annotation/video_figure.py +38 -4
- supervisely/volume/stl_converter.py +2 -0
- supervisely/worker_api/agent_rpc.py +24 -1
- supervisely/worker_api/rpc_servicer.py +31 -7
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/METADATA +22 -14
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/RECORD +167 -148
- supervisely_lib/__init__.py +6 -1
- supervisely/app/widgets/experiment_selector/style.css +0 -27
- supervisely/app/widgets/experiment_selector/template.html +0 -61
- supervisely/nn/tracker/bot_sort/__init__.py +0 -21
- supervisely/nn/tracker/bot_sort/fast_reid_interface.py +0 -152
- supervisely/nn/tracker/bot_sort/matching.py +0 -127
- supervisely/nn/tracker/bot_sort/sly_tracker.py +0 -401
- supervisely/nn/tracker/deep_sort/__init__.py +0 -6
- supervisely/nn/tracker/deep_sort/deep_sort/__init__.py +0 -1
- supervisely/nn/tracker/deep_sort/deep_sort/detection.py +0 -49
- supervisely/nn/tracker/deep_sort/deep_sort/iou_matching.py +0 -81
- supervisely/nn/tracker/deep_sort/deep_sort/linear_assignment.py +0 -202
- supervisely/nn/tracker/deep_sort/deep_sort/nn_matching.py +0 -176
- supervisely/nn/tracker/deep_sort/deep_sort/track.py +0 -166
- supervisely/nn/tracker/deep_sort/deep_sort/tracker.py +0 -145
- supervisely/nn/tracker/deep_sort/deep_sort.py +0 -301
- supervisely/nn/tracker/deep_sort/generate_clip_detections.py +0 -90
- supervisely/nn/tracker/deep_sort/preprocessing.py +0 -70
- supervisely/nn/tracker/deep_sort/sly_tracker.py +0 -273
- supervisely/nn/tracker/tracker.py +0 -285
- supervisely/nn/tracker/utils/kalman_filter.py +0 -492
- supervisely/nn/tracking/__init__.py +0 -1
- supervisely/nn/tracking/boxmot.py +0 -114
- supervisely/nn/tracking/tracking.py +0 -24
- /supervisely/{nn/tracker/utils → app/widgets/deploy_model}/__init__.py +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/LICENSE +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/WHEEL +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/entry_points.txt +0 -0
- {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
|
|
2
|
-
|
|
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
|
-
|
|
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()
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from
|
|
4
|
-
|
|
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
|
-
|
|
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):
|