diff --git a/genesis/ext/pyrender/viewer.py b/genesis/ext/pyrender/viewer.py index 780e10b0a..efa640d65 100644 --- a/genesis/ext/pyrender/viewer.py +++ b/genesis/ext/pyrender/viewer.py @@ -205,6 +205,7 @@ def __init__( plane_reflection=False, env_separate_rigid=False, enable_interaction=False, + disable_keyboard_shortcuts=False, **kwargs, ): ####################################################################### @@ -284,6 +285,8 @@ def __init__( if registered_keys is not None: self._registered_keys = {ord(k.lower()): registered_keys[k] for k in registered_keys} + self._disable_keyboard_shortcuts = disable_keyboard_shortcuts + ####################################################################### # Save internal settings ####################################################################### @@ -294,6 +297,7 @@ def __init__( self._message_opac = 1.0 + self._ticks_till_fade self._display_instr = False + self._instr_texts = [ ["> [i]: show keyboard instructions"], [ @@ -735,47 +739,48 @@ def on_draw(self): self.viewer_interaction.on_draw() - if self._display_instr: - self._renderer.render_texts( - self._instr_texts[1], - TEXT_PADDING, - self.viewport_size[1] - TEXT_PADDING, - font_pt=26, - color=np.array([1.0, 1.0, 1.0, 0.85]), - ) - else: - self._renderer.render_texts( - self._instr_texts[0], - TEXT_PADDING, - self.viewport_size[1] - TEXT_PADDING, - font_pt=26, - color=np.array([1.0, 1.0, 1.0, 0.85]), - ) - - if self._message_text is not None: - self._renderer.render_text( - self._message_text, - self.viewport_size[0] - TEXT_PADDING, - TEXT_PADDING, - font_pt=20, - color=np.array([0.1, 0.7, 0.2, np.clip(self._message_opac, 0.0, 1.0)]), - align=TextAlign.BOTTOM_RIGHT, - ) + if not self._disable_keyboard_shortcuts: + if self._display_instr: + self._renderer.render_texts( + self._instr_texts[1], + TEXT_PADDING, + self.viewport_size[1] - TEXT_PADDING, + font_pt=26, + color=np.array([1.0, 1.0, 1.0, 0.85]), + ) + else: + self._renderer.render_texts( + self._instr_texts[0], + TEXT_PADDING, + self.viewport_size[1] - TEXT_PADDING, + font_pt=26, + color=np.array([1.0, 1.0, 1.0, 0.85]), + ) - if self.viewer_flags["caption"] is not None: - for caption in self.viewer_flags["caption"]: - xpos, ypos = self._location_to_x_y(caption["location"]) + if self._message_text is not None: self._renderer.render_text( - caption["text"], - xpos, - ypos, - font_name=caption["font_name"], - font_pt=caption["font_pt"], - color=caption["color"], - scale=caption["scale"], - align=caption["location"], + self._message_text, + self.viewport_size[0] - TEXT_PADDING, + TEXT_PADDING, + font_pt=20, + color=np.array([0.1, 0.7, 0.2, np.clip(self._message_opac, 0.0, 1.0)]), + align=TextAlign.BOTTOM_RIGHT, ) + if self.viewer_flags["caption"] is not None: + for caption in self.viewer_flags["caption"]: + xpos, ypos = self._location_to_x_y(caption["location"]) + self._renderer.render_text( + caption["text"], + xpos, + ypos, + font_name=caption["font_name"], + font_pt=caption["font_pt"], + color=caption["color"], + scale=caption["scale"], + align=caption["location"], + ) + if self._run_in_thread or not self.auto_start: self.render_lock.release() @@ -870,6 +875,10 @@ def on_key_press(self, symbol: int, modifiers: int) -> EVENT_HANDLE_STATE: callback(self, *args, **kwargs) return self.viewer_interaction.on_key_press(symbol, modifiers) + # If keyboard shortcuts are disabled, skip default key functions + if self._disable_keyboard_shortcuts: + return self.viewer_interaction.on_key_press(symbol, modifiers) + # Otherwise, use default key functions # A causes the frame to rotate diff --git a/genesis/options/vis.py b/genesis/options/vis.py index 1d955c8b8..fce8ed0d2 100644 --- a/genesis/options/vis.py +++ b/genesis/options/vis.py @@ -33,6 +33,8 @@ class ViewerOptions(Options): The up vector of the camera's extrinsic pose. camera_fov : float The field of view (in degrees) of the camera. + disable_keyboard_shortcuts : bool + Whether to disable all keyboard shortcuts in the viewer. Defaults to False. """ res: Optional[tuple] = None @@ -44,6 +46,7 @@ class ViewerOptions(Options): camera_up: tuple = (0.0, 0.0, 1.0) camera_fov: float = 40 enable_interaction: bool = False + disable_keyboard_shortcuts: bool = False class VisOptions(Options): diff --git a/genesis/vis/viewer.py b/genesis/vis/viewer.py index 77c4bce34..2ec2074d1 100644 --- a/genesis/vis/viewer.py +++ b/genesis/vis/viewer.py @@ -41,6 +41,7 @@ def __init__(self, options: "ViewerOptions", context): self._camera_up = np.asarray(options.camera_up, dtype=gs.np_float) self._camera_fov = options.camera_fov self._enable_interaction = options.enable_interaction + self._disable_keyboard_shortcuts = options.disable_keyboard_shortcuts # Validate viewer options if any(e.shape != (3,) for e in (self._camera_init_pos, self._camera_init_lookat, self._camera_up)): @@ -100,6 +101,7 @@ def build(self, scene): plane_reflection=self.context.plane_reflection, env_separate_rigid=self.context.env_separate_rigid, enable_interaction=self._enable_interaction, + disable_keyboard_shortcuts=self._disable_keyboard_shortcuts, viewer_flags={ "window_title": f"Genesis {gs.__version__}", "refresh_rate": self._refresh_rate, diff --git a/tests/test_render.py b/tests/test_render.py index 00e2b2e6d..9081b4305 100644 --- a/tests/test_render.py +++ b/tests/test_render.py @@ -1068,6 +1068,27 @@ def on_key_press(self, symbol: int, modifiers: int): assert f.read() == png_snapshot +@pytest.mark.required +@pytest.mark.parametrize("renderer_type", [RENDERER_TYPE.RASTERIZER]) +@pytest.mark.skipif(not IS_INTERACTIVE_VIEWER_AVAILABLE, reason="Interactive viewer not supported on this platform.") +def test_interactive_viewer_disable_keyboard_shortcuts(): + """Test that keyboard shortcuts can be disabled in the interactive viewer.""" + + # Test with keyboard shortcuts DISABLED + scene = gs.Scene( + viewer_options=gs.options.ViewerOptions( + disable_keyboard_shortcuts=True, + ), + show_viewer=True, + ) + scene.build() + pyrender_viewer = scene.visualizer.viewer._pyrender_viewer + assert pyrender_viewer.is_active + + # Verify the flag is set correctly + assert pyrender_viewer._disable_keyboard_shortcuts is True + + @pytest.mark.parametrize( "renderer_type", [RENDERER_TYPE.RASTERIZER, RENDERER_TYPE.BATCHRENDER_RASTERIZER, RENDERER_TYPE.BATCHRENDER_RAYTRACER],