HomeDashboard/.venv/lib/python3.12/site-packages/nicegui/elements/scene/scene_view.py
2026-01-03 14:54:18 +01:00

134 lines
5.6 KiB
Python

import asyncio
from typing import Optional
from typing_extensions import Self
from ...element import Element
from ...events import (
ClickEventArguments,
GenericEventArguments,
Handler,
SceneClickEventArguments,
SceneClickHit,
handle_event,
)
from .scene import Scene, SceneCamera
class SceneView(Element, component='scene_view.js', default_classes='nicegui-scene-view'):
# NOTE: The ESM is already registered in scene.py.
def __init__(self,
scene: Scene,
# DEPRECATED: enforce keyword-only arguments in NiceGUI 4.0
width: int = 400,
height: int = 300,
camera: Optional[SceneCamera] = None,
on_click: Optional[Handler[ClickEventArguments]] = None,
fps: int = 20,
show_stats: bool = False,
) -> None:
"""Scene View
Display an additional view of a 3D scene using `three.js <https://threejs.org/>`_.
This component can only show a scene and not modify it.
You can, however, independently move the camera.
Current limitation: 2D and 3D text objects are not supported and will not be displayed in the scene view.
:param scene: the scene which will be shown on the canvas
:param width: width of the canvas
:param height: height of the canvas
:param camera: camera definition, either instance of ``ui.scene.perspective_camera`` (default) or ``ui.scene.orthographic_camera``
:param on_click: callback to execute when a 3D object is clicked
:param fps: target frame rate for the scene view in frames per second (default: 20, *added in version 3.2.0*)
:param show_stats: whether to show performance stats (default: ``False``, *added in version 3.2.0*)
"""
super().__init__()
self._props['width'] = width
self._props['height'] = height
self._props['fps'] = fps
self._props['show_stats'] = show_stats
self._props['scene_id'] = scene.id
self.camera = camera or Scene.perspective_camera()
self._props['camera_type'] = self.camera.type
self._props['camera_params'] = self.camera.params
self._click_handlers = [on_click] if on_click else []
self.on('init', self._handle_init)
self.on('click3d', self._handle_click)
def on_click(self, callback: Handler[ClickEventArguments]) -> Self:
"""Add a callback to be invoked when a 3D object is clicked."""
self._click_handlers.append(callback)
return self
def _handle_init(self) -> None:
self.move_camera(duration=0)
self.run_method('init')
async def initialized(self) -> None:
"""Wait until the scene is initialized."""
event = asyncio.Event()
self.on('init', event.set, [])
await self.client.connected()
await event.wait()
def _handle_click(self, e: GenericEventArguments) -> None:
arguments = SceneClickEventArguments(
sender=self,
client=self.client,
click_type=e.args['click_type'],
button=e.args['button'],
alt=e.args['alt_key'],
ctrl=e.args['ctrl_key'],
meta=e.args['meta_key'],
shift=e.args['shift_key'],
hits=[SceneClickHit(
object_id=hit['object_id'],
object_name=hit['object_name'],
x=hit['point']['x'],
y=hit['point']['y'],
z=hit['point']['z'],
) for hit in e.args['hits']],
)
for handler in self._click_handlers:
handle_event(handler, arguments)
def move_camera(self,
x: Optional[float] = None,
y: Optional[float] = None,
z: Optional[float] = None,
look_at_x: Optional[float] = None,
look_at_y: Optional[float] = None,
look_at_z: Optional[float] = None,
up_x: Optional[float] = None,
up_y: Optional[float] = None,
up_z: Optional[float] = None,
duration: float = 0.5) -> None:
"""Move the camera to a new position.
:param x: camera x position
:param y: camera y position
:param z: camera z position
:param look_at_x: camera look-at x position
:param look_at_y: camera look-at y position
:param look_at_z: camera look-at z position
:param up_x: x component of the camera up vector
:param up_y: y component of the camera up vector
:param up_z: z component of the camera up vector
:param duration: duration of the movement in seconds (default: `0.5`)
"""
self.camera.x = self.camera.x if x is None else x
self.camera.y = self.camera.y if y is None else y
self.camera.z = self.camera.z if z is None else z
self.camera.look_at_x = self.camera.look_at_x if look_at_x is None else look_at_x
self.camera.look_at_y = self.camera.look_at_y if look_at_y is None else look_at_y
self.camera.look_at_z = self.camera.look_at_z if look_at_z is None else look_at_z
self.camera.up_x = self.camera.up_x if up_x is None else up_x
self.camera.up_y = self.camera.up_y if up_y is None else up_y
self.camera.up_z = self.camera.up_z if up_z is None else up_z
self.run_method('move_camera',
self.camera.x, self.camera.y, self.camera.z,
self.camera.look_at_x, self.camera.look_at_y, self.camera.look_at_z,
self.camera.up_x, self.camera.up_y, self.camera.up_z, duration)