Skip to content

Commit

Permalink
Merge pull request #165 from nerfstudio-project/jkulhanek/multislider
Browse files Browse the repository at this point in the history
Add multi-slider
  • Loading branch information
jkulhanek authored Jan 30, 2024
2 parents abf6484 + da5c19a commit bcb5057
Show file tree
Hide file tree
Showing 10 changed files with 1,455 additions and 45 deletions.
26 changes: 26 additions & 0 deletions examples/02_gui.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# mypy: disable-error-code="assignment"
#
# Asymmetric properties are supported in Pyright, but not yet in mypy.
# - https://github.com/python/mypy/issues/3004
# - https://github.com/python/mypy/pull/11643
"""GUI basics
Examples of basic GUI elements that we can create, read from, and write to."""
Expand Down Expand Up @@ -57,6 +62,21 @@ def main() -> None:
"Color",
initial_value=(255, 255, 0),
)
gui_multi_slider = server.add_gui_multi_slider(
"Multi slider",
min=0,
max=100,
step=1,
initial_value=(0, 30, 100),
)
gui_slider_positions = server.add_gui_slider(
"# sliders",
min=0,
max=10,
step=1,
initial_value=3,
marks=((0, "0"), (5, "5"), (7, "7"), 10),
)

# Pre-generate a point cloud to send.
point_positions = onp.random.uniform(low=-1.0, high=1.0, size=(5000, 3))
Expand Down Expand Up @@ -87,6 +107,12 @@ def main() -> None:
gui_button.visible = not gui_checkbox_hide.value
gui_rgb.disabled = gui_checkbox_disable.value

# Update the number of handles in the multi-slider.
if gui_slider_positions.value != len(gui_multi_slider.value):
gui_multi_slider.value = onp.linspace(
0, 100, gui_slider_positions.value, dtype=onp.int64
)

counter += 1
time.sleep(0.01)

Expand Down
98 changes: 98 additions & 0 deletions src/viser/_gui_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,7 @@ def add_gui_slider(
max: IntOrFloat,
step: IntOrFloat,
initial_value: IntOrFloat,
marks: Optional[Tuple[IntOrFloat | Tuple[IntOrFloat, str], ...]] = None,
disabled: bool = False,
visible: bool = True,
hint: Optional[str] = None,
Expand All @@ -806,6 +807,9 @@ def add_gui_slider(
max: Maximum value of the slider.
step: Step size of the slider.
initial_value: Initial value of the slider.
marks: Tuple of marks to display below the slider. Each mark should
either be a numerical or a (number, label) tuple, where the
label is provided as a string.
disabled: Whether the slider is disabled.
visible: Whether the slider is visible.
hint: Optional hint to display on hover.
Expand All @@ -821,6 +825,8 @@ def add_gui_slider(

# GUI callbacks cast incoming values to match the type of the initial value. If
# the min, max, or step is a float, we should cast to a float.
#
# This should also match what the IntOrFloat TypeVar resolves to.
if type(initial_value) is int and (
type(min) is float or type(max) is float or type(step) is float
):
Expand All @@ -846,6 +852,98 @@ def add_gui_slider(
step=step,
initial_value=initial_value,
precision=_compute_precision_digits(step),
marks=tuple(
{"value": float(x[0]), "label": x[1]}
if isinstance(x, tuple)
else {"value": float(x)}
for x in marks
)
if marks is not None
else None,
),
disabled=disabled,
visible=visible,
is_button=False,
)

def add_gui_multi_slider(
self,
label: str,
min: IntOrFloat,
max: IntOrFloat,
step: IntOrFloat,
initial_value: Tuple[IntOrFloat, ...],
min_range: Optional[IntOrFloat] = None,
fixed_endpoints: bool = False,
marks: Optional[Tuple[IntOrFloat | Tuple[IntOrFloat, str], ...]] = None,
disabled: bool = False,
visible: bool = True,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiInputHandle[Tuple[IntOrFloat, ...]]:
"""Add a multi slider to the GUI. Types of the min, max, step, and initial value should match.
Args:
label: Label to display on the slider.
min: Minimum value of the slider.
max: Maximum value of the slider.
step: Step size of the slider.
initial_value: Initial values of the slider.
min_range: Optional minimum difference between two values of the slider.
fixed_endpoints: Whether the endpoints of the slider are fixed.
marks: Tuple of marks to display below the slider. Each mark should
either be a numerical or a (number, label) tuple, where the
label is provided as a string.
disabled: Whether the slider is disabled.
visible: Whether the slider is visible.
hint: Optional hint to display on hover.
order: Optional ordering, smallest values will be displayed first.
Returns:
A handle that can be used to interact with the GUI element.
"""
assert max >= min
if step > max - min:
step = max - min
assert all(max >= x >= min for x in initial_value)

# GUI callbacks cast incoming values to match the type of the initial value. If
# any of the arguments are floats, we should always use a float value.
#
# This should also match what the IntOrFloat TypeVar resolves to.
if (
type(min) is float
or type(max) is float
or type(step) is float
or type(min_range) is float
):
initial_value = tuple(float(x) for x in initial_value) # type: ignore

id = _make_unique_id()
order = _apply_default_order(order)
return self._create_gui_input(
initial_value=initial_value,
message=_messages.GuiAddMultiSliderMessage(
order=order,
id=id,
label=label,
container_id=self._get_container_id(),
hint=hint,
min=min,
min_range=min_range,
max=max,
step=step,
initial_value=initial_value,
fixed_endpoints=fixed_endpoints,
precision=_compute_precision_digits(step),
marks=tuple(
{"value": float(x[0]), "label": x[1]}
if isinstance(x, tuple)
else {"value": float(x)}
for x in marks
)
if marks is not None
else None,
),
disabled=disabled,
visible=visible,
Expand Down
3 changes: 1 addition & 2 deletions src/viser/_gui_handles.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
Tuple,
Type,
TypeVar,
Union,
)

import imageio.v3 as iio
Expand Down Expand Up @@ -130,7 +129,7 @@ def value(self) -> T:
return self._impl.value

@value.setter
def value(self, value: Union[T, onp.ndarray]) -> None:
def value(self, value: T | onp.ndarray) -> None:
if isinstance(value, onp.ndarray):
assert len(value.shape) <= 1, f"{value.shape} should be at most 1D!"
value = tuple(map(float, value)) # type: ignore
Expand Down
17 changes: 16 additions & 1 deletion src/viser/_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@

import numpy as onp
import numpy.typing as onpt
from typing_extensions import Literal, override
from typing_extensions import Literal, NotRequired, TypedDict, override

from . import infra, theme

GuiSliderMark = TypedDict("GuiSliderMark", {"value": float, "label": NotRequired[str]})


class Message(infra.Message):
@override
Expand Down Expand Up @@ -430,6 +432,19 @@ class GuiAddSliderMessage(_GuiAddInputBase):
step: Optional[float]
initial_value: float
precision: int
marks: Optional[Tuple[GuiSliderMark, ...]] = None


@dataclasses.dataclass
class GuiAddMultiSliderMessage(_GuiAddInputBase):
min: float
max: float
step: Optional[float]
min_range: Optional[float]
initial_value: Tuple[float, ...]
precision: int
fixed_endpoints: bool = False
marks: Optional[Tuple[GuiSliderMark, ...]] = None


@dataclasses.dataclass
Expand Down
Loading

0 comments on commit bcb5057

Please sign in to comment.