bootstack 0.1.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bootstack/__init__.py +249 -0
- bootstack/__main__.py +5 -0
- bootstack/api/__init__.py +127 -0
- bootstack/api/app.py +30 -0
- bootstack/api/constants.py +3 -0
- bootstack/api/data.py +23 -0
- bootstack/api/dialogs.py +44 -0
- bootstack/api/i18n.py +17 -0
- bootstack/api/localization.py +16 -0
- bootstack/api/menu.py +7 -0
- bootstack/api/style.py +23 -0
- bootstack/api/utils.py +24 -0
- bootstack/api/widgets.py +137 -0
- bootstack/assets/__init__.py +24 -0
- bootstack/assets/bootstack-transparent.png +0 -0
- bootstack/assets/bootstack.ico +0 -0
- bootstack/assets/bootstack.png +0 -0
- bootstack/assets/elements/__init__.py +0 -0
- bootstack/assets/elements/badge-pill.png +0 -0
- bootstack/assets/elements/badge-square.png +0 -0
- bootstack/assets/elements/border.png +0 -0
- bootstack/assets/elements/button-compact.png +0 -0
- bootstack/assets/elements/button-default.png +0 -0
- bootstack/assets/elements/button-group-horizontal-after-compact.png +0 -0
- bootstack/assets/elements/button-group-horizontal-after-default.png +0 -0
- bootstack/assets/elements/button-group-horizontal-before-compact.png +0 -0
- bootstack/assets/elements/button-group-horizontal-before-default.png +0 -0
- bootstack/assets/elements/button-group-horizontal-center-compact.png +0 -0
- bootstack/assets/elements/button-group-horizontal-center-default.png +0 -0
- bootstack/assets/elements/button-group-vertical-after-compact.png +0 -0
- bootstack/assets/elements/button-group-vertical-after-default.png +0 -0
- bootstack/assets/elements/button-group-vertical-before-compact.png +0 -0
- bootstack/assets/elements/button-group-vertical-before-default.png +0 -0
- bootstack/assets/elements/button-group-vertical-center-compact.png +0 -0
- bootstack/assets/elements/button-group-vertical-center-default.png +0 -0
- bootstack/assets/elements/checkbox-checked.png +0 -0
- bootstack/assets/elements/checkbox-indeterminate.png +0 -0
- bootstack/assets/elements/checkbox-unchecked.png +0 -0
- bootstack/assets/elements/field.png +0 -0
- bootstack/assets/elements/input-after-compact.png +0 -0
- bootstack/assets/elements/input-after-default.png +0 -0
- bootstack/assets/elements/input-before-compact.png +0 -0
- bootstack/assets/elements/input-before-default.png +0 -0
- bootstack/assets/elements/input-compact.png +0 -0
- bootstack/assets/elements/input-default.png +0 -0
- bootstack/assets/elements/list-item-separated.png +0 -0
- bootstack/assets/elements/list-item.png +0 -0
- bootstack/assets/elements/manifest.toml +480 -0
- bootstack/assets/elements/menu-item.png +0 -0
- bootstack/assets/elements/nav-button-compact.png +0 -0
- bootstack/assets/elements/nav-button-default.png +0 -0
- bootstack/assets/elements/nav-icon-button-compact.png +0 -0
- bootstack/assets/elements/nav-icon-button-default.png +0 -0
- bootstack/assets/elements/notebook-client-border.png +0 -0
- bootstack/assets/elements/notebook-tab-active.png +0 -0
- bootstack/assets/elements/notebook-tab-bar.png +0 -0
- bootstack/assets/elements/notebook-tab-normal.png +0 -0
- bootstack/assets/elements/notebook-tab-pill.png +0 -0
- bootstack/assets/elements/progress-bar-horizontal-striped.png +0 -0
- bootstack/assets/elements/progress-bar-solid.png +0 -0
- bootstack/assets/elements/progress-bar-thin.png +0 -0
- bootstack/assets/elements/progress-bar-vertical-striped.png +0 -0
- bootstack/assets/elements/radio-selected.png +0 -0
- bootstack/assets/elements/radio-unselected.png +0 -0
- bootstack/assets/elements/scrollbar-horizontal.png +0 -0
- bootstack/assets/elements/scrollbar-vertical.png +0 -0
- bootstack/assets/elements/slider-handle-focus.png +0 -0
- bootstack/assets/elements/slider-handle.png +0 -0
- bootstack/assets/elements/slider-track-horizontal.png +0 -0
- bootstack/assets/elements/slider-track-vertical.png +0 -0
- bootstack/assets/elements/switch-off.png +0 -0
- bootstack/assets/elements/switch-on.png +0 -0
- bootstack/assets/elements/tabs-bar-horizontal.png +0 -0
- bootstack/assets/elements/tabs-bar-vertical.png +0 -0
- bootstack/assets/elements/tabs-pill.png +0 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +875 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +875 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +851 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +841 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +914 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +841 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +841 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/themes/__init__.py +0 -0
- bootstack/assets/themes/amber-dark.json +32 -0
- bootstack/assets/themes/amber-light.json +32 -0
- bootstack/assets/themes/aurora-dark.json +32 -0
- bootstack/assets/themes/aurora-light.json +32 -0
- bootstack/assets/themes/bootstrap-dark.json +32 -0
- bootstack/assets/themes/bootstrap-light.json +32 -0
- bootstack/assets/themes/classic-dark.json +32 -0
- bootstack/assets/themes/classic-light.json +32 -0
- bootstack/assets/themes/docs-dark.json +32 -0
- bootstack/assets/themes/docs-light.json +32 -0
- bootstack/assets/themes/forest-dark.json +32 -0
- bootstack/assets/themes/forest-light.json +32 -0
- bootstack/assets/themes/ocean-dark.json +32 -0
- bootstack/assets/themes/ocean-light.json +32 -0
- bootstack/assets/themes/rose-dark.json +32 -0
- bootstack/assets/themes/rose-light.json +32 -0
- bootstack/assets/widgets/__init__.py +0 -0
- bootstack/assets/widgets/badge-default.png +0 -0
- bootstack/assets/widgets/badge-pill.png +0 -0
- bootstack/assets/widgets/border.png +0 -0
- bootstack/assets/widgets/button-group-horizontal-after.png +0 -0
- bootstack/assets/widgets/button-group-horizontal-before.png +0 -0
- bootstack/assets/widgets/button-group-horizontal-center.png +0 -0
- bootstack/assets/widgets/button-group-vertical-after.png +0 -0
- bootstack/assets/widgets/button-group-vertical-before.png +0 -0
- bootstack/assets/widgets/button-group-vertical-center.png +0 -0
- bootstack/assets/widgets/button.png +0 -0
- bootstack/assets/widgets/checkbox-checked.png +0 -0
- bootstack/assets/widgets/checkbox-indeterminate.png +0 -0
- bootstack/assets/widgets/checkbox-unchecked.png +0 -0
- bootstack/assets/widgets/field.png +0 -0
- bootstack/assets/widgets/icon-button.png +0 -0
- bootstack/assets/widgets/input-inner.png +0 -0
- bootstack/assets/widgets/input-prefix.png +0 -0
- bootstack/assets/widgets/input-suffix.png +0 -0
- bootstack/assets/widgets/input.png +0 -0
- bootstack/assets/widgets/list-item-focus.png +0 -0
- bootstack/assets/widgets/list-item-separated.png +0 -0
- bootstack/assets/widgets/menu-item-separated.png +0 -0
- bootstack/assets/widgets/notebook-client-border.png +0 -0
- bootstack/assets/widgets/notebook-pill-active.png +0 -0
- bootstack/assets/widgets/notebook-pill-inactive.png +0 -0
- bootstack/assets/widgets/notebook-tab-active.png +0 -0
- bootstack/assets/widgets/notebook-tab-border.png +0 -0
- bootstack/assets/widgets/notebook-tab-normal.png +0 -0
- bootstack/assets/widgets/notebook-underline.png +0 -0
- bootstack/assets/widgets/progress-bar-horizontal-default.png +0 -0
- bootstack/assets/widgets/progress-bar-horizontal-striped.png +0 -0
- bootstack/assets/widgets/progress-bar-vertical-default.png +0 -0
- bootstack/assets/widgets/progress-bar-vertical-striped.png +0 -0
- bootstack/assets/widgets/progress-trough-horizontal.png +0 -0
- bootstack/assets/widgets/progress-trough-vertical.png +0 -0
- bootstack/assets/widgets/radio-selected.png +0 -0
- bootstack/assets/widgets/radio-unselected.png +0 -0
- bootstack/assets/widgets/scrollbar-horizontal-rounded.png +0 -0
- bootstack/assets/widgets/scrollbar-vertical-rounded.png +0 -0
- bootstack/assets/widgets/separator-horizontal.png +0 -0
- bootstack/assets/widgets/separator-vertical.png +0 -0
- bootstack/assets/widgets/slider-handle-focus.png +0 -0
- bootstack/assets/widgets/slider-handle.png +0 -0
- bootstack/assets/widgets/slider-track-horizontal.png +0 -0
- bootstack/assets/widgets/slider-track-vertical.png +0 -0
- bootstack/assets/widgets/switch-off.png +0 -0
- bootstack/assets/widgets/switch-on.png +0 -0
- bootstack/assets/widgets/tabs-bar-horizontal.png +0 -0
- bootstack/assets/widgets/tabs-bar-vertical.png +0 -0
- bootstack/assets/widgets/tabs-pill.png +0 -0
- bootstack/cli/__init__.py +124 -0
- bootstack/cli/__main__.py +6 -0
- bootstack/cli/add.py +439 -0
- bootstack/cli/build.py +115 -0
- bootstack/cli/config.py +287 -0
- bootstack/cli/demo.py +1267 -0
- bootstack/cli/doctor.py +195 -0
- bootstack/cli/list_cmd.py +71 -0
- bootstack/cli/promote.py +120 -0
- bootstack/cli/pyinstaller.py +246 -0
- bootstack/cli/run.py +99 -0
- bootstack/cli/start.py +105 -0
- bootstack/cli/templates/__init__.py +861 -0
- bootstack/constants.py +325 -0
- bootstack/core/__init__.py +34 -0
- bootstack/core/capabilities/__init__.py +45 -0
- bootstack/core/capabilities/after.py +103 -0
- bootstack/core/capabilities/bind.py +154 -0
- bootstack/core/capabilities/bindtags.py +112 -0
- bootstack/core/capabilities/busy.py +61 -0
- bootstack/core/capabilities/clipboard.py +88 -0
- bootstack/core/capabilities/focus.py +118 -0
- bootstack/core/capabilities/grab.py +65 -0
- bootstack/core/capabilities/grid.py +188 -0
- bootstack/core/capabilities/localization.py +231 -0
- bootstack/core/capabilities/pack.py +119 -0
- bootstack/core/capabilities/place.py +92 -0
- bootstack/core/capabilities/selection.py +136 -0
- bootstack/core/capabilities/signals.py +242 -0
- bootstack/core/capabilities/winfo.py +315 -0
- bootstack/core/colorutils.py +234 -0
- bootstack/core/exceptions.py +95 -0
- bootstack/core/images.py +283 -0
- bootstack/core/localization/README.md +90 -0
- bootstack/core/localization/__init__.py +13 -0
- bootstack/core/localization/intl_format.py +580 -0
- bootstack/core/localization/msgcat.py +425 -0
- bootstack/core/localization/specs.py +143 -0
- bootstack/core/mixins/__init__.py +1 -0
- bootstack/core/mixins/ttk_state.py +35 -0
- bootstack/core/mixins/widget.py +132 -0
- bootstack/core/publisher.py +147 -0
- bootstack/core/signals/README.md +112 -0
- bootstack/core/signals/__init__.py +8 -0
- bootstack/core/signals/integration.py +100 -0
- bootstack/core/signals/signal.py +317 -0
- bootstack/core/signals/types.py +4 -0
- bootstack/core/validation/__init__.py +5 -0
- bootstack/core/validation/types.py +13 -0
- bootstack/core/validation/validation_result.py +17 -0
- bootstack/core/validation/validation_rules.py +112 -0
- bootstack/core/variables.py +62 -0
- bootstack/datasource/README.md +607 -0
- bootstack/datasource/__init__.py +51 -0
- bootstack/datasource/base.py +474 -0
- bootstack/datasource/file_source.py +541 -0
- bootstack/datasource/memory_source.py +482 -0
- bootstack/datasource/sqlite_source.py +453 -0
- bootstack/datasource/types.py +259 -0
- bootstack/dialogs/__init__.py +56 -0
- bootstack/dialogs/colorchooser.py +674 -0
- bootstack/dialogs/colordropper.py +257 -0
- bootstack/dialogs/datedialog.py +404 -0
- bootstack/dialogs/dialog.py +514 -0
- bootstack/dialogs/filterdialog.py +358 -0
- bootstack/dialogs/fontdialog.py +339 -0
- bootstack/dialogs/formdialog.py +541 -0
- bootstack/dialogs/message.py +489 -0
- bootstack/dialogs/query.py +561 -0
- bootstack/py.typed +1 -0
- bootstack/runtime/__init__.py +3 -0
- bootstack/runtime/app.py +879 -0
- bootstack/runtime/base_window.py +786 -0
- bootstack/runtime/events.py +399 -0
- bootstack/runtime/menu.py +510 -0
- bootstack/runtime/shortcuts.py +423 -0
- bootstack/runtime/tk_patch.py +31 -0
- bootstack/runtime/toplevel.py +131 -0
- bootstack/runtime/utility.py +371 -0
- bootstack/runtime/visual_focus.py +228 -0
- bootstack/runtime/window_utilities.py +1043 -0
- bootstack/style/__init__.py +5498 -0
- bootstack/style/bootstyle.py +507 -0
- bootstack/style/bootstyle_builder_base.py +752 -0
- bootstack/style/bootstyle_builder_mixed.py +93 -0
- bootstack/style/bootstyle_builder_tk.py +109 -0
- bootstack/style/bootstyle_builder_ttk.py +354 -0
- bootstack/style/builders/__init__.py +51 -0
- bootstack/style/builders/badge.py +44 -0
- bootstack/style/builders/button.py +453 -0
- bootstack/style/builders/buttongroup.py +344 -0
- bootstack/style/builders/calendar.py +271 -0
- bootstack/style/builders/checkbutton.py +95 -0
- bootstack/style/builders/combobox.py +112 -0
- bootstack/style/builders/contextmenu.py +268 -0
- bootstack/style/builders/entry.py +83 -0
- bootstack/style/builders/expander.py +171 -0
- bootstack/style/builders/field.py +312 -0
- bootstack/style/builders/frame.py +27 -0
- bootstack/style/builders/label.py +28 -0
- bootstack/style/builders/labelframe.py +41 -0
- bootstack/style/builders/listview.py +267 -0
- bootstack/style/builders/menubar.py +74 -0
- bootstack/style/builders/menubutton.py +408 -0
- bootstack/style/builders/notebook.py +316 -0
- bootstack/style/builders/panedwindow.py +25 -0
- bootstack/style/builders/progressbar.py +71 -0
- bootstack/style/builders/radiobutton.py +68 -0
- bootstack/style/builders/scale.py +66 -0
- bootstack/style/builders/scrollbar.py +360 -0
- bootstack/style/builders/separator.py +45 -0
- bootstack/style/builders/sidenav.py +313 -0
- bootstack/style/builders/sizegrip.py +15 -0
- bootstack/style/builders/spinbox.py +119 -0
- bootstack/style/builders/switch.py +67 -0
- bootstack/style/builders/tabitem.py +205 -0
- bootstack/style/builders/toolbutton.py +260 -0
- bootstack/style/builders/tooltip.py +26 -0
- bootstack/style/builders/treeview.py +269 -0
- bootstack/style/builders/utils.py +404 -0
- bootstack/style/builders_tk/__init__.py +16 -0
- bootstack/style/builders_tk/defaults.py +229 -0
- bootstack/style/element.py +173 -0
- bootstack/style/style.py +499 -0
- bootstack/style/theme_provider.py +449 -0
- bootstack/style/tk_patch.py +5 -0
- bootstack/style/token_maps.py +42 -0
- bootstack/style/types.py +32 -0
- bootstack/style/typography.py +527 -0
- bootstack/style/utility.py +696 -0
- bootstack/themes/__init__.py +12 -0
- bootstack/themes/standard.py +415 -0
- bootstack/themes/user.py +45 -0
- bootstack/widgets/__init__.py +53 -0
- bootstack/widgets/composites/__init__.py +38 -0
- bootstack/widgets/composites/accordion.py +385 -0
- bootstack/widgets/composites/appshell.py +445 -0
- bootstack/widgets/composites/buttongroup.py +391 -0
- bootstack/widgets/composites/calendar.py +914 -0
- bootstack/widgets/composites/compositeframe.py +282 -0
- bootstack/widgets/composites/contextmenu.py +1754 -0
- bootstack/widgets/composites/dateentry.py +261 -0
- bootstack/widgets/composites/dropdownbutton.py +190 -0
- bootstack/widgets/composites/expander.py +508 -0
- bootstack/widgets/composites/field.py +448 -0
- bootstack/widgets/composites/floodgauge.py +434 -0
- bootstack/widgets/composites/form.py +983 -0
- bootstack/widgets/composites/labeledscale.py +209 -0
- bootstack/widgets/composites/list/__init__.py +15 -0
- bootstack/widgets/composites/list/listitem.py +733 -0
- bootstack/widgets/composites/list/listview.py +1507 -0
- bootstack/widgets/composites/menubar.py +303 -0
- bootstack/widgets/composites/meter.py +882 -0
- bootstack/widgets/composites/numericentry.py +183 -0
- bootstack/widgets/composites/pagestack.py +330 -0
- bootstack/widgets/composites/passwordentry.py +149 -0
- bootstack/widgets/composites/pathentry.py +223 -0
- bootstack/widgets/composites/radiogroup.py +466 -0
- bootstack/widgets/composites/scrolledtext.py +388 -0
- bootstack/widgets/composites/scrolledtext.pyi +186 -0
- bootstack/widgets/composites/scrollview.py +675 -0
- bootstack/widgets/composites/selectbox.py +544 -0
- bootstack/widgets/composites/sidenav/__init__.py +24 -0
- bootstack/widgets/composites/sidenav/group.py +485 -0
- bootstack/widgets/composites/sidenav/header.py +83 -0
- bootstack/widgets/composites/sidenav/item.py +413 -0
- bootstack/widgets/composites/sidenav/separator.py +51 -0
- bootstack/widgets/composites/sidenav/view.py +919 -0
- bootstack/widgets/composites/spinnerentry.py +232 -0
- bootstack/widgets/composites/tableview/__init__.py +5 -0
- bootstack/widgets/composites/tableview/tableview.py +2254 -0
- bootstack/widgets/composites/tableview/types.py +169 -0
- bootstack/widgets/composites/tabs/__init__.py +6 -0
- bootstack/widgets/composites/tabs/tabitem.py +372 -0
- bootstack/widgets/composites/tabs/tabs.py +478 -0
- bootstack/widgets/composites/tabs/tabview.py +352 -0
- bootstack/widgets/composites/textentry.py +90 -0
- bootstack/widgets/composites/timeentry.py +189 -0
- bootstack/widgets/composites/toast.py +364 -0
- bootstack/widgets/composites/togglegroup.py +382 -0
- bootstack/widgets/composites/toolbar.py +393 -0
- bootstack/widgets/composites/tooltip.py +404 -0
- bootstack/widgets/internal/__init__.py +0 -0
- bootstack/widgets/internal/wrapper_base.py +304 -0
- bootstack/widgets/mixins/__init__.py +25 -0
- bootstack/widgets/mixins/configure_mixin.py +186 -0
- bootstack/widgets/mixins/entry_mixin.py +70 -0
- bootstack/widgets/mixins/font_mixin.py +346 -0
- bootstack/widgets/mixins/icon_mixin.py +38 -0
- bootstack/widgets/mixins/localization_mixin.py +255 -0
- bootstack/widgets/mixins/signal_mixin.py +272 -0
- bootstack/widgets/mixins/validation_mixin.py +204 -0
- bootstack/widgets/parts/__init__.py +11 -0
- bootstack/widgets/parts/numberentry_part.py +345 -0
- bootstack/widgets/parts/spinnerentry_part.py +394 -0
- bootstack/widgets/parts/textentry_part.py +344 -0
- bootstack/widgets/primitives/__init__.py +55 -0
- bootstack/widgets/primitives/badge.py +44 -0
- bootstack/widgets/primitives/button.py +89 -0
- bootstack/widgets/primitives/card.py +66 -0
- bootstack/widgets/primitives/checkbutton.py +124 -0
- bootstack/widgets/primitives/checktoggle.py +53 -0
- bootstack/widgets/primitives/combobox.py +165 -0
- bootstack/widgets/primitives/entry.py +98 -0
- bootstack/widgets/primitives/frame.py +206 -0
- bootstack/widgets/primitives/gridframe.py +479 -0
- bootstack/widgets/primitives/label.py +95 -0
- bootstack/widgets/primitives/labelframe.py +63 -0
- bootstack/widgets/primitives/menubutton.py +118 -0
- bootstack/widgets/primitives/notebook.py +551 -0
- bootstack/widgets/primitives/optionmenu.py +248 -0
- bootstack/widgets/primitives/packframe.py +228 -0
- bootstack/widgets/primitives/panedwindow.py +58 -0
- bootstack/widgets/primitives/progressbar.py +95 -0
- bootstack/widgets/primitives/radiobutton.py +115 -0
- bootstack/widgets/primitives/radiotoggle.py +50 -0
- bootstack/widgets/primitives/scale.py +85 -0
- bootstack/widgets/primitives/scrollbar.py +56 -0
- bootstack/widgets/primitives/separator.py +56 -0
- bootstack/widgets/primitives/sizegrip.py +47 -0
- bootstack/widgets/primitives/spinbox.py +91 -0
- bootstack/widgets/primitives/switch.py +41 -0
- bootstack/widgets/primitives/treeview.py +77 -0
- bootstack/widgets/types.py +20 -0
- bootstack-0.1.0a1.dist-info/METADATA +196 -0
- bootstack-0.1.0a1.dist-info/RECORD +419 -0
- bootstack-0.1.0a1.dist-info/WHEEL +5 -0
- bootstack-0.1.0a1.dist-info/entry_points.txt +2 -0
- bootstack-0.1.0a1.dist-info/licenses/LICENSE +22 -0
- bootstack-0.1.0a1.dist-info/licenses/NOTICE +10 -0
- bootstack-0.1.0a1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
from tkinter import Event, TclError
|
|
2
|
+
from typing import Any, Callable
|
|
3
|
+
|
|
4
|
+
from bootstack.core.localization import MessageCatalog, IntlFormatter
|
|
5
|
+
from bootstack.widgets.primitives.entry import Entry
|
|
6
|
+
from bootstack.widgets.mixins import ValidationMixin
|
|
7
|
+
from bootstack.widgets.mixins.configure_mixin import configure_delegate
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TextEntryPart(ValidationMixin, Entry):
|
|
11
|
+
"""Internationalization-aware entry widget with deferred parsing and formatting.
|
|
12
|
+
|
|
13
|
+
This widget separates user input (display text) from the committed/parsed value,
|
|
14
|
+
providing a clean pattern for handling formatted data entry. Parsing and formatting
|
|
15
|
+
only occur when the user commits the value via `<FocusOut>` or `<Return>`.
|
|
16
|
+
|
|
17
|
+
!!! note "Events"
|
|
18
|
+
- `<<Input>>`: Triggered on each keystroke. `event.data = {'text': str}`
|
|
19
|
+
- `<<Change>>`: Triggered when value changes after commit. `event.data = {'value': Any, 'prev_value': Any, 'text': str}`
|
|
20
|
+
- `<Return>`: Triggered on Enter key press. `event.data = {'value': Any, 'text': str}`
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
master=None,
|
|
26
|
+
*,
|
|
27
|
+
value='',
|
|
28
|
+
value_format=None,
|
|
29
|
+
initial_focus: bool = False,
|
|
30
|
+
allow_blank: bool = True,
|
|
31
|
+
**kwargs
|
|
32
|
+
):
|
|
33
|
+
"""Initialize a TextEntryPart widget with internationalization support.
|
|
34
|
+
|
|
35
|
+
Creates an entry widget that separates display text from the parsed value.
|
|
36
|
+
The widget defers parsing and formatting until the user commits the value
|
|
37
|
+
by pressing Enter or moving focus away from the widget.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
master: Parent widget. If None, uses the default root window.
|
|
41
|
+
value: Initial value to display and parse. Can be a string or any value
|
|
42
|
+
that can be formatted using value_format. Default is empty string.
|
|
43
|
+
value_format (str): ICU format pattern for parsing and formatting the value.
|
|
44
|
+
Common patterns: `'#,##0.00'` (decimal), `'¤#,##0.00'` (currency),
|
|
45
|
+
`'yyyy-MM-dd'` (date), `'#,##0.00%'` (percent).
|
|
46
|
+
If None, value is treated as plain text (no parsing/formatting).
|
|
47
|
+
initial_focus (bool): If True, widget receives focus when created.
|
|
48
|
+
allow_blank (bool): If True, empty input is parsed as None. If False, empty
|
|
49
|
+
input preserves the previous value.
|
|
50
|
+
**kwargs: Additional keyword arguments passed to the Entry base class.
|
|
51
|
+
|
|
52
|
+
Note:
|
|
53
|
+
The widget automatically subscribes to text changes and sets up
|
|
54
|
+
event handlers for `<FocusIn>`, `<FocusOut>`, and `<Return>`.
|
|
55
|
+
"""
|
|
56
|
+
kwargs.setdefault('ttk_class', 'TField')
|
|
57
|
+
kwargs.setdefault('variant', 'input')
|
|
58
|
+
super().__init__(master, **kwargs)
|
|
59
|
+
|
|
60
|
+
# configuration
|
|
61
|
+
self._value_format = value_format
|
|
62
|
+
self._allow_blank = allow_blank
|
|
63
|
+
self._on_input_fid = None
|
|
64
|
+
self._fmt = IntlFormatter(locale=MessageCatalog.locale())
|
|
65
|
+
|
|
66
|
+
# set the initial display value
|
|
67
|
+
# Convert to string if it's a number
|
|
68
|
+
if isinstance(value, (int, float)):
|
|
69
|
+
initial_display = str(value)
|
|
70
|
+
else:
|
|
71
|
+
initial_display = value or self.textsignal.get() or ''
|
|
72
|
+
|
|
73
|
+
# Parse initial value if format is specified
|
|
74
|
+
if value_format is not None:
|
|
75
|
+
initial_value = self._parse_or_none(initial_display)
|
|
76
|
+
else:
|
|
77
|
+
initial_value = initial_display or ''
|
|
78
|
+
|
|
79
|
+
self._value = initial_value
|
|
80
|
+
self._prev_changed_value = initial_value
|
|
81
|
+
|
|
82
|
+
# normalize initial display if we already have a parsed value
|
|
83
|
+
if self._value is not None:
|
|
84
|
+
formatted_text = self._format_value(self._value)
|
|
85
|
+
self.textsignal.set(formatted_text)
|
|
86
|
+
else:
|
|
87
|
+
self.textsignal.set('')
|
|
88
|
+
|
|
89
|
+
# track last text emitted for CHANGE
|
|
90
|
+
self._prev_change_text = self.textsignal.get()
|
|
91
|
+
|
|
92
|
+
# subscribe to text changes
|
|
93
|
+
self._on_input_fid = self.textsignal.subscribe(self._handle_change)
|
|
94
|
+
|
|
95
|
+
# Commit on focus out / enter;
|
|
96
|
+
self.bind('<FocusIn>', self._store_prev_value, add=True)
|
|
97
|
+
self.bind('<FocusOut>', self._handle_focus_out, add=True)
|
|
98
|
+
self.bind('<Return>', self._handle_return, add=True)
|
|
99
|
+
self.winfo_toplevel().bind('<<LocaleChanged>>', self._on_locale_changed, add='+')
|
|
100
|
+
|
|
101
|
+
# set initial focus
|
|
102
|
+
if initial_focus:
|
|
103
|
+
self.focus()
|
|
104
|
+
|
|
105
|
+
def _store_prev_value(self, _: Any):
|
|
106
|
+
"""Store current value on focus-in to detect changes later."""
|
|
107
|
+
self._prev_changed_value = self._value
|
|
108
|
+
|
|
109
|
+
def _handle_focus_out(self, _):
|
|
110
|
+
"""Commit value and check for changes when focus leaves the widget."""
|
|
111
|
+
self.commit()
|
|
112
|
+
self._check_if_changed()
|
|
113
|
+
|
|
114
|
+
def _handle_return(self, _):
|
|
115
|
+
"""Commit value and check for changes when Return key is pressed."""
|
|
116
|
+
self.commit()
|
|
117
|
+
self._check_if_changed()
|
|
118
|
+
|
|
119
|
+
def _handle_change(self, event):
|
|
120
|
+
"""Emit <<Input>> event on every text change without parsing."""
|
|
121
|
+
text = self.textsignal.get()
|
|
122
|
+
if text == self._prev_change_text:
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
self._prev_change_text = text
|
|
126
|
+
self.event_generate('<<Input>>', data={"text": text})
|
|
127
|
+
|
|
128
|
+
def _check_if_changed(self):
|
|
129
|
+
"""Emit <<Change>> event if parsed value changed since focus-in."""
|
|
130
|
+
if self._value != self._prev_changed_value:
|
|
131
|
+
data = {
|
|
132
|
+
"value": self._value,
|
|
133
|
+
"prev_value": self._prev_changed_value,
|
|
134
|
+
"text": self.textsignal.get()
|
|
135
|
+
}
|
|
136
|
+
self.event_generate('<<Change>>', data=data)
|
|
137
|
+
self._prev_changed_value = self._value
|
|
138
|
+
|
|
139
|
+
def _parse_or_none(self, s: str):
|
|
140
|
+
"""Parse string using value_format, returning None on empty/invalid input."""
|
|
141
|
+
# If a non-string is passed (e.g., datetime/date), assume it's already parsed.
|
|
142
|
+
if not isinstance(s, str):
|
|
143
|
+
return s
|
|
144
|
+
s2 = (s or '').strip()
|
|
145
|
+
if not s2:
|
|
146
|
+
return None
|
|
147
|
+
try:
|
|
148
|
+
if self._value_format is None:
|
|
149
|
+
return s2
|
|
150
|
+
return self._fmt.parse(s2, self._value_format)
|
|
151
|
+
except ValueError:
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
def _format_value(self, value: Any) -> str:
|
|
155
|
+
"""Format a value for display using value_format.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
value: The value to format (can be string, int, float, etc.)
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Formatted string representation of the value
|
|
162
|
+
"""
|
|
163
|
+
if value is None:
|
|
164
|
+
return ''
|
|
165
|
+
|
|
166
|
+
# If no format specified, just convert to string
|
|
167
|
+
if self._value_format is None:
|
|
168
|
+
return str(value)
|
|
169
|
+
|
|
170
|
+
# IntlFormatter.format() expects numeric values, not strings
|
|
171
|
+
# So pass the value directly (it will be a number from parse())
|
|
172
|
+
try:
|
|
173
|
+
return self._fmt.format(value, self._value_format)
|
|
174
|
+
except (ValueError, TypeError):
|
|
175
|
+
# If formatting fails, return string representation
|
|
176
|
+
return str(value)
|
|
177
|
+
|
|
178
|
+
def on_input(self, callback: Callable) -> str:
|
|
179
|
+
"""Bind to `<<Input>>`. Callback receives `event.data = {'text': str}`."""
|
|
180
|
+
return self.bind('<<Input>>', callback, add=True)
|
|
181
|
+
|
|
182
|
+
def off_input(self, bind_id: str | None = None) -> None:
|
|
183
|
+
"""Unbind from `<<Input>>`."""
|
|
184
|
+
self.unbind('<<Input>>', bind_id)
|
|
185
|
+
|
|
186
|
+
def on_enter(self, callback: Callable) -> str:
|
|
187
|
+
"""Bind to `<Return>`. Callback receives `event.data = {'value': Any, 'text': str}`."""
|
|
188
|
+
|
|
189
|
+
def enrich_callback(event: Event) -> None:
|
|
190
|
+
data = {"value": self._value, "text": self.textsignal.get()}
|
|
191
|
+
event.data = data
|
|
192
|
+
return callback(event)
|
|
193
|
+
|
|
194
|
+
return self.bind('<Return>', enrich_callback, add=True)
|
|
195
|
+
|
|
196
|
+
def off_enter(self, bind_id: str | None = None) -> None:
|
|
197
|
+
"""Unbind from `<Return>`."""
|
|
198
|
+
self.unbind('<Return>', bind_id)
|
|
199
|
+
|
|
200
|
+
def on_changed(self, callback: Callable) -> str:
|
|
201
|
+
"""Bind to `<<Change>>`. Callback receives `event.data = {'value': Any, 'prev_value': Any, 'text': str}`."""
|
|
202
|
+
return self.bind("<<Change>>", callback, add=True)
|
|
203
|
+
|
|
204
|
+
def off_changed(self, bind_id: str | None = None) -> None:
|
|
205
|
+
"""Unbind from `<<Change>>`."""
|
|
206
|
+
self.unbind('<<Change>>', bind_id)
|
|
207
|
+
|
|
208
|
+
def value(self, value=None):
|
|
209
|
+
"""Get or set the parsed/committed value.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
value: If provided, sets the display text and internal value with formatting
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Current parsed value if no argument provided, None otherwise
|
|
216
|
+
"""
|
|
217
|
+
if value is None:
|
|
218
|
+
return self._value
|
|
219
|
+
else:
|
|
220
|
+
# Store the value and format it for display
|
|
221
|
+
if isinstance(value, (int, float)):
|
|
222
|
+
value_str = str(value)
|
|
223
|
+
else:
|
|
224
|
+
value_str = str(value) if value is not None else ''
|
|
225
|
+
|
|
226
|
+
# Parse the value if format is specified
|
|
227
|
+
if self._value_format is not None and value_str:
|
|
228
|
+
self._value = self._parse_or_none(value_str)
|
|
229
|
+
else:
|
|
230
|
+
self._value = value_str if value_str else None
|
|
231
|
+
|
|
232
|
+
# Format and display
|
|
233
|
+
formatted_text = self._format_value(self._value)
|
|
234
|
+
self.textsignal.set(formatted_text)
|
|
235
|
+
return None
|
|
236
|
+
|
|
237
|
+
def text(self, value=None):
|
|
238
|
+
"""Get or set the raw display text without committing.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
value: If provided, sets the display text without parsing
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
Current display text if no argument provided, None otherwise
|
|
245
|
+
"""
|
|
246
|
+
if value is None:
|
|
247
|
+
return self.textsignal.get()
|
|
248
|
+
else:
|
|
249
|
+
self.textsignal.set(value)
|
|
250
|
+
return None
|
|
251
|
+
|
|
252
|
+
def commit(self):
|
|
253
|
+
"""Parse display text, update value, and normalize display (called on FocusOut/Return)."""
|
|
254
|
+
s = self.get().strip()
|
|
255
|
+
|
|
256
|
+
# parse once
|
|
257
|
+
if s == '':
|
|
258
|
+
self._value = None if self._allow_blank else self._value
|
|
259
|
+
else:
|
|
260
|
+
try:
|
|
261
|
+
self._value = s if self._value_format is None else self._fmt.parse(s, self._value_format)
|
|
262
|
+
except ValueError:
|
|
263
|
+
# keep prior value on parse failure
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
# Format the value for display
|
|
267
|
+
new_text = self._format_value(self._value)
|
|
268
|
+
|
|
269
|
+
if new_text != self.textsignal.get():
|
|
270
|
+
# temporarily silence CHANGE while normalizing text
|
|
271
|
+
fid = getattr(self, '_on_input_fid', None)
|
|
272
|
+
if fid:
|
|
273
|
+
try:
|
|
274
|
+
self.textsignal.unsubscribe(fid)
|
|
275
|
+
except TclError:
|
|
276
|
+
pass
|
|
277
|
+
self.textsignal.set(new_text)
|
|
278
|
+
if fid:
|
|
279
|
+
self._on_input_fid = self.textsignal.subscribe(self._handle_change)
|
|
280
|
+
|
|
281
|
+
def _on_locale_changed(self, event=None):
|
|
282
|
+
"""Respond to global changes in locale by updating formatter and text"""
|
|
283
|
+
self._fmt = IntlFormatter(locale=MessageCatalog.locale())
|
|
284
|
+
|
|
285
|
+
# reformat current value with new locale + same value_format
|
|
286
|
+
if self._value is not None:
|
|
287
|
+
formatted_text = self._format_value(self._value)
|
|
288
|
+
self.textsignal.set(formatted_text)
|
|
289
|
+
|
|
290
|
+
@configure_delegate('text')
|
|
291
|
+
def _delegate_text(self, value=None):
|
|
292
|
+
if value is None:
|
|
293
|
+
return self.text()
|
|
294
|
+
else:
|
|
295
|
+
return self.text(value)
|
|
296
|
+
|
|
297
|
+
@configure_delegate('value')
|
|
298
|
+
def _delegate_value(self, value=None):
|
|
299
|
+
if value is None:
|
|
300
|
+
return self.value()
|
|
301
|
+
else:
|
|
302
|
+
return self.value(value)
|
|
303
|
+
|
|
304
|
+
@configure_delegate('value_format')
|
|
305
|
+
def _delegate_value_format(self, value: str):
|
|
306
|
+
"""Get or set the value format pattern and reformat display.
|
|
307
|
+
|
|
308
|
+
Can be accessed via:
|
|
309
|
+
widget.configure(value_format='#,##0.00')
|
|
310
|
+
widget['value_format'] = '#,##0.00'
|
|
311
|
+
widget.cget('value_format')
|
|
312
|
+
"""
|
|
313
|
+
if value is None:
|
|
314
|
+
return self._value_format
|
|
315
|
+
|
|
316
|
+
self._value_format = value
|
|
317
|
+
# Reformat current value with new format
|
|
318
|
+
if self._value is not None:
|
|
319
|
+
formatted_text = self._format_value(self._value)
|
|
320
|
+
self.textsignal.set(formatted_text)
|
|
321
|
+
|
|
322
|
+
@configure_delegate('allow_blank')
|
|
323
|
+
def _delegate_allow_blank(self, value: bool):
|
|
324
|
+
"""Get or set whether blank input is allowed.
|
|
325
|
+
|
|
326
|
+
Can be accessed via:
|
|
327
|
+
widget.configure(allow_blank=True)
|
|
328
|
+
widget['allow_blank'] = True
|
|
329
|
+
widget.cget('allow_blank')
|
|
330
|
+
"""
|
|
331
|
+
if value is None:
|
|
332
|
+
return self._allow_blank
|
|
333
|
+
|
|
334
|
+
self._allow_blank = bool(value)
|
|
335
|
+
|
|
336
|
+
def destroy(self):
|
|
337
|
+
"""Clean up signal subscriptions and destroy the widget."""
|
|
338
|
+
if self._on_input_fid:
|
|
339
|
+
try:
|
|
340
|
+
self.textsignal.unsubscribe(self._on_input_fid)
|
|
341
|
+
except Exception:
|
|
342
|
+
pass # Ignore all errors during cleanup
|
|
343
|
+
self._on_input_fid = None
|
|
344
|
+
super().destroy()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Primitive bootstack widgets."""
|
|
2
|
+
|
|
3
|
+
from .badge import Badge
|
|
4
|
+
from .button import Button
|
|
5
|
+
from .card import Card
|
|
6
|
+
from .checkbutton import CheckButton
|
|
7
|
+
from .checktoggle import CheckToggle
|
|
8
|
+
from .combobox import Combobox
|
|
9
|
+
from .entry import Entry
|
|
10
|
+
from .frame import Frame
|
|
11
|
+
from .gridframe import GridFrame
|
|
12
|
+
from .label import Label
|
|
13
|
+
from .labelframe import LabelFrame
|
|
14
|
+
from .menubutton import MenuButton
|
|
15
|
+
from .notebook import Notebook
|
|
16
|
+
from .packframe import PackFrame
|
|
17
|
+
from .panedwindow import PanedWindow
|
|
18
|
+
from .progressbar import Progressbar
|
|
19
|
+
from .radiobutton import RadioButton
|
|
20
|
+
from .radiotoggle import RadioToggle
|
|
21
|
+
from .scale import Scale
|
|
22
|
+
from .scrollbar import Scrollbar
|
|
23
|
+
from .separator import Separator
|
|
24
|
+
from .sizegrip import SizeGrip
|
|
25
|
+
from .spinbox import Spinbox
|
|
26
|
+
from .switch import Switch
|
|
27
|
+
from .treeview import TreeView
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"Badge",
|
|
31
|
+
"Button",
|
|
32
|
+
"Card",
|
|
33
|
+
"CheckButton",
|
|
34
|
+
"CheckToggle",
|
|
35
|
+
"Combobox",
|
|
36
|
+
"Entry",
|
|
37
|
+
"Frame",
|
|
38
|
+
"GridFrame",
|
|
39
|
+
"Label",
|
|
40
|
+
"LabelFrame",
|
|
41
|
+
"MenuButton",
|
|
42
|
+
"Notebook",
|
|
43
|
+
"PackFrame",
|
|
44
|
+
"PanedWindow",
|
|
45
|
+
"Progressbar",
|
|
46
|
+
"RadioButton",
|
|
47
|
+
"RadioToggle",
|
|
48
|
+
"Scale",
|
|
49
|
+
"Scrollbar",
|
|
50
|
+
"Separator",
|
|
51
|
+
"SizeGrip",
|
|
52
|
+
"Spinbox",
|
|
53
|
+
"Switch",
|
|
54
|
+
"TreeView",
|
|
55
|
+
]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from typing_extensions import Unpack
|
|
2
|
+
from bootstack.widgets.primitives.label import Label, LabelKwargs
|
|
3
|
+
from bootstack.widgets.types import Master
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Badge(Label):
|
|
7
|
+
"""bootstack wrapper for `ttk.Label` that renders with a badge style."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, master: Master = None, **kwargs: Unpack[LabelKwargs]):
|
|
10
|
+
"""Create a themed bootstack Badge.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
master: Parent widget. If None, uses the default root window.
|
|
14
|
+
|
|
15
|
+
Other Parameters:
|
|
16
|
+
text (str): Text to display on the badge.
|
|
17
|
+
image (PhotoImage): Image to display.
|
|
18
|
+
icon (str | dict): Theme-aware icon spec handled by the style system.
|
|
19
|
+
icon_only (bool): If True, removes the additional padding reserved for label text.
|
|
20
|
+
compound (str): Placement of the image relative to text.
|
|
21
|
+
anchor (str): Alignment of the badge content within its area.
|
|
22
|
+
justify (str): How to justify multiple lines of text.
|
|
23
|
+
localize (bool | Literal['auto']): Determines the widget's localization mode.
|
|
24
|
+
value_format (str | dict): Format specification for the badge value.
|
|
25
|
+
padding (int | tuple): Extra space around the badge content.
|
|
26
|
+
width (int): Width of the badge in characters.
|
|
27
|
+
wraplength (int): Maximum width before wrapping text.
|
|
28
|
+
font (str | Font): Font for the badge text.
|
|
29
|
+
foreground (str): Text color.
|
|
30
|
+
background (str): Background color.
|
|
31
|
+
relief (str): Border style.
|
|
32
|
+
state (str): Widget state.
|
|
33
|
+
takefocus (bool): Whether the widget participates in focus traversal.
|
|
34
|
+
style (str): Explicit ttk style name (overrides accent/variant).
|
|
35
|
+
accent (str): Accent token for styling, e.g. 'primary', 'success', 'danger'.
|
|
36
|
+
variant (str): Shape of badge. 'pill' or 'square' (default).
|
|
37
|
+
surface (str): Optional surface token; otherwise inherited.
|
|
38
|
+
style_options (dict): Optional dict forwarded to the style builder.
|
|
39
|
+
"""
|
|
40
|
+
kwargs.setdefault('ttk_class', 'TBadge')
|
|
41
|
+
kwargs.setdefault('anchor', 'center')
|
|
42
|
+
kwargs.setdefault('font', '-size 8')
|
|
43
|
+
kwargs.setdefault('variant', 'square')
|
|
44
|
+
super().__init__(master=master, **kwargs)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from tkinter import ttk
|
|
4
|
+
from typing import Any, Callable, Literal, Optional, TypedDict, TYPE_CHECKING
|
|
5
|
+
from typing_extensions import Unpack
|
|
6
|
+
|
|
7
|
+
from bootstack.core.mixins.ttk_state import TtkStateMixin
|
|
8
|
+
from bootstack.core.mixins.widget import WidgetCapabilitiesMixin
|
|
9
|
+
from bootstack.widgets.internal.wrapper_base import TTKWrapperBase
|
|
10
|
+
from bootstack.widgets.mixins import IconMixin, TextSignalMixin, LocalizationMixin
|
|
11
|
+
from bootstack.widgets.types import Master
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from bootstack.core.signals import Signal
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ButtonKwargs(TypedDict, total=False):
|
|
18
|
+
# Standard ttk.Button options
|
|
19
|
+
text: Any
|
|
20
|
+
command: Optional[Callable[[], Any]]
|
|
21
|
+
image: Any
|
|
22
|
+
icon: Any
|
|
23
|
+
icon_only: bool
|
|
24
|
+
anchor: str
|
|
25
|
+
compound: Literal['text', 'image', 'top', 'bottom', 'left', 'right', 'center', 'none'] | str
|
|
26
|
+
padding: Any
|
|
27
|
+
width: int
|
|
28
|
+
underline: int
|
|
29
|
+
state: Literal['normal', 'active', 'disabled', 'readonly'] | str
|
|
30
|
+
takefocus: Any
|
|
31
|
+
localize: bool | Literal['auto']
|
|
32
|
+
style: str
|
|
33
|
+
class_: str
|
|
34
|
+
cursor: str
|
|
35
|
+
default: Any
|
|
36
|
+
name: str
|
|
37
|
+
textvariable: Any
|
|
38
|
+
textsignal: Signal[str]
|
|
39
|
+
|
|
40
|
+
# bootstack-specific extensions
|
|
41
|
+
bootstyle: str # DEPRECATED: Use accent and variant instead
|
|
42
|
+
accent: str
|
|
43
|
+
density: Literal['default', 'compact']
|
|
44
|
+
variant: str
|
|
45
|
+
surface: str
|
|
46
|
+
style_options: dict[str, Any]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Button(LocalizationMixin, TextSignalMixin, IconMixin, TTKWrapperBase, WidgetCapabilitiesMixin, TtkStateMixin, ttk.Button):
|
|
50
|
+
"""TTK Bootstrap Button
|
|
51
|
+
|
|
52
|
+
bootstack wrapper for `ttk.Button` with bootstyle and icon support.
|
|
53
|
+
"""
|
|
54
|
+
_ttk_base = ttk.Button
|
|
55
|
+
|
|
56
|
+
def __init__(self, master: Master = None, **kwargs: Unpack[ButtonKwargs]) -> None:
|
|
57
|
+
"""Create a themed bootstack Button.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
master: Parent widget. If None, uses the default root window.
|
|
61
|
+
|
|
62
|
+
Other Parameters:
|
|
63
|
+
text (str): Text to display on the button.
|
|
64
|
+
textvariable (Variable): Tk variable linked to the button text.
|
|
65
|
+
textsignal (Signal[str]): Reactive Signal linked to the button text (auto-synced with textvariable).
|
|
66
|
+
command (Callable): Callable invoked when the button is pressed.
|
|
67
|
+
image (PhotoImage): Image to display on the button.
|
|
68
|
+
icon (str | dict): Optional icon spec integrated via the style system.
|
|
69
|
+
icon_only (bool): If true, removes the extra padding reserved for the text labels.
|
|
70
|
+
compound (str): Placement of the image relative to text (e.g., 'left').
|
|
71
|
+
padding (int | tuple): Extra space around the button content.
|
|
72
|
+
anchor (str): Determines how the content is aligned in the container. Combination of 'n', 's', 'e', 'w', or 'center' (default).
|
|
73
|
+
localize (bool | Literal['auto']): Determines the widgets localization mode.
|
|
74
|
+
width (int): Width of the button in characters.
|
|
75
|
+
underline (int): Index of the character to underline in `text`.
|
|
76
|
+
state (str): Widget state (e.g., 'normal', 'disabled').
|
|
77
|
+
takefocus (bool): Whether the widget accepts focus during traversal.
|
|
78
|
+
style (str): Explicit ttk style name to apply (overrides accent/variant).
|
|
79
|
+
accent (str): Accent token for styling, e.g. 'primary', 'danger', 'success'.
|
|
80
|
+
variant (str): Style variant, e.g. 'solid', 'outline', 'link', 'text'. Defaults to 'solid'.
|
|
81
|
+
density (str): The vertical and horizontal compactness of widget content, e.g. 'default', 'compact'.
|
|
82
|
+
bootstyle (str): DEPRECATED - Use `accent` and `variant` instead.
|
|
83
|
+
surface (str): Optional surface token to use for this button; if not provided, surface color is inherited from the parent.
|
|
84
|
+
style_options (dict): Optional dict forwarded to the style builder.
|
|
85
|
+
"""
|
|
86
|
+
if 'bootstyle' not in kwargs:
|
|
87
|
+
kwargs.setdefault('variant', 'solid')
|
|
88
|
+
kwargs.update(style_options=self._capture_style_options(['icon_only', 'icon', 'anchor', 'density'], kwargs))
|
|
89
|
+
super().__init__(master, **kwargs)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, TypedDict
|
|
4
|
+
|
|
5
|
+
from typing_extensions import Unpack
|
|
6
|
+
|
|
7
|
+
from bootstack.widgets.primitives.frame import Frame
|
|
8
|
+
from bootstack.widgets.types import Master
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CardKwargs(TypedDict, total=False):
|
|
12
|
+
# Standard ttk.Frame options
|
|
13
|
+
padding: Any
|
|
14
|
+
width: int
|
|
15
|
+
height: int
|
|
16
|
+
style: str
|
|
17
|
+
cursor: str
|
|
18
|
+
name: str
|
|
19
|
+
takefocus: bool
|
|
20
|
+
class_: str
|
|
21
|
+
|
|
22
|
+
# bootstack-specific extensions
|
|
23
|
+
accent: str
|
|
24
|
+
variant: str
|
|
25
|
+
surface: str
|
|
26
|
+
show_border: bool
|
|
27
|
+
style_options: dict[str, Any]
|
|
28
|
+
bootstyle: str # DEPRECATED: Use accent and variant instead
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Card(Frame):
|
|
32
|
+
"""A convenience wrapper for Frame with card styling.
|
|
33
|
+
|
|
34
|
+
Card is a Frame with `surface='card'` and `show_border=True` by default,
|
|
35
|
+
providing an elevated container with a visible border for grouping content.
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
```python
|
|
39
|
+
card = ttk.Card(app, padding=20)
|
|
40
|
+
ttk.Label(card, text="Card content").pack()
|
|
41
|
+
```
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(self, master: Master = None, **kwargs: Unpack[CardKwargs]) -> None:
|
|
45
|
+
"""Create a themed Card container.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
master: Parent widget. If None, uses the default root window.
|
|
49
|
+
|
|
50
|
+
Other Parameters:
|
|
51
|
+
padding (int | tuple): Extra padding inside the card. Default 16.
|
|
52
|
+
width (int): Requested width in pixels.
|
|
53
|
+
height (int): Requested height in pixels.
|
|
54
|
+
takefocus (bool): Widget accepts focus during keyboard traversal.
|
|
55
|
+
style (str): Explicit ttk style name (overrides accent/variant).
|
|
56
|
+
accent (str): Accent/color token for the card. Default 'card'.
|
|
57
|
+
variant (str): Style variant (if applicable).
|
|
58
|
+
surface (str): Surface token for the parent background.
|
|
59
|
+
show_border (bool): Draw a border around the card. Default True.
|
|
60
|
+
style_options (dict): Optional dict forwarded to the style builder.
|
|
61
|
+
"""
|
|
62
|
+
if 'bootstyle' not in kwargs:
|
|
63
|
+
kwargs.setdefault('accent', 'card')
|
|
64
|
+
kwargs.setdefault('show_border', True)
|
|
65
|
+
kwargs.setdefault('padding', 16)
|
|
66
|
+
super().__init__(master, **kwargs)
|