Add show_{left,right}_ui kwarg to viewer.launch(_passive).

Fixes #857

PiperOrigin-RevId: 572333909
Change-Id: I12247fe856afddf808eb8f386a594acac47c34b5
This commit is contained in:
Saran Tunyasuvunakool
2023-10-10 12:29:24 -07:00
committed by Copybara-Service
parent 2e15574b58
commit a1d0cbd654
5 changed files with 81 additions and 31 deletions
+7 -4
View File
@@ -133,6 +133,8 @@ Python bindings
camera name used the default camera. camera name used the default camera.
24. Added ``user_scn`` to the :ref:`passive viewer<PyViewerPassive>` handle, which allows users to add custom 24. Added ``user_scn`` to the :ref:`passive viewer<PyViewerPassive>` handle, which allows users to add custom
visualization geoms (`#1023 <https://github.com/google-deepmind/mujoco/issues/870>`__). visualization geoms (`#1023 <https://github.com/google-deepmind/mujoco/issues/870>`__).
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 Simulate
^^^^^^^^ ^^^^^^^^
@@ -141,21 +143,22 @@ Simulate
:align: right :align: right
:width: 240px :width: 240px
25. Added **state history** mechanism to :ref:`simulate<saSimulate>` and the managed 26. Added **state history** mechanism to :ref:`simulate<saSimulate>` and the managed
:ref:`Python viewer<PyViewerManaged>`. State history can be viewed by scrubbing the History slider and (more :ref:`Python viewer<PyViewerManaged>`. State history can be viewed by scrubbing the History slider and (more
precisely) with the left and right arrow keys. See screen capture: 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 <https://github.com/google-deepmind/mujoco/pull/1070>`__ by `Contribution <https://github.com/google-deepmind/mujoco/pull/1070>`__ by
`Levi Burner <https://github.com/aftersomemath>`__. `Levi Burner <https://github.com/aftersomemath>`__.
Bug fixes Bug fixes
^^^^^^^^^ ^^^^^^^^^
27. Fixed a bug that was causing :ref:`geom margin<body-geom-margin>` to be ignored during the construction of 28. Fixed a bug that was causing :ref:`geom margin<body-geom-margin>` to be ignored during the construction of
midphase collision trees. 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) Version 2.3.7 (July 20, 2023)
----------------------------- -----------------------------
+22 -17
View File
@@ -191,28 +191,33 @@ illustrative example that does **not** necessarily keep the physics ticking at t
if time_until_next_step > 0: if time_until_next_step > 0:
time.sleep(time_until_next_step) time.sleep(time_until_next_step)
Optionally, ``viewer.launch_passive`` also accepts a callable as a keyword argument ``key_callback``, which gets called Optionally, ``viewer.launch_passive`` accepts the following keyword arguments.
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.
.. 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): paused = False
if chr(keycode) == ' ':
nonlocal paused
paused = not paused
... 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():
... with mujoco.viewer.launch_passive(m, d, key_callback=key_callback) as viewer:
if not paused: while viewer.is_running():
mujoco.mj_step(m, d) ...
viewer.sync() 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: .. _PyUsage:
+9 -3
View File
@@ -294,11 +294,17 @@ while True:
break break
# Otherwise, launch the viewer. # 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() ctypes.CDLL(None).mjpython_show_dock_icon()
mujoco.viewer._launch_internal( mujoco.viewer._launch_internal(
model, data, run_physics_thread=False, handle_return=handle_return, model,
key_callback=key_callback) 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() ctypes.CDLL(None).mjpython_hide_dock_icon()
finally: finally:
+11 -1
View File
@@ -314,7 +314,17 @@ PYBIND11_MODULE(_simulate, pymodule) {
const auto max_length = sizeof_arr(sim.load_error); const auto max_length = sizeof_arr(sim.load_error);
std::strncpy(sim.load_error, error.c_str(), max_length - 1); std::strncpy(sim.load_error, error.c_str(), max_length - 1);
sim.load_error[max_length - 1] = '\0'; 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<py::gil_scoped_release>())
.def_property("ui1_enable", GetIfNotNull(&mujoco::Simulate::ui1_enable),
CallIfNotNull(+[](mujoco::Simulate& sim, int enabled) {
sim.ui1_enable = enabled;
}),
py::call_guard<py::gil_scoped_release>());
pymodule.def("set_glfw_dlhandle", [](std::uintptr_t dlhandle) { pymodule.def("set_glfw_dlhandle", [](std::uintptr_t dlhandle) {
mujoco::Glfw(reinterpret_cast<void*>(dlhandle)); mujoco::Glfw(reinterpret_cast<void*>(dlhandle));
+32 -6
View File
@@ -319,6 +319,8 @@ def _launch_internal(
loader: Optional[_InternalLoaderType] = None, loader: Optional[_InternalLoaderType] = None,
handle_return: Optional['queue.Queue[Handle]'] = None, handle_return: Optional['queue.Queue[Handle]'] = None,
key_callback: Optional[KeyCallbackType] = None, key_callback: Optional[KeyCallbackType] = None,
show_left_ui: bool = True,
show_right_ui: bool = True,
) -> None: ) -> None:
"""Internal API, so that the public API has more readable type annotations.""" """Internal API, so that the public API has more readable type annotations."""
if model is None and data is not None: 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 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. # Initialize GLFW if not using mjpython.
if _MJPYTHON is None: if _MJPYTHON is None:
if not glfw.init(): if not glfw.init():
@@ -385,13 +390,23 @@ def _launch_internal(
simulate.destroy() simulate.destroy()
def launch(model: Optional[mujoco.MjModel] = None, def launch(
data: Optional[mujoco.MjData] = None, model: Optional[mujoco.MjModel] = None,
*, data: Optional[mujoco.MjData] = None,
loader: Optional[LoaderType] = None) -> None: *,
loader: Optional[LoaderType] = None,
show_left_ui: bool = True,
show_right_ui: bool = True,
) -> None:
"""Launches the Simulate GUI.""" """Launches the Simulate GUI."""
_launch_internal( _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: def launch_from_path(path: str) -> None:
@@ -404,6 +419,8 @@ def launch_passive(
data: mujoco.MjData, data: mujoco.MjData,
*, *,
key_callback: Optional[KeyCallbackType] = None, key_callback: Optional[KeyCallbackType] = None,
show_left_ui: bool = True,
show_right_ui: bool = True,
) -> Handle: ) -> Handle:
"""Launches a passive Simulate GUI without blocking the running thread.""" """Launches a passive Simulate GUI without blocking the running thread."""
if not isinstance(model, mujoco.MjModel): if not isinstance(model, mujoco.MjModel):
@@ -425,6 +442,8 @@ def launch_passive(
run_physics_thread=False, run_physics_thread=False,
handle_return=handle_return, handle_return=handle_return,
key_callback=key_callback, key_callback=key_callback,
show_left_ui=show_left_ui,
show_right_ui=show_right_ui,
), ),
) )
thread.daemon = True thread.daemon = True
@@ -434,7 +453,14 @@ def launch_passive(
raise RuntimeError( raise RuntimeError(
'`launch_passive` requires that the Python script be run under ' '`launch_passive` requires that the Python script be run under '
'`mjpython` on macOS') '`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() return handle_return.get()