App Configuration#
An app is configured entirely through flat constructor keyword arguments, and
that same configuration is read and changed at runtime through matching app.*
properties. What goes in comes back out symmetrically — bs.App(theme=...) in,
app.theme out — so there is no separate settings object to construct, inject,
or look up. The same surface is shared by App and
AppShell.
import bootstack as bs
with bs.App(
title="My App",
theme="bootstrap-dark",
locale="de_DE",
size=(900, 600),
) as app:
bs.Label("Hallo!")
app.run()
Reading and changing configuration#
Every configuration option is a property on the app. Reading returns the current value; assigning changes it, and for the options that take effect live (theme, locale, title) the change applies immediately:
app.theme # 'bootstrap-dark'
app.theme = "bootstrap-light" # switches the theme now
app.title = "Renamed window" # updates the title bar now
app.locale = "fr_FR" # re-localizes now
The available options:
Property |
Access |
Meaning |
|---|---|---|
|
read/write |
Window title bar text and the app’s display name. |
|
read/write |
Active theme name; assigning switches the theme live. |
|
read/write |
The pair |
|
read/write |
Track the OS light/dark setting (currently effective on macOS). |
|
read/write |
Active locale (e.g. |
|
read-only |
Base language derived from the locale (e.g. |
|
read-only |
Short date / time pattern for the active locale. |
|
read-only |
Decimal / thousands separator for the active locale. |
|
read/write |
Windows-only window effect ( |
|
read/write |
Save the window geometry on close and restore it next launch. |
Construction-only options#
Some configuration is set once at construction and has no live app.* property
(changing it at runtime would be incomplete or meaningless): localize_mode,
macos_quit_behavior, state_path, available_themes, and the window
geometry options (size, position, min_size, max_size,
resizable, scaling, hdpi). Pass them to App(...) / AppShell(...).
The exposed theme list is read at runtime via get_themes.
Theme and appearance#
Set the startup theme with theme=; switch it later through app.theme or
the global bs.toggle_theme() / bs.set_theme() helpers. toggle_theme
flips between the light_theme and dark_theme pair:
app = bs.App(theme="bootstrap-light",
light_theme="bootstrap-light",
dark_theme="bootstrap-dark")
bs.Button("Toggle", on_click=bs.toggle_theme)
On macOS, follow_system_appearance=True switches between the light and dark
themes automatically when the user changes the OS appearance. See
Theming for defining and installing custom themes.
Localization#
locale= sets the active locale. localize_mode= controls automatic text
translation for the whole app — "auto" (the default — translate when a
translation is registered for the locale, otherwise show the literal), True
(always), or False (never); an individual widget overrides it with
localize=. Register your own translations with
add_translations() — see Localization (i18n).
The locale-derived formatting values are exposed as read-only properties (they
are computed from the locale, so they update whenever app.locale changes):
app = bs.App(locale="de_DE")
app.locale_date_format # 'dd.MM.yy'
app.locale_decimal # ','
app.locale = "en_US"
app.locale_decimal # '.'
Reacting to changes#
Subscribe to configuration changes to keep other state in sync — the handler receives the new value directly:
app.on_theme_change(lambda theme: print("theme is now", theme))
app.on_locale_change(lambda locale: print("locale is now", locale))
Like other event hooks, calling these without a handler returns a composable stream.
Persisting configuration#
Because configuration is flat keyword arguments and a Store is a flat dict, persistence is symmetric: splat the
store in to restore on startup, and write each value back when it changes.
App.from_store() does the restore and tolerantly ignores keys that are not
valid configuration, so a settings file from an older or newer version (with
renamed or removed keys) still loads cleanly instead of raising:
from bootstack.store import Store
store = Store("settings") # app config, in its own store
app = bs.App.from_store(store) # restore; empty store → defaults
app.on_theme_change(lambda theme: store.update(theme=theme))
app.on_locale_change(lambda locale: store.update(locale=locale))
app.run()
AppShell.from_store() works the same way. A few practices keep this clean:
Keep app *config* separate from app *state*. Put restorable configuration (theme, locale) in one store and transient state (recent files, last-opened tab) in another, so
from_storeonly ever sees real configuration keys.Window geometry is the one exception. It is a fiddly
"WxH+X+Y"string updated continuously as the window moves, not a clean keyword — let the built-inremember_window_state=Trueflag handle it rather than the store.Per-value write-back, not a config dump. React to the specific change events and write just that key.
Window state persistence#
remember_window_state=True saves the window’s size and position when it
closes and restores them on the next launch (off-screen positions are clamped
back onto a visible monitor). It is stored in a per-app file under the OS config
directory; pass state_path= to override the location.
bs.App(title="My App", remember_window_state=True)
See also#
Theming — defining themes and the values worth persisting.
Preferences Store — the preferences store and persistence patterns.