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,394 @@
|
|
|
1
|
+
"""Spinner entry widget with value management and validation.
|
|
2
|
+
|
|
3
|
+
This module provides a spinner entry widget that extends Spinbox with
|
|
4
|
+
value management features like parsing, formatting, and event handling for
|
|
5
|
+
both text and numeric values.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from tkinter import Event, TclError
|
|
9
|
+
from typing import Any, Callable, Union, List
|
|
10
|
+
|
|
11
|
+
from bootstack.core.localization import MessageCatalog, IntlFormatter
|
|
12
|
+
from bootstack.widgets.primitives.spinbox import Spinbox
|
|
13
|
+
from bootstack.widgets.mixins import ValidationMixin
|
|
14
|
+
from bootstack.widgets.mixins.configure_mixin import configure_delegate
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SpinnerEntryPart(ValidationMixin, Spinbox):
|
|
18
|
+
"""Spinner widget with internationalization and value management.
|
|
19
|
+
|
|
20
|
+
This widget extends Spinbox to provide the same value/text management
|
|
21
|
+
pattern as TextEntryPart and NumberEntryPart. It can safely handle both
|
|
22
|
+
text and numeric values with optional formatting.
|
|
23
|
+
|
|
24
|
+
!!! note "Events"
|
|
25
|
+
|
|
26
|
+
- `<<Input>>`: Triggered on each keystroke.
|
|
27
|
+
Provides `event.data` with keys: `text`.
|
|
28
|
+
|
|
29
|
+
- `<<Change>>`: Triggered when value changes after commit.
|
|
30
|
+
Provides `event.data` with keys: `value`, `prev_value`, `text`.
|
|
31
|
+
|
|
32
|
+
- **<Return>**: Triggered on Enter key press.
|
|
33
|
+
Provides `event.data` with keys: `value`, `text`.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
master=None,
|
|
39
|
+
*,
|
|
40
|
+
value: Union[int, float, str] = '',
|
|
41
|
+
value_format: str = None,
|
|
42
|
+
values: List[str] = None,
|
|
43
|
+
from_: Union[int, float] = None,
|
|
44
|
+
to: Union[int, float] = None,
|
|
45
|
+
increment: Union[int, float] = 1,
|
|
46
|
+
wrap: bool = False,
|
|
47
|
+
initial_focus: bool = False,
|
|
48
|
+
allow_blank: bool = True,
|
|
49
|
+
**kwargs
|
|
50
|
+
):
|
|
51
|
+
"""Initialize a SpinnerEntryPart widget.
|
|
52
|
+
|
|
53
|
+
Creates a spinner widget that separates display text from the parsed value.
|
|
54
|
+
The widget defers parsing and formatting until the user commits the value
|
|
55
|
+
by pressing Enter or moving focus away from the widget.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
master: Parent widget. If None, uses the default root window.
|
|
59
|
+
value: Initial value to display and parse. Can be a string, int, or float.
|
|
60
|
+
Default is empty string.
|
|
61
|
+
value_format: ICU format pattern for parsing and formatting the value.
|
|
62
|
+
Common patterns:
|
|
63
|
+
- Numbers: '#,##0.00' (decimal with thousands separator)
|
|
64
|
+
- Currency: '¤#,##0.00' (currency symbol with amount)
|
|
65
|
+
- Dates: 'yyyy-MM-dd' (ISO date format)
|
|
66
|
+
- Percent: '#,##0.00%' (percentage)
|
|
67
|
+
If None, value is treated as plain text (no parsing/formatting).
|
|
68
|
+
values: List of valid string values for the spinner. If provided,
|
|
69
|
+
the spinner cycles through these values. Mutually exclusive with
|
|
70
|
+
from_/to/increment.
|
|
71
|
+
from_: Minimum numeric value (inclusive). Only used for numeric spinners.
|
|
72
|
+
to: Maximum numeric value (inclusive). Only used for numeric spinners.
|
|
73
|
+
increment: Step size for increment/decrement. Default is 1.
|
|
74
|
+
wrap: If True, values wrap around at boundaries. Default is False.
|
|
75
|
+
initial_focus: If True, widget receives focus when created. Default is False.
|
|
76
|
+
allow_blank: If True, empty input is parsed as None. If False, empty
|
|
77
|
+
input preserves the previous value. Default is True.
|
|
78
|
+
**kwargs: Additional keyword arguments passed to SpinnerPart.
|
|
79
|
+
|
|
80
|
+
Note:
|
|
81
|
+
The widget automatically subscribes to text changes and sets up
|
|
82
|
+
event handlers for <FocusIn>, <FocusOut>, and <Return>.
|
|
83
|
+
"""
|
|
84
|
+
# Store configuration
|
|
85
|
+
self._value_format = value_format
|
|
86
|
+
self._allow_blank = allow_blank
|
|
87
|
+
self._on_input_fid = None
|
|
88
|
+
self._fmt = IntlFormatter(locale=MessageCatalog.locale())
|
|
89
|
+
|
|
90
|
+
# Configure Spinbox parameters
|
|
91
|
+
spinbox_kwargs = {}
|
|
92
|
+
if values is not None:
|
|
93
|
+
spinbox_kwargs['values'] = values
|
|
94
|
+
if from_ is not None:
|
|
95
|
+
spinbox_kwargs['from_'] = from_
|
|
96
|
+
if to is not None:
|
|
97
|
+
spinbox_kwargs['to'] = to
|
|
98
|
+
if increment is not None:
|
|
99
|
+
spinbox_kwargs['increment'] = increment
|
|
100
|
+
spinbox_kwargs['wrap'] = wrap
|
|
101
|
+
|
|
102
|
+
# Merge with user kwargs and set ttk_class/variant for field styling
|
|
103
|
+
spinbox_kwargs.update(kwargs)
|
|
104
|
+
spinbox_kwargs.setdefault('ttk_class', 'TField')
|
|
105
|
+
spinbox_kwargs.setdefault('variant', 'spinner')
|
|
106
|
+
|
|
107
|
+
# Initialize base Spinbox with ValidationMixin
|
|
108
|
+
super().__init__(master, **spinbox_kwargs)
|
|
109
|
+
|
|
110
|
+
# Set the initial display value
|
|
111
|
+
if isinstance(value, (int, float)):
|
|
112
|
+
initial_display = str(value)
|
|
113
|
+
else:
|
|
114
|
+
initial_display = value or self.textsignal.get() or ''
|
|
115
|
+
|
|
116
|
+
# Parse initial value if format is specified
|
|
117
|
+
if value_format is not None:
|
|
118
|
+
initial_value = self._parse_or_none(initial_display)
|
|
119
|
+
else:
|
|
120
|
+
initial_value = initial_display or ''
|
|
121
|
+
|
|
122
|
+
self._value = initial_value
|
|
123
|
+
self._prev_changed_value = initial_value
|
|
124
|
+
|
|
125
|
+
# Normalize initial display if we already have a parsed value
|
|
126
|
+
if self._value is not None:
|
|
127
|
+
formatted_text = self._format_value(self._value)
|
|
128
|
+
self.textsignal.set(formatted_text)
|
|
129
|
+
else:
|
|
130
|
+
self.textsignal.set('')
|
|
131
|
+
|
|
132
|
+
# Track last text emitted for input events
|
|
133
|
+
self._prev_change_text = self.textsignal.get()
|
|
134
|
+
|
|
135
|
+
# Subscribe to text changes
|
|
136
|
+
self._on_input_fid = self.textsignal.subscribe(self._handle_change)
|
|
137
|
+
|
|
138
|
+
# Commit on focus out / enter
|
|
139
|
+
self.bind('<FocusIn>', self._store_prev_value, add=True)
|
|
140
|
+
self.bind('<FocusOut>', self._handle_focus_out, add=True)
|
|
141
|
+
self.bind('<Return>', self._handle_return, add=True)
|
|
142
|
+
self.winfo_toplevel().bind('<<LocaleChanged>>', self._on_locale_changed, add='+')
|
|
143
|
+
|
|
144
|
+
# Set initial focus
|
|
145
|
+
if initial_focus:
|
|
146
|
+
self.focus()
|
|
147
|
+
|
|
148
|
+
def _store_prev_value(self, _: Any):
|
|
149
|
+
"""Store current value on focus-in to detect changes later."""
|
|
150
|
+
self._prev_changed_value = self._value
|
|
151
|
+
|
|
152
|
+
def _handle_focus_out(self, _):
|
|
153
|
+
"""Commit value and check for changes when focus leaves the widget."""
|
|
154
|
+
self.commit()
|
|
155
|
+
self._check_if_changed()
|
|
156
|
+
|
|
157
|
+
def _handle_return(self, _):
|
|
158
|
+
"""Commit value and check for changes when Return key is pressed."""
|
|
159
|
+
self.commit()
|
|
160
|
+
self._check_if_changed()
|
|
161
|
+
|
|
162
|
+
def _handle_change(self, event):
|
|
163
|
+
"""Emit <<Input>> event on every text change without parsing."""
|
|
164
|
+
text = self.textsignal.get()
|
|
165
|
+
if text == self._prev_change_text:
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
self._prev_change_text = text
|
|
169
|
+
self.event_generate('<<Input>>', data={"text": text})
|
|
170
|
+
|
|
171
|
+
def _check_if_changed(self):
|
|
172
|
+
"""Emit <<Change>> event if parsed value changed since focus-in."""
|
|
173
|
+
if self._value != self._prev_changed_value:
|
|
174
|
+
data = {
|
|
175
|
+
"value": self._value,
|
|
176
|
+
"prev_value": self._prev_changed_value,
|
|
177
|
+
"text": self.textsignal.get()
|
|
178
|
+
}
|
|
179
|
+
self.event_generate('<<Change>>', data={"value": self._value})
|
|
180
|
+
self._prev_changed_value = self._value
|
|
181
|
+
|
|
182
|
+
def _parse_or_none(self, s: str):
|
|
183
|
+
"""Parse string using value_format, returning None on empty/invalid input."""
|
|
184
|
+
# If a non-string is passed (e.g., datetime/date), assume it's already parsed
|
|
185
|
+
if not isinstance(s, str):
|
|
186
|
+
return s
|
|
187
|
+
s2 = (s or '').strip()
|
|
188
|
+
if not s2:
|
|
189
|
+
return None
|
|
190
|
+
try:
|
|
191
|
+
if self._value_format is None:
|
|
192
|
+
return s2
|
|
193
|
+
return self._fmt.parse(s2, self._value_format)
|
|
194
|
+
except ValueError:
|
|
195
|
+
return None
|
|
196
|
+
|
|
197
|
+
def _format_value(self, value: Any) -> str:
|
|
198
|
+
"""Format a value for display using value_format.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
value: The value to format (can be string, int, float, etc.)
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Formatted string representation of the value
|
|
205
|
+
"""
|
|
206
|
+
if value is None:
|
|
207
|
+
return ''
|
|
208
|
+
|
|
209
|
+
# If no format specified, just convert to string
|
|
210
|
+
if self._value_format is None:
|
|
211
|
+
return str(value)
|
|
212
|
+
|
|
213
|
+
# IntlFormatter.format() expects numeric values for numeric formats
|
|
214
|
+
try:
|
|
215
|
+
return self._fmt.format(value, self._value_format)
|
|
216
|
+
except (ValueError, TypeError):
|
|
217
|
+
# If formatting fails, return string representation
|
|
218
|
+
return str(value)
|
|
219
|
+
|
|
220
|
+
def on_input(self, callback: Callable[[Any], Any]) -> str:
|
|
221
|
+
"""Bind callback to <<Input>> event (fires on every keystroke).
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
callback: Function receiving event.data = {"text": str}
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Binding identifier for use with off_input()
|
|
228
|
+
"""
|
|
229
|
+
return self.bind('<<Input>>', callback, add=True)
|
|
230
|
+
|
|
231
|
+
def off_input(self, bind_id: str | None = None):
|
|
232
|
+
"""Remove callback from <<Input>> event."""
|
|
233
|
+
self.unbind('<<Input>>', bind_id)
|
|
234
|
+
|
|
235
|
+
def on_enter(self, callback: Callable[[Any], Any]) -> str:
|
|
236
|
+
"""Bind callback to <Return> event.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
callback: Function receiving event.data = {"value": Any, "text": str}
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
Binding identifier for use with off_enter()
|
|
243
|
+
"""
|
|
244
|
+
def enrich_callback(event: Event) -> None:
|
|
245
|
+
data = {"value": self._value, "text": self.textsignal.get()}
|
|
246
|
+
event.data = data
|
|
247
|
+
return callback(event)
|
|
248
|
+
|
|
249
|
+
return self.bind('<Return>', enrich_callback, add=True)
|
|
250
|
+
|
|
251
|
+
def off_enter(self, bind_id: str | None = None):
|
|
252
|
+
"""Remove callback from <Return> event."""
|
|
253
|
+
self.unbind('<Return>', bind_id)
|
|
254
|
+
|
|
255
|
+
def on_changed(self, callback: Callable[[Any], Any]) -> str:
|
|
256
|
+
"""Bind callback to <<Change>> event (fires when value changes on commit).
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
callback: Function receiving event.data = {"value": Any, "prev_value": Any, "text": str}
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Binding identifier for use with off_changed()
|
|
263
|
+
"""
|
|
264
|
+
return self.bind("<<Change>>", callback)
|
|
265
|
+
|
|
266
|
+
def off_changed(self, bind_id: str | None = None):
|
|
267
|
+
"""Remove callback from <<Change>> event."""
|
|
268
|
+
self.unbind('<<Change>>', bind_id)
|
|
269
|
+
|
|
270
|
+
def value(self, value=None):
|
|
271
|
+
"""Get or set the parsed/committed value.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
value: If provided, sets the display text and internal value with formatting
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
Current parsed value if no argument provided, None otherwise
|
|
278
|
+
"""
|
|
279
|
+
if value is None:
|
|
280
|
+
return self._value
|
|
281
|
+
else:
|
|
282
|
+
# Store the value and format it for display
|
|
283
|
+
if isinstance(value, (int, float)):
|
|
284
|
+
value_str = str(value)
|
|
285
|
+
else:
|
|
286
|
+
value_str = str(value) if value is not None else ''
|
|
287
|
+
|
|
288
|
+
# Parse the value if format is specified
|
|
289
|
+
if self._value_format is not None and value_str:
|
|
290
|
+
self._value = self._parse_or_none(value_str)
|
|
291
|
+
else:
|
|
292
|
+
self._value = value_str if value_str else None
|
|
293
|
+
|
|
294
|
+
# Format and display
|
|
295
|
+
formatted_text = self._format_value(self._value)
|
|
296
|
+
self.textsignal.set(formatted_text)
|
|
297
|
+
return None
|
|
298
|
+
|
|
299
|
+
def text(self, value=None):
|
|
300
|
+
"""Get or set the raw display text without committing.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
value: If provided, sets the display text without parsing
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
Current display text if no argument provided, None otherwise
|
|
307
|
+
"""
|
|
308
|
+
if value is None:
|
|
309
|
+
return self.textsignal.get()
|
|
310
|
+
else:
|
|
311
|
+
self.textsignal.set(value)
|
|
312
|
+
return None
|
|
313
|
+
|
|
314
|
+
def commit(self):
|
|
315
|
+
"""Parse display text, update value, and normalize display (called on FocusOut/Return)."""
|
|
316
|
+
s = self.get().strip()
|
|
317
|
+
|
|
318
|
+
# Parse once
|
|
319
|
+
if s == '':
|
|
320
|
+
self._value = None if self._allow_blank else self._value
|
|
321
|
+
else:
|
|
322
|
+
try:
|
|
323
|
+
self._value = s if self._value_format is None else self._fmt.parse(s, self._value_format)
|
|
324
|
+
except ValueError:
|
|
325
|
+
# Keep prior value on parse failure
|
|
326
|
+
return
|
|
327
|
+
|
|
328
|
+
# Format the value for display
|
|
329
|
+
new_text = self._format_value(self._value)
|
|
330
|
+
|
|
331
|
+
if new_text != self.textsignal.get():
|
|
332
|
+
# Temporarily silence input events while normalizing text
|
|
333
|
+
fid = getattr(self, '_on_input_fid', None)
|
|
334
|
+
if fid:
|
|
335
|
+
try:
|
|
336
|
+
self.textsignal.unsubscribe(fid)
|
|
337
|
+
except TclError:
|
|
338
|
+
pass
|
|
339
|
+
self.textsignal.set(new_text)
|
|
340
|
+
if fid:
|
|
341
|
+
self._on_input_fid = self.textsignal.subscribe(self._handle_change)
|
|
342
|
+
|
|
343
|
+
def _on_locale_changed(self, event=None):
|
|
344
|
+
"""Respond to global changes in locale by updating formatter and text."""
|
|
345
|
+
self._fmt = IntlFormatter(locale=MessageCatalog.locale())
|
|
346
|
+
|
|
347
|
+
# Reformat current value with new locale + same value_format
|
|
348
|
+
if self._value is not None:
|
|
349
|
+
formatted_text = self._format_value(self._value)
|
|
350
|
+
self.textsignal.set(formatted_text)
|
|
351
|
+
|
|
352
|
+
@configure_delegate('text')
|
|
353
|
+
def _delegate_text(self, value=None):
|
|
354
|
+
if value is None:
|
|
355
|
+
return self.text()
|
|
356
|
+
else:
|
|
357
|
+
return self.text(value)
|
|
358
|
+
|
|
359
|
+
@configure_delegate('value')
|
|
360
|
+
def _delegate_value(self, value=None):
|
|
361
|
+
if value is None:
|
|
362
|
+
return self.value()
|
|
363
|
+
else:
|
|
364
|
+
return self.value(value)
|
|
365
|
+
|
|
366
|
+
@configure_delegate('value_format')
|
|
367
|
+
def _delegate_value_format(self, value: str):
|
|
368
|
+
"""Get or set the value format pattern and reformat display."""
|
|
369
|
+
if value is None:
|
|
370
|
+
return self._value_format
|
|
371
|
+
|
|
372
|
+
self._value_format = value
|
|
373
|
+
# Reformat current value with new format
|
|
374
|
+
if self._value is not None:
|
|
375
|
+
formatted_text = self._format_value(self._value)
|
|
376
|
+
self.textsignal.set(formatted_text)
|
|
377
|
+
|
|
378
|
+
@configure_delegate('allow_blank')
|
|
379
|
+
def _delegate_allow_blank(self, value: bool):
|
|
380
|
+
"""Get or set whether blank input is allowed."""
|
|
381
|
+
if value is None:
|
|
382
|
+
return self._allow_blank
|
|
383
|
+
|
|
384
|
+
self._allow_blank = bool(value)
|
|
385
|
+
|
|
386
|
+
def destroy(self):
|
|
387
|
+
"""Clean up signal subscriptions and destroy the widget."""
|
|
388
|
+
if self._on_input_fid:
|
|
389
|
+
try:
|
|
390
|
+
self.textsignal.unsubscribe(self._on_input_fid)
|
|
391
|
+
except Exception:
|
|
392
|
+
pass # Ignore all errors during cleanup
|
|
393
|
+
self._on_input_fid = None
|
|
394
|
+
super().destroy()
|