Form
Form is a spec-driven form builder.
It makes it easy to build consistent data-entry UIs by composing the standard v2 input widgets (TextEntry, NumericEntry, SelectBox, DateEntry, etc.) from a single definition.
Quick start
Define fields and read the committed data:
import bootstack as bs
app = bs.App()
form = bs.Form(
app,
items=[
{"key": "name", "label": "Name", "editor": "text"},
{"key": "age", "label": "Age", "editor": "int"},
{"key": "status", "label": "Status", "editor": "select", "items": ["New", "In Progress", "Done"]},
],
)
form.pack(fill="both", expand=True, padx=20, pady=20)
def submit():
if form.validate():
print(form.data) # dict-like
else:
print("invalid")
bs.Button(app, text="Submit", command=submit).pack(pady=(0, 20))
app.mainloop()
When to use
Use Form when:
-
you want to build forms from a spec (instead of manually wiring every widget)
-
you need consistent label/message/validation behavior across fields
-
you need consistent layout + validation across many fields
-
the form structure changes dynamically (feature flags, permissions, metadata-driven UI)
-
you want a single place to read/write form data and run validation
Consider a different control when...
-
the form is small (1–3 fields) — hand-built layouts may be simpler
-
the layout is highly custom or artistic — manual composition gives more control
-
you need extremely bespoke widget composition per row
-
the entire task is "fill this form and confirm/cancel" in a modal flow — prefer FormDialog
Appearance
A Form takes a list of field definitions (and optional layout/grouping instructions) and produces:
-
labeled field widgets (using your v2 field controls)
-
an internal data model (readable as a dict)
-
validation helpers and a consistent "submit" flow
-
optional grouping and tabs for larger forms
This is a builder/composite: it does not replace your input widgets—it standardizes how they're assembled and managed.
Design System
See the Design System for form layout guidelines and spacing recommendations.
Examples and patterns
Form spec
Form is configured with a list of item definitions.
Field items
A field item describes one value:
-
key— unique field key (used inform.data) -
label— label text for the field -
editor— what widget/editor to use -
value— optional initial value -
required/validate— validation options -
help/message— optional helper text
Example:
{"key": "email", "label": "Email", "editor": "text", "required": True}
Editor-specific options
Some editors accept additional options:
-
select-like editors:
items=[...] -
numeric editors:
min,max,step, formatting options -
date editors:
min_date,max_date, formatting options
Layout
Columns
For wide forms, you can specify a column count:
form = bs.Form(app, col_count=2, items=[...])
Explicit placement (advanced)
If your implementation supports explicit row/column placement, use it for custom layouts:
{"key": "first", "label": "First name", "editor": "text", "row": 0, "column": 0}
{"key": "last", "label": "Last name", "editor": "text", "row": 0, "column": 1}
Grouping and tabs
For larger forms, organize fields into groups and tabs.
Groups
Groups visually separate related fields (like a section):
{
"type": "group",
"label": "Account",
"items": [
{"key": "user", "label": "User", "editor": "text"},
{"key": "role", "label": "Role", "editor": "select", "items": ["Admin", "User"]},
]
}
Tabs
Tabs separate major sections (profile/settings/permissions):
{
"type": "tabs",
"tabs": [
{"label": "General", "items": [...]},
{"label": "Advanced", "items": [...]},
]
}
Data model
Reading data
Use get(), the value property, or the data property to get all field values:
# All equivalent
data = form.get()
data = form.value
data = form.data
print(data["name"])
Get a single field's value:
email = form.get_field_value("email")
Setting data
Set all values at once:
form.set({"name": "Alice", "age": 34})
# Or use the property
form.value = {"name": "Alice", "age": 34}
Set a single field's value:
form.set_field_value("status", "Done")
Validation
Validate the full form
ok = form.validate()
Required fields
{"key": "name", "label": "Name", "editor": "text", "required": True}
Custom validation (if supported)
If your spec supports validators:
{"key": "email", "label": "Email", "editor": "text", "validate": "email"}
Or a callable:
{"key": "port", "label": "Port", "editor": "int", "validate": lambda v: 0 <= v <= 65535}
Validation UX
Prefer showing validation messages inline at the field level, and only use modal dialogs for form-level failures.
Footer actions and "submit" flows
A common pattern is to:
- validate the form
- read
form.data - commit changes / close dialog / navigate
If your Form supports built-in footer buttons, use them for consistent layouts. Otherwise, pair with ButtonGroup or a simple button row.
Behavior
Accessing fields and widgets
Access a field widget by key:
email_field = form.field("email") # Returns the Field widget
email_field.focus_set()
Get all field widgets or keys:
for field in form.fields():
print(field.value)
for key in form.keys():
print(key, form.get_field_value(key))
Use this to:
-
focus the first invalid field
-
dynamically enable/disable fields
-
update items for select fields
Variable and signal access
Access Tk Variables and Signals for reactive binding:
# Get the Tk Variable for a field
var = form.field_variable("name")
var.trace_add("write", lambda *_: print("name changed"))
# Get the Signal for a field's value
signal = form.field_signal("age")
if signal:
signal.subscribe(lambda v: print(f"age is now {v}"))
# Get the text Signal for a field
text_signal = form.field_textsignal("email")
Localization
Form labels and validation messages should be localized for international users.
Localization
See the Localization guide for details on translating form content.
Reactivity
Form data can be bound to reactive signals for automatic UI updates when values change.
Signals
See the Signals documentation for reactive data binding patterns.
Additional resources
Related widgets
-
TextEntry / NumericEntry / DateEntry / SelectBox — the underlying field widgets
-
FormDialog — modal form flow
-
PageStack — multi-step (wizard) forms
-
FilterDialog — specialized form for filters
Framework concepts
-
Forms & Input — picking input widgets and end-to-end form patterns
-
Design System — layout and spacing guidelines
-
Validation — form validation patterns
-
Localization — internationalization support