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,474 @@
|
|
|
1
|
+
"""Abstract base class for datasource implementations.
|
|
2
|
+
|
|
3
|
+
Provides a common foundation for all datasource implementations with:
|
|
4
|
+
- Abstract method definitions enforcing the DataSourceProtocol interface
|
|
5
|
+
- Shared utility methods for type inference, literal parsing, and validation
|
|
6
|
+
- Template methods for common operations
|
|
7
|
+
- Hook methods for extensibility
|
|
8
|
+
|
|
9
|
+
Subclasses must implement storage-specific operations (set_data, get_page, CRUD, etc.)
|
|
10
|
+
while inheriting common utilities and patterns.
|
|
11
|
+
|
|
12
|
+
This base class makes it easier to:
|
|
13
|
+
- Create custom datasource implementations
|
|
14
|
+
- Reduce code duplication across implementations
|
|
15
|
+
- Ensure consistent behavior across all datasources
|
|
16
|
+
- Extend datasource functionality through hooks
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from abc import ABC, abstractmethod
|
|
22
|
+
from typing import Any, Dict, List, Optional, Sequence, Mapping
|
|
23
|
+
|
|
24
|
+
from bootstack.datasource.types import Primitive, Record
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class BaseDataSource(ABC):
|
|
28
|
+
"""Abstract base class for datasource implementations.
|
|
29
|
+
|
|
30
|
+
Provides shared utilities and enforces the DataSourceProtocol interface through
|
|
31
|
+
abstract methods. Subclasses implement storage-specific logic while inheriting
|
|
32
|
+
common functionality.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
page_size(int): Number of records per page (default: 10)
|
|
36
|
+
|
|
37
|
+
Attributes:
|
|
38
|
+
page_size: Current page size setting
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
```python
|
|
42
|
+
class RedisDataSource(BaseDataSource):
|
|
43
|
+
def __init__(self, redis_client, page_size=10):
|
|
44
|
+
super().__init__(page_size)
|
|
45
|
+
self.redis = redis_client
|
|
46
|
+
|
|
47
|
+
def set_data(self, records):
|
|
48
|
+
pass # Redis-specific implementation
|
|
49
|
+
|
|
50
|
+
def get_page(self, page=None):
|
|
51
|
+
pass # Redis-specific implementation
|
|
52
|
+
```
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, page_size: int = 10):
|
|
56
|
+
"""Initialize base datasource with pagination settings.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
page_size: Number of records returned per page when paginating.
|
|
60
|
+
"""
|
|
61
|
+
self.page_size = page_size
|
|
62
|
+
self._page = 0
|
|
63
|
+
|
|
64
|
+
# ========== ABSTRACT METHODS - MUST BE IMPLEMENTED ==========
|
|
65
|
+
|
|
66
|
+
# Data & View Configuration
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def set_data(self, records: Sequence[Primitive] | Sequence[Mapping[str, Any]]) -> 'BaseDataSource':
|
|
69
|
+
"""Load data records into the datasource.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
records: Sequence of records (dicts) or primitives (auto-wrapped)
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Self for method chaining
|
|
76
|
+
"""
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def set_filter(self, where_sql: str = "") -> None:
|
|
81
|
+
"""Apply SQL-like WHERE clause filter to data.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
where_sql: SQL WHERE condition (e.g., "age > 25 AND status = 'active'")
|
|
85
|
+
"""
|
|
86
|
+
...
|
|
87
|
+
|
|
88
|
+
@abstractmethod
|
|
89
|
+
def set_sort(self, order_by_sql: str = "") -> None:
|
|
90
|
+
"""Apply SQL-like ORDER BY clause to data.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
order_by_sql: SQL ORDER BY clause (e.g., "name ASC, age DESC")
|
|
94
|
+
"""
|
|
95
|
+
...
|
|
96
|
+
|
|
97
|
+
# Pagination
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def get_page(self, page: Optional[int] = None) -> List[Record]:
|
|
100
|
+
"""Get records for specified page (or current page if None).
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
page: Page number (0-indexed); updates current page if provided
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
List of record dictionaries for the page
|
|
107
|
+
"""
|
|
108
|
+
...
|
|
109
|
+
|
|
110
|
+
@abstractmethod
|
|
111
|
+
def next_page(self) -> List[Record]:
|
|
112
|
+
"""Advance to next page and return its records.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
List of record dictionaries for the new page
|
|
116
|
+
"""
|
|
117
|
+
...
|
|
118
|
+
|
|
119
|
+
@abstractmethod
|
|
120
|
+
def prev_page(self) -> List[Record]:
|
|
121
|
+
"""Move to previous page and return its records.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
List of record dictionaries for the new page
|
|
125
|
+
"""
|
|
126
|
+
...
|
|
127
|
+
|
|
128
|
+
@abstractmethod
|
|
129
|
+
def has_next_page(self) -> bool:
|
|
130
|
+
"""Check if more pages exist after current page.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
True if next page exists, False otherwise
|
|
134
|
+
"""
|
|
135
|
+
...
|
|
136
|
+
|
|
137
|
+
@abstractmethod
|
|
138
|
+
def total_count(self) -> int:
|
|
139
|
+
"""Get total number of records matching current filter.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Total record count (respects active filter)
|
|
143
|
+
"""
|
|
144
|
+
...
|
|
145
|
+
|
|
146
|
+
# CRUD Operations
|
|
147
|
+
@abstractmethod
|
|
148
|
+
def create_record(self, record: Dict[str, Any]) -> int:
|
|
149
|
+
"""Create new record and return its ID.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
record: Dictionary with record data
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
The ID assigned to the new record
|
|
156
|
+
"""
|
|
157
|
+
...
|
|
158
|
+
|
|
159
|
+
@abstractmethod
|
|
160
|
+
def read_record(self, record_id: Any) -> Optional[Record]:
|
|
161
|
+
"""Retrieve single record by ID.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
record_id: Unique identifier of the record
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Record dictionary or None if not found
|
|
168
|
+
"""
|
|
169
|
+
...
|
|
170
|
+
|
|
171
|
+
@abstractmethod
|
|
172
|
+
def update_record(self, record_id: Any, updates: Dict[str, Any]) -> bool:
|
|
173
|
+
"""Update record fields by ID.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
record_id: Unique identifier of the record
|
|
177
|
+
updates: Dictionary with fields to update
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
True if record was updated, False if not found
|
|
181
|
+
"""
|
|
182
|
+
...
|
|
183
|
+
|
|
184
|
+
@abstractmethod
|
|
185
|
+
def delete_record(self, record_id: Any) -> bool:
|
|
186
|
+
"""Delete record by ID.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
record_id: Unique identifier of the record
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
True if record was deleted, False if not found
|
|
193
|
+
"""
|
|
194
|
+
...
|
|
195
|
+
|
|
196
|
+
# Selection Management
|
|
197
|
+
@abstractmethod
|
|
198
|
+
def select_record(self, record_id: Any) -> bool:
|
|
199
|
+
"""Mark record as selected.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
record_id: Unique identifier of the record
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
True if record was selected, False if not found
|
|
206
|
+
"""
|
|
207
|
+
...
|
|
208
|
+
|
|
209
|
+
@abstractmethod
|
|
210
|
+
def unselect_record(self, record_id: Any) -> bool:
|
|
211
|
+
"""Mark record as unselected.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
record_id: Unique identifier of the record
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
True if record was unselected, False if not found
|
|
218
|
+
"""
|
|
219
|
+
...
|
|
220
|
+
|
|
221
|
+
@abstractmethod
|
|
222
|
+
def select_all(self, current_page_only: bool = False) -> int:
|
|
223
|
+
"""Select all records (optionally only current page).
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
current_page_only: If True, select only records on current page
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Number of records selected
|
|
230
|
+
"""
|
|
231
|
+
...
|
|
232
|
+
|
|
233
|
+
@abstractmethod
|
|
234
|
+
def unselect_all(self, current_page_only: bool = False) -> int:
|
|
235
|
+
"""Unselect all records (optionally only current page).
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
current_page_only: If True, unselect only records on current page
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
Number of records unselected
|
|
242
|
+
"""
|
|
243
|
+
...
|
|
244
|
+
|
|
245
|
+
@abstractmethod
|
|
246
|
+
def get_selected(self, page: Optional[int] = None) -> List[Record]:
|
|
247
|
+
"""Get selected records, optionally paginated.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
page: Optional page number for paginating selected records
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
List of selected record dictionaries
|
|
254
|
+
"""
|
|
255
|
+
...
|
|
256
|
+
|
|
257
|
+
@abstractmethod
|
|
258
|
+
def selected_count(self) -> int:
|
|
259
|
+
"""Get total number of selected records.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Count of selected records
|
|
263
|
+
"""
|
|
264
|
+
...
|
|
265
|
+
|
|
266
|
+
# Export
|
|
267
|
+
@abstractmethod
|
|
268
|
+
def export_to_csv(self, filepath: str, include_all: bool = True) -> None:
|
|
269
|
+
"""Export records to CSV file.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
filepath: Path to output CSV file
|
|
273
|
+
include_all: If True, export all records; if False, export only selected
|
|
274
|
+
"""
|
|
275
|
+
...
|
|
276
|
+
|
|
277
|
+
# Index-based Access
|
|
278
|
+
@abstractmethod
|
|
279
|
+
def get_page_from_index(self, start_index: int, count: int) -> List[Record]:
|
|
280
|
+
"""Get records by start index and count (respects filter/sort).
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
start_index: Starting record index
|
|
284
|
+
count: Number of records to retrieve
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
List of record dictionaries
|
|
288
|
+
"""
|
|
289
|
+
...
|
|
290
|
+
|
|
291
|
+
# ========== SHARED UTILITY METHODS ==========
|
|
292
|
+
|
|
293
|
+
@staticmethod
|
|
294
|
+
def _infer_type(value: Any) -> str:
|
|
295
|
+
"""Infer SQL-compatible type from Python value.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
value: Python value to infer type from
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
SQL type string ('INTEGER', 'REAL', 'BLOB', or 'TEXT')
|
|
302
|
+
|
|
303
|
+
Examples:
|
|
304
|
+
>>> BaseDataSource._infer_type(42)
|
|
305
|
+
'INTEGER'
|
|
306
|
+
>>> BaseDataSource._infer_type(3.14)
|
|
307
|
+
'REAL'
|
|
308
|
+
>>> BaseDataSource._infer_type("hello")
|
|
309
|
+
'TEXT'
|
|
310
|
+
"""
|
|
311
|
+
if isinstance(value, int):
|
|
312
|
+
return "INTEGER"
|
|
313
|
+
elif isinstance(value, float):
|
|
314
|
+
return "REAL"
|
|
315
|
+
elif isinstance(value, (bytes, bytearray)):
|
|
316
|
+
return "BLOB"
|
|
317
|
+
return "TEXT"
|
|
318
|
+
|
|
319
|
+
@staticmethod
|
|
320
|
+
def _is_mapping(x: Any) -> bool:
|
|
321
|
+
"""Check if value is a mapping (dict-like).
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
x: Value to check
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
True if value is a mapping, False otherwise
|
|
328
|
+
|
|
329
|
+
Examples:
|
|
330
|
+
>>> BaseDataSource._is_mapping({'a': 1})
|
|
331
|
+
True
|
|
332
|
+
>>> BaseDataSource._is_mapping([1, 2, 3])
|
|
333
|
+
False
|
|
334
|
+
"""
|
|
335
|
+
return isinstance(x, Mapping)
|
|
336
|
+
|
|
337
|
+
@staticmethod
|
|
338
|
+
def _coerce_literal(s: str) -> Any:
|
|
339
|
+
"""Parse string literal into Python value (int, float, bool, None, or str).
|
|
340
|
+
|
|
341
|
+
Converts SQL/JSON-like literal strings into appropriate Python types:
|
|
342
|
+
- Quoted strings ('foo', "bar") -> str
|
|
343
|
+
- true/false -> bool
|
|
344
|
+
- null/none -> None
|
|
345
|
+
- Numeric strings -> int or float
|
|
346
|
+
- Everything else -> str
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
s: String literal to parse
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Parsed Python value
|
|
353
|
+
|
|
354
|
+
Examples:
|
|
355
|
+
>>> BaseDataSource._coerce_literal("'hello'")
|
|
356
|
+
'hello'
|
|
357
|
+
>>> BaseDataSource._coerce_literal("42")
|
|
358
|
+
42
|
|
359
|
+
>>> BaseDataSource._coerce_literal("3.14")
|
|
360
|
+
3.14
|
|
361
|
+
>>> BaseDataSource._coerce_literal("true")
|
|
362
|
+
True
|
|
363
|
+
>>> BaseDataSource._coerce_literal("null")
|
|
364
|
+
None
|
|
365
|
+
"""
|
|
366
|
+
t = s.strip()
|
|
367
|
+
|
|
368
|
+
# Handle quoted strings
|
|
369
|
+
if len(t) >= 2 and ((t[0] == t[-1] == "'") or (t[0] == t[-1] == '"')):
|
|
370
|
+
return t[1:-1]
|
|
371
|
+
|
|
372
|
+
# Handle booleans
|
|
373
|
+
if t.lower() == "true":
|
|
374
|
+
return True
|
|
375
|
+
if t.lower() == "false":
|
|
376
|
+
return False
|
|
377
|
+
|
|
378
|
+
# Handle null/none
|
|
379
|
+
if t.lower() in ("null", "none"):
|
|
380
|
+
return None
|
|
381
|
+
|
|
382
|
+
# Try numeric conversion
|
|
383
|
+
try:
|
|
384
|
+
return int(t)
|
|
385
|
+
except Exception:
|
|
386
|
+
pass
|
|
387
|
+
|
|
388
|
+
try:
|
|
389
|
+
return float(t)
|
|
390
|
+
except Exception:
|
|
391
|
+
pass
|
|
392
|
+
|
|
393
|
+
# Default to string
|
|
394
|
+
return t
|
|
395
|
+
|
|
396
|
+
@staticmethod
|
|
397
|
+
def _validate_record(record: Any) -> None:
|
|
398
|
+
"""Validate that a record is a dictionary.
|
|
399
|
+
|
|
400
|
+
Args:
|
|
401
|
+
record: Value to validate
|
|
402
|
+
|
|
403
|
+
Raises:
|
|
404
|
+
ValueError: If record is not a dictionary
|
|
405
|
+
"""
|
|
406
|
+
if not isinstance(record, dict):
|
|
407
|
+
raise ValueError(f"Record must be a dictionary, got {type(record).__name__}")
|
|
408
|
+
|
|
409
|
+
# ========== HOOK METHODS (Can be overridden by subclasses) ==========
|
|
410
|
+
|
|
411
|
+
def _before_create(self, record: Dict[str, Any]) -> Dict[str, Any]:
|
|
412
|
+
"""Hook called before creating a record.
|
|
413
|
+
|
|
414
|
+
Subclasses can override this to perform validation, transformation,
|
|
415
|
+
or side effects before record creation.
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
record: Record about to be created
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
Modified record (or original if no changes)
|
|
422
|
+
"""
|
|
423
|
+
return record
|
|
424
|
+
|
|
425
|
+
def _after_create(self, record_id: int, record: Dict[str, Any]) -> None:
|
|
426
|
+
"""Hook called after creating a record.
|
|
427
|
+
|
|
428
|
+
Subclasses can override this to perform logging, caching updates,
|
|
429
|
+
or other side effects after record creation.
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
record_id: ID of the created record
|
|
433
|
+
record: The created record
|
|
434
|
+
"""
|
|
435
|
+
pass
|
|
436
|
+
|
|
437
|
+
def _before_update(self, record_id: Any, updates: Dict[str, Any]) -> Dict[str, Any]:
|
|
438
|
+
"""Hook called before updating a record.
|
|
439
|
+
|
|
440
|
+
Args:
|
|
441
|
+
record_id: ID of record being updated
|
|
442
|
+
updates: Updates to apply
|
|
443
|
+
|
|
444
|
+
Returns:
|
|
445
|
+
Modified updates (or original if no changes)
|
|
446
|
+
"""
|
|
447
|
+
return updates
|
|
448
|
+
|
|
449
|
+
def _after_update(self, record_id: Any, updates: Dict[str, Any], success: bool) -> None:
|
|
450
|
+
"""Hook called after updating a record.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
record_id: ID of updated record
|
|
454
|
+
updates: Updates that were applied
|
|
455
|
+
success: Whether the update succeeded
|
|
456
|
+
"""
|
|
457
|
+
pass
|
|
458
|
+
|
|
459
|
+
def _before_delete(self, record_id: Any) -> None:
|
|
460
|
+
"""Hook called before deleting a record.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
record_id: ID of record about to be deleted
|
|
464
|
+
"""
|
|
465
|
+
pass
|
|
466
|
+
|
|
467
|
+
def _after_delete(self, record_id: Any, success: bool) -> None:
|
|
468
|
+
"""Hook called after deleting a record.
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
record_id: ID of deleted record
|
|
472
|
+
success: Whether the deletion succeeded
|
|
473
|
+
"""
|
|
474
|
+
pass
|