From a1d0cbd6543e41ebb2612fcff3b9d3b0a0c1e9ea Mon Sep 17 00:00:00 2001 From: Saran Tunyasuvunakool Date: Tue, 10 Oct 2023 12:29:24 -0700 Subject: [PATCH] Add `show_{left,right}_ui` kwarg to `viewer.launch(_passive)`. Fixes #857 PiperOrigin-RevId: 572333909 Change-Id: I12247fe856afddf808eb8f386a594acac47c34b5 --- doc/changelog.rst | 11 ++++++--- doc/python.rst | 39 +++++++++++++++++------------- python/mujoco/mjpython/mjpython.mm | 12 ++++++--- python/mujoco/simulate.cc | 12 ++++++++- python/mujoco/viewer.py | 38 ++++++++++++++++++++++++----- 5 files changed, 81 insertions(+), 31 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index e0255298..19601f9e 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -133,6 +133,8 @@ Python bindings camera name used the default camera. 24. Added ``user_scn`` to the :ref:`passive viewer` handle, which allows users to add custom visualization geoms (`#1023 `__). +25. Added optional boolean keyword arguments ``show_left_ui`` and ``show_right_ui`` to the functions ``viewer.launch`` + and ``viewer.launch_passive``, which allow users to launch a viewer with UI panels hidden. Simulate ^^^^^^^^ @@ -141,21 +143,22 @@ Simulate :align: right :width: 240px -25. Added **state history** mechanism to :ref:`simulate` and the managed +26. Added **state history** mechanism to :ref:`simulate` and the managed :ref:`Python viewer`. State history can be viewed by scrubbing the History slider and (more precisely) with the left and right arrow keys. See screen capture: -26. The ``LOADING...`` label is now shown correctly. +27. The ``LOADING...`` label is now shown correctly. `Contribution `__ by `Levi Burner `__. Bug fixes ^^^^^^^^^ -27. Fixed a bug that was causing :ref:`geom margin` to be ignored during the construction of +28. Fixed a bug that was causing :ref:`geom margin` to be ignored during the construction of midphase collision trees. -27. Fixed a bug that was generating incorrect values in ``efc_diagApprox`` for weld equality constraints. +29. Fixed a bug that was generating incorrect values in ``efc_diagApprox`` for weld equality constraints. + Version 2.3.7 (July 20, 2023) ----------------------------- diff --git a/doc/python.rst b/doc/python.rst index 8bb75072..4b60e79c 100644 --- a/doc/python.rst +++ b/doc/python.rst @@ -191,28 +191,33 @@ illustrative example that does **not** necessarily keep the physics ticking at t if time_until_next_step > 0: time.sleep(time_until_next_step) -Optionally, ``viewer.launch_passive`` also accepts a callable as a keyword argument ``key_callback``, which gets called -each time a keyboard event occurs in the viewer window. This allows user scripts to react to various key presses, e.g., -pause or resume the run loop when the spacebar is pressed. +Optionally, ``viewer.launch_passive`` accepts the following keyword arguments. -.. code-block:: python +- ``key_callback``: A callable which gets called each time a keyboard event occurs in the viewer window. This allows + user scripts to react to various key presses, e.g., pause or resume the run loop when the spacebar is pressed. - paused = False + .. code-block:: python - def key_callback(keycode): - if chr(keycode) == ' ': - nonlocal paused - paused = not paused + paused = False - ... + def key_callback(keycode): + if chr(keycode) == ' ': + nonlocal paused + paused = not paused - with mujoco.viewer.launch_passive(m, d, key_callback=key_callback) as viewer: - while viewer.is_running(): - ... - if not paused: - mujoco.mj_step(m, d) - viewer.sync() - ... + ... + + with mujoco.viewer.launch_passive(m, d, key_callback=key_callback) as viewer: + while viewer.is_running(): + ... + if not paused: + mujoco.mj_step(m, d) + viewer.sync() + ... + +- ``show_left_ui_panel`` and ``show_right_ui_panel``: Boolean arguments indicating whether UI panels should be visible + or hidden when the viewer is launched. Note that regardless of the values specified, the user can still toggle the + visibility of these panels after launch by pressing Tab or Shift+Tab. .. _PyUsage: diff --git a/python/mujoco/mjpython/mjpython.mm b/python/mujoco/mjpython/mjpython.mm index 14ab9030..25c6dc33 100644 --- a/python/mujoco/mjpython/mjpython.mm +++ b/python/mujoco/mjpython/mjpython.mm @@ -294,11 +294,17 @@ while True: break # Otherwise, launch the viewer. - model, data, handle_return, key_callback = task + model, data, handle_return, key_callback, show_left_ui, show_right_ui = task ctypes.CDLL(None).mjpython_show_dock_icon() mujoco.viewer._launch_internal( - model, data, run_physics_thread=False, handle_return=handle_return, - key_callback=key_callback) + model, + data, + run_physics_thread=False, + handle_return=handle_return, + key_callback=key_callback, + show_left_ui=show_left_ui, + show_right_ui=show_right_ui, + ) ctypes.CDLL(None).mjpython_hide_dock_icon() finally: diff --git a/python/mujoco/simulate.cc b/python/mujoco/simulate.cc index 820c73b8..36fdf407 100644 --- a/python/mujoco/simulate.cc +++ b/python/mujoco/simulate.cc @@ -314,7 +314,17 @@ PYBIND11_MODULE(_simulate, pymodule) { const auto max_length = sizeof_arr(sim.load_error); std::strncpy(sim.load_error, error.c_str(), max_length - 1); sim.load_error[max_length - 1] = '\0'; - })); + })) + .def_property("ui0_enable", GetIfNotNull(&mujoco::Simulate::ui0_enable), + CallIfNotNull(+[](mujoco::Simulate& sim, int enabled) { + sim.ui0_enable = enabled; + }), + py::call_guard()) + .def_property("ui1_enable", GetIfNotNull(&mujoco::Simulate::ui1_enable), + CallIfNotNull(+[](mujoco::Simulate& sim, int enabled) { + sim.ui1_enable = enabled; + }), + py::call_guard()); pymodule.def("set_glfw_dlhandle", [](std::uintptr_t dlhandle) { mujoco::Glfw(reinterpret_cast(dlhandle)); diff --git a/python/mujoco/viewer.py b/python/mujoco/viewer.py index 6492d41c..80621eea 100644 --- a/python/mujoco/viewer.py +++ b/python/mujoco/viewer.py @@ -319,6 +319,8 @@ def _launch_internal( loader: Optional[_InternalLoaderType] = None, handle_return: Optional['queue.Queue[Handle]'] = None, key_callback: Optional[KeyCallbackType] = None, + show_left_ui: bool = True, + show_right_ui: bool = True, ) -> None: """Internal API, so that the public API has more readable type annotations.""" if model is None and data is not None: @@ -351,6 +353,9 @@ def _launch_internal( cam, opt, pert, user_scn, run_physics_thread, key_callback ) + simulate.ui0_enable = show_left_ui + simulate.ui1_enable = show_right_ui + # Initialize GLFW if not using mjpython. if _MJPYTHON is None: if not glfw.init(): @@ -385,13 +390,23 @@ def _launch_internal( simulate.destroy() -def launch(model: Optional[mujoco.MjModel] = None, - data: Optional[mujoco.MjData] = None, - *, - loader: Optional[LoaderType] = None) -> None: +def launch( + model: Optional[mujoco.MjModel] = None, + data: Optional[mujoco.MjData] = None, + *, + loader: Optional[LoaderType] = None, + show_left_ui: bool = True, + show_right_ui: bool = True, +) -> None: """Launches the Simulate GUI.""" _launch_internal( - model, data, run_physics_thread=True, loader=loader) + model, + data, + run_physics_thread=True, + loader=loader, + show_left_ui=show_left_ui, + show_right_ui=show_right_ui, + ) def launch_from_path(path: str) -> None: @@ -404,6 +419,8 @@ def launch_passive( data: mujoco.MjData, *, key_callback: Optional[KeyCallbackType] = None, + show_left_ui: bool = True, + show_right_ui: bool = True, ) -> Handle: """Launches a passive Simulate GUI without blocking the running thread.""" if not isinstance(model, mujoco.MjModel): @@ -425,6 +442,8 @@ def launch_passive( run_physics_thread=False, handle_return=handle_return, key_callback=key_callback, + show_left_ui=show_left_ui, + show_right_ui=show_right_ui, ), ) thread.daemon = True @@ -434,7 +453,14 @@ def launch_passive( raise RuntimeError( '`launch_passive` requires that the Python script be run under ' '`mjpython` on macOS') - _MJPYTHON.launch_on_ui_thread(model, data, handle_return, key_callback) + _MJPYTHON.launch_on_ui_thread( + model, + data, + handle_return, + key_callback, + show_left_ui, + show_right_ui, + ) return handle_return.get()