PageStack#
A browser-style navigation container that shows one page at a time. Add named
pages with .add(), place child widgets inside each page using the returned
context manager, then call navigate() to switch between them. Forward and back
navigation is tracked automatically.
Usage#
Adding pages#
Call .add(key) once per page. Use the returned StackPage as a context
manager — child widgets created inside the with block are placed on that
page.
ps = bs.PageStack(fill="both", expand=True)
with ps.add("home"):
bs.Label("Home page content")
with ps.add("settings"):
bs.Label("Settings page content")
ps.navigate("home")
Back and forward#
Every navigate() call pushes a history entry. Use back() and forward()
to move through history, guarded by can_back and can_forward.
ps.navigate("home")
ps.navigate("settings")
ps.can_back # True
ps.back() # → "home"
ps.can_forward # True
ps.forward() # → "settings"
Page layout modes#
Each page has an independent internal layout controlled by the layout=
argument of add(). The default is 'vstack' (vertical stack).
# Vertical stack (default)
with ps.add("home", layout="vstack", gap=8):
bs.Label("Row 1")
bs.Label("Row 2")
# Horizontal stack
with ps.add("toolbar", layout="hstack", gap=6):
bs.Button("Cut")
bs.Button("Copy")
bs.Button("Paste")
# Grid
with ps.add("form", layout="grid", columns=["auto", 1], gap=8, sticky_items="ew"):
bs.Label("Username")
bs.TextField()
bs.Label("Password")
bs.PasswordField()
Passing data to pages#
Pass an optional data dict to navigate(). The dict is forwarded to the
page’s on_page_mount event payload so the receiving page can act on it.
ps.navigate("detail", data={"record_id": 42})
def on_mount(event):
record_id = event.data.get("record_id")
ps.on_page_mount(on_mount)
Events#
on_page_change fires after every navigation. on_page_mount fires when
a page becomes visible. Both receive a PageChangeEvent with context about the transition — the
page and prev_page keys, the nav direction, and any data passed
to navigate().
def on_change(event):
page = event.page # current page key
prev = event.prev_page # previous page key (or None)
nav = event.nav # 'push', 'back', or 'forward'
ps.on_page_change(on_change)
# Stream form — compose with operators
ps.on_page_change().listen(lambda e: print(e.page))
Introspection#
ps.current # key of the visible page, or None
ps.can_back # True if back() would succeed
ps.can_forward # True if forward() would succeed
ps.page_keys() # ('home', 'settings', 'about')
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#
ScrollView —
scrollable container with vertical and/or horizontal scrollbars.
SplitView —
resizable split container with draggable sashes.
Accordion —
collapsible section container.
VStack,
HStack, and
Grid —
non-navigating layout containers.
API#
The complete reference for PageStack and its
StackPage handles lives on the
Widgets API page. At a glance:
Full Example#
1
2with bs.App(title="PageStack", size=(680, 500), padding=20, gap=12) as app:
3
4 bs.Label("PageStack — one page visible at a time", font="heading-md")
5
6 # ── Navigation bar ────────────────────────────────────────────────────────
7 with bs.HStack(gap=6):
8 btn_home = bs.Button("Home", accent="primary")
9 btn_settings = bs.Button("Settings")
10 btn_about = bs.Button("About")
11
12 bs.Separator()
13
14 # ── Page container ────────────────────────────────────────────────────────
15 ps = bs.PageStack(fill="both", expand=True)
16
17 with ps.add("home", gap=12):
18 bs.Label("Welcome", font="heading-lg")
19 bs.Label("This is the Home page.")
20 bs.Label("Use the buttons above to switch pages.")
21 bs.Separator()
22 with bs.HStack(gap=8):
23 bs.Button("Get Started", accent="primary")
24 bs.Button("Learn More", variant="outline")
25
26 with ps.add("settings", layout="grid", columns=["auto", 1], gap=8, sticky_items="ew"):
27 bs.Label("Username")
28 bs.TextField(placeholder="your-username")
29 bs.Label("Theme")
30 bs.Select(values=["Light", "Dark", "System"])
31 bs.Label("Language")
32 bs.Select(values=["English", "Spanish", "French"])
33
34 with ps.add("about", gap=12):
35 bs.Label("About bootstack", font="heading-lg")
36 bs.Label("A batteries-included Python desktop UI framework.")
37 bs.Label("Build modern desktop apps without touching Tkinter.")
38
39 ps.navigate("home")
40
41 btn_home.on_click(lambda e: ps.navigate("home"))
42 btn_settings.on_click(lambda e: ps.navigate("settings"))
43 btn_about.on_click(lambda e: ps.navigate("about"))
44
45app.run()