SpinnerField#
A text-entry field with up/down spin buttons for stepping through a fixed list of values or a numeric range.
Usage#
Text mode#
Pass options= to step through a fixed list of strings. The spin buttons
cycle forward and back through the list.
bs.SpinnerField(
label="Priority",
options=["Low", "Medium", "High", "Critical"],
value="Medium",
)
Numeric mode#
Use min_value=, max_value=, and step= instead of options=
for a numeric range. Only one mode should be used at a time.
bs.SpinnerField(
label="Quantity",
value=1,
min_value=1,
max_value=99,
step=1,
)
Value formatting#
In numeric mode, value_format= displays the value with a locale-aware ICU
pattern — the raw number is preserved internally; only the display changes.
Requires localization to be enabled.
bs.SpinnerField(label="Price", value=10, min_value=0, max_value=100,
value_format="currency")
Wrap around#
Set wrap=True to cycle back to the start when the end of the list (or
range) is reached, and vice versa.
bs.SpinnerField(
label="Month",
options=["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
value="Jan",
wrap=True,
)
Label and message#
Use label= for a field title and message= for helper text below.
bs.SpinnerField(
label="Font size",
value=12,
min_value=6,
max_value=72,
step=2,
message="Applies to the selected text.",
)
States#
bs.SpinnerField(value=5, min_value=1, max_value=10, label="Normal")
bs.SpinnerField(value=5, min_value=1, max_value=10, label="Read only", read_only=True)
bs.SpinnerField(value=5, min_value=1, max_value=10, label="Disabled", disabled=True)
Reactive binding#
Bind a Signal[str] with textsignal=. The field and signal stay in
sync automatically.
size = bs.Signal("M")
bs.SpinnerField(
label="T-shirt size",
options=["XS", "S", "M", "L", "XL", "XXL"],
textsignal=size,
)
bs.Label(textsignal=size, accent="secondary")
Handling changes#
Use on_change() to respond when the value changes — whether from a spin
button click, keyboard arrow key, or direct text entry.
sf = bs.SpinnerField(
label="Rating",
options=["★", "★★", "★★★", "★★★★", "★★★★★"],
value="★★★",
wrap=True,
)
def handle_change(e):
print("Selected:", sf.value)
sf.on_change(handle_change)
# As a subscription (cancellable)
sub = sf.on_change(handle_change)
sub.cancel()
# As a Stream (composable)
sf.on_change().listen(handle_change)
Widget sizing#
All widgets accept self-placement kwargs via **kwargs. The parent
container determines which options apply — stack-based parents use stack
kwargs, grid-based parents use grid kwargs. Unrecognised keys are
silently ignored.
Stack#
Used inside VStack, HStack, App, and other stack containers.
|
Fill direction: |
|
Grow to consume extra space in the parent. |
|
Alignment when the widget does not fill the available slot:
|
|
External spacing in pixels. Accepts an integer (equal on all
sides), a 2-tuple |
|
Horizontal external spacing (left and right). Accepts an integer
or a 2-tuple |
|
Vertical external spacing (top and bottom). Accepts an integer
or a 2-tuple |
Grid#
Used inside a Grid container.
|
Zero-based row and column indices. |
|
Number of rows or columns to span. |
|
Alignment and fill within the grid cell. Any combination of
|
|
External spacing in pixels. Accepts an integer, a 2-tuple
|
|
Horizontal external spacing. Accepts an integer or |
|
Vertical external spacing. Accepts an integer or |
See also#
NumberField — numeric-only input with stepper buttons
Select — dropdown picker for longer lists
API#
The complete reference for SpinnerField lives on the
Widgets API page. At a glance:
A text-entry field with spin buttons for stepping through values. |
Full Example#
1
2with bs.App(title="SpinnerField Demo", padding=20, gap=16) as app:
3
4 # Text mode — fixed list of options
5 bs.Label("Text Mode", font="heading-sm")
6 bs.SpinnerField(
7 label="Priority",
8 options=["Low", "Medium", "High", "Critical"],
9 value="Medium",
10 )
11
12 # Numeric mode — min / max / step
13 bs.Label("Numeric Mode", font="heading-sm")
14 bs.SpinnerField(
15 label="Quantity",
16 value=1,
17 min_value=1,
18 max_value=99,
19 step=1,
20 )
21
22 # Wrap-around
23 bs.Label("Wrap Around", font="heading-sm")
24 bs.SpinnerField(
25 label="Month",
26 options=["Jan", "Feb", "Mar", "Apr", "May", "Jun",
27 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
28 value="Jan",
29 wrap=True,
30 )
31
32 # Label and message
33 bs.Label("Label and Message", font="heading-sm")
34 bs.SpinnerField(
35 label="Font size",
36 value=12,
37 min_value=6,
38 max_value=72,
39 step=2,
40 message="Applies to the selected text.",
41 )
42
43 # Reactive binding
44 bs.Label("Reactive Binding", font="heading-sm")
45 size_sig = bs.Signal("Medium")
46 bs.SpinnerField(
47 label="T-shirt size",
48 options=["XS", "S", "M", "L", "XL", "XXL"],
49 value="M",
50 textsignal=size_sig,
51 )
52 bs.Label(textsignal=size_sig, accent="secondary")
53
54 # States
55 bs.Label("States", font="heading-sm")
56 bs.SpinnerField(value=5, min_value=1, max_value=10, label="Normal")
57 bs.SpinnerField(value=5, min_value=1, max_value=10, label="Read only", read_only=True)
58 bs.SpinnerField(value=5, min_value=1, max_value=10, label="Disabled", disabled=True)
59
60 # Handling changes
61 bs.Label("Handling Changes", font="heading-sm")
62 last = bs.Signal("(none)")
63 sf = bs.SpinnerField(
64 label="Rating",
65 options=["★", "★★", "★★★", "★★★★", "★★★★★"],
66 value="★★★",
67 wrap=True,
68 )
69 sf.on_change(lambda e: last.set(sf.value))
70 bs.Label(textsignal=last, accent="secondary")
71
72app.run()