supervisely 6.73.452__py3-none-any.whl → 6.73.513__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. supervisely/__init__.py +25 -1
  2. supervisely/annotation/annotation.py +8 -2
  3. supervisely/annotation/json_geometries_map.py +13 -12
  4. supervisely/api/annotation_api.py +6 -3
  5. supervisely/api/api.py +2 -0
  6. supervisely/api/app_api.py +10 -1
  7. supervisely/api/dataset_api.py +74 -12
  8. supervisely/api/entities_collection_api.py +10 -0
  9. supervisely/api/entity_annotation/figure_api.py +28 -0
  10. supervisely/api/entity_annotation/object_api.py +3 -3
  11. supervisely/api/entity_annotation/tag_api.py +63 -12
  12. supervisely/api/guides_api.py +210 -0
  13. supervisely/api/image_api.py +4 -0
  14. supervisely/api/labeling_job_api.py +83 -1
  15. supervisely/api/labeling_queue_api.py +33 -7
  16. supervisely/api/module_api.py +5 -0
  17. supervisely/api/project_api.py +71 -26
  18. supervisely/api/storage_api.py +3 -1
  19. supervisely/api/task_api.py +13 -2
  20. supervisely/api/team_api.py +4 -3
  21. supervisely/api/video/video_annotation_api.py +119 -3
  22. supervisely/api/video/video_api.py +65 -14
  23. supervisely/app/__init__.py +1 -1
  24. supervisely/app/content.py +23 -7
  25. supervisely/app/development/development.py +18 -2
  26. supervisely/app/fastapi/__init__.py +1 -0
  27. supervisely/app/fastapi/custom_static_files.py +1 -1
  28. supervisely/app/fastapi/multi_user.py +105 -0
  29. supervisely/app/fastapi/subapp.py +88 -42
  30. supervisely/app/fastapi/websocket.py +77 -9
  31. supervisely/app/singleton.py +21 -0
  32. supervisely/app/v1/app_service.py +18 -2
  33. supervisely/app/v1/constants.py +7 -1
  34. supervisely/app/widgets/__init__.py +6 -0
  35. supervisely/app/widgets/activity_feed/__init__.py +0 -0
  36. supervisely/app/widgets/activity_feed/activity_feed.py +239 -0
  37. supervisely/app/widgets/activity_feed/style.css +78 -0
  38. supervisely/app/widgets/activity_feed/template.html +22 -0
  39. supervisely/app/widgets/card/card.py +20 -0
  40. supervisely/app/widgets/classes_list_selector/classes_list_selector.py +121 -9
  41. supervisely/app/widgets/classes_list_selector/template.html +60 -93
  42. supervisely/app/widgets/classes_mapping/classes_mapping.py +13 -12
  43. supervisely/app/widgets/classes_table/classes_table.py +1 -0
  44. supervisely/app/widgets/deploy_model/deploy_model.py +56 -35
  45. supervisely/app/widgets/ecosystem_model_selector/ecosystem_model_selector.py +1 -1
  46. supervisely/app/widgets/experiment_selector/experiment_selector.py +8 -0
  47. supervisely/app/widgets/fast_table/fast_table.py +184 -60
  48. supervisely/app/widgets/fast_table/template.html +1 -1
  49. supervisely/app/widgets/heatmap/__init__.py +0 -0
  50. supervisely/app/widgets/heatmap/heatmap.py +564 -0
  51. supervisely/app/widgets/heatmap/script.js +533 -0
  52. supervisely/app/widgets/heatmap/style.css +233 -0
  53. supervisely/app/widgets/heatmap/template.html +21 -0
  54. supervisely/app/widgets/modal/__init__.py +0 -0
  55. supervisely/app/widgets/modal/modal.py +198 -0
  56. supervisely/app/widgets/modal/template.html +10 -0
  57. supervisely/app/widgets/object_class_view/object_class_view.py +3 -0
  58. supervisely/app/widgets/radio_tabs/radio_tabs.py +18 -2
  59. supervisely/app/widgets/radio_tabs/template.html +1 -0
  60. supervisely/app/widgets/select/select.py +6 -3
  61. supervisely/app/widgets/select_class/__init__.py +0 -0
  62. supervisely/app/widgets/select_class/select_class.py +363 -0
  63. supervisely/app/widgets/select_class/template.html +50 -0
  64. supervisely/app/widgets/select_cuda/select_cuda.py +22 -0
  65. supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +65 -7
  66. supervisely/app/widgets/select_tag/__init__.py +0 -0
  67. supervisely/app/widgets/select_tag/select_tag.py +352 -0
  68. supervisely/app/widgets/select_tag/template.html +64 -0
  69. supervisely/app/widgets/select_team/select_team.py +37 -4
  70. supervisely/app/widgets/select_team/template.html +4 -5
  71. supervisely/app/widgets/select_user/__init__.py +0 -0
  72. supervisely/app/widgets/select_user/select_user.py +270 -0
  73. supervisely/app/widgets/select_user/template.html +13 -0
  74. supervisely/app/widgets/select_workspace/select_workspace.py +59 -10
  75. supervisely/app/widgets/select_workspace/template.html +9 -12
  76. supervisely/app/widgets/table/table.py +68 -13
  77. supervisely/app/widgets/tree_select/tree_select.py +2 -0
  78. supervisely/aug/aug.py +6 -2
  79. supervisely/convert/base_converter.py +1 -0
  80. supervisely/convert/converter.py +2 -2
  81. supervisely/convert/image/image_converter.py +3 -1
  82. supervisely/convert/image/image_helper.py +48 -4
  83. supervisely/convert/image/label_studio/label_studio_converter.py +2 -0
  84. supervisely/convert/image/medical2d/medical2d_helper.py +2 -24
  85. supervisely/convert/image/multispectral/multispectral_converter.py +6 -0
  86. supervisely/convert/image/pascal_voc/pascal_voc_converter.py +8 -5
  87. supervisely/convert/image/pascal_voc/pascal_voc_helper.py +7 -0
  88. supervisely/convert/pointcloud/kitti_3d/kitti_3d_converter.py +33 -3
  89. supervisely/convert/pointcloud/kitti_3d/kitti_3d_helper.py +12 -5
  90. supervisely/convert/pointcloud/las/las_converter.py +13 -1
  91. supervisely/convert/pointcloud/las/las_helper.py +110 -11
  92. supervisely/convert/pointcloud/nuscenes_conv/nuscenes_converter.py +27 -16
  93. supervisely/convert/pointcloud/pointcloud_converter.py +91 -3
  94. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py +58 -22
  95. supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py +21 -47
  96. supervisely/convert/video/__init__.py +1 -0
  97. supervisely/convert/video/multi_view/__init__.py +0 -0
  98. supervisely/convert/video/multi_view/multi_view.py +543 -0
  99. supervisely/convert/video/sly/sly_video_converter.py +359 -3
  100. supervisely/convert/video/video_converter.py +22 -2
  101. supervisely/convert/volume/dicom/dicom_converter.py +13 -5
  102. supervisely/convert/volume/dicom/dicom_helper.py +30 -18
  103. supervisely/geometry/constants.py +1 -0
  104. supervisely/geometry/geometry.py +4 -0
  105. supervisely/geometry/helpers.py +5 -1
  106. supervisely/geometry/oriented_bbox.py +676 -0
  107. supervisely/geometry/rectangle.py +2 -1
  108. supervisely/io/env.py +76 -1
  109. supervisely/io/fs.py +21 -0
  110. supervisely/nn/benchmark/base_evaluator.py +104 -11
  111. supervisely/nn/benchmark/instance_segmentation/evaluator.py +1 -8
  112. supervisely/nn/benchmark/object_detection/evaluator.py +20 -4
  113. supervisely/nn/benchmark/object_detection/vis_metrics/pr_curve.py +10 -5
  114. supervisely/nn/benchmark/semantic_segmentation/evaluator.py +34 -16
  115. supervisely/nn/benchmark/semantic_segmentation/vis_metrics/confusion_matrix.py +1 -1
  116. supervisely/nn/benchmark/semantic_segmentation/vis_metrics/frequently_confused.py +1 -1
  117. supervisely/nn/benchmark/semantic_segmentation/vis_metrics/overview.py +1 -1
  118. supervisely/nn/benchmark/visualization/evaluation_result.py +66 -4
  119. supervisely/nn/inference/cache.py +43 -18
  120. supervisely/nn/inference/gui/serving_gui_template.py +5 -2
  121. supervisely/nn/inference/inference.py +795 -199
  122. supervisely/nn/inference/inference_request.py +42 -9
  123. supervisely/nn/inference/predict_app/gui/classes_selector.py +83 -12
  124. supervisely/nn/inference/predict_app/gui/gui.py +676 -488
  125. supervisely/nn/inference/predict_app/gui/input_selector.py +205 -26
  126. supervisely/nn/inference/predict_app/gui/model_selector.py +2 -4
  127. supervisely/nn/inference/predict_app/gui/output_selector.py +46 -6
  128. supervisely/nn/inference/predict_app/gui/settings_selector.py +756 -59
  129. supervisely/nn/inference/predict_app/gui/tags_selector.py +1 -1
  130. supervisely/nn/inference/predict_app/gui/utils.py +236 -119
  131. supervisely/nn/inference/predict_app/predict_app.py +2 -2
  132. supervisely/nn/inference/session.py +43 -35
  133. supervisely/nn/inference/tracking/bbox_tracking.py +113 -34
  134. supervisely/nn/inference/tracking/tracker_interface.py +7 -2
  135. supervisely/nn/inference/uploader.py +139 -12
  136. supervisely/nn/live_training/__init__.py +7 -0
  137. supervisely/nn/live_training/api_server.py +111 -0
  138. supervisely/nn/live_training/artifacts_utils.py +243 -0
  139. supervisely/nn/live_training/checkpoint_utils.py +229 -0
  140. supervisely/nn/live_training/dynamic_sampler.py +44 -0
  141. supervisely/nn/live_training/helpers.py +14 -0
  142. supervisely/nn/live_training/incremental_dataset.py +146 -0
  143. supervisely/nn/live_training/live_training.py +497 -0
  144. supervisely/nn/live_training/loss_plateau_detector.py +111 -0
  145. supervisely/nn/live_training/request_queue.py +52 -0
  146. supervisely/nn/model/model_api.py +9 -0
  147. supervisely/nn/prediction_dto.py +12 -1
  148. supervisely/nn/tracker/base_tracker.py +11 -1
  149. supervisely/nn/tracker/botsort/botsort_config.yaml +0 -1
  150. supervisely/nn/tracker/botsort/tracker/mc_bot_sort.py +7 -4
  151. supervisely/nn/tracker/botsort_tracker.py +94 -65
  152. supervisely/nn/tracker/visualize.py +87 -90
  153. supervisely/nn/training/gui/classes_selector.py +16 -1
  154. supervisely/nn/training/train_app.py +28 -29
  155. supervisely/project/data_version.py +115 -51
  156. supervisely/project/download.py +1 -1
  157. supervisely/project/pointcloud_episode_project.py +37 -8
  158. supervisely/project/pointcloud_project.py +30 -2
  159. supervisely/project/project.py +14 -2
  160. supervisely/project/project_meta.py +27 -1
  161. supervisely/project/project_settings.py +32 -18
  162. supervisely/project/versioning/__init__.py +1 -0
  163. supervisely/project/versioning/common.py +20 -0
  164. supervisely/project/versioning/schema_fields.py +35 -0
  165. supervisely/project/versioning/video_schema.py +221 -0
  166. supervisely/project/versioning/volume_schema.py +87 -0
  167. supervisely/project/video_project.py +717 -15
  168. supervisely/project/volume_project.py +623 -5
  169. supervisely/template/experiment/experiment.html.jinja +4 -4
  170. supervisely/template/experiment/experiment_generator.py +14 -21
  171. supervisely/template/live_training/__init__.py +0 -0
  172. supervisely/template/live_training/header.html.jinja +96 -0
  173. supervisely/template/live_training/live_training.html.jinja +51 -0
  174. supervisely/template/live_training/live_training_generator.py +464 -0
  175. supervisely/template/live_training/sly-style.css +402 -0
  176. supervisely/template/live_training/template.html.jinja +18 -0
  177. supervisely/versions.json +28 -26
  178. supervisely/video/sampling.py +39 -20
  179. supervisely/video/video.py +40 -11
  180. supervisely/video_annotation/video_object.py +29 -4
  181. supervisely/volume/stl_converter.py +2 -0
  182. supervisely/worker_api/agent_rpc.py +24 -1
  183. supervisely/worker_api/rpc_servicer.py +31 -7
  184. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/METADATA +56 -39
  185. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/RECORD +189 -142
  186. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/WHEEL +1 -1
  187. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/entry_points.txt +0 -0
  188. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info/licenses}/LICENSE +0 -0
  189. {supervisely-6.73.452.dist-info → supervisely-6.73.513.dist-info}/top_level.txt +0 -0
@@ -5,23 +5,19 @@ import time
5
5
  import traceback
6
6
  import uuid
7
7
  from concurrent.futures import Future, ThreadPoolExecutor
8
- from functools import partial, wraps
8
+ from functools import partial
9
9
  from typing import Any, Dict, List, Tuple, Union
10
10
 
11
- from supervisely._utils import rand_str
12
11
  from supervisely.nn.utils import get_gpu_usage, get_ram_usage
13
12
  from supervisely.sly_logger import logger
14
13
  from supervisely.task.progress import Progress
15
14
 
16
15
 
17
- def generate_uuid(self) -> str:
18
- """
19
- Generates a unique UUID for the inference request.
20
- """
21
- return uuid.uuid5(namespace=uuid.NAMESPACE_URL, name=f"{time.time()}-{rand_str(10)}").hex
22
-
23
-
24
16
  class InferenceRequest:
17
+ PAUSE_SLEEP_INTERVAL = 1.0
18
+ PAUSE_SLEEP_MAX_WAIT = 60 * 60 # 1 hour
19
+ PENDING_RESULTS_MAX_SIZE = 500
20
+
25
21
  class Stage:
26
22
  PREPARING = "Preparing model for inference..."
27
23
  INFERENCE = "Running inference..."
@@ -47,6 +43,8 @@ class InferenceRequest:
47
43
  self._final_result = None
48
44
  self._exception = None
49
45
  self._stopped = threading.Event()
46
+ self._paused_time = None
47
+ self._paused = threading.Event()
50
48
  self._progress_log_interval = 5.0
51
49
  self._last_progress_report_time = 0
52
50
  self.progress = Progress(
@@ -60,6 +58,8 @@ class InferenceRequest:
60
58
  self._updated_at = self._created_at
61
59
  self._finished = False
62
60
 
61
+ self.tracker = None
62
+
63
63
  self.global_progress = None
64
64
  self.global_progress_total = 1
65
65
  self.global_progress_current = 0
@@ -97,10 +97,16 @@ class InferenceRequest:
97
97
  def add_results(self, results: List[Dict]):
98
98
  with self._lock:
99
99
  self._pending_results.extend(results)
100
+ if self.pending_num() >= self.PENDING_RESULTS_MAX_SIZE:
101
+ logger.warning(
102
+ f"Pending results exceeded max size {self.PENDING_RESULTS_MAX_SIZE}, pausing inference request {self._uuid} for {self.PAUSE_SLEEP_MAX_WAIT} seconds."
103
+ )
104
+ self.pause()
100
105
  self._updated()
101
106
 
102
107
  def pop_pending_results(self, n: int = None):
103
108
  with self._lock:
109
+ self.continue_if_paused()
104
110
  if len(self._pending_results) == 0:
105
111
  return []
106
112
  if n is None:
@@ -176,6 +182,27 @@ class InferenceRequest:
176
182
  def is_stopped(self):
177
183
  return self._stopped.is_set()
178
184
 
185
+ def pause(self):
186
+ self._paused.set()
187
+ self._paused_time = time.monotonic()
188
+ self._updated()
189
+
190
+ def is_paused(self):
191
+ return self._paused.is_set()
192
+
193
+ def continue_if_paused(self):
194
+ if not self.is_paused():
195
+ return
196
+ logger.debug(f"Continuing paused inference request {self._uuid}")
197
+ self._paused.clear()
198
+ self._paused_time = None
199
+ self._updated()
200
+
201
+ def paused_for(self):
202
+ if not self.is_paused():
203
+ return 0
204
+ return time.monotonic() - self._paused_time
205
+
179
206
  def is_finished(self):
180
207
  return self._finished
181
208
 
@@ -212,6 +239,7 @@ class InferenceRequest:
212
239
  "exception": self.exception_json(),
213
240
  "is_inferring": self.is_inferring(),
214
241
  "stopped": self.is_stopped(),
242
+ "paused": self.is_paused(),
215
243
  "finished": self._finished,
216
244
  "created_at": self._created_at,
217
245
  "updated_at": self._updated_at,
@@ -365,6 +393,11 @@ class InferenceRequestsManager:
365
393
  inference_request = self._inference_requests.get(inference_request_uuid)
366
394
  if inference_request is None:
367
395
  continue
396
+ if (
397
+ inference_request.is_paused()
398
+ and inference_request.paused_for() < InferenceRequest.PAUSE_SLEEP_MAX_WAIT
399
+ ):
400
+ continue
368
401
  if inference_request.is_expired():
369
402
  self.remove(inference_request_uuid)
370
403
  logger.debug(f"Expired inference request {inference_request_uuid} was deleted")
@@ -1,10 +1,50 @@
1
- from typing import Dict, Any
2
- from supervisely.app.widgets import Button, Card, ClassesTable, Container, Text
3
-
4
-
1
+ from typing import Any, Dict
2
+
3
+ import pandas as pd
4
+
5
+ from supervisely.app.widgets import (
6
+ Button,
7
+ Card,
8
+ ClassesTable,
9
+ Container,
10
+ FastTable,
11
+ Text,
12
+ )
13
+ from supervisely.geometry.any_geometry import AnyGeometry
14
+ from supervisely.geometry.bitmap import Bitmap
15
+ from supervisely.geometry.cuboid import Cuboid
16
+ from supervisely.geometry.cuboid_3d import Cuboid3d
17
+ from supervisely.geometry.geometry import Geometry
18
+ from supervisely.geometry.graph import GraphNodes
19
+ from supervisely.geometry.multichannel_bitmap import MultichannelBitmap
20
+ from supervisely.geometry.point import Point
21
+ from supervisely.geometry.point_3d import Point3d
22
+ from supervisely.geometry.pointcloud import Pointcloud
23
+ from supervisely.geometry.polygon import Polygon
24
+ from supervisely.geometry.polyline import Polyline
25
+ from supervisely.geometry.rectangle import Rectangle
26
+ from supervisely.geometry.oriented_bbox import OrientedBBox
27
+ from supervisely.project.project_meta import ProjectMeta
28
+
29
+ type_to_zmdi_icon = {
30
+ AnyGeometry: "zmdi zmdi-shape",
31
+ Rectangle: "zmdi zmdi-crop-din", # "zmdi zmdi-square-o"
32
+ # sly.Polygon: "icons8-polygon", # "zmdi zmdi-edit"
33
+ Polygon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAB6klEQVRYhe2Wuy8EURTGf+u5VESNXq2yhYZCoeBv8RcI1i6NVUpsoVCKkHjUGlFTiYb1mFmh2MiKjVXMudmb3cPOzB0VXzKZm5k53/nmvO6Ff4RHD5AD7gFP1l3Kd11AHvCBEpAVW2esAvWmK6t8l1O+W0lCQEnIJoAZxUnzNQNkZF36jrQjgoA+uaciCgc9VaExBOyh/6WWAi1VhbjOJ4FbIXkBtgkK0BNHnYqNKUIPeBPbKyDdzpld5T6wD9SE4AwYjfEDaXFeFzE/doUWuhqwiFsOCwqv2hV2lU/L+sHBscGTxdvSFVoXpAjCZdauMHVic6ndl6U1VBsJCFhTeNUU9IiIEo3qvQYGHAV0AyfC5wNLhKipXuBCjA5wT8WxcM1FMRoBymK44CjAE57hqIazwCfwQdARcXa3UXHuRXVucIjb7jYvNkdxBZg0TBFid7PQTRAtX2xOiXkuMAMqYwkIE848rZFbjyNAmw9bIeweaZ2A5TgC7PnwKkTPtN+cTOrsyN3FEWAjRTAX6sA5ek77gSL6+WHZVQDAIHAjhJtN78aAS3lXAXYIivBOnCdyOAUYB6o0xqsvziry7FLE/Cp20cNcJEjDr8MUmVOVRzkVN+Nd7vZGVXXgiwxtPiRS5WFhz4fEq/zv4AvToMn7vCn3eAAAAABJRU5ErkJggg==",
34
+ Bitmap: "zmdi zmdi-brush",
35
+ Polyline: "zmdi zmdi-gesture",
36
+ Point: "zmdi zmdi-dot-circle-alt",
37
+ Cuboid: "zmdi zmdi-ungroup", #
38
+ GraphNodes: "zmdi zmdi-grain",
39
+ Cuboid3d: "zmdi zmdi-codepen",
40
+ Pointcloud: "zmdi zmdi-cloud-outline", # "zmdi zmdi-border-clear"
41
+ MultichannelBitmap: "zmdi zmdi-layers", # "zmdi zmdi-collection-item"
42
+ Point3d: "zmdi zmdi-filter-center-focus", # "zmdi zmdi-select-all"
43
+ OrientedBBox: "zmdi zmdi-rotate-cw",
44
+ }
5
45
  class ClassesSelector:
6
- title = "Classes Selector"
7
- description = "Select classes that will be used for inference"
46
+ title = "Classes"
47
+ description = "Select classes that will be used for inference. This classes are defined by the deployed model"
8
48
  lock_message = "Select previous step to unlock"
9
49
 
10
50
  def __init__(self):
@@ -24,7 +64,9 @@ class ClassesSelector:
24
64
  # -------------------------------- #
25
65
 
26
66
  # Classes
27
- self.classes_table = ClassesTable()
67
+ columns = ["class", "shape"]
68
+ self.classes_table = FastTable(columns=columns, page_size=100, is_selectable=True)
69
+ # self.classes_table = ClassesTable()
28
70
  self.classes_table.hide()
29
71
  # Add widgets to display ------------ #
30
72
  self.display_widgets.extend([self.classes_table])
@@ -45,25 +87,36 @@ class ClassesSelector:
45
87
  content=self.container,
46
88
  lock_message=self.lock_message,
47
89
  )
48
- self.card.lock()
49
90
  # -------------------------------- #
50
91
 
92
+ def lock(self):
93
+ self.card.lock()
94
+ self.classes_table.hide()
95
+ self.validator_text.hide()
96
+
97
+ def unlock(self):
98
+ self.card.unlock()
99
+ self.classes_table.show()
100
+
51
101
  @property
52
102
  def widgets_to_disable(self) -> list:
53
103
  return [self.classes_table]
54
104
 
105
+ def set_project_meta(self, project_meta) -> None:
106
+ self._update_meta(project_meta)
107
+
55
108
  def load_from_json(self, data: Dict[str, Any]) -> None:
56
109
  if "classes" in data:
57
110
  self.set_classes(data["classes"])
58
111
 
59
112
  def get_selected_classes(self) -> list:
60
- return self.classes_table.get_selected_classes()
113
+ return [row.row[0] for row in self.classes_table.get_selected_rows()]
61
114
 
62
115
  def set_classes(self, classes) -> None:
63
- self.classes_table.select_classes(classes)
116
+ self.classes_table.select_rows_by_value("class", classes)
64
117
 
65
118
  def select_all_classes(self) -> None:
66
- self.classes_table.select_all()
119
+ self.classes_table.select_rows(list(range(len(self.classes_table._rows_total))))
67
120
 
68
121
  def get_settings(self) -> Dict[str, Any]:
69
122
  return {"classes": self.get_selected_classes()}
@@ -73,7 +126,7 @@ class ClassesSelector:
73
126
  return True
74
127
 
75
128
  self.validator_text.hide()
76
- selected_classes = self.classes_table.get_selected_classes()
129
+ selected_classes = self.get_selected_classes()
77
130
  n_classes = len(selected_classes)
78
131
 
79
132
  if n_classes == 0:
@@ -89,3 +142,21 @@ class ClassesSelector:
89
142
  self.validator_text.set(text=". ".join(message_parts), status=status)
90
143
  self.validator_text.show()
91
144
  return is_valid
145
+
146
+ def _update_meta(self, project_meta: ProjectMeta) -> None:
147
+ table_data = []
148
+ for obj_class in project_meta.obj_classes:
149
+ table_line = []
150
+ name = obj_class.name
151
+ icon = type_to_zmdi_icon[AnyGeometry]
152
+ icon = type_to_zmdi_icon.get(obj_class.geometry_type, icon)
153
+ shape = obj_class.geometry_type.geometry_name().lower()
154
+ if shape == "graph":
155
+ shape = "graph (keypoints)"
156
+ table_line = [name, shape]
157
+ table_data.append(table_line)
158
+ self._table_data = table_data
159
+ if self._table_data:
160
+ self.classes_table.read_pandas(pd.DataFrame(self._table_data))
161
+ else:
162
+ self.classes_table.clear()