supervisely 6.73.257__py3-none-any.whl → 6.73.259__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/api/pointcloud/pointcloud_annotation_api.py +94 -1
- supervisely/api/video/video_annotation_api.py +78 -11
- supervisely/api/volume/volume_annotation_api.py +102 -2
- supervisely/app/widgets/__init__.py +1 -0
- supervisely/app/widgets/bokeh/__init__.py +0 -0
- supervisely/app/widgets/bokeh/bokeh.py +278 -0
- supervisely/app/widgets/bokeh/template.html +2 -0
- supervisely/app/widgets/iframe/iframe.py +2 -0
- supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +1 -2
- {supervisely-6.73.257.dist-info → supervisely-6.73.259.dist-info}/METADATA +1 -1
- {supervisely-6.73.257.dist-info → supervisely-6.73.259.dist-info}/RECORD +15 -12
- {supervisely-6.73.257.dist-info → supervisely-6.73.259.dist-info}/LICENSE +0 -0
- {supervisely-6.73.257.dist-info → supervisely-6.73.259.dist-info}/WHEEL +0 -0
- {supervisely-6.73.257.dist-info → supervisely-6.73.259.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.257.dist-info → supervisely-6.73.259.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
3
|
# docs
|
|
4
|
-
|
|
4
|
+
import asyncio
|
|
5
|
+
from typing import Callable, Dict, List, Optional, Union
|
|
5
6
|
|
|
7
|
+
from tqdm import tqdm
|
|
8
|
+
|
|
9
|
+
from supervisely._utils import batched
|
|
6
10
|
from supervisely.api.entity_annotation.entity_annotation_api import EntityAnnotationAPI
|
|
7
11
|
from supervisely.api.module_api import ApiField
|
|
8
12
|
from supervisely.pointcloud_annotation.pointcloud_annotation import PointcloudAnnotation
|
|
@@ -174,3 +178,92 @@ class PointcloudAnnotationAPI(EntityAnnotationAPI):
|
|
|
174
178
|
ann.figures,
|
|
175
179
|
key_id_map,
|
|
176
180
|
)
|
|
181
|
+
|
|
182
|
+
async def download_async(
|
|
183
|
+
self,
|
|
184
|
+
pointcloud_id: int,
|
|
185
|
+
semaphore: Optional[asyncio.Semaphore] = None,
|
|
186
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
187
|
+
) -> Dict:
|
|
188
|
+
"""
|
|
189
|
+
Download information about PointcloudAnnotation by Point Cloud ID from API asynchronously.
|
|
190
|
+
|
|
191
|
+
:param pointcloud_id: Point Cloud ID in Supervisely.
|
|
192
|
+
:type pointcloud_id: int
|
|
193
|
+
:param semaphore: Semaphore to limit the number of parallel downloads.
|
|
194
|
+
:type semaphore: asyncio.Semaphore, optional
|
|
195
|
+
:param progress_cb: Progress callback to track download progress.
|
|
196
|
+
:type progress_cb: Union[tqdm, Callable], optional
|
|
197
|
+
:return: Information about PointcloudAnnotation in json format
|
|
198
|
+
:rtype: :class:`dict`
|
|
199
|
+
|
|
200
|
+
:Usage example:
|
|
201
|
+
|
|
202
|
+
.. code-block:: python
|
|
203
|
+
|
|
204
|
+
import supervisely as sly
|
|
205
|
+
|
|
206
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
207
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
208
|
+
api = sly.Api.from_env()
|
|
209
|
+
|
|
210
|
+
pointcloud_id = 198702499
|
|
211
|
+
loop = sly.utils.get_or_create_event_loop()
|
|
212
|
+
ann_info = loop.run_until_complete(api.pointcloud.annotation.download_async(pointcloud_id))
|
|
213
|
+
"""
|
|
214
|
+
return await self.download_bulk_async(
|
|
215
|
+
pointcloud_ids=[pointcloud_id],
|
|
216
|
+
semaphore=semaphore,
|
|
217
|
+
progress_cb=progress_cb,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
async def download_bulk_async(
|
|
221
|
+
self,
|
|
222
|
+
pointcloud_ids: List[int],
|
|
223
|
+
semaphore: Optional[asyncio.Semaphore] = None,
|
|
224
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
225
|
+
) -> List[Dict]:
|
|
226
|
+
"""
|
|
227
|
+
Download information about PointcloudAnnotation in bulk by Point Cloud IDs from API asynchronously.
|
|
228
|
+
|
|
229
|
+
:param pointcloud_ids: Point Cloud IDs in Supervisely.
|
|
230
|
+
:type pointcloud_ids: List[int]
|
|
231
|
+
:param semaphore: Semaphore to limit the number of parallel downloads.
|
|
232
|
+
:type semaphore: asyncio.Semaphore, optional
|
|
233
|
+
:param progress_cb: Progress callback to track download progress.
|
|
234
|
+
:type progress_cb: Union[tqdm, Callable], optional
|
|
235
|
+
:return: Information about PointcloudAnnotations in json format
|
|
236
|
+
:rtype: :class:`list`
|
|
237
|
+
|
|
238
|
+
:Usage example:
|
|
239
|
+
|
|
240
|
+
.. code-block:: python
|
|
241
|
+
|
|
242
|
+
import supervisely as sly
|
|
243
|
+
|
|
244
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
245
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
246
|
+
api = sly.Api.from_env()
|
|
247
|
+
|
|
248
|
+
pointcloud_ids = [198702499, 198702500, 198702501]
|
|
249
|
+
loop = sly.utils.get_or_create_event_loop()
|
|
250
|
+
ann_infos = loop.run_until_complete(api.pointcloud.annotation.download_bulk_async(pointcloud_ids))
|
|
251
|
+
"""
|
|
252
|
+
if semaphore is None:
|
|
253
|
+
semaphore = self._api.get_default_semaphore()
|
|
254
|
+
|
|
255
|
+
async def fetch_with_semaphore(batch):
|
|
256
|
+
async with semaphore:
|
|
257
|
+
json_data = {self._entity_ids_str: batch}
|
|
258
|
+
response = await self._api.post_async(
|
|
259
|
+
self._method_download_bulk,
|
|
260
|
+
json=json_data,
|
|
261
|
+
)
|
|
262
|
+
if progress_cb is not None:
|
|
263
|
+
progress_cb(len(batch))
|
|
264
|
+
return response.json()
|
|
265
|
+
|
|
266
|
+
tasks = [fetch_with_semaphore(batch) for batch in batched(pointcloud_ids)]
|
|
267
|
+
responses = await asyncio.gather(*tasks)
|
|
268
|
+
json_response = [item for response in responses for item in response]
|
|
269
|
+
return json_response
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import asyncio
|
|
5
|
-
import json
|
|
6
5
|
from typing import Callable, Dict, List, Optional, Union
|
|
7
6
|
|
|
8
7
|
from tqdm import tqdm
|
|
@@ -12,7 +11,6 @@ from supervisely.api.entity_annotation.entity_annotation_api import EntityAnnota
|
|
|
12
11
|
from supervisely.api.module_api import ApiField
|
|
13
12
|
from supervisely.io.json import load_json_file
|
|
14
13
|
from supervisely.project.project_meta import ProjectMeta
|
|
15
|
-
from supervisely.task.progress import Progress
|
|
16
14
|
from supervisely.video_annotation.key_id_map import KeyIdMap
|
|
17
15
|
from supervisely.video_annotation.video_annotation import VideoAnnotation
|
|
18
16
|
|
|
@@ -254,18 +252,28 @@ class VideoAnnotationAPI(EntityAnnotationAPI):
|
|
|
254
252
|
video_id: int,
|
|
255
253
|
video_info=None,
|
|
256
254
|
semaphore: Optional[asyncio.Semaphore] = None,
|
|
255
|
+
force_metadata_for_links: bool = True,
|
|
256
|
+
integer_coords: bool = True,
|
|
257
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
257
258
|
) -> Dict:
|
|
258
259
|
"""
|
|
259
260
|
Download information about VideoAnnotation by video ID from API asynchronously.
|
|
260
261
|
|
|
261
262
|
:param video_id: Video ID in Supervisely.
|
|
262
263
|
:type video_id: int
|
|
263
|
-
:param video_info:
|
|
264
|
+
:param video_info: Does not affect the result, but is left for compatibility with the method signature.
|
|
264
265
|
:type video_info: VideoInfo, optional
|
|
265
266
|
:param semaphore: Semaphore to limit the number of parallel downloads.
|
|
266
267
|
:type semaphore: asyncio.Semaphore, optional
|
|
268
|
+
:param force_metadata_for_links: If True, updates meta for videos with links.
|
|
269
|
+
:type force_metadata_for_links: bool, optional
|
|
270
|
+
:param integer_coords: If True, returns coordinates as integers for objects. If False, returns as floats.
|
|
271
|
+
:type integer_coords: bool, optional
|
|
272
|
+
:param progress_cb: Progress callback to track download progress.
|
|
273
|
+
:type progress_cb: Union[tqdm, Callable], optional
|
|
267
274
|
:return: Information about VideoAnnotation in json format
|
|
268
275
|
:rtype: :class:`dict`
|
|
276
|
+
|
|
269
277
|
:Usage example:
|
|
270
278
|
|
|
271
279
|
.. code-block:: python
|
|
@@ -280,15 +288,74 @@ class VideoAnnotationAPI(EntityAnnotationAPI):
|
|
|
280
288
|
loop = sly.utils.get_or_create_event_loop()
|
|
281
289
|
ann_info = loop.run_until_complete(api.video.annotation.download_async(video_id))
|
|
282
290
|
"""
|
|
283
|
-
|
|
284
|
-
|
|
291
|
+
return await self.download_bulk_async(
|
|
292
|
+
video_ids=[video_id],
|
|
293
|
+
semaphore=semaphore,
|
|
294
|
+
force_metadata_for_links=force_metadata_for_links,
|
|
295
|
+
integer_coords=integer_coords,
|
|
296
|
+
progress_cb=progress_cb,
|
|
297
|
+
)
|
|
285
298
|
|
|
299
|
+
async def download_bulk_async(
|
|
300
|
+
self,
|
|
301
|
+
video_ids: List[int],
|
|
302
|
+
semaphore: Optional[asyncio.Semaphore] = None,
|
|
303
|
+
force_metadata_for_links: bool = True,
|
|
304
|
+
integer_coords: bool = True,
|
|
305
|
+
batch_size: int = 10,
|
|
306
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
307
|
+
) -> List[Dict]:
|
|
308
|
+
"""
|
|
309
|
+
Download information about VideoAnnotation in bulk by video IDs from API asynchronously.
|
|
310
|
+
|
|
311
|
+
:param video_ids: List of Video IDs in Supervisely. All videos must be from the same dataset.
|
|
312
|
+
:type video_ids: int
|
|
313
|
+
:param semaphore: Semaphore to limit the number of parallel downloads.
|
|
314
|
+
:type semaphore: asyncio.Semaphore, optional
|
|
315
|
+
:param force_metadata_for_links: If True, updates meta for videos with links.
|
|
316
|
+
:type force_metadata_for_links: bool, optional
|
|
317
|
+
:param integer_coords: If True, returns coordinates as integers for objects. If False, returns as floats.
|
|
318
|
+
:type integer_coords: bool, optional
|
|
319
|
+
:param batch_size: Batch size for parallel downloads. Default is 10 as an optimal value.
|
|
320
|
+
:type batch_size: int, optional
|
|
321
|
+
:param progress_cb: Function for tracking download progress.
|
|
322
|
+
:type progress_cb: tqdm or callable, optional
|
|
323
|
+
:return: Information about VideoAnnotations in json format
|
|
324
|
+
:rtype: :class:`list`
|
|
325
|
+
|
|
326
|
+
:Usage example:
|
|
327
|
+
|
|
328
|
+
.. code-block:: python
|
|
329
|
+
|
|
330
|
+
import supervisely as sly
|
|
331
|
+
|
|
332
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
333
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
334
|
+
api = sly.Api.from_env()
|
|
335
|
+
|
|
336
|
+
video_ids = [198702499, 198702500, 198702501]
|
|
337
|
+
loop = sly.utils.get_or_create_event_loop()
|
|
338
|
+
ann_infos = loop.run_until_complete(api.video.annotation.download_bulk_async(video_ids))
|
|
339
|
+
"""
|
|
286
340
|
if semaphore is None:
|
|
287
341
|
semaphore = self._api.get_default_semaphore()
|
|
288
342
|
|
|
289
|
-
async
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
343
|
+
async def fetch_with_semaphore(batch):
|
|
344
|
+
async with semaphore:
|
|
345
|
+
json_data = {
|
|
346
|
+
self._entity_ids_str: batch,
|
|
347
|
+
ApiField.FORCE_METADATA_FOR_LINKS: force_metadata_for_links,
|
|
348
|
+
ApiField.INTEGER_COORDS: integer_coords,
|
|
349
|
+
}
|
|
350
|
+
response = await self._api.post_async(
|
|
351
|
+
self._method_download_bulk,
|
|
352
|
+
json=json_data,
|
|
353
|
+
)
|
|
354
|
+
if progress_cb is not None:
|
|
355
|
+
progress_cb(len(batch))
|
|
356
|
+
return response.json()
|
|
357
|
+
|
|
358
|
+
tasks = [fetch_with_semaphore(batch) for batch in batched(video_ids, batch_size=batch_size)]
|
|
359
|
+
responses = await asyncio.gather(*tasks)
|
|
360
|
+
json_response = [item for response in responses for item in response]
|
|
361
|
+
return json_response
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import os
|
|
4
5
|
import re
|
|
5
|
-
from typing import Callable, List, Literal, Optional, Tuple, Union
|
|
6
|
+
from typing import Callable, Dict, List, Literal, Optional, Tuple, Union
|
|
6
7
|
|
|
7
8
|
import numpy as np
|
|
8
9
|
from tqdm import tqdm
|
|
9
10
|
|
|
11
|
+
from supervisely._utils import batched
|
|
10
12
|
from supervisely.annotation.obj_class import ObjClass
|
|
11
13
|
from supervisely.api.entity_annotation.entity_annotation_api import EntityAnnotationAPI
|
|
12
14
|
from supervisely.api.module_api import ApiField
|
|
13
|
-
from supervisely.collection.key_indexed_collection import DuplicateKeyError
|
|
14
15
|
from supervisely.geometry.any_geometry import AnyGeometry
|
|
15
16
|
from supervisely.geometry.mask_3d import Mask3D
|
|
16
17
|
from supervisely.io.fs import (
|
|
@@ -456,3 +457,102 @@ class VolumeAnnotationAPI(EntityAnnotationAPI):
|
|
|
456
457
|
nrrd_paths.remove(nrrd_path)
|
|
457
458
|
keep_nrrd_paths.remove(nrrd_path)
|
|
458
459
|
return stl_paths, nrrd_paths, keep_nrrd_paths
|
|
460
|
+
|
|
461
|
+
async def download_async(
|
|
462
|
+
self,
|
|
463
|
+
volume_id: int,
|
|
464
|
+
semaphore: Optional[asyncio.Semaphore] = None,
|
|
465
|
+
integer_coords: bool = True,
|
|
466
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
467
|
+
) -> Dict:
|
|
468
|
+
"""
|
|
469
|
+
Download information about VolumeAnnotation by volume ID from API asynchronously.
|
|
470
|
+
|
|
471
|
+
:param volume_id: Volume ID in Supervisely.
|
|
472
|
+
:type volume_id: int
|
|
473
|
+
:param semaphore: Semaphore to limit the number of parallel downloads.
|
|
474
|
+
:type semaphore: asyncio.Semaphore, optional
|
|
475
|
+
:param integer_coords: If True, returns coordinates as integers for objects. If False, returns as floats.
|
|
476
|
+
:type integer_coords: bool, optional
|
|
477
|
+
:param progress_cb: Function for tracking download progress.
|
|
478
|
+
:type progress_cb: tqdm or callable, optional
|
|
479
|
+
:return: Information about VolumeAnnotation in json format
|
|
480
|
+
:rtype: :class:`dict`
|
|
481
|
+
|
|
482
|
+
:Usage example:
|
|
483
|
+
|
|
484
|
+
.. code-block:: python
|
|
485
|
+
|
|
486
|
+
import supervisely as sly
|
|
487
|
+
|
|
488
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
489
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
490
|
+
api = sly.Api.from_env()
|
|
491
|
+
|
|
492
|
+
volume_id = 198702499
|
|
493
|
+
loop = sly.utils.get_or_create_event_loop()
|
|
494
|
+
ann_info = loop.run_until_complete(api.volume.annotation.download_async(volume_id))
|
|
495
|
+
"""
|
|
496
|
+
return await self.download_bulk_async(
|
|
497
|
+
volume_ids=[volume_id],
|
|
498
|
+
semaphore=semaphore,
|
|
499
|
+
integer_coords=integer_coords,
|
|
500
|
+
progress_cb=progress_cb,
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
async def download_bulk_async(
|
|
504
|
+
self,
|
|
505
|
+
volume_ids: List[int],
|
|
506
|
+
semaphore: Optional[asyncio.Semaphore] = None,
|
|
507
|
+
integer_coords: bool = True,
|
|
508
|
+
progress_cb: Optional[Union[tqdm, Callable]] = None,
|
|
509
|
+
) -> List[Dict]:
|
|
510
|
+
"""
|
|
511
|
+
Download information about VolumeAnnotation in bulk by volume IDs from API asynchronously.
|
|
512
|
+
|
|
513
|
+
:param volume_ids: List of Volume IDs in Supervisely. All volumes must be from the same dataset.
|
|
514
|
+
:type volume_ids: int
|
|
515
|
+
:param semaphore: Semaphore to limit the number of parallel downloads.
|
|
516
|
+
:type semaphore: asyncio.Semaphore, optional
|
|
517
|
+
:param integer_coords: If True, returns coordinates as integers for objects. If False, returns as floats.
|
|
518
|
+
:type integer_coords: bool, optional
|
|
519
|
+
:param progress_cb: Function for tracking download progress.
|
|
520
|
+
:type progress_cb: tqdm or callable, optional
|
|
521
|
+
:return: Information about VolumeAnnotations in json format
|
|
522
|
+
:rtype: :class:`list`
|
|
523
|
+
|
|
524
|
+
:Usage example:
|
|
525
|
+
|
|
526
|
+
.. code-block:: python
|
|
527
|
+
|
|
528
|
+
import supervisely as sly
|
|
529
|
+
|
|
530
|
+
os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
|
|
531
|
+
os.environ['API_TOKEN'] = 'Your Supervisely API Token'
|
|
532
|
+
api = sly.Api.from_env()
|
|
533
|
+
|
|
534
|
+
volume_ids = [198702499, 198702500, 198702501]
|
|
535
|
+
loop = sly.utils.get_or_create_event_loop()
|
|
536
|
+
ann_infos = loop.run_until_complete(api.volume.annotation.download_bulk_async(volume_ids))
|
|
537
|
+
"""
|
|
538
|
+
if semaphore is None:
|
|
539
|
+
semaphore = self._api.get_default_semaphore()
|
|
540
|
+
|
|
541
|
+
async def fetch_with_semaphore(batch):
|
|
542
|
+
async with semaphore:
|
|
543
|
+
json_data = {
|
|
544
|
+
self._entity_ids_str: batch,
|
|
545
|
+
ApiField.INTEGER_COORDS: integer_coords,
|
|
546
|
+
}
|
|
547
|
+
response = await self._api.post_async(
|
|
548
|
+
self._method_download_bulk,
|
|
549
|
+
json=json_data,
|
|
550
|
+
)
|
|
551
|
+
if progress_cb is not None:
|
|
552
|
+
progress_cb(len(batch))
|
|
553
|
+
return response.json()
|
|
554
|
+
|
|
555
|
+
tasks = [fetch_with_semaphore(batch) for batch in batched(volume_ids)]
|
|
556
|
+
responses = await asyncio.gather(*tasks)
|
|
557
|
+
json_response = [item for response in responses for item in response]
|
|
558
|
+
return json_response
|
|
@@ -148,3 +148,4 @@ from supervisely.app.widgets.select_dataset_tree.select_dataset_tree import Sele
|
|
|
148
148
|
from supervisely.app.widgets.grid_gallery_v2.grid_gallery_v2 import GridGalleryV2
|
|
149
149
|
from supervisely.app.widgets.report_thumbnail.report_thumbnail import ReportThumbnail
|
|
150
150
|
from supervisely.app.widgets.experiment_selector.experiment_selector import ExperimentSelector
|
|
151
|
+
from supervisely.app.widgets.bokeh.bokeh import Bokeh
|
|
File without changes
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Any, Callable, Dict, List, Literal, Optional, Union
|
|
7
|
+
|
|
8
|
+
from fastapi.responses import HTMLResponse
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
from supervisely.app.widgets import Widget
|
|
11
|
+
from supervisely._utils import is_production
|
|
12
|
+
from supervisely.io.env import task_id
|
|
13
|
+
from supervisely.api.api import Api
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SelectedIds(BaseModel):
|
|
17
|
+
selected_ids: List[Any]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Bokeh(Widget):
|
|
21
|
+
class Routes:
|
|
22
|
+
VALUE_CHANGED = "value_changed"
|
|
23
|
+
HTML_ROUTE = "bokeh.html"
|
|
24
|
+
|
|
25
|
+
class Plot(ABC):
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def add(self, plot) -> None:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def register(self, route_path: str) -> None:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
class Circle(Plot):
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
x_coordinates: List[Union[int, float]],
|
|
38
|
+
y_coordinates: List[Union[int, float]],
|
|
39
|
+
radii: Optional[Union[Union[int, float], List[Union[int, float]]]] = None,
|
|
40
|
+
colors: Optional[Union[str, List[str]]] = None,
|
|
41
|
+
data: Optional[List[Any]] = None,
|
|
42
|
+
dynamic_selection: bool = False,
|
|
43
|
+
fill_alpha: float = 0.5,
|
|
44
|
+
line_color: Optional[str] = None,
|
|
45
|
+
):
|
|
46
|
+
if not colors:
|
|
47
|
+
colors = Bokeh._generate_colors(x_coordinates, y_coordinates)
|
|
48
|
+
elif isinstance(colors, str):
|
|
49
|
+
colors = [colors] * len(x_coordinates)
|
|
50
|
+
|
|
51
|
+
if not radii:
|
|
52
|
+
radii = Bokeh._generate_radii(x_coordinates, y_coordinates)
|
|
53
|
+
elif isinstance(radii, (int, float)):
|
|
54
|
+
radii = [radii] * len(x_coordinates)
|
|
55
|
+
|
|
56
|
+
if not len(x_coordinates) == len(y_coordinates) == len(radii) == len(colors):
|
|
57
|
+
raise ValueError(
|
|
58
|
+
"x_coordinates, y_coordinates, radii, and colors must have the same length"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if data is not None and len(data) != len(x_coordinates):
|
|
62
|
+
raise ValueError("data must have the same length as x_coordinates")
|
|
63
|
+
|
|
64
|
+
if data is None:
|
|
65
|
+
data = list(range(len(x_coordinates)))
|
|
66
|
+
|
|
67
|
+
self._x_coordinates = x_coordinates
|
|
68
|
+
self._y_coordinates = y_coordinates
|
|
69
|
+
self._radii = radii
|
|
70
|
+
self._colors = colors
|
|
71
|
+
self._data = data
|
|
72
|
+
self._source = None
|
|
73
|
+
self._dynamic_selection = dynamic_selection
|
|
74
|
+
self._fill_alpha = fill_alpha
|
|
75
|
+
self._line_color = line_color
|
|
76
|
+
|
|
77
|
+
def add(self, plot) -> None:
|
|
78
|
+
from bokeh.models import ( # pylint: disable=import-error
|
|
79
|
+
ColumnDataSource,
|
|
80
|
+
LassoSelectTool,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
data = dict(
|
|
84
|
+
x=self._x_coordinates,
|
|
85
|
+
y=self._y_coordinates,
|
|
86
|
+
radius=self._radii,
|
|
87
|
+
colors=self._colors,
|
|
88
|
+
ids=self._data,
|
|
89
|
+
)
|
|
90
|
+
self._source = ColumnDataSource(data=data)
|
|
91
|
+
|
|
92
|
+
renderer = plot.circle(
|
|
93
|
+
"x",
|
|
94
|
+
"y",
|
|
95
|
+
radius="radius",
|
|
96
|
+
fill_color="colors",
|
|
97
|
+
fill_alpha=self._fill_alpha,
|
|
98
|
+
line_color=self._line_color,
|
|
99
|
+
source=self._source,
|
|
100
|
+
)
|
|
101
|
+
if not self._dynamic_selection:
|
|
102
|
+
for tool in plot.tools:
|
|
103
|
+
if isinstance(tool, (LassoSelectTool)):
|
|
104
|
+
tool.continuous = False
|
|
105
|
+
|
|
106
|
+
return renderer
|
|
107
|
+
|
|
108
|
+
def register(self, route_path: str) -> None:
|
|
109
|
+
from bokeh.models import CustomJS # pylint: disable=import-error
|
|
110
|
+
|
|
111
|
+
if not hasattr(self, "_source"):
|
|
112
|
+
raise ValueError("Plot must be added to a Bokeh plot before registering")
|
|
113
|
+
|
|
114
|
+
if is_production():
|
|
115
|
+
api = Api()
|
|
116
|
+
task_info = api.task.get_info_by_id(task_id())
|
|
117
|
+
if task_info is not None:
|
|
118
|
+
route_path = f"/net/{task_info['meta']['sessionToken']}{route_path}"
|
|
119
|
+
callback = CustomJS(
|
|
120
|
+
args=dict(source=self._source),
|
|
121
|
+
code="""
|
|
122
|
+
var indices = source.selected.indices;
|
|
123
|
+
var selected_ids = [];
|
|
124
|
+
for (var i = 0; i < indices.length; i++) {{
|
|
125
|
+
selected_ids.push(source.data['ids'][indices[i]]);
|
|
126
|
+
}}
|
|
127
|
+
var xhr = new XMLHttpRequest();
|
|
128
|
+
xhr.open("POST", "{route_path}", true);
|
|
129
|
+
xhr.setRequestHeader("Content-Type", "application/json");
|
|
130
|
+
xhr.send(JSON.stringify({{selected_ids: selected_ids}}));
|
|
131
|
+
""".format(
|
|
132
|
+
route_path=route_path
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
self._source.selected.js_on_change("indices", callback)
|
|
136
|
+
|
|
137
|
+
def __init__(
|
|
138
|
+
self,
|
|
139
|
+
plots: List[Plot],
|
|
140
|
+
width: int = 1000,
|
|
141
|
+
height: int = 600,
|
|
142
|
+
tools: List[str] = [
|
|
143
|
+
"pan",
|
|
144
|
+
"wheel_zoom",
|
|
145
|
+
"box_zoom",
|
|
146
|
+
"reset",
|
|
147
|
+
"save",
|
|
148
|
+
"poly_select",
|
|
149
|
+
"tap",
|
|
150
|
+
"lasso_select",
|
|
151
|
+
],
|
|
152
|
+
toolbar_location: Literal["above", "below", "left", "right"] = "above",
|
|
153
|
+
x_axis_visible: bool = False,
|
|
154
|
+
y_axis_visible: bool = False,
|
|
155
|
+
grid_visible: bool = False,
|
|
156
|
+
widget_id: Optional[str] = None,
|
|
157
|
+
**kwargs,
|
|
158
|
+
):
|
|
159
|
+
from bokeh.plotting import figure # pylint: disable=import-error
|
|
160
|
+
|
|
161
|
+
self.widget_id = widget_id
|
|
162
|
+
self._plots = plots
|
|
163
|
+
self._plot = figure(width=width, height=height, tools=tools, toolbar_location="above")
|
|
164
|
+
self._renderers = []
|
|
165
|
+
|
|
166
|
+
self._plot.xaxis.visible = x_axis_visible
|
|
167
|
+
self._plot.yaxis.visible = y_axis_visible
|
|
168
|
+
self._plot.grid.visible = grid_visible
|
|
169
|
+
super().__init__(widget_id=widget_id, file_path=__file__)
|
|
170
|
+
|
|
171
|
+
self._process_plots(plots)
|
|
172
|
+
self._update_html()
|
|
173
|
+
|
|
174
|
+
server = self._sly_app.get_server()
|
|
175
|
+
|
|
176
|
+
@server.get(self.html_route)
|
|
177
|
+
def _html_response() -> None:
|
|
178
|
+
return HTMLResponse(content=self.get_html())
|
|
179
|
+
|
|
180
|
+
# JinjaWidgets().context.pop(self.widget_id, None) # remove the widget from index.html
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def route_path(self) -> str:
|
|
184
|
+
return self.get_route_path(Bokeh.Routes.VALUE_CHANGED)
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def html_route(self) -> str:
|
|
188
|
+
return self.get_route_path(Bokeh.Routes.HTML_ROUTE)
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def html_route_with_timestamp(self) -> str:
|
|
192
|
+
return f".{self.html_route}?t={datetime.now().timestamp()}"
|
|
193
|
+
|
|
194
|
+
def add_plots(self, plots: List[Plot]) -> None:
|
|
195
|
+
self._plots.extend(plots)
|
|
196
|
+
self._process_plots(plots)
|
|
197
|
+
self._update_html()
|
|
198
|
+
|
|
199
|
+
def clear(self) -> None:
|
|
200
|
+
self._plots = []
|
|
201
|
+
self._renderers = []
|
|
202
|
+
self._plot.renderers = []
|
|
203
|
+
self._update_html()
|
|
204
|
+
|
|
205
|
+
def remove_plot(self, idx: int) -> None:
|
|
206
|
+
renderer = self._renderers.pop(idx)
|
|
207
|
+
self._plot.renderers.remove(renderer)
|
|
208
|
+
self._update_html()
|
|
209
|
+
|
|
210
|
+
def _process_plots(self, plots: List[Plot]) -> None:
|
|
211
|
+
for plot in plots:
|
|
212
|
+
renderer = plot.add(self._plot)
|
|
213
|
+
plot.register(self.route_path)
|
|
214
|
+
self._renderers.append(renderer)
|
|
215
|
+
|
|
216
|
+
def _update_html(self) -> None:
|
|
217
|
+
from bokeh.embed import components # pylint: disable=import-error
|
|
218
|
+
|
|
219
|
+
script, self._div = components(self._plot, wrap_script=False)
|
|
220
|
+
self._div_id = self._get_div_id(self._div)
|
|
221
|
+
self._script = self._update_script(script)
|
|
222
|
+
|
|
223
|
+
@staticmethod
|
|
224
|
+
def _generate_colors(x_coordinates: List[int], y_coordinates: List[int]) -> List[str]:
|
|
225
|
+
colors = [
|
|
226
|
+
"#%02x%02x%02x" % (int(r), int(g), 150)
|
|
227
|
+
for r, g in zip(
|
|
228
|
+
[50 + 2 * xi for xi in x_coordinates], [30 + 2 * yi for yi in y_coordinates]
|
|
229
|
+
)
|
|
230
|
+
]
|
|
231
|
+
return colors
|
|
232
|
+
|
|
233
|
+
@staticmethod
|
|
234
|
+
def _generate_radii(x_coordinates: List[int], y_coordinates: List[int]) -> List[int]:
|
|
235
|
+
return [1] * len(x_coordinates)
|
|
236
|
+
|
|
237
|
+
def _get_div_id(self, div: str) -> str:
|
|
238
|
+
match = re.search(r'id="([^"]+)"', div)
|
|
239
|
+
if match:
|
|
240
|
+
return match.group(1)
|
|
241
|
+
raise ValueError(f"No div id found in {div}")
|
|
242
|
+
|
|
243
|
+
def _update_script(self, script: str) -> str:
|
|
244
|
+
# TODO: Reimplement using regex.
|
|
245
|
+
insert_after = "const fn = function() {"
|
|
246
|
+
updated_script = ""
|
|
247
|
+
for line in script.split("\n"):
|
|
248
|
+
if line.strip().startswith(insert_after):
|
|
249
|
+
line = line + f"\n const el = document.querySelector('#{self._div_id}');"
|
|
250
|
+
line += "\n if (el === null) {"
|
|
251
|
+
line += "\n setTimeout(fn, 500);"
|
|
252
|
+
line += "\n return;"
|
|
253
|
+
line += "\n }"
|
|
254
|
+
updated_script += line + "\n"
|
|
255
|
+
return updated_script
|
|
256
|
+
|
|
257
|
+
def get_json_data(self):
|
|
258
|
+
return {}
|
|
259
|
+
|
|
260
|
+
def get_json_state(self):
|
|
261
|
+
return {}
|
|
262
|
+
|
|
263
|
+
def value_changed(self, func: Callable) -> Callable:
|
|
264
|
+
server = self._sly_app.get_server()
|
|
265
|
+
self._changes_handled = True
|
|
266
|
+
|
|
267
|
+
@server.post(self.route_path)
|
|
268
|
+
def _click(selected_ids: SelectedIds) -> None:
|
|
269
|
+
func(selected_ids.selected_ids)
|
|
270
|
+
|
|
271
|
+
return _click
|
|
272
|
+
|
|
273
|
+
def get_html(self) -> str:
|
|
274
|
+
return f"""<div>
|
|
275
|
+
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.1.1.min.js"></script>
|
|
276
|
+
<script type="text/javascript"> {self._script} </script>
|
|
277
|
+
{self._div}
|
|
278
|
+
</div>"""
|
|
@@ -56,6 +56,8 @@ class IFrame(Widget):
|
|
|
56
56
|
height: Optional[Union[int, str]] = None,
|
|
57
57
|
width: Optional[Union[int, str]] = None,
|
|
58
58
|
):
|
|
59
|
+
height = height or self._height
|
|
60
|
+
width = width or self._width
|
|
59
61
|
self._update(path_to_html=path_to_html, height=height, width=width)
|
|
60
62
|
|
|
61
63
|
def clean_up(self):
|
|
@@ -120,6 +120,7 @@ class SelectDatasetTree(Widget):
|
|
|
120
120
|
|
|
121
121
|
# Extract values from Enum to match the .type property of the ProjectInfo object.
|
|
122
122
|
|
|
123
|
+
self._project_types = None
|
|
123
124
|
if allowed_project_types is not None:
|
|
124
125
|
if all(allowed_project_types) is isinstance(allowed_project_types, ProjectType):
|
|
125
126
|
self._project_types = (
|
|
@@ -129,8 +130,6 @@ class SelectDatasetTree(Widget):
|
|
|
129
130
|
)
|
|
130
131
|
elif all(allowed_project_types) is isinstance(allowed_project_types, str):
|
|
131
132
|
self._project_types = allowed_project_types
|
|
132
|
-
else:
|
|
133
|
-
self._project_types = None
|
|
134
133
|
|
|
135
134
|
# Widget components.
|
|
136
135
|
self._select_team = None
|
|
@@ -53,7 +53,7 @@ supervisely/api/entity_annotation/figure_api.py,sha256=_JS1x0jn5neoCnZCBKHUBwspo
|
|
|
53
53
|
supervisely/api/entity_annotation/object_api.py,sha256=gbcNvN_KY6G80Me8fHKQgryc2Co7VU_kfFd1GYILZ4E,8875
|
|
54
54
|
supervisely/api/entity_annotation/tag_api.py,sha256=M-28m9h8R4k9Eqo6P1S0UH8_D5kqCwAvQLYY6_Yz4oM,11161
|
|
55
55
|
supervisely/api/pointcloud/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
|
-
supervisely/api/pointcloud/pointcloud_annotation_api.py,sha256=
|
|
56
|
+
supervisely/api/pointcloud/pointcloud_annotation_api.py,sha256=x2Bw_1ZaGZffc89k670LWQiwMhlb4CbB-6suDpHJRgg,11256
|
|
57
57
|
supervisely/api/pointcloud/pointcloud_api.py,sha256=pn72znCr5hkAfgniXxfD6Vi8-HqRb1Nrf6l23-HQ7Bc,53277
|
|
58
58
|
supervisely/api/pointcloud/pointcloud_episode_annotation_api.py,sha256=YGpU7g05XNV9o5daH5mFcUMmPPfgd085yIMNzXOVJqc,7009
|
|
59
59
|
supervisely/api/pointcloud/pointcloud_episode_api.py,sha256=K_oPJeibj5oRYooeEWuSe6VxlxCYK3D8yLunm7vDeM0,7919
|
|
@@ -63,14 +63,14 @@ supervisely/api/pointcloud/pointcloud_figure_api.py,sha256=r1sk3g9IgYVHuNhxyZT1T
|
|
|
63
63
|
supervisely/api/pointcloud/pointcloud_object_api.py,sha256=bO1USWb9HAywG_CW4CDu1HLu6l58OqQFuD3ikS9F3bM,5130
|
|
64
64
|
supervisely/api/pointcloud/pointcloud_tag_api.py,sha256=iShtr052nOElxsyMyZEUT2vypEm6kP00gnP13ABX24A,4691
|
|
65
65
|
supervisely/api/video/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
|
-
supervisely/api/video/video_annotation_api.py,sha256=
|
|
66
|
+
supervisely/api/video/video_annotation_api.py,sha256=nvbn_ofcqFCZ2qKgu0O5y5zOHxFc4tsY-o93sUgqlWk,14134
|
|
67
67
|
supervisely/api/video/video_api.py,sha256=KO_Nfqa4xDWrc7FqOR14PciC0TX8PF0g0peqgPnctEE,94677
|
|
68
68
|
supervisely/api/video/video_figure_api.py,sha256=quksohjhgrK2l2-PtbbNE99fOW6uWXX59-_4xfc-I-k,6244
|
|
69
69
|
supervisely/api/video/video_frame_api.py,sha256=4GwSI4xdCNYEUvTqzKc-Ewd44fw5zqkFoD24jrrN_aY,10214
|
|
70
70
|
supervisely/api/video/video_object_api.py,sha256=IC0NP8EoIT_d3xxDRgz2cA3ixSiuJ5ymy64eS-RfmDM,2227
|
|
71
71
|
supervisely/api/video/video_tag_api.py,sha256=oJgdJt_0w-5UfXaxZ7jdxK0PetZjax1vOfjm0IMYwe8,12266
|
|
72
72
|
supervisely/api/volume/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
73
|
-
supervisely/api/volume/volume_annotation_api.py,sha256=
|
|
73
|
+
supervisely/api/volume/volume_annotation_api.py,sha256=q1nzSeMBndxCTPf-3apliXvqcOaOjYWohSO5CZDHITQ,22243
|
|
74
74
|
supervisely/api/volume/volume_api.py,sha256=-n3r5qj4I4EtoERKTHFT8PpsKFJ141SvQfamIcHqWK4,55387
|
|
75
75
|
supervisely/api/volume/volume_figure_api.py,sha256=WwmcMw7o3Nvyv52tzmz64yF-WJI0qzAU-zL2JlD7_w0,26039
|
|
76
76
|
supervisely/api/volume/volume_object_api.py,sha256=F7pLV2MTlBlyN6fEKdxBSUatIMGWSuu8bWj3Hvcageo,2139
|
|
@@ -110,7 +110,7 @@ supervisely/app/v1/widgets/grid_gallery.py,sha256=hEMC0MNfZ4xG2N118Mou_hptLhrikg
|
|
|
110
110
|
supervisely/app/v1/widgets/predictions_dynamics_gallery.py,sha256=l6Ee8-c14yeSnlu4qFsLbmZ5Su63zacO3wmdtH86TMM,8079
|
|
111
111
|
supervisely/app/v1/widgets/progress_bar.py,sha256=8gvQbAUHXPU8_JgC0JZkEBSRCccvg2l4Gtg8DeBCgC8,3184
|
|
112
112
|
supervisely/app/v1/widgets/single_image_gallery.py,sha256=fyuC4jfCHC5rNL1JrHJCE8NaneH0nv0k-0iVkOnY0Wc,2958
|
|
113
|
-
supervisely/app/widgets/__init__.py,sha256=
|
|
113
|
+
supervisely/app/widgets/__init__.py,sha256=ilrM0pr7RVbw05cvke4dnXYXsDAKSod9ZcUb7lc-2Ps,10101
|
|
114
114
|
supervisely/app/widgets/select_sly_utils.py,sha256=gBenYkJyCl3Fa4u2GI6BKXul-AqnzvGK32Y6hxXKccA,288
|
|
115
115
|
supervisely/app/widgets/widget.py,sha256=e9tyZj7XhqDWiN5Wwk2xScXOmf__vRCoHflpGtv1RS0,9820
|
|
116
116
|
supervisely/app/widgets/agent_selector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -128,6 +128,9 @@ supervisely/app/widgets/badge/template.html,sha256=B77SyKHTaoIPrvjJtAxQfBzCs7Y8v
|
|
|
128
128
|
supervisely/app/widgets/binded_input_number/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
129
129
|
supervisely/app/widgets/binded_input_number/binded_input_number.py,sha256=RXTAGaMXtGOl4pprVLyQosr61t2rI_U_U8VNHhSyBhA,6251
|
|
130
130
|
supervisely/app/widgets/binded_input_number/template.html,sha256=uCEZ54BNC2itr39wxxThXw62WlJ9659cuz8osCm0WZE,162
|
|
131
|
+
supervisely/app/widgets/bokeh/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
132
|
+
supervisely/app/widgets/bokeh/bokeh.py,sha256=9s9Vx8f-NI-2hr9xjuYapFMsZkFF_n65WAWeMXgqTWY,9563
|
|
133
|
+
supervisely/app/widgets/bokeh/template.html,sha256=ntsh7xx4q9OHG62sa_r3INDxsXgvdPFIWTtYaWn_0t8,12
|
|
131
134
|
supervisely/app/widgets/button/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
132
135
|
supervisely/app/widgets/button/button.py,sha256=zDQinhOOjNNxdP2GIFrwTmVfGAeJJoKV6CT6C8KzQNI,10405
|
|
133
136
|
supervisely/app/widgets/button/style.css,sha256=ya4kKrQGwVhVtBydftudgjPpDdN3rcUvKSlrY-2h1Kw,93
|
|
@@ -299,7 +302,7 @@ supervisely/app/widgets/identity/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
|
|
|
299
302
|
supervisely/app/widgets/identity/identity.py,sha256=a_JihO_iXGILBfB4WVKvMPqywS9kmheO6c_f2YtaKok,328
|
|
300
303
|
supervisely/app/widgets/identity/template.html,sha256=stmCHZRUnUMu1b6ocPHtRsJ48JmX5yCZIuROPISvlJM,33
|
|
301
304
|
supervisely/app/widgets/iframe/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
302
|
-
supervisely/app/widgets/iframe/iframe.py,sha256=
|
|
305
|
+
supervisely/app/widgets/iframe/iframe.py,sha256=9D18tg2DpTix7ZBG6uS8Qff_1i-ArRFsLYvesICVYSo,2806
|
|
303
306
|
supervisely/app/widgets/iframe/template.html,sha256=5JFK7OY2zfvd9MxLRqBVq1RjWlQ9vHud_Ylk2tG_0yk,268
|
|
304
307
|
supervisely/app/widgets/image/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
305
308
|
supervisely/app/widgets/image/image.py,sha256=PJYzWfFAfMSnfuoAxJZ51n1blWLsimBwRT3UWDnkKFc,2592
|
|
@@ -433,7 +436,7 @@ supervisely/app/widgets/select_dataset/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JC
|
|
|
433
436
|
supervisely/app/widgets/select_dataset/select_dataset.py,sha256=2bltbJ70plGFoF8mpLxAU2huVBce8D6CsCcX9wxO5KU,9713
|
|
434
437
|
supervisely/app/widgets/select_dataset/template.html,sha256=7O_ZgmRs0vOL8tng6QvYbI_0o6A4yMAPB2MlfzWHeHQ,984
|
|
435
438
|
supervisely/app/widgets/select_dataset_tree/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
436
|
-
supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py,sha256=
|
|
439
|
+
supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py,sha256=abFPT8MJv6a7oPZzwCvak6q4HB72ijpW7axJHjoJvH0,22319
|
|
437
440
|
supervisely/app/widgets/select_dataset_tree/template.html,sha256=_uvKCMP0nkpSl3FiTUxqy10JZw3q8-9hXCv22W3BDF0,38
|
|
438
441
|
supervisely/app/widgets/select_item/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
439
442
|
supervisely/app/widgets/select_item/select_item.py,sha256=dcB0UN46rn3nFQybgrGpLRfwB6xnPo-GGrv9rsMeCbA,3833
|
|
@@ -1054,9 +1057,9 @@ supervisely/worker_proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
1054
1057
|
supervisely/worker_proto/worker_api_pb2.py,sha256=VQfi5JRBHs2pFCK1snec3JECgGnua3Xjqw_-b3aFxuM,59142
|
|
1055
1058
|
supervisely/worker_proto/worker_api_pb2_grpc.py,sha256=3BwQXOaP9qpdi0Dt9EKG--Lm8KGN0C5AgmUfRv77_Jk,28940
|
|
1056
1059
|
supervisely_lib/__init__.py,sha256=7-3QnN8Zf0wj8NCr2oJmqoQWMKKPKTECvjH9pd2S5vY,159
|
|
1057
|
-
supervisely-6.73.
|
|
1058
|
-
supervisely-6.73.
|
|
1059
|
-
supervisely-6.73.
|
|
1060
|
-
supervisely-6.73.
|
|
1061
|
-
supervisely-6.73.
|
|
1062
|
-
supervisely-6.73.
|
|
1060
|
+
supervisely-6.73.259.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
1061
|
+
supervisely-6.73.259.dist-info/METADATA,sha256=HIPNQSEj3JiqMJTUJwVoxBUSorGAhnvQ4tXlf1e0Xhk,33573
|
|
1062
|
+
supervisely-6.73.259.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
1063
|
+
supervisely-6.73.259.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
|
|
1064
|
+
supervisely-6.73.259.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
|
|
1065
|
+
supervisely-6.73.259.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|