Basic GUI controls

Create interactive controls like sliders, buttons, and text inputs.

This example demonstrates the full range of GUI controls available in viser, including:

Input Controls:

Interactive Elements:

Layout & Organization:

The example shows how to read control values with .value, manage visibility with .visible, and disable controls with .disabled. GUI state automatically synchronizes with all connected clients.

Source: examples/02_gui/00_basic_controls.py

Basic GUI controls

Code

  1import time
  2
  3import numpy as np
  4
  5import viser
  6
  7
  8def main() -> None:
  9    server = viser.ViserServer()
 10
 11    # Add some common GUI elements: number inputs, sliders, vectors, checkboxes.
 12    with server.gui.add_folder("Read-only"):
 13        gui_counter = server.gui.add_number(
 14            "Counter",
 15            initial_value=0,
 16            disabled=True,
 17        )
 18        gui_slider = server.gui.add_slider(
 19            "Slider",
 20            min=0,
 21            max=100,
 22            step=1,
 23            initial_value=0,
 24            disabled=True,
 25        )
 26        gui_progress = server.gui.add_progress_bar(25, animated=True)
 27
 28    with server.gui.add_folder("Editable"):
 29        gui_vector2 = server.gui.add_vector2(
 30            "Position",
 31            initial_value=(0.0, 0.0),
 32            step=0.1,
 33        )
 34        gui_vector3 = server.gui.add_vector3(
 35            "Size",
 36            initial_value=(1.0, 1.0, 1.0),
 37            step=0.25,
 38        )
 39
 40        # Buttons with on_hold for continuous size adjustment while held.
 41        gui_grow = server.gui.add_button("Grow (hold)", icon=viser.Icon.PLUS)
 42        gui_shrink = server.gui.add_button("Shrink (hold)", icon=viser.Icon.MINUS)
 43
 44        @gui_grow.on_hold(callback_hz=10.0)
 45        def _(_: viser.GuiEvent[viser.GuiButtonHandle]) -> None:
 46            x, y, z = gui_vector3.value
 47            gui_vector3.value = (x + 0.05, y + 0.05, z + 0.05)
 48
 49        @gui_shrink.on_hold(callback_hz=10.0)
 50        def _(_: viser.GuiEvent[viser.GuiButtonHandle]) -> None:
 51            x, y, z = gui_vector3.value
 52            gui_vector3.value = (
 53                max(0.1, x - 0.05),
 54                max(0.1, y - 0.05),
 55                max(0.1, z - 0.05),
 56            )
 57
 58        with server.gui.add_folder("Text toggle"):
 59            gui_checkbox_hide = server.gui.add_checkbox(
 60                "Hide",
 61                initial_value=False,
 62            )
 63            gui_text = server.gui.add_text(
 64                "Text",
 65                initial_value="Hello world",
 66            )
 67            gui_button = server.gui.add_button("Button", icon=viser.Icon.MOUSE)
 68            gui_checkbox_disable = server.gui.add_checkbox(
 69                "Disable",
 70                initial_value=False,
 71            )
 72            gui_rgb = server.gui.add_rgb(
 73                "Color",
 74                initial_value=(255, 255, 0),
 75            )
 76            gui_multi_slider = server.gui.add_multi_slider(
 77                "Multi slider",
 78                min=0,
 79                max=100,
 80                step=1,
 81                initial_value=(0, 30, 100),
 82                marks=((0, "0"), (50, "5"), (70, "7"), 99),
 83            )
 84            gui_slider_positions = server.gui.add_slider(
 85                "# sliders",
 86                min=0,
 87                max=10,
 88                step=1,
 89                initial_value=3,
 90                marks=((0, "0"), (5, "5"), (7, "7"), 10),
 91            )
 92            gui_upload_button = server.gui.add_upload_button(
 93                "Upload", icon=viser.Icon.UPLOAD
 94            )
 95
 96    @gui_upload_button.on_upload
 97    def _(_) -> None:
 98        file = gui_upload_button.value
 99        print(file.name, len(file.content), "bytes")
100
101    # Pre-generate a point cloud to send.
102    point_positions = np.random.uniform(low=-1.0, high=1.0, size=(5000, 3))
103    color_coeffs = np.random.uniform(0.4, 1.0, size=(point_positions.shape[0]))
104
105    counter = 0
106    while True:
107        # We can set the value of an input to a particular value. Changes are
108        # automatically reflected in connected clients.
109        gui_counter.value = counter
110        gui_slider.value = counter % 100
111
112        # We can set the position of a scene node with `.position`, and read the value
113        # of a gui element with `.value`. Changes are automatically reflected in
114        # connected clients.
115        server.scene.add_point_cloud(
116            "/point_cloud",
117            points=point_positions * np.array(gui_vector3.value, dtype=np.float32),
118            colors=(np.array(gui_rgb.value) * color_coeffs[:, None]).astype(np.uint8),
119            position=gui_vector2.value + (0,),
120            point_shape="circle",
121        )
122        gui_button.color = gui_rgb.value
123
124        gui_progress.value = float((counter % 100))
125
126        # We can use `.visible` and `.disabled` to toggle GUI elements.
127        gui_text.visible = not gui_checkbox_hide.value
128        gui_button.visible = not gui_checkbox_hide.value
129        gui_rgb.disabled = gui_checkbox_disable.value
130        gui_multi_slider.disabled = gui_checkbox_disable.value
131        gui_slider_positions.disabled = gui_checkbox_disable.value
132        gui_button.disabled = gui_checkbox_disable.value
133        gui_upload_button.disabled = gui_checkbox_disable.value
134
135        # Update the number of handles in the multi-slider.
136        if gui_slider_positions.value != len(gui_multi_slider.value):
137            gui_multi_slider.value = np.linspace(
138                0, 100, gui_slider_positions.value, dtype=np.int64
139            )
140
141        counter += 1
142        time.sleep(0.01)
143
144
145if __name__ == "__main__":
146    main()