Skip to content

Commit

Permalink
GUI container refactor, tab support (#63)
Browse files Browse the repository at this point in the history
* GUI container refactor

* Clean up panel display, still some kinks to work out

* Improve expanded panel behavior; drag regression for mobile view

* Fix toggle for sidebar display

* Fix type

* Folder / tab remove(), switch back to contexts

* Put icons in tarball, clean up SMPL-X example

* Lint

* Suppress mypy error

* Add missing icons

* Remove debug print

* Remove outdated client README

* Nits

* Fix Accordion for empty folder names

* Address comments from Chungmin, fix container removal, context thread safety

* Fix type error
  • Loading branch information
brentyi authored Jul 20, 2023
1 parent ee96371 commit 307f06d
Show file tree
Hide file tree
Showing 33 changed files with 5,619 additions and 1,116 deletions.
15 changes: 15 additions & 0 deletions docs/source/gui_handles.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,19 @@ clients. When a GUI element is added to a client (for example, via
:undoc-members:
:inherited-members:

.. autoapiclass:: viser.GuiFolderHandle
:members:
:undoc-members:
:inherited-members:

.. autoapiclass:: viser.GuiTabGroupHandle
:members:
:undoc-members:
:inherited-members:

.. autoapiclass:: viser.GuiTabHandle
:members:
:undoc-members:
:inherited-members:

<!-- prettier-ignore-end -->
6 changes: 3 additions & 3 deletions examples/02_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def main():
server = viser.ViserServer()

# Add some common GUI elements: number inputs, sliders, vectors, checkboxes.
with server.gui_folder("Read-only"):
with server.add_gui_folder("Read-only"):
gui_counter = server.add_gui_number(
"Counter",
initial_value=0,
Expand All @@ -29,7 +29,7 @@ def main():
disabled=True,
)

with server.gui_folder("Editable"):
with server.add_gui_folder("Editable"):
gui_vector2 = server.add_gui_vector2(
"Position",
initial_value=(0.0, 0.0),
Expand All @@ -41,7 +41,7 @@ def main():
step=0.25,
lock=True,
)
with server.gui_folder("Text toggle"):
with server.add_gui_folder("Text toggle"):
gui_checkbox_hide = server.add_gui_checkbox(
"Hide",
initial_value=False,
Expand Down
8 changes: 4 additions & 4 deletions examples/03_gui_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
def main() -> None:
server = viser.ViserServer()

with server.gui_folder("Control"):
gui_reset_scene = server.add_gui_button("Reset Scene")

with server.add_gui_folder("Control"):
gui_show = server.add_gui_checkbox("Show Frame", initial_value=True)
gui_axis = server.add_gui_dropdown("Axis", ("x", "y", "z"))
gui_include_z = server.add_gui_checkbox("Z in dropdown", initial_value=True)
Expand All @@ -27,16 +29,14 @@ def main() -> None:
def _(_) -> None:
gui_axis.options = ("x", "y", "z") if gui_include_z.value else ("x", "y")

with server.gui_folder("Sliders"):
with server.add_gui_folder("Sliders"):
gui_location = server.add_gui_slider(
"Location", min=-5.0, max=5.0, step=0.05, initial_value=0.0
)
gui_num_points = server.add_gui_slider(
"# Points", min=1000, max=200_000, step=1000, initial_value=10_000
)

gui_reset_scene = server.add_gui_button("Reset Scene")

def draw_frame() -> None:
axis = gui_axis.value
if axis == "x":
Expand Down
2 changes: 1 addition & 1 deletion examples/07_record3d_visualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def main(
num_frames = min(max_frames, loader.num_frames())

# Add playback UI.
with server.gui_folder("Playback"):
with server.add_gui_folder("Playback"):
gui_timestep = server.add_gui_slider(
"Timestep", min=0, max=num_frames - 1, step=1, initial_value=0
)
Expand Down
27 changes: 14 additions & 13 deletions examples/08_smplx_visualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def main(
# Main loop. We'll just keep read from the joints, deform the mesh, then sending the
# updated mesh in a loop. This could be made a lot more efficient.
gui_elements = make_gui_elements(
server, num_betas=model.num_betas, num_body_joints=model.NUM_BODY_JOINTS
server, num_betas=model.num_betas, num_body_joints=int(model.NUM_BODY_JOINTS)
)
while True:
# Do nothing if no change.
Expand All @@ -68,6 +68,10 @@ def main(
continue
gui_elements.changed = False

full_pose = torch.from_numpy(
onp.array([j.value for j in gui_elements.gui_joints[1:]], dtype=onp.float32)[None, ...] # type: ignore
)

# Get deformed mesh.
output = model.forward(
betas=torch.from_numpy( # type: ignore
Expand All @@ -77,9 +81,7 @@ def main(
),
expression=None,
return_verts=True,
body_pose=torch.from_numpy(
onp.array([j.value for j in gui_elements.gui_joints[1:]], dtype=onp.float32)[None, ...] # type: ignore
),
body_pose=full_pose[:, : model.NUM_BODY_JOINTS], # type: ignore
global_orient=torch.from_numpy(onp.array(gui_elements.gui_joints[0].value, dtype=onp.float32)[None, ...]), # type: ignore
return_full_pose=True,
)
Expand Down Expand Up @@ -126,28 +128,28 @@ def make_gui_elements(
) -> GuiElements:
"""Make GUI elements for interacting with the model."""

tab_group = server.add_gui_tab_group()

# GUI elements: mesh settings + visibility.
with server.gui_folder("View"):
with tab_group.add_tab("View", viser.Icon.VIEWFINDER):
gui_rgb = server.add_gui_rgb("Color", initial_value=(90, 200, 255))
gui_wireframe = server.add_gui_checkbox("Wireframe", initial_value=False)
gui_show_controls = server.add_gui_checkbox("Handles", initial_value=False)

@gui_rgb.on_update
def _(_):
out.changed = True

gui_wireframe = server.add_gui_checkbox("Wireframe", initial_value=False)

@gui_wireframe.on_update
def _(_):
out.changed = True

gui_show_controls = server.add_gui_checkbox("Handles", initial_value=False)

@gui_show_controls.on_update
def _(_):
add_transform_controls(enabled=gui_show_controls.value)

# GUI elements: shape parameters.
with server.gui_folder("Shape"):
with tab_group.add_tab("Shape", viser.Icon.BOX):
gui_reset_shape = server.add_gui_button("Reset Shape")
gui_random_shape = server.add_gui_button("Random Shape")

Expand All @@ -173,8 +175,7 @@ def _(_):
out.changed = True

# GUI elements: joint angles.
with server.gui_folder("Joints"):
# Reset button.
with tab_group.add_tab("Joints", viser.Icon.ANGLE):
gui_reset_joints = server.add_gui_button("Reset Joints")
gui_random_joints = server.add_gui_button("Random Joints")

Expand Down Expand Up @@ -214,7 +215,7 @@ def _(_):
transform_controls: List[viser.TransformControlsHandle] = []

def add_transform_controls(enabled: bool) -> List[viser.TransformControlsHandle]:
for i in range(num_body_joints + 1):
for i in range(1 + num_body_joints):
controls = server.add_transform_controls(
f"/reoriented/smpl/joint_{i}/controls",
depth_test=False,
Expand Down
2 changes: 1 addition & 1 deletion examples/09_urdf_visualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def frame_name_with_parents(frame_name: str) -> str:
)

gui_joints: List[viser.GuiHandle[float]] = []
with server.gui_folder("Joints"):
with server.add_gui_folder("Joints"):
button = server.add_gui_button("Reset")

@button.on_click
Expand Down
2 changes: 1 addition & 1 deletion examples/12_click_meshes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def main() -> None:
grid_shape = (4, 5)
server = viser.ViserServer()

with server.gui_folder("Last clicked"):
with server.add_gui_folder("Last clicked"):
x_value = server.add_gui_number(
label="x",
initial_value=0,
Expand Down
28 changes: 16 additions & 12 deletions viser/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
from ._message_api import GuiButtonGroupHandle as GuiButtonGroupHandle
from ._message_api import GuiButtonHandle as GuiButtonHandle
from ._message_api import GuiDropdownHandle as GuiDropdownHandle
from ._message_api import GuiHandle as GuiHandle
from ._scene_handle import CameraFrustumHandle as CameraFrustumHandle
from ._scene_handle import FrameHandle as FrameHandle
from ._scene_handle import ImageHandle as ImageHandle
from ._scene_handle import LabelHandle as LabelHandle
from ._scene_handle import MeshHandle as MeshHandle
from ._scene_handle import PointCloudHandle as PointCloudHandle
from ._scene_handle import SceneNodeHandle as SceneNodeHandle
from ._scene_handle import TransformControlsHandle as TransformControlsHandle
from ._gui_api import GuiFolderHandle as GuiFolderHandle
from ._gui_api import GuiTabGroupHandle as GuiTabGroupHandle
from ._gui_api import GuiTabHandle as GuiTabHandle
from ._gui_handles import GuiButtonGroupHandle as GuiButtonGroupHandle
from ._gui_handles import GuiButtonHandle as GuiButtonHandle
from ._gui_handles import GuiDropdownHandle as GuiDropdownHandle
from ._gui_handles import GuiHandle as GuiHandle
from ._icons_enum import Icon as Icon
from ._scene_handles import CameraFrustumHandle as CameraFrustumHandle
from ._scene_handles import FrameHandle as FrameHandle
from ._scene_handles import ImageHandle as ImageHandle
from ._scene_handles import LabelHandle as LabelHandle
from ._scene_handles import MeshHandle as MeshHandle
from ._scene_handles import PointCloudHandle as PointCloudHandle
from ._scene_handles import SceneNodeHandle as SceneNodeHandle
from ._scene_handles import TransformControlsHandle as TransformControlsHandle
from ._viser import CameraHandle as CameraHandle
from ._viser import ClientHandle as ClientHandle
from ._viser import ViserServer as ViserServer
2 changes: 1 addition & 1 deletion viser/_client_autobuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def _install_sandboxed_node() -> Path:
environment root."""
env_dir = client_dir / ".nodeenv"
if (env_dir / "bin" / "npx").exists():
print("[viser] nodejs already set up!")
print("[viser] nodejs is set up!")
return env_dir

subprocess.run([sys.executable, "-m", "nodeenv", "--node=20.4.0", env_dir])
Expand Down
Loading

0 comments on commit 307f06d

Please sign in to comment.