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
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
import tkinter as tk
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Any, Callable, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Job:
|
|
10
|
+
"""Handle for a scheduled task.
|
|
11
|
+
|
|
12
|
+
Returned by every `Schedule` method. Call `.cancel()` to stop a pending
|
|
13
|
+
job before it fires. Truthy while active, falsy once cancelled or complete.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
__slots__ = ("_owner", "_after_id", "_active", "_discard")
|
|
17
|
+
|
|
18
|
+
def __init__(self, owner: tk.Misc, discard: Callable[[], None]) -> None:
|
|
19
|
+
self._owner = owner
|
|
20
|
+
self._after_id: Optional[str] = None
|
|
21
|
+
self._active: bool = True
|
|
22
|
+
self._discard = discard
|
|
23
|
+
|
|
24
|
+
def cancel(self) -> None:
|
|
25
|
+
"""Cancel the job if it has not already fired or been cancelled."""
|
|
26
|
+
if not self._active:
|
|
27
|
+
return
|
|
28
|
+
try:
|
|
29
|
+
if self._after_id is not None:
|
|
30
|
+
self._owner.after_cancel(self._after_id)
|
|
31
|
+
except Exception:
|
|
32
|
+
pass
|
|
33
|
+
finally:
|
|
34
|
+
self._after_id = None
|
|
35
|
+
self._active = False
|
|
36
|
+
self._discard()
|
|
37
|
+
|
|
38
|
+
def __bool__(self) -> bool:
|
|
39
|
+
return self._active
|
|
40
|
+
|
|
41
|
+
def __repr__(self) -> str:
|
|
42
|
+
state = "active" if self._active else "cancelled"
|
|
43
|
+
return f"<Job {state}>"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Schedule:
|
|
47
|
+
"""Timed task scheduler tied to a widget's lifetime.
|
|
48
|
+
|
|
49
|
+
Runs callbacks after a delay, at idle, at an absolute time, or on a repeat,
|
|
50
|
+
with automatic cleanup: all pending jobs are cancelled when the owner widget
|
|
51
|
+
is destroyed.
|
|
52
|
+
|
|
53
|
+
Usage::
|
|
54
|
+
|
|
55
|
+
sched = widget.schedule # property on every public widget
|
|
56
|
+
sched = Schedule(widget) # or construct directly
|
|
57
|
+
|
|
58
|
+
job = sched.delay(500, callback) # one-shot after delay
|
|
59
|
+
sched.idle(callback) # one-shot at next idle
|
|
60
|
+
sched.at(datetime(...), callback) # one-shot at absolute time
|
|
61
|
+
job = sched.every(1000, tick) # repeating every 1 second
|
|
62
|
+
|
|
63
|
+
job.cancel() # cancel one job
|
|
64
|
+
sched.cancel_all() # cancel everything
|
|
65
|
+
|
|
66
|
+
All jobs are automatically cancelled when `owner` is destroyed.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
owner: Widget that owns this scheduler. Its lifetime bounds all created
|
|
70
|
+
jobs.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
__slots__ = ("_owner", "_jobs")
|
|
74
|
+
|
|
75
|
+
def __init__(self, owner: Any) -> None:
|
|
76
|
+
# Accept both public wrapper widgets and the underlying widget object.
|
|
77
|
+
if hasattr(owner, "_internal"):
|
|
78
|
+
owner = owner._internal
|
|
79
|
+
self._owner: tk.Misc = owner
|
|
80
|
+
self._jobs: set[Job] = set()
|
|
81
|
+
try:
|
|
82
|
+
owner.bind("<Destroy>", self._on_destroy, add=True)
|
|
83
|
+
except Exception:
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
# ----- public API --------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
def delay(self, ms: int, fn: Callable, *args: Any, **kwargs: Any) -> Job:
|
|
89
|
+
"""Schedule `fn` to run once after `ms` milliseconds.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
ms: Delay in milliseconds (clamped to 0).
|
|
93
|
+
fn: Callable to invoke.
|
|
94
|
+
*args: Positional arguments forwarded to `fn`.
|
|
95
|
+
**kwargs: Keyword arguments forwarded to `fn`.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Job handle — call `.cancel()` to prevent execution.
|
|
99
|
+
"""
|
|
100
|
+
ms = max(0, int(ms))
|
|
101
|
+
job = Job(self._owner, lambda: self._jobs.discard(job))
|
|
102
|
+
|
|
103
|
+
def _run():
|
|
104
|
+
job._after_id = None
|
|
105
|
+
if job._active:
|
|
106
|
+
job._active = False
|
|
107
|
+
self._jobs.discard(job)
|
|
108
|
+
fn(*args, **kwargs)
|
|
109
|
+
|
|
110
|
+
job._after_id = self._owner.after(ms, _run)
|
|
111
|
+
self._jobs.add(job)
|
|
112
|
+
return job
|
|
113
|
+
|
|
114
|
+
def idle(self, fn: Callable, *args: Any, **kwargs: Any) -> Job:
|
|
115
|
+
"""Schedule `fn` to run once when the event loop is next idle.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
fn: Callable to invoke.
|
|
119
|
+
*args: Positional arguments forwarded to `fn`.
|
|
120
|
+
**kwargs: Keyword arguments forwarded to `fn`.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Job handle — call `.cancel()` to prevent execution.
|
|
124
|
+
"""
|
|
125
|
+
job = Job(self._owner, lambda: self._jobs.discard(job))
|
|
126
|
+
|
|
127
|
+
def _run():
|
|
128
|
+
job._after_id = None
|
|
129
|
+
if job._active:
|
|
130
|
+
job._active = False
|
|
131
|
+
self._jobs.discard(job)
|
|
132
|
+
fn(*args, **kwargs)
|
|
133
|
+
|
|
134
|
+
job._after_id = self._owner.after_idle(_run)
|
|
135
|
+
self._jobs.add(job)
|
|
136
|
+
return job
|
|
137
|
+
|
|
138
|
+
def at(self, when: datetime, fn: Callable, *args: Any, **kwargs: Any) -> Job:
|
|
139
|
+
"""Schedule `fn` to run at an absolute `datetime`.
|
|
140
|
+
|
|
141
|
+
If `when` is in the past the job runs as soon as possible.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
when: Target `datetime` (local time).
|
|
145
|
+
fn: Callable to invoke.
|
|
146
|
+
*args: Positional arguments forwarded to `fn`.
|
|
147
|
+
**kwargs: Keyword arguments forwarded to `fn`.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Job handle.
|
|
151
|
+
"""
|
|
152
|
+
delay_ms = max(0, int((when - datetime.now()).total_seconds() * 1000))
|
|
153
|
+
return self.delay(delay_ms, fn, *args, **kwargs)
|
|
154
|
+
|
|
155
|
+
def every(self, ms: int, fn: Callable, *args: Any, **kwargs: Any) -> Job:
|
|
156
|
+
"""Schedule `fn` to run repeatedly every `ms` milliseconds.
|
|
157
|
+
|
|
158
|
+
Compensates for callback execution time to reduce drift. If the
|
|
159
|
+
callback raises an exception the interval is stopped and the exception
|
|
160
|
+
is re-raised into the application's error handler.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
ms: Period in milliseconds (minimum 1).
|
|
164
|
+
fn: Callable to invoke on each tick.
|
|
165
|
+
*args: Positional arguments forwarded to `fn`.
|
|
166
|
+
**kwargs: Keyword arguments forwarded to `fn`.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Job handle — call `.cancel()` to stop the repeating task.
|
|
170
|
+
"""
|
|
171
|
+
period = max(1, int(ms))
|
|
172
|
+
job = Job(self._owner, lambda: self._jobs.discard(job))
|
|
173
|
+
|
|
174
|
+
def _tick():
|
|
175
|
+
if not job._active:
|
|
176
|
+
return
|
|
177
|
+
start = time.perf_counter()
|
|
178
|
+
try:
|
|
179
|
+
fn(*args, **kwargs)
|
|
180
|
+
except Exception:
|
|
181
|
+
# Stop the interval cleanly before re-raising.
|
|
182
|
+
job._active = False
|
|
183
|
+
job._after_id = None
|
|
184
|
+
self._jobs.discard(job)
|
|
185
|
+
raise
|
|
186
|
+
if job._active:
|
|
187
|
+
elapsed = int((time.perf_counter() - start) * 1000)
|
|
188
|
+
next_ms = max(0, period - elapsed)
|
|
189
|
+
try:
|
|
190
|
+
job._after_id = self._owner.after(next_ms, _tick)
|
|
191
|
+
except Exception:
|
|
192
|
+
job._active = False
|
|
193
|
+
job._after_id = None
|
|
194
|
+
self._jobs.discard(job)
|
|
195
|
+
|
|
196
|
+
job._after_id = self._owner.after(period, _tick)
|
|
197
|
+
self._jobs.add(job)
|
|
198
|
+
return job
|
|
199
|
+
|
|
200
|
+
def cancel_all(self) -> None:
|
|
201
|
+
"""Cancel all pending jobs created by this `Schedule`."""
|
|
202
|
+
for j in list(self._jobs):
|
|
203
|
+
try:
|
|
204
|
+
j.cancel()
|
|
205
|
+
except Exception:
|
|
206
|
+
pass
|
|
207
|
+
self._jobs.clear()
|
|
208
|
+
|
|
209
|
+
# ----- internal ----------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
def _on_destroy(self, event: Any = None) -> None:
|
|
212
|
+
# `<Destroy>` propagates up from descendants, so this handler fires
|
|
213
|
+
# whenever any child of the owner is destroyed. Only cancel the owner's
|
|
214
|
+
# jobs when the owner itself is going away — otherwise destroying a child
|
|
215
|
+
# widget would wipe out unrelated scheduled work on its ancestors.
|
|
216
|
+
if event is not None and getattr(event, "widget", None) is not self._owner:
|
|
217
|
+
return
|
|
218
|
+
self.cancel_all()
|
bootstack/shortcuts.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Cross-platform keyboard shortcuts — the public home for the shortcut service.
|
|
2
|
+
|
|
3
|
+
Register named shortcuts with platform-agnostic patterns (`Mod+S` becomes
|
|
4
|
+
`Ctrl+S` on Windows/Linux and `⌘S` on macOS), bind them to an app, and let
|
|
5
|
+
menus display the resolved shortcut text automatically.
|
|
6
|
+
|
|
7
|
+
Usage::
|
|
8
|
+
|
|
9
|
+
from bootstack.shortcuts import get_shortcuts
|
|
10
|
+
|
|
11
|
+
shortcuts = get_shortcuts()
|
|
12
|
+
shortcuts.register("save", "Mod+S", save_file)
|
|
13
|
+
shortcuts.bind_to(app)
|
|
14
|
+
"""
|
|
15
|
+
from bootstack._runtime.shortcuts import Shortcut, Shortcuts, get_shortcuts
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"Shortcuts",
|
|
19
|
+
"Shortcut",
|
|
20
|
+
"get_shortcuts",
|
|
21
|
+
]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
Signals for bootstack
|
|
2
|
+
=====================
|
|
3
|
+
|
|
4
|
+
Lightweight reactive state. A ``Signal[T]`` holds a value, notifies subscribers
|
|
5
|
+
when it changes, and can be bound two-way to widgets. It is backed by a Tk
|
|
6
|
+
variable, but that variable is created lazily, so the Tk surface never leaks
|
|
7
|
+
into the public API.
|
|
8
|
+
|
|
9
|
+
> The user-facing guide is ``docs/reference/signals.rst`` (teaching) and the
|
|
10
|
+
> API Reference page for ``bootstack.signals`` (lookup). This README covers the
|
|
11
|
+
> package internals; keep it in sync with ``signal.py``.
|
|
12
|
+
|
|
13
|
+
Architecture
|
|
14
|
+
- ``Signal[T]`` (public): the reactive cell — ``signal()`` to read, ``set()`` to
|
|
15
|
+
write, ``subscribe()`` to observe, ``map()`` to derive.
|
|
16
|
+
- Lazy Tk-var realization: ``Signal(...)`` creates **no** Tk variable at
|
|
17
|
+
construction, so a signal can be created at module level (before any ``App``).
|
|
18
|
+
The backing variable is realized on first bind/escape-hatch access against the
|
|
19
|
+
running interpreter, and re-realized cleanly across ``App`` lifecycles.
|
|
20
|
+
- Object mode: values that are not Tk-native (anything other than
|
|
21
|
+
``bool``/``int``/``float``/``str``/``set`` — e.g. a ``date`` or a dataclass)
|
|
22
|
+
are held as the authoritative Python object; the Tk variable is used only as a
|
|
23
|
+
change-notification bus. Native values are stored in the Tk variable directly.
|
|
24
|
+
- ``_SignalTrace`` (internal): manages the Tcl variable trace
|
|
25
|
+
(``trace_add``/``trace_remove``).
|
|
26
|
+
|
|
27
|
+
Key behaviors
|
|
28
|
+
- Read / write: ``signal()`` returns the current value; ``signal.set(v)`` updates
|
|
29
|
+
it. A ``set()`` to an equal value is a no-op (no notification).
|
|
30
|
+
- Subscriptions: ``subscribe(callback, *, immediate=False)`` returns a
|
|
31
|
+
``streams.Handle``; call ``handle.cancel()`` to unsubscribe. ``immediate=True``
|
|
32
|
+
fires the callback once with the current value at subscribe time.
|
|
33
|
+
- Derived signals: ``map(transform)`` returns a new signal that follows this one.
|
|
34
|
+
A weak reference keeps the parent from holding the derived signal alive.
|
|
35
|
+
- Robustness: the last value is cached and returned even if the backing variable
|
|
36
|
+
has been dropped (e.g. between ``App`` lifecycles).
|
|
37
|
+
- Escape hatch: ``Signal.from_variable(tk_var)`` wraps an existing Tk variable,
|
|
38
|
+
and ``signal.var`` exposes the backing variable. These are the only Tk-aware
|
|
39
|
+
paths; everything else stays Tk-free.
|
|
40
|
+
|
|
41
|
+
Usage
|
|
42
|
+
1) Quick start (works at module level — no ``App`` required)
|
|
43
|
+
```python
|
|
44
|
+
from bootstack.signals import Signal
|
|
45
|
+
|
|
46
|
+
count = Signal(0)
|
|
47
|
+
print(count()) # -> 0
|
|
48
|
+
count.set(1)
|
|
49
|
+
print(count()) # -> 1
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
2) Two-way binding to a widget
|
|
53
|
+
```python
|
|
54
|
+
import bootstack as bs
|
|
55
|
+
|
|
56
|
+
with bs.App() as app:
|
|
57
|
+
name = bs.Signal("")
|
|
58
|
+
bs.TextField(textsignal=name) # edits flow into the signal
|
|
59
|
+
bs.Label(textsignal=name.map(lambda v: f"Hello, {v}!")) # derived display
|
|
60
|
+
app.run()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
3) Subscribing and unsubscribing
|
|
64
|
+
```python
|
|
65
|
+
import bootstack as bs
|
|
66
|
+
|
|
67
|
+
with bs.App() as app:
|
|
68
|
+
flag = bs.Signal(False)
|
|
69
|
+
|
|
70
|
+
def on_flag(value):
|
|
71
|
+
print("flag:", value)
|
|
72
|
+
|
|
73
|
+
handle = flag.subscribe(on_flag, immediate=True) # prints: flag: False
|
|
74
|
+
flag.set(True) # prints: flag: True
|
|
75
|
+
handle.cancel() # stop observing
|
|
76
|
+
app.run()
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
4) Typed (object-mode) values
|
|
80
|
+
```python
|
|
81
|
+
from datetime import date
|
|
82
|
+
import bootstack as bs
|
|
83
|
+
|
|
84
|
+
with bs.App():
|
|
85
|
+
due = bs.Signal(date.today())
|
|
86
|
+
bs.DateField(signal=due) # binds the typed value
|
|
87
|
+
print(type(due())) # -> <class 'datetime.date'> (not str)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Import path
|
|
91
|
+
- ``import bootstack as bs; bs.Signal``
|
|
92
|
+
- ``from bootstack.signals import Signal``
|
|
93
|
+
|
|
94
|
+
Notes
|
|
95
|
+
- The UI toolkit is not thread-safe. Marshal cross-thread updates onto the UI
|
|
96
|
+
via the framework's scheduling, not by touching the signal from a worker
|
|
97
|
+
thread.
|
|
98
|
+
- Keep subscriber callbacks fast; offload long-running work.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Reactive signal primitives for bootstack widgets."""
|
|
2
|
+
from .signal import Signal
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"Signal",
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
# TraceOperation is an internal low-level type for variable-trace callbacks
|
|
9
|
+
# (no public Signal method exposes it). Import it from `bootstack.signals.types`
|
|
10
|
+
# if internal code needs it.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Integration helpers for using Signal with tkinter/ttk widgets.
|
|
2
|
+
|
|
3
|
+
Call `enable_widget_integration()` once early (after creating your Tk/Window)
|
|
4
|
+
to allow passing `Signal` objects directly to widget constructors and
|
|
5
|
+
`configure(...)` for options like `textvariable` and `variable`.
|
|
6
|
+
|
|
7
|
+
Without this, some environments/widgets may not coerce custom objects to the
|
|
8
|
+
required Tcl variable name automatically.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Any, Iterable, Tuple
|
|
14
|
+
|
|
15
|
+
import tkinter as tk
|
|
16
|
+
import tkinter.ttk as ttk
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _is_signal(obj: Any) -> bool:
|
|
20
|
+
try:
|
|
21
|
+
# Cheap duck-typing to avoid hard import dependency
|
|
22
|
+
m = type(obj).__module__
|
|
23
|
+
return (
|
|
24
|
+
(m.startswith("bootstack.signals") or m.startswith("bootstack.signals"))
|
|
25
|
+
and hasattr(obj, "name")
|
|
26
|
+
and hasattr(obj, "var")
|
|
27
|
+
)
|
|
28
|
+
except Exception:
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _coerce_kwargs(kwargs: dict[str, Any]) -> None:
|
|
33
|
+
for key in ("textvariable", "variable"):
|
|
34
|
+
if key in kwargs:
|
|
35
|
+
val = kwargs[key]
|
|
36
|
+
if _is_signal(val):
|
|
37
|
+
kwargs[key] = str(val)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _wrap_init(cls: type) -> None:
|
|
41
|
+
orig_init = cls.__init__ # type: ignore[attr-defined]
|
|
42
|
+
|
|
43
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None: # type: ignore[override]
|
|
44
|
+
_coerce_kwargs(kwargs)
|
|
45
|
+
orig_init(self, *args, **kwargs) # type: ignore[misc]
|
|
46
|
+
|
|
47
|
+
cls.__init__ = __init__ # type: ignore[assignment]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _wrap_configure(cls: type) -> None:
|
|
51
|
+
orig_config = cls.configure # type: ignore[attr-defined]
|
|
52
|
+
|
|
53
|
+
def configure(self, cnf: Any = None, **kwargs: Any): # type: ignore[override]
|
|
54
|
+
if isinstance(cnf, dict):
|
|
55
|
+
_coerce_kwargs(cnf)
|
|
56
|
+
_coerce_kwargs(kwargs)
|
|
57
|
+
return orig_config(self, cnf, **kwargs) # type: ignore[misc]
|
|
58
|
+
|
|
59
|
+
cls.configure = configure # type: ignore[assignment]
|
|
60
|
+
cls.config = configure # type: ignore[assignment]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _patch(classes: Iterable[type]) -> None:
|
|
64
|
+
for cls in classes:
|
|
65
|
+
_wrap_init(cls)
|
|
66
|
+
_wrap_configure(cls)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def enable_widget_integration() -> None:
|
|
70
|
+
"""Enable passing `Signal` directly to widget options.
|
|
71
|
+
|
|
72
|
+
Patches common tk/ttk widget classes so that when you pass a `Signal`
|
|
73
|
+
instance to `textvariable` or `variable` in the constructor or in
|
|
74
|
+
`configure(...)`, it is automatically converted to its Tcl name.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
ttk_classes: Tuple[type, ...] = (
|
|
78
|
+
ttk.Entry,
|
|
79
|
+
ttk.Label,
|
|
80
|
+
ttk.Button,
|
|
81
|
+
ttk.Checkbutton,
|
|
82
|
+
ttk.Radiobutton,
|
|
83
|
+
ttk.Combobox,
|
|
84
|
+
ttk.Spinbox,
|
|
85
|
+
ttk.Progressbar,
|
|
86
|
+
ttk.Scale,
|
|
87
|
+
ttk.OptionMenu,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
tk_classes: Tuple[type, ...] = (
|
|
91
|
+
tk.Entry,
|
|
92
|
+
tk.Label,
|
|
93
|
+
tk.Button,
|
|
94
|
+
tk.Checkbutton,
|
|
95
|
+
tk.Radiobutton,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
_patch(ttk_classes)
|
|
99
|
+
_patch(tk_classes)
|
|
100
|
+
|