bootstack 0.1.0__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 +157 -0
- bootstack/__main__.py +5 -0
- bootstack/_core/__init__.py +21 -0
- bootstack/_core/capabilities/__init__.py +45 -0
- bootstack/_core/capabilities/after.py +103 -0
- bootstack/_core/capabilities/bind.py +192 -0
- bootstack/_core/capabilities/bindtags.py +112 -0
- bootstack/_core/capabilities/busy.py +72 -0
- bootstack/_core/capabilities/clipboard.py +89 -0
- bootstack/_core/capabilities/focus.py +118 -0
- bootstack/_core/capabilities/grab.py +65 -0
- bootstack/_core/capabilities/grid.py +211 -0
- bootstack/_core/capabilities/localization.py +231 -0
- bootstack/_core/capabilities/pack.py +140 -0
- bootstack/_core/capabilities/place.py +113 -0
- bootstack/_core/capabilities/selection.py +136 -0
- bootstack/_core/capabilities/signals.py +244 -0
- bootstack/_core/capabilities/winfo.py +315 -0
- bootstack/_core/colorutils.py +234 -0
- bootstack/_core/exceptions.py +25 -0
- bootstack/_core/images.py +463 -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/paths.py +49 -0
- bootstack/_core/publisher.py +149 -0
- bootstack/_core/variables.py +62 -0
- bootstack/_runtime/__init__.py +3 -0
- bootstack/_runtime/app.py +930 -0
- bootstack/_runtime/base_window.py +945 -0
- bootstack/_runtime/events.py +399 -0
- bootstack/_runtime/shortcuts.py +496 -0
- bootstack/_runtime/tk_patch.py +43 -0
- bootstack/_runtime/toplevel.py +276 -0
- bootstack/_runtime/utility.py +457 -0
- bootstack/_runtime/visual_focus.py +240 -0
- bootstack/_runtime/window_utilities.py +1123 -0
- bootstack/assets/__init__.py +21 -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/button-compact.png +0 -0
- bootstack/assets/elements/button-default.png +0 -0
- bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
- bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
- bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
- bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
- bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
- bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
- bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
- bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
- bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
- bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
- bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
- bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
- bootstack/assets/elements/card.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-addon-compact.png +0 -0
- bootstack/assets/elements/input-addon-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/listrow-compact.png +0 -0
- bootstack/assets/elements/listrow-default.png +0 -0
- bootstack/assets/elements/manifest.toml +361 -0
- bootstack/assets/elements/menu-item.png +0 -0
- bootstack/assets/elements/navitem-compact.png +0 -0
- bootstack/assets/elements/navitem-default.png +0 -0
- bootstack/assets/elements/progressbar-h-compact.png +0 -0
- bootstack/assets/elements/progressbar-h-default.png +0 -0
- bootstack/assets/elements/progressbar-v-compact.png +0 -0
- bootstack/assets/elements/progressbar-v-default.png +0 -0
- bootstack/assets/elements/radiobutton.png +0 -0
- bootstack/assets/elements/scrollbar-horizontal.png +0 -0
- bootstack/assets/elements/scrollbar-vertical.png +0 -0
- bootstack/assets/elements/slider-handle.png +0 -0
- bootstack/assets/elements/slider-track-h.png +0 -0
- bootstack/assets/elements/slider-track-v.png +0 -0
- bootstack/assets/elements/switch-off.png +0 -0
- bootstack/assets/elements/switch-on.png +0 -0
- bootstack/assets/elements/tab-h.png +0 -0
- bootstack/assets/elements/tab-v.png +0 -0
- bootstack/assets/icons/bootstrap.ttf +0 -0
- bootstack/assets/icons/glyphmap.json +2080 -0
- bootstack/assets/icons/icon_metrics.json +12470 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +878 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +878 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +854 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +844 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +917 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +844 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +844 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +845 -0
- bootstack/cli/__init__.py +133 -0
- bootstack/cli/__main__.py +6 -0
- bootstack/cli/add.py +395 -0
- bootstack/cli/appicon.py +285 -0
- bootstack/cli/build.py +115 -0
- bootstack/cli/config.py +313 -0
- bootstack/cli/demo.py +564 -0
- bootstack/cli/dev.py +153 -0
- bootstack/cli/doctor.py +195 -0
- bootstack/cli/icons.py +98 -0
- bootstack/cli/promote.py +120 -0
- bootstack/cli/pyinstaller.py +268 -0
- bootstack/cli/run.py +95 -0
- bootstack/cli/start.py +117 -0
- bootstack/cli/templates/__init__.py +931 -0
- bootstack/clipboard.py +48 -0
- bootstack/constants.py +318 -0
- bootstack/data/README.md +615 -0
- bootstack/data/__init__.py +78 -0
- bootstack/data/_observable.py +276 -0
- bootstack/data/base.py +780 -0
- bootstack/data/file_source.py +367 -0
- bootstack/data/memory_source.py +450 -0
- bootstack/data/query.py +367 -0
- bootstack/data/readers.py +289 -0
- bootstack/data/sqlite_source.py +869 -0
- bootstack/data/types.py +354 -0
- bootstack/data/writers.py +232 -0
- bootstack/dev/__init__.py +36 -0
- bootstack/dev/_capture.py +141 -0
- bootstack/dev/_env.py +43 -0
- bootstack/dev/_registry.py +140 -0
- bootstack/dev/_reloader.py +351 -0
- bootstack/dev/_reset.py +50 -0
- bootstack/dev/_watcher.py +91 -0
- bootstack/dialogs/__init__.py +948 -0
- bootstack/dialogs/_impl/__init__.py +47 -0
- bootstack/dialogs/_impl/colorchooser.py +588 -0
- bootstack/dialogs/_impl/datedialog.py +450 -0
- bootstack/dialogs/_impl/dialog.py +594 -0
- bootstack/dialogs/_impl/filterdialog.py +358 -0
- bootstack/dialogs/_impl/fontdialog.py +364 -0
- bootstack/dialogs/_impl/formdialog.py +564 -0
- bootstack/dialogs/_impl/message.py +486 -0
- bootstack/dialogs/_impl/query.py +570 -0
- bootstack/errors.py +67 -0
- bootstack/events/__init__.py +111 -0
- bootstack/events/_event.py +135 -0
- bootstack/events/_payloads.py +539 -0
- bootstack/events/_subscription.py +38 -0
- bootstack/i18n/README.md +77 -0
- bootstack/i18n/__init__.py +23 -0
- bootstack/i18n/catalog.py +121 -0
- bootstack/i18n/intl_format.py +584 -0
- bootstack/i18n/msgcat.py +425 -0
- bootstack/i18n/specs.py +156 -0
- bootstack/images.py +563 -0
- bootstack/py.typed +1 -0
- bootstack/scheduling/__init__.py +11 -0
- bootstack/scheduling/_schedule.py +218 -0
- bootstack/shortcuts.py +21 -0
- bootstack/signals/README.md +98 -0
- bootstack/signals/__init__.py +10 -0
- bootstack/signals/integration.py +100 -0
- bootstack/signals/signal.py +353 -0
- bootstack/signals/types.py +6 -0
- bootstack/store.py +286 -0
- bootstack/streams/__init__.py +12 -0
- bootstack/streams/_stream.py +321 -0
- bootstack/style/__init__.py +38 -0
- bootstack/style/builders/__init__.py +51 -0
- bootstack/style/builders/badge.py +46 -0
- bootstack/style/builders/button.py +339 -0
- bootstack/style/builders/buttongroup.py +311 -0
- bootstack/style/builders/calendar.py +271 -0
- bootstack/style/builders/checkbutton.py +110 -0
- bootstack/style/builders/combobox.py +113 -0
- bootstack/style/builders/contextmenu.py +268 -0
- bootstack/style/builders/entry.py +82 -0
- bootstack/style/builders/expander.py +148 -0
- bootstack/style/builders/field.py +335 -0
- bootstack/style/builders/frame.py +50 -0
- bootstack/style/builders/label.py +28 -0
- bootstack/style/builders/labelframe.py +34 -0
- bootstack/style/builders/listview.py +369 -0
- bootstack/style/builders/menubar.py +91 -0
- bootstack/style/builders/menubutton.py +359 -0
- bootstack/style/builders/panedwindow.py +25 -0
- bootstack/style/builders/progressbar.py +67 -0
- bootstack/style/builders/radiobutton.py +99 -0
- bootstack/style/builders/scale.py +64 -0
- bootstack/style/builders/scrollbar.py +225 -0
- bootstack/style/builders/separator.py +49 -0
- bootstack/style/builders/sidenav.py +643 -0
- bootstack/style/builders/sizegrip.py +15 -0
- bootstack/style/builders/spinbox.py +119 -0
- bootstack/style/builders/switch.py +70 -0
- bootstack/style/builders/tabitem.py +204 -0
- bootstack/style/builders/togglegroup.py +294 -0
- bootstack/style/builders/toolbutton.py +275 -0
- bootstack/style/builders/tooltip.py +26 -0
- bootstack/style/builders/treeview.py +193 -0
- bootstack/style/builders/utils.py +455 -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/fonts.py +123 -0
- bootstack/style/style.py +609 -0
- bootstack/style/style_builder_base.py +716 -0
- bootstack/style/style_builder_mixed.py +93 -0
- bootstack/style/style_builder_tk.py +109 -0
- bootstack/style/style_builder_ttk.py +353 -0
- bootstack/style/style_resolver.py +447 -0
- bootstack/style/theme.py +245 -0
- bootstack/style/theme_provider.py +471 -0
- bootstack/style/themes/__init__.py +128 -0
- bootstack/style/tk_patch.py +5 -0
- bootstack/style/token_maps.py +41 -0
- bootstack/style/types.py +32 -0
- bootstack/style/typography.py +523 -0
- bootstack/style/utility.py +746 -0
- bootstack/types.py +39 -0
- bootstack/validation/__init__.py +6 -0
- bootstack/validation/types.py +15 -0
- bootstack/validation/validation_result.py +17 -0
- bootstack/validation/validation_rules.py +205 -0
- bootstack/widgets/__init__.py +74 -0
- bootstack/widgets/_core/__init__.py +31 -0
- bootstack/widgets/_core/app_config.py +266 -0
- bootstack/widgets/_core/base.py +461 -0
- bootstack/widgets/_core/container.py +334 -0
- bootstack/widgets/_core/context.py +35 -0
- bootstack/widgets/_core/events.py +130 -0
- bootstack/widgets/_core/field_mixin.py +353 -0
- bootstack/widgets/_core/icon_image_props.py +72 -0
- bootstack/widgets/_core/image_binding.py +93 -0
- bootstack/widgets/_core/navmodel.py +345 -0
- bootstack/widgets/_core/options.py +210 -0
- bootstack/widgets/_core/selection_group.py +130 -0
- bootstack/widgets/_core/window_controls.py +96 -0
- bootstack/widgets/_core/window_menu.py +246 -0
- bootstack/widgets/_impl/__init__.py +1 -0
- bootstack/widgets/_impl/_internal/__init__.py +0 -0
- bootstack/widgets/_impl/_internal/wrapper_base.py +307 -0
- bootstack/widgets/_impl/_parts/__init__.py +11 -0
- bootstack/widgets/_impl/_parts/numberentry_part.py +385 -0
- bootstack/widgets/_impl/_parts/spinnerentry_part.py +434 -0
- bootstack/widgets/_impl/_parts/textentry_part.py +406 -0
- bootstack/widgets/_impl/composites/__init__.py +33 -0
- bootstack/widgets/_impl/composites/_dateutils.py +33 -0
- bootstack/widgets/_impl/composites/_image_fit.py +105 -0
- bootstack/widgets/_impl/composites/accordion.py +381 -0
- bootstack/widgets/_impl/composites/avatar.py +191 -0
- bootstack/widgets/_impl/composites/buttongroup.py +371 -0
- bootstack/widgets/_impl/composites/calendar.py +972 -0
- bootstack/widgets/_impl/composites/carousel.py +567 -0
- bootstack/widgets/_impl/composites/chart.py +882 -0
- bootstack/widgets/_impl/composites/compositeframe.py +298 -0
- bootstack/widgets/_impl/composites/contextmenu.py +1951 -0
- bootstack/widgets/_impl/composites/dateentry.py +404 -0
- bootstack/widgets/_impl/composites/dropdownbutton.py +325 -0
- bootstack/widgets/_impl/composites/expander.py +515 -0
- bootstack/widgets/_impl/composites/field.py +670 -0
- bootstack/widgets/_impl/composites/form.py +1066 -0
- bootstack/widgets/_impl/composites/gallery.py +551 -0
- bootstack/widgets/_impl/composites/list/__init__.py +15 -0
- bootstack/widgets/_impl/composites/list/listitem.py +802 -0
- bootstack/widgets/_impl/composites/list/listview.py +1433 -0
- bootstack/widgets/_impl/composites/menu/__init__.py +18 -0
- bootstack/widgets/_impl/composites/menu/model.py +358 -0
- bootstack/widgets/_impl/composites/menu/render_native.py +134 -0
- bootstack/widgets/_impl/composites/menu/render_themed.py +134 -0
- bootstack/widgets/_impl/composites/meter.py +860 -0
- bootstack/widgets/_impl/composites/numericentry.py +201 -0
- bootstack/widgets/_impl/composites/pagestack.py +395 -0
- bootstack/widgets/_impl/composites/passwordentry.py +142 -0
- bootstack/widgets/_impl/composites/pathentry.py +168 -0
- bootstack/widgets/_impl/composites/picture.py +289 -0
- bootstack/widgets/_impl/composites/radiogroup.py +511 -0
- bootstack/widgets/_impl/composites/scrolledtext.py +375 -0
- bootstack/widgets/_impl/composites/scrolledtext.pyi +186 -0
- bootstack/widgets/_impl/composites/scrollview.py +764 -0
- bootstack/widgets/_impl/composites/selectbox.py +1026 -0
- bootstack/widgets/_impl/composites/shell/__init__.py +40 -0
- bootstack/widgets/_impl/composites/shell/content_host.py +52 -0
- bootstack/widgets/_impl/composites/shell/layout.py +335 -0
- bootstack/widgets/_impl/composites/shell/nav_panel.py +345 -0
- bootstack/widgets/_impl/composites/shell/providers.py +558 -0
- bootstack/widgets/_impl/composites/shell/rail.py +117 -0
- bootstack/widgets/_impl/composites/shell/shell.py +581 -0
- bootstack/widgets/_impl/composites/shell/workspace.py +273 -0
- bootstack/widgets/_impl/composites/sidenav/__init__.py +16 -0
- bootstack/widgets/_impl/composites/sidenav/header.py +81 -0
- bootstack/widgets/_impl/composites/sidenav/separator.py +44 -0
- bootstack/widgets/_impl/composites/slider/__init__.py +7 -0
- bootstack/widgets/_impl/composites/slider/_shared.py +195 -0
- bootstack/widgets/_impl/composites/slider/rangeslider.py +982 -0
- bootstack/widgets/_impl/composites/slider/slider.py +851 -0
- bootstack/widgets/_impl/composites/spinnerentry.py +185 -0
- bootstack/widgets/_impl/composites/tableview/__init__.py +5 -0
- bootstack/widgets/_impl/composites/tableview/tableview.py +3388 -0
- bootstack/widgets/_impl/composites/tableview/types.py +169 -0
- bootstack/widgets/_impl/composites/tabs/__init__.py +23 -0
- bootstack/widgets/_impl/composites/tabs/tabitem.py +389 -0
- bootstack/widgets/_impl/composites/tabs/tabs.py +974 -0
- bootstack/widgets/_impl/composites/tabs/tabview.py +650 -0
- bootstack/widgets/_impl/composites/textarea/__init__.py +26 -0
- bootstack/widgets/_impl/composites/textarea/change.py +46 -0
- bootstack/widgets/_impl/composites/textarea/codeeditor.py +526 -0
- bootstack/widgets/_impl/composites/textarea/core.py +495 -0
- bootstack/widgets/_impl/composites/textarea/decoration.py +42 -0
- bootstack/widgets/_impl/composites/textarea/diff.py +127 -0
- bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +1 -0
- bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +138 -0
- bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +158 -0
- bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +138 -0
- bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +312 -0
- bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +177 -0
- bootstack/widgets/_impl/composites/textarea/filter.py +171 -0
- bootstack/widgets/_impl/composites/textarea/search_overlay.py +459 -0
- bootstack/widgets/_impl/composites/textarea/sidebar.py +88 -0
- bootstack/widgets/_impl/composites/textarea/style_registry.py +178 -0
- bootstack/widgets/_impl/composites/textarea/textarea.py +606 -0
- bootstack/widgets/_impl/composites/textarea/undo.py +217 -0
- bootstack/widgets/_impl/composites/textentry.py +57 -0
- bootstack/widgets/_impl/composites/timeentry.py +176 -0
- bootstack/widgets/_impl/composites/toast.py +390 -0
- bootstack/widgets/_impl/composites/toast_stack.py +156 -0
- bootstack/widgets/_impl/composites/togglegroup.py +418 -0
- bootstack/widgets/_impl/composites/toolbar.py +608 -0
- bootstack/widgets/_impl/composites/tooltip.py +491 -0
- bootstack/widgets/_impl/composites/tree/__init__.py +7 -0
- bootstack/widgets/_impl/composites/tree/source_binding.py +136 -0
- bootstack/widgets/_impl/composites/tree/treeitem.py +393 -0
- bootstack/widgets/_impl/composites/tree/treenode.py +174 -0
- bootstack/widgets/_impl/composites/tree/treeview.py +841 -0
- bootstack/widgets/_impl/mixins/__init__.py +24 -0
- bootstack/widgets/_impl/mixins/configure_mixin.py +216 -0
- bootstack/widgets/_impl/mixins/entry_mixin.py +134 -0
- bootstack/widgets/_impl/mixins/font_mixin.py +368 -0
- bootstack/widgets/_impl/mixins/icon_mixin.py +61 -0
- bootstack/widgets/_impl/mixins/localization_mixin.py +253 -0
- bootstack/widgets/_impl/mixins/signal_mixin.py +268 -0
- bootstack/widgets/_impl/mixins/validation_mixin.py +226 -0
- bootstack/widgets/_impl/primitives/__init__.py +49 -0
- bootstack/widgets/_impl/primitives/_menubutton.py +107 -0
- bootstack/widgets/_impl/primitives/badge.py +45 -0
- bootstack/widgets/_impl/primitives/button.py +76 -0
- bootstack/widgets/_impl/primitives/card.py +45 -0
- bootstack/widgets/_impl/primitives/checkbutton.py +124 -0
- bootstack/widgets/_impl/primitives/checktoggle.py +62 -0
- bootstack/widgets/_impl/primitives/combobox.py +156 -0
- bootstack/widgets/_impl/primitives/entry.py +87 -0
- bootstack/widgets/_impl/primitives/flexframe.py +448 -0
- bootstack/widgets/_impl/primitives/frame.py +185 -0
- bootstack/widgets/_impl/primitives/gridframe.py +546 -0
- bootstack/widgets/_impl/primitives/label.py +84 -0
- bootstack/widgets/_impl/primitives/labelframe.py +54 -0
- bootstack/widgets/_impl/primitives/optionmenu.py +387 -0
- bootstack/widgets/_impl/primitives/packframe.py +227 -0
- bootstack/widgets/_impl/primitives/panedwindow.py +45 -0
- bootstack/widgets/_impl/primitives/progressbar.py +83 -0
- bootstack/widgets/_impl/primitives/radiobutton.py +115 -0
- bootstack/widgets/_impl/primitives/radiotoggle.py +54 -0
- bootstack/widgets/_impl/primitives/scrollbar.py +42 -0
- bootstack/widgets/_impl/primitives/separator.py +43 -0
- bootstack/widgets/_impl/primitives/sizegrip.py +33 -0
- bootstack/widgets/_impl/primitives/spinbox.py +95 -0
- bootstack/widgets/_impl/primitives/switch.py +44 -0
- bootstack/widgets/_impl/primitives/treeview.py +69 -0
- bootstack/widgets/app.py +371 -0
- bootstack/widgets/appshell.py +1179 -0
- bootstack/widgets/avatar.py +140 -0
- bootstack/widgets/boolean_controls.py +455 -0
- bootstack/widgets/button.py +224 -0
- bootstack/widgets/buttongroup.py +239 -0
- bootstack/widgets/calendar.py +195 -0
- bootstack/widgets/card.py +159 -0
- bootstack/widgets/carousel.py +241 -0
- bootstack/widgets/chart.py +302 -0
- bootstack/widgets/codeeditor.py +675 -0
- bootstack/widgets/contextmenu.py +371 -0
- bootstack/widgets/datatable.py +688 -0
- bootstack/widgets/datefield.py +395 -0
- bootstack/widgets/divider.py +60 -0
- bootstack/widgets/expander.py +579 -0
- bootstack/widgets/form.py +200 -0
- bootstack/widgets/gallery.py +250 -0
- bootstack/widgets/gauge.py +168 -0
- bootstack/widgets/grid.py +121 -0
- bootstack/widgets/groupbox.py +162 -0
- bootstack/widgets/label.py +229 -0
- bootstack/widgets/listview.py +354 -0
- bootstack/widgets/menubutton.py +419 -0
- bootstack/widgets/numberfield.py +431 -0
- bootstack/widgets/pagestack.py +345 -0
- bootstack/widgets/passwordfield.py +383 -0
- bootstack/widgets/pathfield.py +454 -0
- bootstack/widgets/picture.py +251 -0
- bootstack/widgets/progressbar.py +106 -0
- bootstack/widgets/radio_variants.py +271 -0
- bootstack/widgets/radiogroup.py +228 -0
- bootstack/widgets/scrollbar.py +64 -0
- bootstack/widgets/scrollview.py +186 -0
- bootstack/widgets/select.py +302 -0
- bootstack/widgets/selectbutton.py +182 -0
- bootstack/widgets/sidebar_toggle.py +130 -0
- bootstack/widgets/sizegrip.py +42 -0
- bootstack/widgets/slider.py +408 -0
- bootstack/widgets/spinbox.py +147 -0
- bootstack/widgets/spinnerfield.py +413 -0
- bootstack/widgets/splash.py +367 -0
- bootstack/widgets/splitview.py +482 -0
- bootstack/widgets/stacks.py +249 -0
- bootstack/widgets/statusbar.py +189 -0
- bootstack/widgets/tabs.py +425 -0
- bootstack/widgets/textarea.py +459 -0
- bootstack/widgets/textfield.py +410 -0
- bootstack/widgets/theme_toggle.py +92 -0
- bootstack/widgets/timefield.py +340 -0
- bootstack/widgets/toast.py +314 -0
- bootstack/widgets/togglegroup.py +231 -0
- bootstack/widgets/toolbar.py +347 -0
- bootstack/widgets/tooltip.py +94 -0
- bootstack/widgets/tree.py +648 -0
- bootstack/widgets/types.py +305 -0
- bootstack/widgets/window.py +307 -0
- bootstack-0.1.0.dist-info/METADATA +301 -0
- bootstack-0.1.0.dist-info/RECORD +471 -0
- bootstack-0.1.0.dist-info/WHEEL +5 -0
- bootstack-0.1.0.dist-info/entry_points.txt +2 -0
- bootstack-0.1.0.dist-info/licenses/LICENSE +21 -0
- bootstack-0.1.0.dist-info/licenses/NOTICE +10 -0
- bootstack-0.1.0.dist-info/top_level.txt +1 -0
bootstack/cli/appicon.py
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""App icon command — a visual designer for application icons.
|
|
2
|
+
|
|
3
|
+
Launches a small bootstack app that previews an `AppIcon` live as you adjust the
|
|
4
|
+
glyph, colors, and corner radius, then exports it (`.ico`/`.icns`/`.png`) or
|
|
5
|
+
copies a ready-to-paste `[build.icon]` snippet for `bootstack.toml`.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
|
|
11
|
+
from bootstack.widgets.types import IconSpec
|
|
12
|
+
from bootstack.clipboard import set_clipboard
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def add_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
16
|
+
parser = subparsers.add_parser(
|
|
17
|
+
"appicon",
|
|
18
|
+
help="Design an application icon and export it",
|
|
19
|
+
description=(
|
|
20
|
+
"Launch the app icon designer.\n\n"
|
|
21
|
+
"Pick a Bootstrap glyph, choose background and foreground colors and "
|
|
22
|
+
"a corner radius, and preview the result live at several sizes. Export "
|
|
23
|
+
"an .ico / .icns / .png for a packaged build, or copy a [build.icon] "
|
|
24
|
+
"snippet for bootstack.toml. (Find glyph names with `bootstack icons`.)"
|
|
25
|
+
),
|
|
26
|
+
)
|
|
27
|
+
parser.set_defaults(func=lambda args: run_appicon())
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Windows/Linux preview sizes (glyph-only), largest first.
|
|
31
|
+
_PREVIEW_SIZES = [96, 64, 48, 32, 16]
|
|
32
|
+
# macOS preview size (the rounded tile).
|
|
33
|
+
_MACOS_SIZE = 196
|
|
34
|
+
|
|
35
|
+
_HEX_DIGITS = set("0123456789abcdefABCDEF")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _is_hex(color: str) -> bool:
|
|
39
|
+
return (
|
|
40
|
+
color.startswith("#")
|
|
41
|
+
and len(color) in (4, 7, 9)
|
|
42
|
+
and all(ch in _HEX_DIGITS for ch in color[1:])
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def run_appicon() -> None:
|
|
47
|
+
import bootstack as bs
|
|
48
|
+
from bootstack.images import AppIcon, list_icons
|
|
49
|
+
|
|
50
|
+
state: dict = {"jobs": {}, "win_imgs": [], "mac_img": None}
|
|
51
|
+
|
|
52
|
+
with bs.App(title="App Icon Designer", size=(800, 600)) as app:
|
|
53
|
+
# Signals require the application root, so create them inside the context.
|
|
54
|
+
glyph_sig = bs.Signal("rocket")
|
|
55
|
+
bg_sig = bs.Signal("#0d6efd")
|
|
56
|
+
fg_sig = bs.Signal("#ffffff")
|
|
57
|
+
radius_sig = bs.Signal(0.22)
|
|
58
|
+
dark_sig = bs.Signal(False)
|
|
59
|
+
status_sig = bs.Signal("Adjust settings to see live preview")
|
|
60
|
+
|
|
61
|
+
with bs.Row(grow=True, horizontal="stretch"):
|
|
62
|
+
|
|
63
|
+
# ── Controls ──────────────────────────────────────────────────────
|
|
64
|
+
with bs.Column(padding=16, gap=24, vertical="stretch", horizontal_items="stretch"):
|
|
65
|
+
bs.Label("App Icon Designer", font="heading-md")
|
|
66
|
+
bs.Label(textsignal=status_sig, font="caption", accent="muted", wrap_width=225)
|
|
67
|
+
|
|
68
|
+
gl = bs.TextField(textsignal=glyph_sig, label="Glyph")
|
|
69
|
+
gl.insert_addon("button", "after", icon="grid-fill", on_click=lambda: _open_picker(gl))
|
|
70
|
+
|
|
71
|
+
bg = bs.TextField(textsignal=bg_sig, label="Background color")
|
|
72
|
+
bg.insert_addon("button", "after", icon="palette-fill", on_click=lambda: _pick(bg_sig))
|
|
73
|
+
|
|
74
|
+
fg = bs.TextField(textsignal=fg_sig, label="Foreground")
|
|
75
|
+
fg.insert_addon("button", "after", icon="palette-fill", on_click=lambda: _pick(fg_sig))
|
|
76
|
+
|
|
77
|
+
bs.NumberField(signal=radius_sig, min_value=0.00, max_value=0.50, step=0.01, label="Corner radius (macOS tile)")
|
|
78
|
+
|
|
79
|
+
with bs.Row():
|
|
80
|
+
bs.Label("Preview Dark Theme", grow=True)
|
|
81
|
+
bs.Switch(None, signal=dark_sig)
|
|
82
|
+
|
|
83
|
+
with bs.Row(gap=6, grow=True, grow_items=True, vertical_items="bottom"):
|
|
84
|
+
bs.Button("Export…", accent="primary", on_click=lambda: _export())
|
|
85
|
+
bs.Button("Copy TOML", variant="outline", on_click=lambda: _copy_toml())
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
bs.Divider(orient="vertical", vertical="stretch")
|
|
89
|
+
|
|
90
|
+
# ── Preview ───────────────────────────────────────────────────────
|
|
91
|
+
with bs.Column(padding=16, grow=True, vertical="stretch"):
|
|
92
|
+
preview = bs.Grid(rows=2, columns=1, gap=10, grow=True, horizontal="stretch")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _current_icon() -> "AppIcon":
|
|
96
|
+
# shape="auto" — exports glyph-only for .ico/.png, a tile for .icns.
|
|
97
|
+
return AppIcon(
|
|
98
|
+
glyph_sig() or "rocket",
|
|
99
|
+
background=bg_sig(),
|
|
100
|
+
foreground=fg_sig(),
|
|
101
|
+
radius=radius_sig(),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def _colors_ok() -> bool:
|
|
105
|
+
return _is_hex(bg_sig()) and _is_hex(fg_sig())
|
|
106
|
+
|
|
107
|
+
def _win_tiled(icon: "AppIcon", size: int) -> bool:
|
|
108
|
+
# Mirror the real shape='auto' .ico: glyph-only at small title-bar frames,
|
|
109
|
+
# a tile at taskbar sizes and up — so the preview matches the export.
|
|
110
|
+
return icon._tiled_for_size(size, suffix=".ico")
|
|
111
|
+
|
|
112
|
+
def _build_preview() -> None:
|
|
113
|
+
# Build the preview skeleton ONCE; updates swap images in place so the
|
|
114
|
+
# screen never flashes from tearing down and rebuilding the widgets.
|
|
115
|
+
icon = _current_icon()
|
|
116
|
+
win_imgs: list = []
|
|
117
|
+
|
|
118
|
+
with bs.Column(parent=preview, row=0, column=0, gap=4, horizontal_items="center"):
|
|
119
|
+
bs.Label("Windows / Linux", font="caption", accent="muted")
|
|
120
|
+
with bs.Row(gap=12, vertical_items="bottom"):
|
|
121
|
+
for size in _PREVIEW_SIZES:
|
|
122
|
+
tiled = _win_tiled(icon, size)
|
|
123
|
+
with bs.Column(gap=2):
|
|
124
|
+
img = bs.Label(image=icon.to_image(size, tiled=tiled))
|
|
125
|
+
bs.Label(f"{size}px", font="caption", accent="muted", vertical="bottom")
|
|
126
|
+
win_imgs.append((size, tiled, img))
|
|
127
|
+
|
|
128
|
+
with bs.Column(parent=preview, row=1, column=0, gap=4, horizontal_items="center"):
|
|
129
|
+
bs.Label("macOS", font="caption", accent="muted")
|
|
130
|
+
mac_img = bs.Label(image=icon.to_image(_MACOS_SIZE, tiled=True))
|
|
131
|
+
|
|
132
|
+
state["win_imgs"] = win_imgs
|
|
133
|
+
state["mac_img"] = mac_img
|
|
134
|
+
|
|
135
|
+
def _colors_ready() -> bool:
|
|
136
|
+
if not _colors_ok():
|
|
137
|
+
status_sig.set("Colors must be hex values (e.g. #0d6efd).")
|
|
138
|
+
return False
|
|
139
|
+
status_sig.set("Adjust the settings — the preview updates live.")
|
|
140
|
+
return True
|
|
141
|
+
|
|
142
|
+
def _update_tiles() -> None:
|
|
143
|
+
# Foreground and corner radius only affect TILED frames — the macOS tile
|
|
144
|
+
# and the larger Windows/Linux frames. The small glyph-only frames are
|
|
145
|
+
# unchanged, so they are left in place.
|
|
146
|
+
if state.get("rendering") or not _colors_ready():
|
|
147
|
+
return
|
|
148
|
+
state["rendering"] = True
|
|
149
|
+
try:
|
|
150
|
+
icon = _current_icon()
|
|
151
|
+
for size, tiled, img in state["win_imgs"]:
|
|
152
|
+
if tiled:
|
|
153
|
+
img.image = icon.to_image(size, tiled=True)
|
|
154
|
+
state["mac_img"].image = icon.to_image(_MACOS_SIZE, tiled=True)
|
|
155
|
+
finally:
|
|
156
|
+
state["rendering"] = False
|
|
157
|
+
|
|
158
|
+
def _update_all() -> None:
|
|
159
|
+
# Guard against re-entrancy: a scheduled update can fire inside a modal
|
|
160
|
+
# dialog's nested event loop (e.g. while the color picker is open), which
|
|
161
|
+
# could otherwise overlap an in-progress swap.
|
|
162
|
+
if state.get("rendering") or not _colors_ready():
|
|
163
|
+
return
|
|
164
|
+
state["rendering"] = True
|
|
165
|
+
try:
|
|
166
|
+
icon = _current_icon()
|
|
167
|
+
# Swap each image in place — no widget rebuild, no flicker. Each frame
|
|
168
|
+
# keeps the shape it was built with (small = glyph-only, large = tile).
|
|
169
|
+
for size, tiled, img in state["win_imgs"]:
|
|
170
|
+
img.image = icon.to_image(size, tiled=tiled)
|
|
171
|
+
state["mac_img"].image = icon.to_image(_MACOS_SIZE, tiled=True)
|
|
172
|
+
finally:
|
|
173
|
+
state["rendering"] = False
|
|
174
|
+
|
|
175
|
+
def _schedule(key: str, fn) -> None:
|
|
176
|
+
# Coalesce rapid signal changes (e.g. dragging the radius slider) into
|
|
177
|
+
# one update via the framework scheduler (cancelable Job, lifetime-tied).
|
|
178
|
+
job = state["jobs"].get(key)
|
|
179
|
+
if job is not None:
|
|
180
|
+
job.cancel()
|
|
181
|
+
state["jobs"][key] = app.schedule.delay(80, fn)
|
|
182
|
+
|
|
183
|
+
def _pick(target: "bs.Signal") -> None:
|
|
184
|
+
choice = bs.ask_color()
|
|
185
|
+
if choice is not None:
|
|
186
|
+
target.set(choice.hex)
|
|
187
|
+
|
|
188
|
+
def _apply_theme(*_) -> None:
|
|
189
|
+
app.theme = "bootstrap-dark" if dark_sig() else "bootstrap-light"
|
|
190
|
+
|
|
191
|
+
def _open_picker(parent) -> None:
|
|
192
|
+
all_names = list_icons()
|
|
193
|
+
pick: dict = {"job": None, "grid": None}
|
|
194
|
+
|
|
195
|
+
with bs.Window(title="Choose an icon", size=(500, 500), parent=parent) as win:
|
|
196
|
+
query = bs.Signal("")
|
|
197
|
+
count = bs.Signal("")
|
|
198
|
+
with bs.Column(padding=12, gap=8, grow=True, horizontal="stretch"):
|
|
199
|
+
bs.TextField(textsignal=query, placeholder="Search icons…", horizontal="stretch")
|
|
200
|
+
bs.Label(textsignal=count, font="caption", accent="muted")
|
|
201
|
+
scroller = bs.ScrollView(grow=True, horizontal="stretch", height=430)
|
|
202
|
+
|
|
203
|
+
def _choose(name: str) -> None:
|
|
204
|
+
glyph_sig.set(name)
|
|
205
|
+
win.close()
|
|
206
|
+
|
|
207
|
+
def _rebuild_grid(*_) -> None:
|
|
208
|
+
if pick["grid"] is not None:
|
|
209
|
+
pick["grid"].destroy()
|
|
210
|
+
q = (query() or "").strip().lower()
|
|
211
|
+
matches = [n for n in all_names if q in n] if q else all_names
|
|
212
|
+
shown = matches[:120]
|
|
213
|
+
count.set(
|
|
214
|
+
f"{len(matches):,} icons"
|
|
215
|
+
+ (" — showing first 120, refine to narrow" if len(matches) > 120 else "")
|
|
216
|
+
)
|
|
217
|
+
with bs.Grid(parent=scroller, columns=8, gap=2) as grid:
|
|
218
|
+
for name in shown:
|
|
219
|
+
spec: IconSpec = {"name": name, "size": 36}
|
|
220
|
+
bs.Button(
|
|
221
|
+
icon=spec,
|
|
222
|
+
icon_only=True,
|
|
223
|
+
variant="ghost",
|
|
224
|
+
on_click=lambda n=name: _choose(n),
|
|
225
|
+
)
|
|
226
|
+
pick["grid"] = grid
|
|
227
|
+
|
|
228
|
+
def _on_query(*_) -> None:
|
|
229
|
+
# Debounce search via the window's own scheduler (auto-cancels when
|
|
230
|
+
# the picker closes).
|
|
231
|
+
if pick["job"] is not None:
|
|
232
|
+
pick["job"].cancel()
|
|
233
|
+
pick["job"] = win.schedule.delay(150, _rebuild_grid)
|
|
234
|
+
|
|
235
|
+
query.subscribe(_on_query)
|
|
236
|
+
_rebuild_grid()
|
|
237
|
+
# Open beside the trigger — the window's top-left at the field's top-right.
|
|
238
|
+
win.show(anchor_to=parent, anchor_point="ne", window_point="nw", offset=(8, 0))
|
|
239
|
+
|
|
240
|
+
def _export() -> None:
|
|
241
|
+
if not _colors_ok():
|
|
242
|
+
status_sig.set("Fix the colors before exporting.")
|
|
243
|
+
return
|
|
244
|
+
path = bs.ask_save_file(
|
|
245
|
+
title="Export app icon",
|
|
246
|
+
default_extension=".ico",
|
|
247
|
+
initial_file=f"{glyph_sig() or 'app'}.ico",
|
|
248
|
+
file_types=[
|
|
249
|
+
("Windows icon", "*.ico"),
|
|
250
|
+
("macOS icon", "*.icns"),
|
|
251
|
+
("PNG image", "*.png"),
|
|
252
|
+
],
|
|
253
|
+
)
|
|
254
|
+
if not path:
|
|
255
|
+
return
|
|
256
|
+
try:
|
|
257
|
+
_current_icon().save(path)
|
|
258
|
+
status_sig.set(f"Exported to {path}")
|
|
259
|
+
except Exception as exc: # pragma: no cover - surfaced to the user
|
|
260
|
+
status_sig.set(f"Could not export: {exc}")
|
|
261
|
+
|
|
262
|
+
def _copy_toml() -> None:
|
|
263
|
+
snippet = (
|
|
264
|
+
"[build.icon]\n"
|
|
265
|
+
f'glyph = "{glyph_sig() or "rocket"}"\n'
|
|
266
|
+
f'background = "{bg_sig()}"\n'
|
|
267
|
+
f'foreground = "{fg_sig()}"\n'
|
|
268
|
+
f"radius = {round(radius_sig(), 3)}\n"
|
|
269
|
+
)
|
|
270
|
+
set_clipboard(snippet)
|
|
271
|
+
status_sig.set("Copied [build.icon] snippet to the clipboard.")
|
|
272
|
+
|
|
273
|
+
_build_preview()
|
|
274
|
+
# Glyph and background affect every frame; foreground and corner radius affect
|
|
275
|
+
# only the tiled frames (the macOS tile and the larger Windows/Linux frames),
|
|
276
|
+
# so those re-render just the tiles.
|
|
277
|
+
glyph_sig.subscribe(lambda *_: _schedule("all", _update_all))
|
|
278
|
+
bg_sig.subscribe(lambda *_: _schedule("all", _update_all))
|
|
279
|
+
fg_sig.subscribe(lambda *_: _schedule("tiles", _update_tiles))
|
|
280
|
+
radius_sig.subscribe(lambda *_: _schedule("tiles", _update_tiles))
|
|
281
|
+
dark_sig.subscribe(_apply_theme)
|
|
282
|
+
app.run()
|
|
283
|
+
|
|
284
|
+
if __name__ == '__main__':
|
|
285
|
+
run_appicon()
|
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)
|