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
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""CLI entry point for bootstack.
|
|
2
|
+
|
|
3
|
+
The bootstack CLI provides commands for:
|
|
4
|
+
- Creating new projects (start)
|
|
5
|
+
- Running applications (run)
|
|
6
|
+
- Building for distribution (build)
|
|
7
|
+
- Adding components (add)
|
|
8
|
+
- Listing resources (list)
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
bootstack start <appname> Create a new project
|
|
12
|
+
bootstack run [path] Run the application
|
|
13
|
+
bootstack promote --pyinstaller Enable PyInstaller support
|
|
14
|
+
bootstack build Build for distribution
|
|
15
|
+
bootstack add page <ClassName> Add a new page (AppShell)
|
|
16
|
+
bootstack add view <ClassName> Add a new view
|
|
17
|
+
bootstack add dialog <ClassName> Add a new dialog
|
|
18
|
+
bootstack add theme <name> Add a custom theme
|
|
19
|
+
bootstack add i18n Add i18n support
|
|
20
|
+
bootstack list themes List available themes
|
|
21
|
+
bootstack doctor Diagnose project and environment health
|
|
22
|
+
bootstack demo Launch the widget demo
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import argparse
|
|
28
|
+
import sys
|
|
29
|
+
from typing import Sequence
|
|
30
|
+
|
|
31
|
+
from bootstack.cli import add, build, doctor, list_cmd, promote, run, start
|
|
32
|
+
from bootstack.cli.demo import run_demo
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def main(argv: Sequence[str] | None = None) -> None:
|
|
36
|
+
"""Dispatch CLI commands registered in bootstack."""
|
|
37
|
+
parser = argparse.ArgumentParser(
|
|
38
|
+
prog="bootstack",
|
|
39
|
+
description="bootstack command line interface",
|
|
40
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
41
|
+
epilog="""\
|
|
42
|
+
Examples:
|
|
43
|
+
bootstack start MyApp Create a new project
|
|
44
|
+
bootstack start MyApp --template appshell Create an AppShell project
|
|
45
|
+
bootstack start MyApp --theme superhero Use a specific theme
|
|
46
|
+
bootstack run Run the application
|
|
47
|
+
bootstack promote --pyinstaller Enable PyInstaller support
|
|
48
|
+
bootstack build Build for distribution
|
|
49
|
+
bootstack add view SettingsView Add a new view
|
|
50
|
+
bootstack list themes List available themes
|
|
51
|
+
bootstack doctor Diagnose project and environment health
|
|
52
|
+
bootstack demo Launch the widget demo
|
|
53
|
+
|
|
54
|
+
For more information on a command:
|
|
55
|
+
bootstack <command> --help
|
|
56
|
+
""",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
parser.add_argument(
|
|
60
|
+
"--version",
|
|
61
|
+
action="version",
|
|
62
|
+
version=_get_version(),
|
|
63
|
+
)
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"-v",
|
|
66
|
+
"--verbose",
|
|
67
|
+
action="store_true",
|
|
68
|
+
help="Print full tracebacks on error",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
subparsers = parser.add_subparsers(
|
|
72
|
+
dest="command",
|
|
73
|
+
title="commands",
|
|
74
|
+
metavar="<command>",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Register commands
|
|
78
|
+
start.add_parser(subparsers)
|
|
79
|
+
run.add_parser(subparsers)
|
|
80
|
+
promote.add_parser(subparsers)
|
|
81
|
+
build.add_parser(subparsers)
|
|
82
|
+
add.add_parser(subparsers)
|
|
83
|
+
list_cmd.add_parser(subparsers)
|
|
84
|
+
doctor.add_parser(subparsers)
|
|
85
|
+
|
|
86
|
+
# Demo command (kept for backwards compatibility)
|
|
87
|
+
demo_parser = subparsers.add_parser(
|
|
88
|
+
"demo",
|
|
89
|
+
help="Launch the widget demo",
|
|
90
|
+
)
|
|
91
|
+
demo_parser.set_defaults(func=lambda args: run_demo())
|
|
92
|
+
|
|
93
|
+
# Parse arguments
|
|
94
|
+
args = parser.parse_args(argv)
|
|
95
|
+
func = getattr(args, "func", None)
|
|
96
|
+
|
|
97
|
+
if func is None:
|
|
98
|
+
parser.print_help()
|
|
99
|
+
sys.exit(0)
|
|
100
|
+
|
|
101
|
+
# Execute command
|
|
102
|
+
try:
|
|
103
|
+
func(args)
|
|
104
|
+
except KeyboardInterrupt:
|
|
105
|
+
print("\nInterrupted.")
|
|
106
|
+
sys.exit(1)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
if getattr(args, "verbose", False):
|
|
109
|
+
import traceback
|
|
110
|
+
traceback.print_exc()
|
|
111
|
+
else:
|
|
112
|
+
print(f"Error: {e}")
|
|
113
|
+
print("(Run with --verbose for the full traceback.)")
|
|
114
|
+
sys.exit(1)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _get_version() -> str:
|
|
118
|
+
"""Get the bootstack version string."""
|
|
119
|
+
try:
|
|
120
|
+
import bootstack
|
|
121
|
+
|
|
122
|
+
return f"bootstack {bootstack.__version__}"
|
|
123
|
+
except Exception:
|
|
124
|
+
return "bootstack (unknown version)"
|
bootstack/cli/add.py
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"""bootstack add command - Add views, dialogs, themes, and i18n to a project."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from bootstack.cli.config import TtkbConfig, find_config
|
|
9
|
+
from bootstack.cli.templates import create_dialog, create_page, create_view
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def add_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
13
|
+
"""Add the 'add' subcommand parser."""
|
|
14
|
+
parser = subparsers.add_parser(
|
|
15
|
+
"add",
|
|
16
|
+
help="Add components to the project",
|
|
17
|
+
description="Add views, pages, dialogs, themes, or i18n support to the project.",
|
|
18
|
+
)
|
|
19
|
+
add_subparsers = parser.add_subparsers(dest="component")
|
|
20
|
+
|
|
21
|
+
# bootstack add page <ClassName>
|
|
22
|
+
page_parser = add_subparsers.add_parser(
|
|
23
|
+
"page",
|
|
24
|
+
help="Add a new page (for AppShell projects)",
|
|
25
|
+
)
|
|
26
|
+
page_parser.add_argument(
|
|
27
|
+
"class_name",
|
|
28
|
+
help="Page class name (CamelCase, e.g., 'DashboardPage')",
|
|
29
|
+
)
|
|
30
|
+
page_parser.add_argument(
|
|
31
|
+
"--dir",
|
|
32
|
+
type=Path,
|
|
33
|
+
default=None,
|
|
34
|
+
help="Target directory (default: src/<app>/pages/)",
|
|
35
|
+
)
|
|
36
|
+
page_parser.add_argument(
|
|
37
|
+
"--scrollable",
|
|
38
|
+
action="store_true",
|
|
39
|
+
help="Make the page scrollable (wraps content in a ScrollView)",
|
|
40
|
+
)
|
|
41
|
+
page_parser.set_defaults(func=run_add_page)
|
|
42
|
+
|
|
43
|
+
# bootstack add view <ClassName>
|
|
44
|
+
view_parser = add_subparsers.add_parser(
|
|
45
|
+
"view",
|
|
46
|
+
help="Add a new view",
|
|
47
|
+
)
|
|
48
|
+
view_parser.add_argument(
|
|
49
|
+
"class_name",
|
|
50
|
+
help="View class name (CamelCase, e.g., 'SettingsView')",
|
|
51
|
+
)
|
|
52
|
+
view_parser.add_argument(
|
|
53
|
+
"--container",
|
|
54
|
+
choices=["grid", "pack"],
|
|
55
|
+
default=None,
|
|
56
|
+
help="Container type (default: from bootstack.toml or 'grid')",
|
|
57
|
+
)
|
|
58
|
+
view_parser.add_argument(
|
|
59
|
+
"--dir",
|
|
60
|
+
type=Path,
|
|
61
|
+
default=None,
|
|
62
|
+
help="Target directory (default: src/<app>/views/)",
|
|
63
|
+
)
|
|
64
|
+
view_parser.set_defaults(func=run_add_view)
|
|
65
|
+
|
|
66
|
+
# bootstack add dialog <ClassName>
|
|
67
|
+
dialog_parser = add_subparsers.add_parser(
|
|
68
|
+
"dialog",
|
|
69
|
+
help="Add a new dialog",
|
|
70
|
+
)
|
|
71
|
+
dialog_parser.add_argument(
|
|
72
|
+
"class_name",
|
|
73
|
+
help="Dialog class name (CamelCase, e.g., 'ConfirmDialog')",
|
|
74
|
+
)
|
|
75
|
+
dialog_parser.add_argument(
|
|
76
|
+
"--dir",
|
|
77
|
+
type=Path,
|
|
78
|
+
default=None,
|
|
79
|
+
help="Target directory (default: src/<app>/dialogs/)",
|
|
80
|
+
)
|
|
81
|
+
dialog_parser.set_defaults(func=run_add_dialog)
|
|
82
|
+
|
|
83
|
+
# bootstack add theme <name>
|
|
84
|
+
theme_parser = add_subparsers.add_parser(
|
|
85
|
+
"theme",
|
|
86
|
+
help="Add a custom theme",
|
|
87
|
+
)
|
|
88
|
+
theme_parser.add_argument(
|
|
89
|
+
"name",
|
|
90
|
+
help="Theme name (e.g., 'mytheme')",
|
|
91
|
+
)
|
|
92
|
+
theme_parser.add_argument(
|
|
93
|
+
"--mode",
|
|
94
|
+
choices=["light", "dark"],
|
|
95
|
+
default="light",
|
|
96
|
+
help="Theme mode (default: light)",
|
|
97
|
+
)
|
|
98
|
+
theme_parser.set_defaults(func=run_add_theme)
|
|
99
|
+
|
|
100
|
+
# bootstack add i18n
|
|
101
|
+
i18n_parser = add_subparsers.add_parser(
|
|
102
|
+
"i18n",
|
|
103
|
+
help="Add internationalization support",
|
|
104
|
+
)
|
|
105
|
+
i18n_parser.add_argument(
|
|
106
|
+
"--languages",
|
|
107
|
+
nargs="+",
|
|
108
|
+
default=["en"],
|
|
109
|
+
help="Languages to support (default: en)",
|
|
110
|
+
)
|
|
111
|
+
i18n_parser.set_defaults(func=run_add_i18n)
|
|
112
|
+
|
|
113
|
+
parser.set_defaults(func=lambda args: parser.print_help())
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def run_add_view(args: argparse.Namespace) -> None:
|
|
117
|
+
"""Add a new view to the project."""
|
|
118
|
+
class_name = args.class_name
|
|
119
|
+
|
|
120
|
+
# Validate class name
|
|
121
|
+
if not class_name[0].isupper():
|
|
122
|
+
print("Error: Class name should be CamelCase (e.g., 'SettingsView')")
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
# Find project configuration
|
|
126
|
+
config_path = find_config()
|
|
127
|
+
if config_path is None:
|
|
128
|
+
print("Error: No bootstack.toml found. Are you in a bootstack project?")
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
project_root = config_path.parent
|
|
132
|
+
config = TtkbConfig.load(config_path)
|
|
133
|
+
|
|
134
|
+
# Reject in AppShell projects — views belong to the basic template
|
|
135
|
+
if config.app.template == "appshell":
|
|
136
|
+
print("Error: 'bootstack add view' is for basic-template projects.")
|
|
137
|
+
print("This project uses the 'appshell' template. Use 'bootstack add page' instead.")
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
# Determine container type
|
|
141
|
+
container = args.container
|
|
142
|
+
if container is None:
|
|
143
|
+
container = config.layout.default_container
|
|
144
|
+
|
|
145
|
+
# Determine target directory
|
|
146
|
+
if args.dir:
|
|
147
|
+
target_dir = args.dir
|
|
148
|
+
else:
|
|
149
|
+
# Parse entry point to find source directory
|
|
150
|
+
entry_path = Path(config.app.entry)
|
|
151
|
+
if entry_path.parts[0] == "src" and len(entry_path.parts) >= 2:
|
|
152
|
+
module_name = entry_path.parts[1]
|
|
153
|
+
target_dir = project_root / "src" / module_name / "views"
|
|
154
|
+
else:
|
|
155
|
+
target_dir = project_root / "views"
|
|
156
|
+
|
|
157
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
158
|
+
|
|
159
|
+
# Ensure __init__.py exists
|
|
160
|
+
init_file = target_dir / "__init__.py"
|
|
161
|
+
if not init_file.exists():
|
|
162
|
+
init_file.write_text('"""Views package."""\n', encoding="utf-8")
|
|
163
|
+
|
|
164
|
+
# Create view
|
|
165
|
+
file_path = create_view(class_name, target_dir, container)
|
|
166
|
+
|
|
167
|
+
print(f"Created view: {file_path.relative_to(project_root)}")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def run_add_page(args: argparse.Namespace) -> None:
|
|
171
|
+
"""Add a new page to an AppShell project."""
|
|
172
|
+
class_name = args.class_name
|
|
173
|
+
|
|
174
|
+
# Validate class name
|
|
175
|
+
if not class_name[0].isupper():
|
|
176
|
+
print("Error: Class name should be CamelCase (e.g., 'DashboardPage')")
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
# Find project configuration
|
|
180
|
+
config_path = find_config()
|
|
181
|
+
if config_path is None:
|
|
182
|
+
print("Error: No bootstack.toml found. Are you in a bootstack project?")
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
project_root = config_path.parent
|
|
186
|
+
config = TtkbConfig.load(config_path)
|
|
187
|
+
|
|
188
|
+
# Check that this is an AppShell project
|
|
189
|
+
if config.app.template != "appshell":
|
|
190
|
+
print("Error: 'bootstack add page' is for AppShell projects.")
|
|
191
|
+
print("This project uses the 'basic' template. Use 'bootstack add view' instead.")
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
# Determine target directory and module name for the import hint
|
|
195
|
+
entry_path = Path(config.app.entry)
|
|
196
|
+
if entry_path.parts[0] == "src" and len(entry_path.parts) >= 2:
|
|
197
|
+
module_name = entry_path.parts[1]
|
|
198
|
+
default_target = project_root / "src" / module_name / "pages"
|
|
199
|
+
else:
|
|
200
|
+
module_name = None
|
|
201
|
+
default_target = project_root / "pages"
|
|
202
|
+
|
|
203
|
+
target_dir = args.dir if args.dir else default_target
|
|
204
|
+
|
|
205
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
206
|
+
|
|
207
|
+
# Ensure __init__.py exists
|
|
208
|
+
init_file = target_dir / "__init__.py"
|
|
209
|
+
if not init_file.exists():
|
|
210
|
+
init_file.write_text('"""Pages package."""\n', encoding="utf-8")
|
|
211
|
+
|
|
212
|
+
# Create page
|
|
213
|
+
scrollable = args.scrollable
|
|
214
|
+
file_path = create_page(class_name, target_dir, scrollable=scrollable)
|
|
215
|
+
|
|
216
|
+
print(f"Created page: {file_path.relative_to(project_root)}")
|
|
217
|
+
print()
|
|
218
|
+
print("This created the file only — it is NOT yet shown in the sidebar.")
|
|
219
|
+
print("To register it with the AppShell, paste these lines into main.py:")
|
|
220
|
+
print()
|
|
221
|
+
import_module = f"{module_name}.pages" if module_name else "<module>.pages"
|
|
222
|
+
print(f" from {import_module}.{file_path.stem} import {class_name}")
|
|
223
|
+
scrollable_arg = ", scrollable=True" if scrollable else ""
|
|
224
|
+
print(f' page = shell.add_page("<id>", text="<Label>", icon="<icon>"{scrollable_arg})')
|
|
225
|
+
print(f" {class_name}(page)")
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def run_add_dialog(args: argparse.Namespace) -> None:
|
|
229
|
+
"""Add a new dialog to the project."""
|
|
230
|
+
class_name = args.class_name
|
|
231
|
+
|
|
232
|
+
# Validate class name
|
|
233
|
+
if not class_name[0].isupper():
|
|
234
|
+
print("Error: Class name should be CamelCase (e.g., 'ConfirmDialog')")
|
|
235
|
+
return
|
|
236
|
+
|
|
237
|
+
# Find project configuration
|
|
238
|
+
config_path = find_config()
|
|
239
|
+
if config_path is None:
|
|
240
|
+
print("Error: No bootstack.toml found. Are you in a bootstack project?")
|
|
241
|
+
return
|
|
242
|
+
|
|
243
|
+
project_root = config_path.parent
|
|
244
|
+
config = TtkbConfig.load(config_path)
|
|
245
|
+
|
|
246
|
+
# Determine target directory
|
|
247
|
+
if args.dir:
|
|
248
|
+
target_dir = args.dir
|
|
249
|
+
else:
|
|
250
|
+
# Parse entry point to find source directory
|
|
251
|
+
entry_path = Path(config.app.entry)
|
|
252
|
+
if entry_path.parts[0] == "src" and len(entry_path.parts) >= 2:
|
|
253
|
+
module_name = entry_path.parts[1]
|
|
254
|
+
target_dir = project_root / "src" / module_name / "dialogs"
|
|
255
|
+
else:
|
|
256
|
+
target_dir = project_root / "dialogs"
|
|
257
|
+
|
|
258
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
259
|
+
|
|
260
|
+
# Ensure __init__.py exists
|
|
261
|
+
init_file = target_dir / "__init__.py"
|
|
262
|
+
if not init_file.exists():
|
|
263
|
+
init_file.write_text('"""Dialogs package."""\n', encoding="utf-8")
|
|
264
|
+
|
|
265
|
+
# Create dialog
|
|
266
|
+
file_path = create_dialog(class_name, target_dir)
|
|
267
|
+
|
|
268
|
+
print(f"Created dialog: {file_path.relative_to(project_root)}")
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def run_add_theme(args: argparse.Namespace) -> None:
|
|
272
|
+
"""Add a custom theme to the project."""
|
|
273
|
+
theme_name = args.name.lower()
|
|
274
|
+
mode = args.mode
|
|
275
|
+
|
|
276
|
+
# Find project configuration
|
|
277
|
+
config_path = find_config()
|
|
278
|
+
if config_path is None:
|
|
279
|
+
print("Error: No bootstack.toml found. Are you in a bootstack project?")
|
|
280
|
+
return
|
|
281
|
+
|
|
282
|
+
project_root = config_path.parent
|
|
283
|
+
|
|
284
|
+
# Create themes directory
|
|
285
|
+
themes_dir = project_root / "themes"
|
|
286
|
+
themes_dir.mkdir(exist_ok=True)
|
|
287
|
+
|
|
288
|
+
# Create theme file
|
|
289
|
+
theme_file = themes_dir / f"{theme_name}.json"
|
|
290
|
+
if theme_file.exists():
|
|
291
|
+
print(f"Error: Theme '{theme_name}' already exists.")
|
|
292
|
+
return
|
|
293
|
+
|
|
294
|
+
# Theme template based on mode
|
|
295
|
+
if mode == "light":
|
|
296
|
+
theme_content = _get_light_theme_template(theme_name)
|
|
297
|
+
else:
|
|
298
|
+
theme_content = _get_dark_theme_template(theme_name)
|
|
299
|
+
|
|
300
|
+
theme_file.write_text(theme_content, encoding="utf-8")
|
|
301
|
+
|
|
302
|
+
print(f"Created theme: {theme_file.relative_to(project_root)}")
|
|
303
|
+
print()
|
|
304
|
+
print("To use this theme, register the JSON file before creating your App:")
|
|
305
|
+
print()
|
|
306
|
+
print(" from bootstack.style.theme_provider import register_user_theme")
|
|
307
|
+
print(f' register_user_theme("{theme_name}", "themes/{theme_name}.json")')
|
|
308
|
+
print(f' app = ttk.App(theme="{theme_name}")')
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def run_add_i18n(args: argparse.Namespace) -> None:
|
|
312
|
+
"""Add internationalization support to the project."""
|
|
313
|
+
languages = args.languages
|
|
314
|
+
|
|
315
|
+
# Find project configuration
|
|
316
|
+
config_path = find_config()
|
|
317
|
+
if config_path is None:
|
|
318
|
+
print("Error: No bootstack.toml found. Are you in a bootstack project?")
|
|
319
|
+
return
|
|
320
|
+
|
|
321
|
+
project_root = config_path.parent
|
|
322
|
+
|
|
323
|
+
# Create locales directory structure
|
|
324
|
+
locales_dir = project_root / "locales"
|
|
325
|
+
|
|
326
|
+
for lang in languages:
|
|
327
|
+
lang_dir = locales_dir / lang / "LC_MESSAGES"
|
|
328
|
+
lang_dir.mkdir(parents=True, exist_ok=True)
|
|
329
|
+
|
|
330
|
+
# Create .po file template
|
|
331
|
+
po_file = lang_dir / "messages.po"
|
|
332
|
+
if not po_file.exists():
|
|
333
|
+
po_content = _get_po_template(lang)
|
|
334
|
+
po_file.write_text(po_content, encoding="utf-8")
|
|
335
|
+
print(f"Created: {po_file.relative_to(project_root)}")
|
|
336
|
+
|
|
337
|
+
print()
|
|
338
|
+
print("Internationalization support added!")
|
|
339
|
+
print()
|
|
340
|
+
print("Next steps:")
|
|
341
|
+
print(" 1. Add translatable strings to your .po files")
|
|
342
|
+
print(" 2. Compile with: msgfmt locales/<lang>/LC_MESSAGES/messages.po -o locales/<lang>/LC_MESSAGES/messages.mo")
|
|
343
|
+
print(" 3. Use translations in code: ttk.mc('Hello')")
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
_BASE_SHADES = {
|
|
347
|
+
"blue": "#0d6efd",
|
|
348
|
+
"indigo": "#6610f2",
|
|
349
|
+
"purple": "#6f42c1",
|
|
350
|
+
"red": "#dc3545",
|
|
351
|
+
"orange": "#fd7e14",
|
|
352
|
+
"yellow": "#ffc107",
|
|
353
|
+
"green": "#198754",
|
|
354
|
+
"teal": "#20c997",
|
|
355
|
+
"cyan": "#0dcaf0",
|
|
356
|
+
"gray": "#adb5bd",
|
|
357
|
+
"pink": "#d63384",
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _theme_display_name(name: str) -> str:
|
|
362
|
+
return " ".join(part.capitalize() for part in name.replace("_", "-").split("-") if part)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def _render_theme(name: str, mode: str) -> str:
|
|
366
|
+
"""Render a v2 theme JSON template.
|
|
367
|
+
|
|
368
|
+
Light themes use the [600] step for semantic accents (so text on white
|
|
369
|
+
has good contrast); dark themes use [400] (lighter accents on a dark
|
|
370
|
+
background). Both schemas match the format consumed by
|
|
371
|
+
`bootstack.style.theme_provider`.
|
|
372
|
+
"""
|
|
373
|
+
if mode == "light":
|
|
374
|
+
foreground, background, step = "#212529", "#ffffff", "600"
|
|
375
|
+
else:
|
|
376
|
+
foreground, background, step = "#f8f9fa", "#212529", "400"
|
|
377
|
+
|
|
378
|
+
payload = {
|
|
379
|
+
"name": name,
|
|
380
|
+
"display_name": _theme_display_name(name),
|
|
381
|
+
"mode": mode,
|
|
382
|
+
"foreground": foreground,
|
|
383
|
+
"background": background,
|
|
384
|
+
"white": "#ffffff",
|
|
385
|
+
"black": "#000000",
|
|
386
|
+
"shades": _BASE_SHADES,
|
|
387
|
+
"semantic": {
|
|
388
|
+
"primary": f"blue[{step}]",
|
|
389
|
+
"secondary": f"gray[{step}]",
|
|
390
|
+
"success": f"green[{step}]",
|
|
391
|
+
"info": f"cyan[{step}]",
|
|
392
|
+
"warning": f"yellow[{step}]",
|
|
393
|
+
"danger": f"red[{step}]",
|
|
394
|
+
"light": "gray[100]",
|
|
395
|
+
"dark": "gray[900]",
|
|
396
|
+
},
|
|
397
|
+
}
|
|
398
|
+
import json as _json
|
|
399
|
+
return _json.dumps(payload, indent=2) + "\n"
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _get_light_theme_template(name: str) -> str:
|
|
403
|
+
"""Get a v2 light-mode theme template."""
|
|
404
|
+
return _render_theme(name, "light")
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def _get_dark_theme_template(name: str) -> str:
|
|
408
|
+
"""Get a v2 dark-mode theme template."""
|
|
409
|
+
return _render_theme(name, "dark")
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _get_po_template(lang: str) -> str:
|
|
413
|
+
"""Get a .po file template."""
|
|
414
|
+
from datetime import datetime, timezone
|
|
415
|
+
|
|
416
|
+
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M%z")
|
|
417
|
+
|
|
418
|
+
return f'''\
|
|
419
|
+
# {lang.upper()} translations for the application.
|
|
420
|
+
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
|
421
|
+
# This file is distributed under the same license as the PACKAGE package.
|
|
422
|
+
#
|
|
423
|
+
msgid ""
|
|
424
|
+
msgstr ""
|
|
425
|
+
"Project-Id-Version: 1.0\\n"
|
|
426
|
+
"Report-Msgid-Bugs-To: \\n"
|
|
427
|
+
"POT-Creation-Date: {now}\\n"
|
|
428
|
+
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
|
|
429
|
+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"
|
|
430
|
+
"Language-Team: {lang.upper()} <LL@li.org>\\n"
|
|
431
|
+
"Language: {lang}\\n"
|
|
432
|
+
"MIME-Version: 1.0\\n"
|
|
433
|
+
"Content-Type: text/plain; charset=UTF-8\\n"
|
|
434
|
+
"Content-Transfer-Encoding: 8bit\\n"
|
|
435
|
+
|
|
436
|
+
# Example translation
|
|
437
|
+
# msgid "Hello"
|
|
438
|
+
# msgstr "Hello"
|
|
439
|
+
'''
|
bootstack/cli/build.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""bootstack build command - Build the application for distribution."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from bootstack.cli.config import TtkbConfig, find_config
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def add_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
15
|
+
"""Add the 'build' subcommand parser."""
|
|
16
|
+
parser = subparsers.add_parser(
|
|
17
|
+
"build",
|
|
18
|
+
help="Build the application for distribution",
|
|
19
|
+
description="Build the application using the configured build backend.",
|
|
20
|
+
)
|
|
21
|
+
parser.add_argument(
|
|
22
|
+
"--clean",
|
|
23
|
+
action="store_true",
|
|
24
|
+
help="Clean build artifacts before building",
|
|
25
|
+
)
|
|
26
|
+
parser.set_defaults(func=run_build)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def run_build(args: argparse.Namespace) -> None:
|
|
30
|
+
"""Execute the build command."""
|
|
31
|
+
# Find project root
|
|
32
|
+
config_path = find_config()
|
|
33
|
+
if config_path is None:
|
|
34
|
+
print("Error: No bootstack.toml found in current directory or parents.")
|
|
35
|
+
print("Run 'bootstack start <appname>' to create a new project first.")
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
project_root = config_path.parent
|
|
39
|
+
config = TtkbConfig.load(config_path)
|
|
40
|
+
|
|
41
|
+
# Check if project has been promoted
|
|
42
|
+
if config.build is None:
|
|
43
|
+
print("Error: Project has not been promoted for building.")
|
|
44
|
+
print("Run 'bootstack promote --pyinstaller' first.")
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
backend = config.build.backend
|
|
48
|
+
|
|
49
|
+
if backend == "pyinstaller":
|
|
50
|
+
_build_pyinstaller(config, project_root, clean=args.clean)
|
|
51
|
+
else:
|
|
52
|
+
print(f"Error: Unknown build backend '{backend}'")
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _build_pyinstaller(
|
|
57
|
+
config: TtkbConfig,
|
|
58
|
+
project_root: Path,
|
|
59
|
+
clean: bool = False,
|
|
60
|
+
) -> None:
|
|
61
|
+
"""Build using PyInstaller."""
|
|
62
|
+
# Check if PyInstaller is installed
|
|
63
|
+
try:
|
|
64
|
+
import PyInstaller # noqa: F401
|
|
65
|
+
except ImportError:
|
|
66
|
+
print("Error: PyInstaller is not installed.")
|
|
67
|
+
print("Install it with: pip install pyinstaller")
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
spec_path = project_root / "build" / "pyinstaller" / "app.spec"
|
|
71
|
+
if not spec_path.exists():
|
|
72
|
+
print("Error: PyInstaller spec file not found.")
|
|
73
|
+
print("Run 'bootstack promote --pyinstaller' to generate it.")
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
# Clean if requested
|
|
77
|
+
if clean:
|
|
78
|
+
print("Cleaning build artifacts...")
|
|
79
|
+
dist_dir = project_root / "dist"
|
|
80
|
+
build_dir = project_root / "build" / "pyinstaller" / "build"
|
|
81
|
+
if dist_dir.exists():
|
|
82
|
+
shutil.rmtree(dist_dir)
|
|
83
|
+
if build_dir.exists():
|
|
84
|
+
shutil.rmtree(build_dir)
|
|
85
|
+
|
|
86
|
+
print(f"Building '{config.app.name}' with PyInstaller...")
|
|
87
|
+
print()
|
|
88
|
+
|
|
89
|
+
# Run PyInstaller
|
|
90
|
+
cmd = [
|
|
91
|
+
sys.executable,
|
|
92
|
+
"-m",
|
|
93
|
+
"PyInstaller",
|
|
94
|
+
str(spec_path),
|
|
95
|
+
"--distpath",
|
|
96
|
+
str(project_root / "dist"),
|
|
97
|
+
"--workpath",
|
|
98
|
+
str(project_root / "build" / "pyinstaller" / "build"),
|
|
99
|
+
"--noconfirm",
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
result = subprocess.run(cmd, cwd=str(project_root))
|
|
104
|
+
if result.returncode == 0:
|
|
105
|
+
print()
|
|
106
|
+
print("Build completed successfully!")
|
|
107
|
+
print()
|
|
108
|
+
print(f"Output: {project_root / 'dist' / config.app.name}")
|
|
109
|
+
else:
|
|
110
|
+
print()
|
|
111
|
+
print("Build failed.")
|
|
112
|
+
sys.exit(result.returncode)
|
|
113
|
+
except KeyboardInterrupt:
|
|
114
|
+
print("\nBuild interrupted.")
|
|
115
|
+
sys.exit(1)
|