GUI callbacks¶
Attach event handlers to GUI elements for real-time interaction.
This example demonstrates how to create responsive interfaces using GUI callbacks. Callbacks are functions that execute automatically when users interact with controls, enabling real-time updates to the 3D scene.
Key callback methods:
viser.GuiInputHandle.on_update()for input changes (sliders, dropdowns, checkboxes)viser.GuiButtonHandle.on_click()for button press eventsviser.GuiUploadButtonHandle.on_upload()for file upload events
Features demonstrated:
Dynamic scene updates using
viser.SceneApi.add_grid()andviser.SceneApi.add_point_cloud()Conditional GUI state changes (dynamically updating dropdown options)
Global scene visibility control with
viser.SceneApi.set_global_visibility()Coordinated updates where multiple controls affect the same scene objects
The pattern shown here—connecting GUI controls to scene updates via callbacks—is fundamental for building interactive 3D applications with viser.
Source: examples/02_gui/01_callbacks.py
Code¶
1import time
2
3import numpy as np
4from typing_extensions import assert_never
5
6import viser
7
8
9def main() -> None:
10 server = viser.ViserServer()
11
12 gui_reset_scene_click = server.gui.add_button("Reset Scene on Click")
13
14 gui_reset_scene_hold = server.gui.add_button("Reset Scene on Hold")
15
16 gui_plane = server.gui.add_dropdown(
17 "Grid plane", ("xz", "xy", "yx", "yz", "zx", "zy")
18 )
19
20 def update_plane() -> None:
21 server.scene.add_grid("/grid", width=10.0, height=20.0, plane=gui_plane.value)
22
23 gui_plane.on_update(lambda _: update_plane())
24
25 with server.gui.add_folder("Control"):
26 gui_show_frame = server.gui.add_checkbox("Show Frame", initial_value=True)
27 gui_show_everything = server.gui.add_checkbox(
28 "Show Everything", initial_value=True
29 )
30 gui_axis = server.gui.add_dropdown("Axis", ("x", "y", "z"))
31 gui_include_z = server.gui.add_checkbox("Z in dropdown", initial_value=True)
32
33 @gui_include_z.on_update
34 def _(_) -> None:
35 gui_axis.options = ("x", "y", "z") if gui_include_z.value else ("x", "y")
36
37 with server.gui.add_folder("Sliders"):
38 gui_location = server.gui.add_slider(
39 "Location", min=-5.0, max=5.0, step=0.05, initial_value=0.0
40 )
41 gui_num_points = server.gui.add_slider(
42 "# Points", min=1000, max=200_000, step=1000, initial_value=10_000
43 )
44
45 def draw_frame() -> None:
46 axis = gui_axis.value
47 if axis == "x":
48 pos = (gui_location.value, 0.0, 0.0)
49 elif axis == "y":
50 pos = (0.0, gui_location.value, 0.0)
51 elif axis == "z":
52 pos = (0.0, 0.0, gui_location.value)
53 else:
54 assert_never(axis)
55
56 server.scene.add_frame(
57 "/frame",
58 wxyz=(1.0, 0.0, 0.0, 0.0),
59 position=pos,
60 show_axes=gui_show_frame.value,
61 axes_length=5.0,
62 )
63
64 def draw_points() -> None:
65 num_points = gui_num_points.value
66 server.scene.add_point_cloud(
67 "/frame/point_cloud",
68 points=np.random.normal(size=(num_points, 3)),
69 colors=np.random.randint(0, 256, size=(num_points, 3)),
70 )
71
72 # We can (optionally) also attach callbacks!
73 # Here, we update the point clouds + frames whenever any of the GUI items are updated.
74 gui_show_frame.on_update(lambda _: draw_frame())
75 gui_show_everything.on_update(
76 lambda _: server.scene.set_global_visibility(gui_show_everything.value)
77 )
78 gui_axis.on_update(lambda _: draw_frame())
79 gui_location.on_update(lambda _: draw_frame())
80 gui_num_points.on_update(lambda _: draw_points())
81
82 @gui_reset_scene_click.on_click
83 def _(_) -> None:
84 gui_show_frame.value = True
85 gui_location.value = 0.0
86 gui_axis.value = "x"
87 gui_num_points.value = 10_000
88
89 draw_frame()
90 draw_points()
91
92 @gui_reset_scene_hold.on_hold
93 def _(_) -> None:
94 gui_show_frame.value = True
95 gui_location.value = 0.0
96 gui_axis.value = "x"
97 gui_num_points.value = 10_000
98
99 draw_frame()
100 draw_points()
101
102 # Finally, let's add the initial frame + point cloud and just loop infinitely. :)
103 update_plane()
104 draw_frame()
105 draw_points()
106 while True:
107 time.sleep(1.0)
108
109
110if __name__ == "__main__":
111 main()