SpinnerEntry
SpinnerEntry is a form-ready input control with integrated step buttons.
It's designed for values that users change in small steps, while still allowing typing. It supports formatting, validation, localization, and consistent field events like other entry controls.

Quick start
import bootstack as bs
app = bs.App()
qty = bs.SpinnerEntry(
app,
label="Quantity",
value=1,
increment=1,
message="How many items?",
)
qty.pack(fill="x", padx=20, pady=10)
app.mainloop()
When to use
Use SpinnerEntry when:
- stepping is the primary interaction
- users frequently increment/decrement values
- visible step buttons improve UX
Consider a different control when...
- users primarily type numbers and stepping is secondary -> use NumericEntry
- you need bounds (
minvalue/maxvalue) and clamping/wrapping behavior -> use NumericEntry - users adjust continuously -> use Scale
Examples and patterns
Value model
SpinnerEntry uses the same text vs committed value model as other field controls.
current = qty.value
raw = qty.get()
qty.value = 10
Commit-time parsing/formatting happens on blur or Enter.
increment
Controls step size for buttons/keys/wheel.
bs.SpinnerEntry(app, label="Retry limit", value=3, increment=1)
Formatting: value_format
bs.SpinnerEntry(app, label="Price", value=9.99, increment=0.01, value_format="currency").pack()

Localization
Currency and number formatting respects locale settings. See Localization for details.
Add-ons
amount = bs.SpinnerEntry(app, label="Amount", value=0, increment=1)
amount.insert_addon(bs.Label, position="before", text="$")

Events
SpinnerEntry emits standard field events:
<<Input>>/on_input<<Changed>>/on_changed- validation lifecycle events
It also emits step intent events:
<<Increment>>/on_increment<<Decrement>>/on_decrement
def on_changed(event):
print("new value:", event.data["value"])
qty.on_changed(on_changed)
def on_increment(event):
print("increment requested")
qty.on_increment(on_increment)
Validation and constraints
Use validation rules for business constraints:
limit = bs.SpinnerEntry(app, label="Retry limit", value=3, increment=1, required=True)
limit.add_validation_rule("required", message="A value is required")
If you need numeric bounds, prefer NumericEntry (min/max) unless SpinnerEntry also supports them in your implementation.
Behavior
SpinnerEntry supports stepping via:
- spin buttons
- Up / Down arrow keys
- mouse wheel (platform-dependent)
Typing is always allowed unless you set the underlying entry to readonly.
Additional resources
Related widgets
- NumericEntry - validated numeric input with bounds
- Spinbox - low-level stepper primitive
- TextEntry - general field control
- Scale - slider-based numeric adjustment
- Form - build forms from field definitions