From a5c0fd6ba5e43871005d81ccca3880ea6b3c26a8 Mon Sep 17 00:00:00 2001 From: PingHsunTsai <47770211+PingHsunTsai@users.noreply.github.com> Date: Wed, 19 Jun 2024 13:08:19 +0200 Subject: [PATCH 1/2] add observer method --- src/compas_viewer/observer.py | 35 ++++++++++++++++++++++++++ src/compas_viewer/scene/sceneobject.py | 16 +++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 src/compas_viewer/observer.py diff --git a/src/compas_viewer/observer.py b/src/compas_viewer/observer.py new file mode 100644 index 0000000000..f5684437e9 --- /dev/null +++ b/src/compas_viewer/observer.py @@ -0,0 +1,35 @@ +from PySide6.QtCore import QTimer + +from compas_viewer.base import Base + + +class Observer(Base): + def __init__(self): + self._observers = set( + ( + self.viewer.renderer, + self.viewer.ui.sidebar, + ) + ) + + self._time = None + self.update_timer = QTimer() + self.update_timer.setSingleShot(True) + self.update_timer.timeout.connect(self.update_observers) + self.debounce_interval = 100 + + def add_observer(self, observer): + if observer not in self._observers: + self._observers.add(observer) + + def remove_observer(self, observer): + if observer in self._observers: + self._observers.remove(observer) + + def request_update(self): + if not self.update_timer.isActive(): + self.update_timer.start(self.debounce_interval) + + def update_observers(self): + for observer in self._observers: + observer.update() diff --git a/src/compas_viewer/scene/sceneobject.py b/src/compas_viewer/scene/sceneobject.py index b8a4567302..9be34cf06e 100644 --- a/src/compas_viewer/scene/sceneobject.py +++ b/src/compas_viewer/scene/sceneobject.py @@ -16,6 +16,7 @@ from compas_viewer.gl import make_vertex_buffer from compas_viewer.gl import update_index_buffer from compas_viewer.gl import update_vertex_buffer +from compas_viewer.observer import Observer from compas_viewer.renderer.shaders import Shader # Type template of point/line/face data for generating the buffers. @@ -101,6 +102,9 @@ def __init__( ): # Basic super().__init__(**kwargs) + + self.observer = Observer() + self.show = show self.show_points = show_points if show_points is not None else False self.show_lines = show_lines if show_lines is not None else True @@ -111,7 +115,7 @@ def __init__( # Selection self._is_locked = is_locked - self.is_selected = not is_locked and is_selected + self._is_selected = False # Visual self.background: bool = False @@ -135,6 +139,16 @@ def __init__( self._inited = False + @property + def is_selected(self): + return self._is_selected + + @is_selected.setter + def is_selected(self, value): + if self._is_selected != value: + self._is_selected = value + self.observer.request_update() + @property def is_locked(self): return self._is_locked From 2eec5f67a5d026e8b1d6a53d5cb1b6c3a7b858d3 Mon Sep 17 00:00:00 2001 From: PingHsunTsai <47770211+PingHsunTsai@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:44:49 +0200 Subject: [PATCH 2/2] clean up --- src/compas_viewer/commands.py | 17 --------------- src/compas_viewer/components/sceneform.py | 2 -- src/compas_viewer/observer.py | 3 +-- src/compas_viewer/scene/scene.py | 26 +++++++++++++++++++---- src/compas_viewer/scene/sceneobject.py | 9 +++++++- 5 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/compas_viewer/commands.py b/src/compas_viewer/commands.py index 92652d07b3..ca0fe9de08 100644 --- a/src/compas_viewer/commands.py +++ b/src/compas_viewer/commands.py @@ -236,9 +236,6 @@ def select_all(viewer: "Viewer"): if obj.show and not obj.is_locked: obj.is_selected = True - viewer.ui.sidebar.update() - viewer.renderer.update() - select_all_cmd = Command(title="Select All", callback=select_all) @@ -247,9 +244,6 @@ def deselect_all(viewer: "Viewer"): for obj in viewer.scene.objects: obj.is_selected = False - viewer.ui.sidebar.update() - viewer.renderer.update() - deselect_all_cmd = Command(title="DeSelect All", callback=deselect_all) @@ -275,8 +269,6 @@ def select_object(viewer: "Viewer", event: QMouseEvent): if selected_obj: selected_obj.is_selected = True - viewer.ui.sidebar.update() - viewer.renderer.update() @@ -295,7 +287,6 @@ def select_multiple(viewer: "Viewer", event: QMouseEvent): selected_obj = viewer.scene.instance_colors.get(tuple(unique_color[0])) # type: ignore if selected_obj: selected_obj.is_selected = True - viewer.ui.sidebar.update() viewer.renderer.update() @@ -343,8 +334,6 @@ def select_window(viewer: "Viewer", event: QMouseEvent): obj.is_selected = True continue - viewer.ui.sidebar.update() - viewer.renderer.update() @@ -364,8 +353,6 @@ def deselect_object(viewer: "Viewer", event: QMouseEvent): if selected_obj: selected_obj.is_selected = False - viewer.ui.sidebar.update() - viewer.renderer.update() @@ -394,7 +381,6 @@ def delete_selected(): if obj.is_selected: viewer.scene.remove(obj) del obj - viewer.renderer.update() # ============================================================================= @@ -411,9 +397,6 @@ def clear_scene(viewer: "Viewer"): viewer.scene.remove(obj) del obj - viewer.ui.sidebar.update() - viewer.renderer.update() - clear_scene_cmd = Command(title="Clear Scene", callback=clear_scene) diff --git a/src/compas_viewer/components/sceneform.py b/src/compas_viewer/components/sceneform.py index 8335a9f230..1f748b0f44 100644 --- a/src/compas_viewer/components/sceneform.py +++ b/src/compas_viewer/components/sceneform.py @@ -114,8 +114,6 @@ def on_item_clicked(self, item, column): if self.callback and node.is_selected: self.callback(node) - self.viewer.renderer.update() - def on_item_selection_changed(self): for item in self.selectedItems(): if self.callback: diff --git a/src/compas_viewer/observer.py b/src/compas_viewer/observer.py index f5684437e9..4cd01d9048 100644 --- a/src/compas_viewer/observer.py +++ b/src/compas_viewer/observer.py @@ -12,11 +12,10 @@ def __init__(self): ) ) - self._time = None self.update_timer = QTimer() self.update_timer.setSingleShot(True) self.update_timer.timeout.connect(self.update_observers) - self.debounce_interval = 100 + self.debounce_interval = 200 def add_observer(self, observer): if observer not in self._observers: diff --git a/src/compas_viewer/scene/scene.py b/src/compas_viewer/scene/scene.py index 1d40140356..001f13a180 100644 --- a/src/compas_viewer/scene/scene.py +++ b/src/compas_viewer/scene/scene.py @@ -9,6 +9,7 @@ from compas.datastructures import Datastructure from compas.geometry import Geometry from compas.scene import Scene +from compas_viewer.observer import Observer from .sceneobject import ViewerSceneObject @@ -68,6 +69,9 @@ class ViewerScene(Scene): def __init__(self, name: str = "ViewerScene", context: str = "Viewer"): super().__init__(name=name, context=context) + # Observer + self._observer = None + # Primitive self.objects: list[ViewerSceneObject] # Selection @@ -75,10 +79,11 @@ def __init__(self, name: str = "ViewerScene", context: str = "Viewer"): self._instance_colors_generator = instance_colors_generator() @property - def viewer(self): - from compas_viewer import Viewer - - return Viewer() + def observer(self): + """Observer: The observer object for the scene.""" + if self._observer is None: + self._observer = Observer() + return self._observer # TODO: These fixed kwargs could be moved to COMPAS core. def add( @@ -183,4 +188,17 @@ def add( u=u, **kwargs, ) + self.observer.request_update() return sceneobject + + def remove(self, obj: ViewerSceneObject) -> None: + """ + Remove an object from the scene. + + Parameters + ---------- + obj : :class:`compas_viewer.scene.ViewerSceneObject` + The object to remove. + """ + super().remove(obj) + self.observer.request_update() diff --git a/src/compas_viewer/scene/sceneobject.py b/src/compas_viewer/scene/sceneobject.py index 9be34cf06e..10cd7170fe 100644 --- a/src/compas_viewer/scene/sceneobject.py +++ b/src/compas_viewer/scene/sceneobject.py @@ -103,7 +103,7 @@ def __init__( # Basic super().__init__(**kwargs) - self.observer = Observer() + self._observer = None self.show = show self.show_points = show_points if show_points is not None else False @@ -139,6 +139,13 @@ def __init__( self._inited = False + @property + def observer(self) -> bool: + """bool : Whether the scene is requested to be updated.""" + if self._observer is None: + self._observer = Observer() + return self._observer + @property def is_selected(self): return self._is_selected