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,234 @@
|
|
|
1
|
+
"""Color utility functions for bootstack.
|
|
2
|
+
|
|
3
|
+
This module provides utilities for converting between different color models
|
|
4
|
+
and manipulating colors (adjusting hue, saturation, luminance).
|
|
5
|
+
|
|
6
|
+
Supported color models:
|
|
7
|
+
- RGB: Red, Green, Blue tuple (0-255)
|
|
8
|
+
- HSL: Hue (0-360), Saturation (0-100), Luminance (0-100)
|
|
9
|
+
- HEX: Hexadecimal color string (#RRGGBB)
|
|
10
|
+
- NAME: Named colors (e.g., 'red', 'blue')
|
|
11
|
+
|
|
12
|
+
Functions:
|
|
13
|
+
color_to_rgb: Convert any color model to RGB
|
|
14
|
+
color_to_hex: Convert any color model to HEX
|
|
15
|
+
color_to_hsl: Convert any color model to HSL
|
|
16
|
+
update_color: Modify HSL values of a color
|
|
17
|
+
contrast_color: Get contrasting foreground color for readability
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
```python
|
|
21
|
+
from bootstack.colorutils import *
|
|
22
|
+
|
|
23
|
+
# Convert hex to RGB
|
|
24
|
+
rgb = color_to_rgb('#FF5733', model=HEX) # (255, 87, 51)
|
|
25
|
+
|
|
26
|
+
# Adjust color lightness
|
|
27
|
+
lighter = update_color('#FF5733', lum=80, inmodel=HEX, outmodel=HEX)
|
|
28
|
+
|
|
29
|
+
# Get contrasting text color
|
|
30
|
+
fg_color = contrast_color('#FF5733') # Returns 'white' or 'black'
|
|
31
|
+
```
|
|
32
|
+
"""
|
|
33
|
+
from PIL import ImageColor
|
|
34
|
+
from colorsys import rgb_to_hls
|
|
35
|
+
|
|
36
|
+
RGB = 'rgb'
|
|
37
|
+
HSL = 'hsl'
|
|
38
|
+
HEX = 'hex'
|
|
39
|
+
NAME = 'name'
|
|
40
|
+
|
|
41
|
+
HUE = 360
|
|
42
|
+
SAT = 100
|
|
43
|
+
LUM = 100
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def color_to_rgb(color, model=HEX):
|
|
47
|
+
"""Convert color value to rgb.
|
|
48
|
+
|
|
49
|
+
The color and model parameters represent the color to be converted.
|
|
50
|
+
The value is expected to be a string for "name" and "hex" models and
|
|
51
|
+
a Tuple or List for "rgb" and "hsl" models.
|
|
52
|
+
|
|
53
|
+
Parameters:
|
|
54
|
+
|
|
55
|
+
color (Any):
|
|
56
|
+
The color values for the model being converted.
|
|
57
|
+
|
|
58
|
+
model (str):
|
|
59
|
+
The color model being converted.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
|
|
63
|
+
tuple[int, int, int]:
|
|
64
|
+
The rgb color values.
|
|
65
|
+
"""
|
|
66
|
+
color_ = conform_color_model(color, model)
|
|
67
|
+
return ImageColor.getrgb(color_)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def color_to_hex(color, model=RGB):
|
|
71
|
+
"""Convert color value to hex.
|
|
72
|
+
|
|
73
|
+
The color and model parameters represent the color to be converted.
|
|
74
|
+
The value is expected to be a string for "name" and "hex" models and
|
|
75
|
+
a Tuple or List for "rgb" and "hsl" models.
|
|
76
|
+
|
|
77
|
+
Parameters:
|
|
78
|
+
|
|
79
|
+
color (Any):
|
|
80
|
+
The color values for the model being converted.
|
|
81
|
+
|
|
82
|
+
model (str):
|
|
83
|
+
The color model being converted.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
|
|
87
|
+
str:
|
|
88
|
+
The hexadecimal color value.
|
|
89
|
+
"""
|
|
90
|
+
r, g, b = color_to_rgb(color, model)
|
|
91
|
+
return f'#{r:02x}{g:02x}{b:02x}'
|
|
92
|
+
|
|
93
|
+
def color_to_hsl(color, model=HEX):
|
|
94
|
+
"""Convert color value to hsl.
|
|
95
|
+
|
|
96
|
+
The color and model parameters represent the color to be converted.
|
|
97
|
+
The value is expected to be a string for "name" and "hex" models and
|
|
98
|
+
a Tuple or List for "rgb" and "hsl" models.
|
|
99
|
+
|
|
100
|
+
Parameters:
|
|
101
|
+
|
|
102
|
+
color (Any):
|
|
103
|
+
The color values for the model being converted.
|
|
104
|
+
|
|
105
|
+
model (str):
|
|
106
|
+
The color model being converted.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
|
|
110
|
+
tuple[int, int, int]:
|
|
111
|
+
The hsl color values.
|
|
112
|
+
"""
|
|
113
|
+
r, g, b = color_to_rgb(color, model)
|
|
114
|
+
hls = rgb_to_hls(r/255, g/255, b/255)
|
|
115
|
+
h = int(hls[0]*HUE)
|
|
116
|
+
l = int(hls[1]*LUM)
|
|
117
|
+
s = int(hls[2]*SAT)
|
|
118
|
+
return h, s, l
|
|
119
|
+
|
|
120
|
+
def update_hsl_value(color, hue=None, sat=None, lum=None, inmodel=HSL, outmodel=HSL):
|
|
121
|
+
"""Change hue, saturation, or lumenosity of the color based on the
|
|
122
|
+
hue, sat, lum parameters provided.
|
|
123
|
+
|
|
124
|
+
Parameters:
|
|
125
|
+
|
|
126
|
+
color (Any):
|
|
127
|
+
The color
|
|
128
|
+
|
|
129
|
+
hue (int):
|
|
130
|
+
A number between 0 and 360.
|
|
131
|
+
|
|
132
|
+
sat (int):
|
|
133
|
+
A number between 0 and 100.
|
|
134
|
+
|
|
135
|
+
lum (int):
|
|
136
|
+
A number between 0 and 100.
|
|
137
|
+
|
|
138
|
+
inmodel (str):
|
|
139
|
+
The color model used by the color to be changed. One of
|
|
140
|
+
hsl, rgb, hex, name.
|
|
141
|
+
|
|
142
|
+
outmodel (str):
|
|
143
|
+
The color value model to be returned when the color is
|
|
144
|
+
changed. One of hsl, rgb, hex.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
|
|
148
|
+
Union[tuple[int, int, int], str]:
|
|
149
|
+
The color value based on the selected color model.
|
|
150
|
+
"""
|
|
151
|
+
h, s, l = color_to_hsl(color, inmodel)
|
|
152
|
+
if hue is not None:
|
|
153
|
+
h = hue
|
|
154
|
+
if sat is not None:
|
|
155
|
+
s = sat
|
|
156
|
+
if lum is not None:
|
|
157
|
+
l = lum
|
|
158
|
+
if outmodel == RGB:
|
|
159
|
+
return color_to_rgb([h, s, l], HSL)
|
|
160
|
+
elif outmodel == HEX:
|
|
161
|
+
return color_to_hex([h, s, l], HSL)
|
|
162
|
+
else:
|
|
163
|
+
return h, s, l
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
"""
|
|
167
|
+
https://stackoverflow.com/questions/1855884/determine-font-color-based-on-background-color
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
def contrast_color(color, model=RGB, darkcolor='#000', lightcolor='#fff'):
|
|
172
|
+
"""Returns the best matching contrasting light or dark color for
|
|
173
|
+
the given color.
|
|
174
|
+
|
|
175
|
+
Parameters:
|
|
176
|
+
|
|
177
|
+
color (Any):
|
|
178
|
+
The color value to evaluate.
|
|
179
|
+
|
|
180
|
+
model (str):
|
|
181
|
+
The model of the color value to be evaluated. 'rgb' by
|
|
182
|
+
default.
|
|
183
|
+
|
|
184
|
+
darkcolor (Any):
|
|
185
|
+
The color value to be returned when the constrasting color
|
|
186
|
+
should be dark.
|
|
187
|
+
|
|
188
|
+
lightcolor (Any):
|
|
189
|
+
The color value to be returned when the constrasting color
|
|
190
|
+
should be light.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
|
|
194
|
+
str:
|
|
195
|
+
The matching color value.
|
|
196
|
+
"""
|
|
197
|
+
if model != RGB:
|
|
198
|
+
r, g, b = color_to_rgb(color, model)
|
|
199
|
+
else:
|
|
200
|
+
r, g, b = color
|
|
201
|
+
|
|
202
|
+
luminance = ((0.299 * r) + (0.587 * g) + (0.114 * b))/255
|
|
203
|
+
if luminance > 0.5:
|
|
204
|
+
return darkcolor
|
|
205
|
+
else:
|
|
206
|
+
return lightcolor
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def conform_color_model(color, model):
|
|
210
|
+
"""Conform the color values to a string that can be interpreted
|
|
211
|
+
by the `PIL.ImageColor.getrgb method`.
|
|
212
|
+
|
|
213
|
+
Parameters:
|
|
214
|
+
|
|
215
|
+
color (Union[tuple[int, int, int], str]):
|
|
216
|
+
The color value to conform.
|
|
217
|
+
|
|
218
|
+
model (str):
|
|
219
|
+
One of 'HSL', 'RGB', or 'HEX'
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
|
|
223
|
+
str:
|
|
224
|
+
A color value string that can be used as a parameter in the
|
|
225
|
+
PIL.ImageColor.getrgb method.
|
|
226
|
+
"""
|
|
227
|
+
if model == HSL:
|
|
228
|
+
h, s, l = color
|
|
229
|
+
return f'hsl({h},{s}%,{l}%)'
|
|
230
|
+
elif model == RGB:
|
|
231
|
+
r, g, b = color
|
|
232
|
+
return f'rgb({r},{g},{b})'
|
|
233
|
+
else:
|
|
234
|
+
return color
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TTKBootstrapError(Exception):
|
|
7
|
+
"""Base for all bootstack errors."""
|
|
8
|
+
__slots__ = ("hint", "code", "widget_id")
|
|
9
|
+
|
|
10
|
+
def __init__(
|
|
11
|
+
self, message: str, *, hint: Optional[str] = None,
|
|
12
|
+
code: Optional[str] = None, widget_id: Optional[str] = None):
|
|
13
|
+
"""Create a bootstack error.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
message: Human-readable description of what went wrong.
|
|
17
|
+
hint: Optional suggestion for how to resolve the error.
|
|
18
|
+
code: Optional machine-readable error code for programmatic handling.
|
|
19
|
+
widget_id: Optional Tk widget path string identifying the source widget.
|
|
20
|
+
"""
|
|
21
|
+
super().__init__(message)
|
|
22
|
+
self.hint = hint
|
|
23
|
+
self.code = code
|
|
24
|
+
self.widget_id = widget_id
|
|
25
|
+
|
|
26
|
+
def __str__(self) -> str:
|
|
27
|
+
base = super().__str__()
|
|
28
|
+
if self.hint:
|
|
29
|
+
return f"{base} — Hint: {self.hint}"
|
|
30
|
+
return base
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class LayoutError(TTKBootstrapError):
|
|
34
|
+
"""Raised when a widget layout operation fails.
|
|
35
|
+
|
|
36
|
+
Examples: invalid geometry manager usage, pane configuration errors,
|
|
37
|
+
or attempting to pack/grid/place a widget into an incompatible container.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ThemeError(TTKBootstrapError):
|
|
42
|
+
"""Raised when a theme-related operation fails.
|
|
43
|
+
|
|
44
|
+
Examples: requesting an unknown theme name, loading a malformed theme
|
|
45
|
+
definition, or applying a theme before the style system is initialised.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ConfigError(TTKBootstrapError):
|
|
50
|
+
"""Raised when a widget or application configuration is invalid.
|
|
51
|
+
|
|
52
|
+
Examples: passing mutually exclusive options, supplying an unsupported
|
|
53
|
+
value for a configuration key, or configuring a widget after it has
|
|
54
|
+
been destroyed.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class StateError(TTKBootstrapError):
|
|
59
|
+
"""Raised when an operation is attempted while the widget is in an invalid state.
|
|
60
|
+
|
|
61
|
+
Examples: calling `start()` on an already-running animation, or
|
|
62
|
+
modifying a widget that has been destroyed.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class NavigationError(TTKBootstrapError):
|
|
67
|
+
"""Raised when a navigation operation fails.
|
|
68
|
+
|
|
69
|
+
Examples: referencing a tab key that does not exist, supplying an index
|
|
70
|
+
that is out of range, or navigating in a container that has no items.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class BootstyleBuilderError(TTKBootstrapError):
|
|
75
|
+
"""Raised when a style builder encounters an error during style construction.
|
|
76
|
+
|
|
77
|
+
Examples: a builder receiving an unsupported option, a required theme
|
|
78
|
+
color token being missing, or an internal style-engine failure.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class BootstyleParsingError(TTKBootstrapError):
|
|
83
|
+
"""Raised when a bootstyle string cannot be parsed into valid tokens.
|
|
84
|
+
|
|
85
|
+
Examples: an unrecognised accent name, an invalid variant, or a
|
|
86
|
+
combination of tokens that the parser cannot resolve.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ConfigurationWarning(Warning):
|
|
91
|
+
"""Issued when a widget receives a deprecated or questionable configuration option.
|
|
92
|
+
|
|
93
|
+
Examples: passing the legacy `bootstyle` keyword instead of the
|
|
94
|
+
recommended `accent`/`variant` pair.
|
|
95
|
+
"""
|
bootstack/core/images.py
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""Image loading and caching utilities for bootstack.
|
|
2
|
+
|
|
3
|
+
This module provides a unified image service for loading, caching, and managing
|
|
4
|
+
Tk-compatible PhotoImage objects. It uses Pillow as the backend to support a wide
|
|
5
|
+
variety of image formats beyond what Tk natively supports.
|
|
6
|
+
|
|
7
|
+
The primary interface is the `Image` class, which provides class methods for
|
|
8
|
+
loading images from various sources while automatically caching them to avoid
|
|
9
|
+
repeated decoding and to prevent garbage collection issues with Tk images.
|
|
10
|
+
|
|
11
|
+
Examples:
|
|
12
|
+
Basic usage with file paths:
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from bootstack import Image
|
|
16
|
+
|
|
17
|
+
# Load an image from disk (cached automatically)
|
|
18
|
+
photo = Image.open("icons/save.png")
|
|
19
|
+
button = Button(app, image=photo)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Loading from bytes (e.g., embedded resources):
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
photo = Image.from_bytes(icon_data)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Creating transparent spacer images:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
spacer = Image.transparent(16, 16)
|
|
32
|
+
```
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
import hashlib
|
|
38
|
+
import io
|
|
39
|
+
from dataclasses import dataclass
|
|
40
|
+
from pathlib import Path
|
|
41
|
+
from typing import Hashable
|
|
42
|
+
|
|
43
|
+
from PIL import Image as PILImage
|
|
44
|
+
from PIL.ImageTk import PhotoImage as PILPhotoImage
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class ImageCacheInfo:
|
|
49
|
+
"""Information about the current state of an image cache.
|
|
50
|
+
|
|
51
|
+
Attributes:
|
|
52
|
+
items: The number of images currently stored in the cache.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
items: int
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class Image:
|
|
59
|
+
"""Platform image service for loading and caching PhotoImage objects.
|
|
60
|
+
|
|
61
|
+
This class provides a centralized service for working with images in
|
|
62
|
+
bootstack applications. It handles:
|
|
63
|
+
|
|
64
|
+
- Loading images from files, bytes, or PIL Image objects
|
|
65
|
+
- Automatic caching to avoid repeated decoding
|
|
66
|
+
- Keeping strong references to prevent Tk garbage collection issues
|
|
67
|
+
- Support for many image formats via Pillow (PNG, JPEG, GIF, BMP, etc.)
|
|
68
|
+
|
|
69
|
+
All methods are class methods, so no instantiation is required.
|
|
70
|
+
|
|
71
|
+
Examples:
|
|
72
|
+
```python
|
|
73
|
+
# From a file path
|
|
74
|
+
icon = Image.open("path/to/icon.png")
|
|
75
|
+
|
|
76
|
+
# From raw bytes
|
|
77
|
+
icon = Image.from_bytes(embedded_data)
|
|
78
|
+
|
|
79
|
+
# From a PIL Image object
|
|
80
|
+
pil_img = PILImage.open("photo.jpg").resize((100, 100))
|
|
81
|
+
icon = Image.from_pil(pil_img)
|
|
82
|
+
|
|
83
|
+
# Create a transparent spacer
|
|
84
|
+
spacer = Image.transparent(32, 32)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Note:
|
|
88
|
+
Images are cached by default using automatically generated keys based
|
|
89
|
+
on the source (file path, content hash, or object id). You can provide
|
|
90
|
+
a custom `key` parameter to control caching behavior.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
_cache: dict[Hashable, PILPhotoImage] = {}
|
|
94
|
+
|
|
95
|
+
# =========================================================================
|
|
96
|
+
# Cache Management
|
|
97
|
+
# =========================================================================
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def get_cached(cls, key: Hashable) -> PILPhotoImage | None:
|
|
101
|
+
"""Retrieve a cached PhotoImage by its key.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
key: The cache key to look up.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
The cached PhotoImage if found, or None if not in cache.
|
|
108
|
+
"""
|
|
109
|
+
return cls._cache.get(key)
|
|
110
|
+
|
|
111
|
+
@classmethod
|
|
112
|
+
def set_cached(cls, key: Hashable, img: PILPhotoImage) -> PILPhotoImage:
|
|
113
|
+
"""Store a PhotoImage in the cache.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
key: The cache key to store the image under.
|
|
117
|
+
img: The PhotoImage to cache.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
The same PhotoImage that was passed in (for chaining).
|
|
121
|
+
"""
|
|
122
|
+
cls._cache[key] = img
|
|
123
|
+
return img
|
|
124
|
+
|
|
125
|
+
@classmethod
|
|
126
|
+
def clear_cache(cls) -> None:
|
|
127
|
+
"""Clear all cached images.
|
|
128
|
+
|
|
129
|
+
This removes all images from the cache. Use with caution, as any
|
|
130
|
+
widgets still referencing these images may display incorrectly.
|
|
131
|
+
"""
|
|
132
|
+
cls._cache.clear()
|
|
133
|
+
|
|
134
|
+
@classmethod
|
|
135
|
+
def cache_info(cls) -> ImageCacheInfo:
|
|
136
|
+
"""Get information about the current cache state.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
An ImageCacheInfo object containing cache statistics.
|
|
140
|
+
|
|
141
|
+
Examples:
|
|
142
|
+
>>> info = Image.cache_info()
|
|
143
|
+
>>> print(f"Cached images: {info.items}")
|
|
144
|
+
"""
|
|
145
|
+
return ImageCacheInfo(items=len(cls._cache))
|
|
146
|
+
|
|
147
|
+
# =========================================================================
|
|
148
|
+
# Image Constructors
|
|
149
|
+
# =========================================================================
|
|
150
|
+
|
|
151
|
+
@classmethod
|
|
152
|
+
def open(cls, path: str | Path, *, key: Hashable | None = None) -> PILPhotoImage:
|
|
153
|
+
"""Load an image from a file path.
|
|
154
|
+
|
|
155
|
+
Opens an image file using Pillow and converts it to a Tk-compatible
|
|
156
|
+
PhotoImage. The result is cached to avoid repeated disk reads and
|
|
157
|
+
decoding for the same file.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
path: Path to the image file. Supports ~ expansion and relative paths.
|
|
161
|
+
key: Optional custom cache key. If not provided, the absolute file
|
|
162
|
+
path is used as the cache key.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
A Tk-compatible PhotoImage that can be used with widgets.
|
|
166
|
+
|
|
167
|
+
Examples:
|
|
168
|
+
>>> photo = Image.open("assets/logo.png")
|
|
169
|
+
>>> label = Label(app, image=photo)
|
|
170
|
+
|
|
171
|
+
>>> # With custom cache key for versioning
|
|
172
|
+
>>> photo = Image.open("icon.png", key=("icon", "v2"))
|
|
173
|
+
"""
|
|
174
|
+
p = Path(path).expanduser().resolve()
|
|
175
|
+
cache_key = key if key is not None else ("file", str(p))
|
|
176
|
+
cached = cls.get_cached(cache_key)
|
|
177
|
+
if cached is not None:
|
|
178
|
+
return cached
|
|
179
|
+
|
|
180
|
+
pil = PILImage.open(p)
|
|
181
|
+
photo = PILPhotoImage(image=pil)
|
|
182
|
+
return cls.set_cached(cache_key, photo)
|
|
183
|
+
|
|
184
|
+
@classmethod
|
|
185
|
+
def from_pil(cls, image: PILImage.Image, *, key: Hashable | None = None) -> PILPhotoImage:
|
|
186
|
+
"""Convert a PIL Image to a Tk PhotoImage.
|
|
187
|
+
|
|
188
|
+
Wraps an existing PIL Image object in a Tk-compatible PhotoImage.
|
|
189
|
+
This is useful when you need to perform image manipulation (resizing,
|
|
190
|
+
filtering, etc.) before displaying the image.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
image: A PIL Image object to convert.
|
|
194
|
+
key: Optional custom cache key. If not provided, the object id
|
|
195
|
+
of the PIL Image is used (note: this means the same PIL Image
|
|
196
|
+
object will be cached, but copies won't hit the cache).
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
A Tk-compatible PhotoImage that can be used with widgets.
|
|
200
|
+
|
|
201
|
+
Examples:
|
|
202
|
+
>>> from PIL import Image as PILImage
|
|
203
|
+
>>> pil_img = PILImage.open("photo.jpg")
|
|
204
|
+
>>> pil_img = pil_img.resize((100, 100))
|
|
205
|
+
>>> photo = Image.from_pil(pil_img)
|
|
206
|
+
"""
|
|
207
|
+
cache_key = key if key is not None else ("pil", id(image))
|
|
208
|
+
cached = cls.get_cached(cache_key)
|
|
209
|
+
if cached is not None:
|
|
210
|
+
return cached
|
|
211
|
+
|
|
212
|
+
photo = PILPhotoImage(image=image)
|
|
213
|
+
return cls.set_cached(cache_key, photo)
|
|
214
|
+
|
|
215
|
+
@classmethod
|
|
216
|
+
def from_bytes(cls, data: bytes, *, key: Hashable | None = None) -> PILPhotoImage:
|
|
217
|
+
"""Create a PhotoImage from raw image bytes.
|
|
218
|
+
|
|
219
|
+
Decodes image data from bytes using Pillow and converts it to a
|
|
220
|
+
Tk-compatible PhotoImage. This is useful for embedded resources,
|
|
221
|
+
downloaded images, or any other source of raw image data.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
data: Raw image bytes in any format supported by Pillow
|
|
225
|
+
(PNG, JPEG, GIF, BMP, etc.).
|
|
226
|
+
key: Optional custom cache key. If not provided, an MD5 hash
|
|
227
|
+
of the bytes is used as the cache key.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
A Tk-compatible PhotoImage that can be used with widgets.
|
|
231
|
+
|
|
232
|
+
Examples:
|
|
233
|
+
>>> # Load from embedded resource
|
|
234
|
+
>>> with open("icon.png", "rb") as f:
|
|
235
|
+
... icon_data = f.read()
|
|
236
|
+
>>> photo = Image.from_bytes(icon_data)
|
|
237
|
+
|
|
238
|
+
>>> # With custom cache key
|
|
239
|
+
>>> photo = Image.from_bytes(data, key="my-icon")
|
|
240
|
+
"""
|
|
241
|
+
digest = hashlib.md5(data).hexdigest()
|
|
242
|
+
cache_key = key if key is not None else ("bytes", digest)
|
|
243
|
+
cached = cls.get_cached(cache_key)
|
|
244
|
+
if cached is not None:
|
|
245
|
+
return cached
|
|
246
|
+
|
|
247
|
+
pil = PILImage.open(io.BytesIO(data))
|
|
248
|
+
photo = PILPhotoImage(image=pil)
|
|
249
|
+
return cls.set_cached(cache_key, photo)
|
|
250
|
+
|
|
251
|
+
@classmethod
|
|
252
|
+
def transparent(cls, width: int, height: int, *, key: Hashable | None = None) -> PILPhotoImage:
|
|
253
|
+
"""Create a transparent spacer image.
|
|
254
|
+
|
|
255
|
+
Creates a fully transparent RGBA image of the specified dimensions.
|
|
256
|
+
This is useful for creating spacing in layouts or as a placeholder
|
|
257
|
+
image.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
width: Width of the image in pixels.
|
|
261
|
+
height: Height of the image in pixels.
|
|
262
|
+
key: Optional custom cache key. If not provided, a tuple of
|
|
263
|
+
("transparent", width, height) is used.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
A transparent Tk-compatible PhotoImage.
|
|
267
|
+
|
|
268
|
+
Examples:
|
|
269
|
+
>>> # Create a 16x16 transparent spacer
|
|
270
|
+
>>> spacer = Image.transparent(16, 16)
|
|
271
|
+
>>> label = Label(app, image=spacer)
|
|
272
|
+
|
|
273
|
+
>>> # Use as compound image padding
|
|
274
|
+
>>> button = Button(app, image=spacer, compound="left")
|
|
275
|
+
"""
|
|
276
|
+
cache_key = key if key is not None else ("transparent", width, height)
|
|
277
|
+
cached = cls.get_cached(cache_key)
|
|
278
|
+
if cached is not None:
|
|
279
|
+
return cached
|
|
280
|
+
|
|
281
|
+
pil = PILImage.new("RGBA", (width, height), (255, 255, 255, 0))
|
|
282
|
+
photo = PILPhotoImage(image=pil)
|
|
283
|
+
return cls.set_cached(cache_key, photo)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Localization (Message Catalog + Babel/gettext)
|
|
2
|
+
|
|
3
|
+
ttkbootstrap uses a unified message catalog that bridges Python gettext (compiled with Babel) and Tcl/Tk msgcat. Catalogs are distributed with the package under `ttkbootstrap/assets/locales`.
|
|
4
|
+
|
|
5
|
+
Highlights
|
|
6
|
+
- Use `_ = MessageCatalog.translate` in code to mark strings.
|
|
7
|
+
- Style auto-initializes the catalog and auto-discovers shipped catalogs.
|
|
8
|
+
- Switch language at runtime with `MessageCatalog.locale('de')`.
|
|
9
|
+
- A virtual event `<<LocaleChanged>>` is emitted on language changes so UIs can refresh.
|
|
10
|
+
- Runtime overrides (`set`, `set_many`) let you add translations that aren't in the catalogs yet.
|
|
11
|
+
|
|
12
|
+
Directory structure
|
|
13
|
+
- `src/ttkbootstrap/assets/locales/<lang>/LC_MESSAGES/ttkbootstrap.po`
|
|
14
|
+
- `src/ttkbootstrap/assets/locales/<lang>/LC_MESSAGES/ttkbootstrap.mo`
|
|
15
|
+
|
|
16
|
+
Marking strings for translation
|
|
17
|
+
- In modules that render UI:
|
|
18
|
+
- `from ttkbootstrap.core.localization import MessageCatalog`
|
|
19
|
+
- `_ = MessageCatalog.translate`
|
|
20
|
+
- Example: `ttk.Label(root, text=_('Cancel'))`
|
|
21
|
+
- Formatting:
|
|
22
|
+
- `_("File: %s", name)` uses Python `%` formatting.
|
|
23
|
+
- Legacy Tcl printf like `%1$s` are supported via a formatting fallback.
|
|
24
|
+
|
|
25
|
+
Language switching
|
|
26
|
+
- Change language at runtime:
|
|
27
|
+
- `MessageCatalog.locale('fr')`
|
|
28
|
+
- Bind a refresh: `root.bind('<<LocaleChanged>>', lambda e: refresh())`
|
|
29
|
+
|
|
30
|
+
Runtime overrides (non-compiled messages)
|
|
31
|
+
- Add translations for a locale during runtime:
|
|
32
|
+
- Single: `MessageCatalog.set('fr', 'Hello', 'Bonjour')`
|
|
33
|
+
- Many: `MessageCatalog.set_many('de', 'Open','Oeffnen', 'Cancel','Abbrechen')`
|
|
34
|
+
- Emit refresh if you're already in that locale: `root.event_generate('<<LocaleChanged>>')`
|
|
35
|
+
|
|
36
|
+
Developer workflow (Babel)
|
|
37
|
+
- Config lives at project root: `babel.cfg` (extracts from `src/**/*.py` and `_()`/gettext keywords).
|
|
38
|
+
- Use the helper to manage catalogs (defaults to `src/ttkbootstrap/assets/locales` and domain `ttkbootstrap`):
|
|
39
|
+
- Extract template: `python tools/make_i18n.py extract`
|
|
40
|
+
- Init locales: `python tools/make_i18n.py init -l de fr`
|
|
41
|
+
- Update catalogs: `python tools/make_i18n.py update`
|
|
42
|
+
- Compile catalogs: `python tools/make_i18n.py compile`
|
|
43
|
+
- The compiled `.mo` files are shipped in the wheel from `assets/locales`.
|
|
44
|
+
|
|
45
|
+
Tools
|
|
46
|
+
- `tools/make_i18n.py` — primary helper for extract/init/update/compile (targets `assets/locales`).
|
|
47
|
+
- `tools/audit_messages.py` — optional QA to spot duplicates and accelerator issues.
|
|
48
|
+
- Scans `src/ttkbootstrap/assets/locales` by default.
|
|
49
|
+
|
|
50
|
+
Note: Legacy migration helpers (e.g., `convert_msgs_to_po.py`, `sync_locales_to_package.py`) have been removed. The workflow compiles directly to `assets/locales`.
|
|
51
|
+
|
|
52
|
+
Contribution notes
|
|
53
|
+
- Prefer base locales (`de`, `fr`, `nl`) unless region-specific differences are required (for example `pt_BR`).
|
|
54
|
+
- Avoid embedding mnemonics `&` in messages; MessageCatalog strips them when rendering.
|
|
55
|
+
- Keep message ids consistent (case and punctuation) to avoid duplicates.
|
|
56
|
+
- Optional: audit keys with `python tools/audit_messages.py`.
|
|
57
|
+
|
|
58
|
+
Demos
|
|
59
|
+
- `examples/localization_widgets_demo.py` - shows `_()` usage and `<<LocaleChanged>>` auto-refresh.
|
|
60
|
+
- `examples/runtime_overrides_demo.py` - shows `set`/`set_many` for messages not in catalogs yet.
|
|
61
|
+
|
|
62
|
+
Contributors
|
|
63
|
+
- Where to place translations:
|
|
64
|
+
- Add or edit `.po` files under `src/ttkbootstrap/assets/locales/<lang>/LC_MESSAGES/ttkbootstrap.po`.
|
|
65
|
+
- Compile to `.mo` with `python tools/make_i18n.py compile`.
|
|
66
|
+
- Workflow for a new language:
|
|
67
|
+
1) Extract: `python tools/make_i18n.py extract`
|
|
68
|
+
2) Init: `python tools/make_i18n.py init -l <lang>`
|
|
69
|
+
3) Translate the new `.po` file
|
|
70
|
+
4) Compile: `python tools/make_i18n.py compile`
|
|
71
|
+
- Minimum keys to translate for a new language (baseline UI):
|
|
72
|
+
- OK, Ok, Retry, Delete, Next, Prev, Previous
|
|
73
|
+
- Yes, No, Open, Close, Add, Remove, Submit, Cancel
|
|
74
|
+
- Family, Weight, Slant, Effects, Preview, Size
|
|
75
|
+
- Should be of data type, Invalid data type
|
|
76
|
+
- Number cannot be greater than, Number cannot be less than, Out of range
|
|
77
|
+
- The quick brown fox jumps over the lazy dog.
|
|
78
|
+
- Font Selector, Color Chooser, Advanced, Themed, Standard
|
|
79
|
+
- Current, New, Hue, Sat, Lum, Hex, Red, Green, Blue
|
|
80
|
+
- color dropper, Search, Page, of
|
|
81
|
+
- Reset table, Columns, Move, Align
|
|
82
|
+
- Hide column, Delete column, Show All
|
|
83
|
+
- Move to left, Move to right, Move to first, Move to last
|
|
84
|
+
- Align left, Align center, Align right
|
|
85
|
+
- Sort, Filter, Export, Delete selected rows
|
|
86
|
+
- Sort Ascending, Sort Descending, Clear filters
|
|
87
|
+
- Filter by cell's value, Hide select rows, Show only select rows
|
|
88
|
+
- Export all records, Export current page, Export current selection, Export records in filter
|
|
89
|
+
- Move up, Move down, Move to top, Move to bottom
|
|
90
|
+
- Mo, Tu, We, Th, Fr, Sa, Su
|