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,948 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import textwrap
|
|
4
|
+
from datetime import date
|
|
5
|
+
from typing import Any, Callable, Iterable, Literal, Mapping, Sequence
|
|
6
|
+
|
|
7
|
+
from bootstack.dialogs._impl.dialog import Dialog, DialogButton, ButtonSpec, ButtonRole
|
|
8
|
+
from bootstack.dialogs._impl.query import QueryBox as _QueryBox
|
|
9
|
+
from bootstack.dialogs._impl.formdialog import FormDialog as _InternalFormDialog
|
|
10
|
+
from bootstack.dialogs._impl.datedialog import DateDialog as _DateDialog
|
|
11
|
+
from bootstack.dialogs._impl.colorchooser import ColorChooserDialog as _InternalColorChooserDialog, ColorChoice
|
|
12
|
+
from bootstack.dialogs._impl.fontdialog import FontDialog as _InternalFontDialog, FontChoice
|
|
13
|
+
from bootstack.dialogs._impl.filterdialog import FilterDialog as _InternalFilterDialog
|
|
14
|
+
from bootstack.widgets._impl.primitives.label import Label as _Label
|
|
15
|
+
from bootstack.widgets._impl.primitives.frame import Frame as _Frame
|
|
16
|
+
from bootstack._core.images import _ImageService
|
|
17
|
+
from bootstack.style.style import get_theme_color as _get_theme_color
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
# one-shot verbs
|
|
21
|
+
"alert", "confirm",
|
|
22
|
+
"ask_string", "ask_integer", "ask_float", "ask_date", "ask_date_range",
|
|
23
|
+
"ask_item", "ask_color", "ask_font", "ask_filter",
|
|
24
|
+
"ask_save_file", "ask_open_file", "ask_open_files", "ask_directory",
|
|
25
|
+
# dialog classes
|
|
26
|
+
"Dialog", "DialogButton", "FormDialog", "FilterDialog",
|
|
27
|
+
"ColorChooserDialog", "ColorChoice", "FontDialog", "FontChoice",
|
|
28
|
+
# type aliases
|
|
29
|
+
"SeverityToken",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
# Module-level convenience functions
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
_SEVERITY_ICONS: dict[str, str] = {
|
|
38
|
+
"info": "info-circle-fill",
|
|
39
|
+
"warning": "exclamation-triangle-fill",
|
|
40
|
+
"danger": "x-circle-fill",
|
|
41
|
+
"success": "check-circle-fill",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
SeverityToken = Literal["info", "warning", "danger", "success"]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _resolve_icon(
|
|
48
|
+
icon: str | None, severity: SeverityToken | None
|
|
49
|
+
) -> tuple[str | None, str]:
|
|
50
|
+
"""Return (icon_name, icon_color) based on explicit icon/severity."""
|
|
51
|
+
resolved = icon or _SEVERITY_ICONS.get(severity or "", None)
|
|
52
|
+
if severity and not icon:
|
|
53
|
+
try:
|
|
54
|
+
color = _get_theme_color(severity)
|
|
55
|
+
except Exception:
|
|
56
|
+
color = _get_theme_color("foreground")
|
|
57
|
+
else:
|
|
58
|
+
try:
|
|
59
|
+
color = _get_theme_color("foreground")
|
|
60
|
+
except Exception:
|
|
61
|
+
color = "#000000"
|
|
62
|
+
return resolved, color
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _build_message_content(
|
|
66
|
+
message: str, icon: str | None, icon_color: str, frame: Any
|
|
67
|
+
) -> None:
|
|
68
|
+
"""Build message body — used by alert() and confirm()."""
|
|
69
|
+
# Two spacers sandwich the content for vertical centering
|
|
70
|
+
_Frame(frame).pack(fill="x", expand=True)
|
|
71
|
+
container = _Frame(frame, padding=(20, 0))
|
|
72
|
+
container.pack(fill="x")
|
|
73
|
+
_Frame(frame).pack(fill="x", expand=True)
|
|
74
|
+
|
|
75
|
+
if icon:
|
|
76
|
+
try:
|
|
77
|
+
img = _ImageService.get_icon(icon, 48, icon_color)
|
|
78
|
+
icon_lbl = _Label(container, image=img)
|
|
79
|
+
icon_lbl.image = img # prevent GC
|
|
80
|
+
icon_lbl.pack(side="left", anchor="center", padx=(0, 16))
|
|
81
|
+
except Exception:
|
|
82
|
+
pass
|
|
83
|
+
msg_frame = _Frame(container)
|
|
84
|
+
msg_frame.pack(side="left", anchor="center")
|
|
85
|
+
for line in message.split("\n"):
|
|
86
|
+
wrapped = "\n".join(textwrap.wrap(line, width=60)) or line
|
|
87
|
+
_Label(msg_frame, text=wrapped).pack(anchor="w", pady=(0, 3))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def alert(
|
|
91
|
+
message: str,
|
|
92
|
+
*,
|
|
93
|
+
title: str = "",
|
|
94
|
+
ok_text: str = "OK",
|
|
95
|
+
severity: SeverityToken | None = None,
|
|
96
|
+
icon: str | None = None,
|
|
97
|
+
sound: bool | None = None,
|
|
98
|
+
parent: Any = None,
|
|
99
|
+
) -> None:
|
|
100
|
+
"""Show a message dialog with an OK button.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
message: The message text to display.
|
|
104
|
+
title: Dialog window title.
|
|
105
|
+
ok_text: Label for the OK button. Defaults to `'OK'`.
|
|
106
|
+
severity: Visual severity level — sets a colored icon and controls the
|
|
107
|
+
default sound behavior. `'warning'` and `'danger'` ring the system
|
|
108
|
+
bell by default; `'info'` and `'success'` are silent.
|
|
109
|
+
icon: Icon name override. Takes precedence over `severity`.
|
|
110
|
+
sound: Override the alert sound. `True` always rings the bell,
|
|
111
|
+
`False` always suppresses it. `None` (default) defers to
|
|
112
|
+
`severity`: rings for `'warning'` and `'danger'`, silent
|
|
113
|
+
otherwise.
|
|
114
|
+
parent: Parent widget. Defaults to the active root window.
|
|
115
|
+
"""
|
|
116
|
+
resolved_icon, icon_color = _resolve_icon(icon, severity)
|
|
117
|
+
if sound is None:
|
|
118
|
+
sound = severity in ("warning", "danger")
|
|
119
|
+
dlg = Dialog(
|
|
120
|
+
title=title or " ",
|
|
121
|
+
content_builder=lambda f: _build_message_content(message, resolved_icon, icon_color, f),
|
|
122
|
+
_raw_content=True,
|
|
123
|
+
buttons=[DialogButton(ok_text, role="secondary", result=True, default=True)],
|
|
124
|
+
alert=sound,
|
|
125
|
+
min_size=(400, 140),
|
|
126
|
+
parent=parent,
|
|
127
|
+
)
|
|
128
|
+
dlg.show()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def confirm(
|
|
132
|
+
message: str,
|
|
133
|
+
*,
|
|
134
|
+
title: str = "",
|
|
135
|
+
confirm_text: str = "Yes",
|
|
136
|
+
cancel_text: str = "No",
|
|
137
|
+
confirm_role: ButtonRole = "primary",
|
|
138
|
+
severity: SeverityToken | None = None,
|
|
139
|
+
icon: str | None = None,
|
|
140
|
+
sound: bool | None = None,
|
|
141
|
+
parent: Any = None,
|
|
142
|
+
) -> bool:
|
|
143
|
+
"""Show a confirmation dialog.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
message: The question text to display.
|
|
147
|
+
title: Dialog window title.
|
|
148
|
+
confirm_text: Label for the confirm button. Defaults to `'Yes'`.
|
|
149
|
+
cancel_text: Label for the cancel button. Defaults to `'No'`.
|
|
150
|
+
confirm_role: Button role controlling styling. Use `'danger'` for
|
|
151
|
+
destructive actions, `'primary'` (default) for standard
|
|
152
|
+
confirmations. When `'danger'`, the confirm button is not
|
|
153
|
+
focused by default so :kbd:`Enter` does not accidentally trigger it.
|
|
154
|
+
Automatically overridden to `'danger'` when `severity='danger'`
|
|
155
|
+
and to warning styling when `severity='warning'`, unless set
|
|
156
|
+
explicitly.
|
|
157
|
+
severity: Visual severity level — sets a colored icon, adjusts the
|
|
158
|
+
confirm button color for `'danger'` and `'warning'`, and
|
|
159
|
+
controls the default sound behavior.
|
|
160
|
+
icon: Icon name override. Takes precedence over `severity`.
|
|
161
|
+
sound: Override the alert sound. `True` always rings the bell,
|
|
162
|
+
`False` always suppresses it. `None` (default) defers to
|
|
163
|
+
`severity`: rings for `'warning'` and `'danger'`, silent
|
|
164
|
+
otherwise.
|
|
165
|
+
parent: Parent widget. Defaults to the active root window.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
`True` if the user clicked the confirm button, `False` otherwise.
|
|
169
|
+
"""
|
|
170
|
+
resolved_icon, icon_color = _resolve_icon(icon, severity)
|
|
171
|
+
if sound is None:
|
|
172
|
+
sound = severity in ("warning", "danger")
|
|
173
|
+
|
|
174
|
+
# Auto-derive confirm button styling from severity when not explicitly set
|
|
175
|
+
if confirm_role == "primary" and severity == "danger":
|
|
176
|
+
confirm_btn = DialogButton(confirm_text, role="danger", result=True, default=False)
|
|
177
|
+
elif confirm_role == "primary" and severity == "warning":
|
|
178
|
+
confirm_btn = DialogButton(confirm_text, role="secondary", accent="warning",
|
|
179
|
+
result=True, default=True)
|
|
180
|
+
else:
|
|
181
|
+
is_danger = confirm_role == "danger"
|
|
182
|
+
confirm_btn = DialogButton(confirm_text, role=confirm_role, result=True,
|
|
183
|
+
default=not is_danger)
|
|
184
|
+
|
|
185
|
+
dlg = Dialog(
|
|
186
|
+
title=title or " ",
|
|
187
|
+
content_builder=lambda f: _build_message_content(message, resolved_icon, icon_color, f),
|
|
188
|
+
_raw_content=True,
|
|
189
|
+
buttons=[
|
|
190
|
+
DialogButton(cancel_text, role="cancel"),
|
|
191
|
+
confirm_btn,
|
|
192
|
+
],
|
|
193
|
+
alert=sound,
|
|
194
|
+
min_size=(400, 140),
|
|
195
|
+
parent=parent,
|
|
196
|
+
)
|
|
197
|
+
dlg.show()
|
|
198
|
+
return dlg.result is True
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def ask_string(
|
|
202
|
+
prompt: str,
|
|
203
|
+
*,
|
|
204
|
+
title: str = "",
|
|
205
|
+
value: str | None = None,
|
|
206
|
+
value_format: str | None = None,
|
|
207
|
+
parent: Any = None,
|
|
208
|
+
) -> str | None:
|
|
209
|
+
"""Show a text-input dialog.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
prompt: Prompt text displayed above the input field.
|
|
213
|
+
title: Dialog window title.
|
|
214
|
+
value: Pre-filled initial value.
|
|
215
|
+
value_format: ICU format pattern for parsing and displaying the value
|
|
216
|
+
(e.g. a phone mask or postal code pattern). See :ref:`value-formats`.
|
|
217
|
+
parent: Parent widget. Defaults to the active root window.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
The entered string, or `None` if canceled.
|
|
221
|
+
"""
|
|
222
|
+
return _QueryBox.get_string(
|
|
223
|
+
prompt,
|
|
224
|
+
title=title or " ",
|
|
225
|
+
value=value,
|
|
226
|
+
value_format=value_format,
|
|
227
|
+
master=parent,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def ask_integer(
|
|
232
|
+
prompt: str,
|
|
233
|
+
*,
|
|
234
|
+
title: str = "",
|
|
235
|
+
value: int | None = None,
|
|
236
|
+
min_value: int | None = None,
|
|
237
|
+
max_value: int | None = None,
|
|
238
|
+
step: int | None = None,
|
|
239
|
+
value_format: str | None = None,
|
|
240
|
+
parent: Any = None,
|
|
241
|
+
) -> int | None:
|
|
242
|
+
"""Show an integer-input dialog with optional range validation.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
prompt: Prompt text displayed above the input field.
|
|
246
|
+
title: Dialog window title.
|
|
247
|
+
value: Pre-filled initial value.
|
|
248
|
+
min_value: Minimum accepted value.
|
|
249
|
+
max_value: Maximum accepted value.
|
|
250
|
+
step: Increment/decrement step size.
|
|
251
|
+
value_format: ICU format pattern for displaying the value
|
|
252
|
+
(e.g. `'#,##0'` for thousands separators). See :ref:`value-formats`.
|
|
253
|
+
parent: Parent widget. Defaults to the active root window.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
The entered integer, or `None` if canceled.
|
|
257
|
+
"""
|
|
258
|
+
return _QueryBox.get_integer(
|
|
259
|
+
prompt,
|
|
260
|
+
title=title or " ",
|
|
261
|
+
value=value,
|
|
262
|
+
minvalue=min_value,
|
|
263
|
+
maxvalue=max_value,
|
|
264
|
+
increment=step,
|
|
265
|
+
value_format=value_format,
|
|
266
|
+
master=parent,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def ask_float(
|
|
271
|
+
prompt: str,
|
|
272
|
+
*,
|
|
273
|
+
title: str = "",
|
|
274
|
+
value: float | None = None,
|
|
275
|
+
min_value: float | None = None,
|
|
276
|
+
max_value: float | None = None,
|
|
277
|
+
step: float | None = None,
|
|
278
|
+
value_format: str | None = None,
|
|
279
|
+
parent: Any = None,
|
|
280
|
+
) -> float | None:
|
|
281
|
+
"""Show a float-input dialog with optional range validation.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
prompt: Prompt text displayed above the input field.
|
|
285
|
+
title: Dialog window title.
|
|
286
|
+
value: Pre-filled initial value.
|
|
287
|
+
min_value: Minimum accepted value.
|
|
288
|
+
max_value: Maximum accepted value.
|
|
289
|
+
step: Increment/decrement step size.
|
|
290
|
+
value_format: ICU format pattern for displaying the value
|
|
291
|
+
(e.g. `'$#,##0.00'` for currency, `'#,##0.##'` for decimals).
|
|
292
|
+
See :ref:`value-formats`.
|
|
293
|
+
parent: Parent widget. Defaults to the active root window.
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
The entered float, or `None` if canceled.
|
|
297
|
+
"""
|
|
298
|
+
return _QueryBox.get_float(
|
|
299
|
+
prompt,
|
|
300
|
+
title=title or " ",
|
|
301
|
+
value=value,
|
|
302
|
+
minvalue=min_value,
|
|
303
|
+
maxvalue=max_value,
|
|
304
|
+
increment=step,
|
|
305
|
+
value_format=value_format,
|
|
306
|
+
master=parent,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def ask_date(
|
|
311
|
+
*,
|
|
312
|
+
title: str = "",
|
|
313
|
+
value: date | None = None,
|
|
314
|
+
min_date: date | None = None,
|
|
315
|
+
max_date: date | None = None,
|
|
316
|
+
first_weekday: int = 6,
|
|
317
|
+
disabled_dates: list[date] | None = None,
|
|
318
|
+
parent: Any = None,
|
|
319
|
+
) -> date | None:
|
|
320
|
+
"""Show a calendar date-picker dialog.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
title: Dialog window title.
|
|
324
|
+
value: Pre-selected initial date. Defaults to today.
|
|
325
|
+
min_date: Earliest selectable date (inclusive).
|
|
326
|
+
max_date: Latest selectable date (inclusive).
|
|
327
|
+
first_weekday: First day of the week. `0` = Monday, `6` = Sunday
|
|
328
|
+
(default).
|
|
329
|
+
disabled_dates: Specific dates to disable from selection.
|
|
330
|
+
parent: Parent widget. Defaults to the active root window.
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
The selected `date`, or `None` if canceled.
|
|
334
|
+
"""
|
|
335
|
+
dlg = _DateDialog(
|
|
336
|
+
master=parent,
|
|
337
|
+
title=title or " ",
|
|
338
|
+
initial_date=value,
|
|
339
|
+
min_date=min_date,
|
|
340
|
+
max_date=max_date,
|
|
341
|
+
first_weekday=first_weekday,
|
|
342
|
+
disabled_dates=disabled_dates,
|
|
343
|
+
)
|
|
344
|
+
dlg.show()
|
|
345
|
+
return dlg.result
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def ask_date_range(
|
|
349
|
+
*,
|
|
350
|
+
title: str = "",
|
|
351
|
+
start_date: date | None = None,
|
|
352
|
+
end_date: date | None = None,
|
|
353
|
+
min_date: date | None = None,
|
|
354
|
+
max_date: date | None = None,
|
|
355
|
+
first_weekday: int = 6,
|
|
356
|
+
disabled_dates: list[date] | None = None,
|
|
357
|
+
parent: Any = None,
|
|
358
|
+
) -> tuple[date, date] | None:
|
|
359
|
+
"""Show a calendar dialog for selecting a start and end date range.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
title: Dialog window title.
|
|
363
|
+
start_date: Pre-selected range start date.
|
|
364
|
+
end_date: Pre-selected range end date.
|
|
365
|
+
min_date: Earliest selectable date (inclusive).
|
|
366
|
+
max_date: Latest selectable date (inclusive).
|
|
367
|
+
first_weekday: First day of the week. `0` = Monday, `6` = Sunday
|
|
368
|
+
(default).
|
|
369
|
+
disabled_dates: Specific dates to disable from selection.
|
|
370
|
+
parent: Parent widget. Defaults to the active root window.
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
A `(start, end)` tuple of `date` objects, or `None` if canceled.
|
|
374
|
+
"""
|
|
375
|
+
dlg = _DateDialog(
|
|
376
|
+
master=parent,
|
|
377
|
+
title=title or " ",
|
|
378
|
+
selection_mode="range",
|
|
379
|
+
start_date=start_date,
|
|
380
|
+
end_date=end_date,
|
|
381
|
+
min_date=min_date,
|
|
382
|
+
max_date=max_date,
|
|
383
|
+
first_weekday=first_weekday,
|
|
384
|
+
disabled_dates=disabled_dates,
|
|
385
|
+
)
|
|
386
|
+
dlg.show()
|
|
387
|
+
return dlg.result
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def ask_item(
|
|
391
|
+
prompt: str,
|
|
392
|
+
options: list[str],
|
|
393
|
+
*,
|
|
394
|
+
title: str = "",
|
|
395
|
+
value: str | None = None,
|
|
396
|
+
parent: Any = None,
|
|
397
|
+
) -> str | None:
|
|
398
|
+
"""Show a dropdown-selection dialog.
|
|
399
|
+
|
|
400
|
+
Args:
|
|
401
|
+
prompt: Prompt text displayed above the dropdown.
|
|
402
|
+
options: List of selectable items.
|
|
403
|
+
title: Dialog window title.
|
|
404
|
+
value: Pre-selected initial item.
|
|
405
|
+
parent: Parent widget. Defaults to the active root window.
|
|
406
|
+
|
|
407
|
+
Returns:
|
|
408
|
+
The selected item string, or `None` if canceled.
|
|
409
|
+
"""
|
|
410
|
+
return _QueryBox.get_item(
|
|
411
|
+
prompt,
|
|
412
|
+
title=title or " ",
|
|
413
|
+
value=value,
|
|
414
|
+
items=options,
|
|
415
|
+
master=parent,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
# ---------------------------------------------------------------------------
|
|
420
|
+
# FormDialog — public wrapper
|
|
421
|
+
# ---------------------------------------------------------------------------
|
|
422
|
+
|
|
423
|
+
class FormDialog:
|
|
424
|
+
"""A dialog window that embeds a Form for structured data entry.
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
title: Dialog window title.
|
|
428
|
+
data: Initial data backing the form. Keys become field names.
|
|
429
|
+
items: Explicit form layout — `FormItem` / `GroupItem` / `TabsItem`
|
|
430
|
+
instances or equivalent dicts. If omitted, fields are inferred
|
|
431
|
+
from `data`.
|
|
432
|
+
col_count: Number of form columns. Default `1`.
|
|
433
|
+
min_col_width: Minimum column width in pixels.
|
|
434
|
+
on_data_change: Callback invoked on every field change, receiving
|
|
435
|
+
the current data dict.
|
|
436
|
+
on_close: Callback fired when the dialog closes by any means.
|
|
437
|
+
width: Explicit form width in pixels.
|
|
438
|
+
height: Explicit form height in pixels.
|
|
439
|
+
buttons: Footer button specs. Defaults to Cancel + OK.
|
|
440
|
+
min_size: Minimum dialog window size `(width, height)`.
|
|
441
|
+
max_size: Maximum dialog window size `(width, height)`.
|
|
442
|
+
resizable: Allow window resizing. Default `False`.
|
|
443
|
+
parent: Parent widget. Defaults to the active root window.
|
|
444
|
+
"""
|
|
445
|
+
|
|
446
|
+
def __init__(
|
|
447
|
+
self,
|
|
448
|
+
*,
|
|
449
|
+
title: str = "Form",
|
|
450
|
+
data: dict[str, Any] | None = None,
|
|
451
|
+
items: Sequence[Any] | None = None,
|
|
452
|
+
col_count: int = 1,
|
|
453
|
+
min_col_width: int | None = None,
|
|
454
|
+
on_data_change: Callable[[dict[str, Any]], Any] | None = None,
|
|
455
|
+
on_close: Callable[[], Any] | None = None,
|
|
456
|
+
width: int | None = None,
|
|
457
|
+
height: int | None = None,
|
|
458
|
+
buttons: Iterable[ButtonSpec | str] | None = None,
|
|
459
|
+
min_size: tuple[int, int] | None = None,
|
|
460
|
+
max_size: tuple[int, int] | None = None,
|
|
461
|
+
resizable: tuple[bool, bool] | bool = False,
|
|
462
|
+
parent: Any = None,
|
|
463
|
+
) -> None:
|
|
464
|
+
internal_kwargs: dict[str, Any] = {
|
|
465
|
+
"title": title,
|
|
466
|
+
"col_count": col_count,
|
|
467
|
+
"resizable": resizable,
|
|
468
|
+
}
|
|
469
|
+
if data is not None:
|
|
470
|
+
internal_kwargs["data"] = data
|
|
471
|
+
if items is not None:
|
|
472
|
+
internal_kwargs["items"] = items
|
|
473
|
+
if min_col_width is not None:
|
|
474
|
+
internal_kwargs["min_col_width"] = min_col_width
|
|
475
|
+
if on_data_change is not None:
|
|
476
|
+
internal_kwargs["on_data_change"] = on_data_change
|
|
477
|
+
if on_close is not None:
|
|
478
|
+
internal_kwargs["on_close"] = on_close
|
|
479
|
+
if width is not None:
|
|
480
|
+
internal_kwargs["width"] = width
|
|
481
|
+
if height is not None:
|
|
482
|
+
internal_kwargs["height"] = height
|
|
483
|
+
if buttons is not None:
|
|
484
|
+
internal_kwargs["buttons"] = buttons
|
|
485
|
+
if min_size is not None:
|
|
486
|
+
internal_kwargs["minsize"] = min_size
|
|
487
|
+
if max_size is not None:
|
|
488
|
+
internal_kwargs["maxsize"] = max_size
|
|
489
|
+
|
|
490
|
+
self._internal = _InternalFormDialog(parent, **internal_kwargs)
|
|
491
|
+
|
|
492
|
+
def show(
|
|
493
|
+
self,
|
|
494
|
+
*,
|
|
495
|
+
position: tuple[int, int] | None = None,
|
|
496
|
+
modal: bool | None = None,
|
|
497
|
+
) -> "FormDialog":
|
|
498
|
+
"""Display the dialog and block until it is closed.
|
|
499
|
+
|
|
500
|
+
Args:
|
|
501
|
+
position: Explicit `(x, y)` screen coordinates for the dialog.
|
|
502
|
+
Defaults to centered on the parent window.
|
|
503
|
+
modal: Override the default modality. `True` blocks the parent;
|
|
504
|
+
`False` shows a non-blocking dialog.
|
|
505
|
+
|
|
506
|
+
Returns:
|
|
507
|
+
`self` — allows chaining: `dlg = FormDialog(...).show(); dlg.result`.
|
|
508
|
+
"""
|
|
509
|
+
self._internal.show(position=position, modal=modal)
|
|
510
|
+
return self
|
|
511
|
+
|
|
512
|
+
@property
|
|
513
|
+
def result(self) -> dict[str, Any] | None:
|
|
514
|
+
"""Form data dict after closing, or `None` if canceled."""
|
|
515
|
+
return self._internal.result
|
|
516
|
+
|
|
517
|
+
@property
|
|
518
|
+
def form(self) -> Any:
|
|
519
|
+
"""The embedded `Form` widget — for advanced programmatic access."""
|
|
520
|
+
return self._internal.form
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
# ---------------------------------------------------------------------------
|
|
524
|
+
# ColorChooserDialog — public wrapper
|
|
525
|
+
# ---------------------------------------------------------------------------
|
|
526
|
+
|
|
527
|
+
class ColorChooserDialog:
|
|
528
|
+
"""A dialog for choosing a color.
|
|
529
|
+
|
|
530
|
+
The chooser has two tabs: a *Themed* tab of the active theme's color bands,
|
|
531
|
+
and a *Custom* tab with a hue/saturation spectrum and luminance slider.
|
|
532
|
+
Numeric fields on the right allow direct entry in RGB, HSL, or hex notation.
|
|
533
|
+
|
|
534
|
+
Args:
|
|
535
|
+
title: Dialog window title. Defaults to the localized "Color" string.
|
|
536
|
+
value: Pre-selected color as a hex string (e.g. `'#ff0000'`). Defaults
|
|
537
|
+
to the current theme background color.
|
|
538
|
+
parent: Parent widget. Defaults to the active root window.
|
|
539
|
+
"""
|
|
540
|
+
|
|
541
|
+
def __init__(
|
|
542
|
+
self,
|
|
543
|
+
*,
|
|
544
|
+
title: str = "",
|
|
545
|
+
value: str | None = None,
|
|
546
|
+
parent: Any = None,
|
|
547
|
+
) -> None:
|
|
548
|
+
self._internal = _InternalColorChooserDialog(
|
|
549
|
+
master=parent,
|
|
550
|
+
title=title or "color.chooser",
|
|
551
|
+
initial_color=value,
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
def show(
|
|
555
|
+
self,
|
|
556
|
+
*,
|
|
557
|
+
position: tuple[int, int] | None = None,
|
|
558
|
+
modal: bool = True,
|
|
559
|
+
) -> "ColorChooserDialog":
|
|
560
|
+
"""Display the dialog and block until it is closed.
|
|
561
|
+
|
|
562
|
+
Args:
|
|
563
|
+
position: Explicit `(x, y)` screen coordinates for the dialog.
|
|
564
|
+
Defaults to centered on the parent window.
|
|
565
|
+
modal: Block the parent window until closed. Default `True`.
|
|
566
|
+
|
|
567
|
+
Returns:
|
|
568
|
+
`self` — allows chaining: `dlg = ColorChooserDialog(...).show(); dlg.result`.
|
|
569
|
+
"""
|
|
570
|
+
self._internal.show(position=position, modal=modal)
|
|
571
|
+
return self
|
|
572
|
+
|
|
573
|
+
@property
|
|
574
|
+
def result(self) -> ColorChoice | None:
|
|
575
|
+
"""The selected color, or `None` if canceled.
|
|
576
|
+
|
|
577
|
+
Returns a `ColorChoice` namedtuple with three attributes:
|
|
578
|
+
|
|
579
|
+
- `rgb` — `(r, g, b)` tuple, each 0–255.
|
|
580
|
+
- `hsl` — `(h, s, l)` tuple: hue 0–360, saturation and luminance 0–100.
|
|
581
|
+
- `hex` — lowercase hex string, e.g. `'#ff0000'`.
|
|
582
|
+
"""
|
|
583
|
+
return self._internal.result
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
# ---------------------------------------------------------------------------
|
|
587
|
+
# FontDialog — public wrapper
|
|
588
|
+
# ---------------------------------------------------------------------------
|
|
589
|
+
|
|
590
|
+
class FontDialog:
|
|
591
|
+
"""A dialog for selecting a font family, size, weight, slant, and effects.
|
|
592
|
+
|
|
593
|
+
The dialog shows a scrollable list of font families, a size list, and
|
|
594
|
+
controls for weight (normal/bold), slant (roman/italic), underline, and
|
|
595
|
+
overstrike. A live preview panel shows sample text rendered in the
|
|
596
|
+
selected font.
|
|
597
|
+
|
|
598
|
+
Args:
|
|
599
|
+
title: Dialog window title. Defaults to the localized "Font" string.
|
|
600
|
+
default_font: Font token to show initially (e.g. `'body'`, `'code'`,
|
|
601
|
+
`'heading-lg'`). Defaults to `'body'`. See :doc:`/reference/typography`.
|
|
602
|
+
parent: Parent widget. Defaults to the active root window.
|
|
603
|
+
"""
|
|
604
|
+
|
|
605
|
+
def __init__(
|
|
606
|
+
self,
|
|
607
|
+
*,
|
|
608
|
+
title: str = "",
|
|
609
|
+
default_font: str = "body",
|
|
610
|
+
parent: Any = None,
|
|
611
|
+
) -> None:
|
|
612
|
+
self._internal = _InternalFontDialog(
|
|
613
|
+
title=title or "font.selector",
|
|
614
|
+
master=parent,
|
|
615
|
+
default_font=default_font,
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
def show(
|
|
619
|
+
self,
|
|
620
|
+
*,
|
|
621
|
+
position: tuple[int, int] | None = None,
|
|
622
|
+
modal: bool | None = None,
|
|
623
|
+
) -> "FontDialog":
|
|
624
|
+
"""Display the dialog and block until it is closed.
|
|
625
|
+
|
|
626
|
+
Args:
|
|
627
|
+
position: Explicit `(x, y)` screen coordinates for the dialog.
|
|
628
|
+
Defaults to centered on screen (the font dialog sizes itself on open).
|
|
629
|
+
modal: Override the default modality. `True` blocks the parent;
|
|
630
|
+
`False` shows a non-blocking dialog.
|
|
631
|
+
|
|
632
|
+
Returns:
|
|
633
|
+
`self` — allows chaining: `dlg = FontDialog(...).show(); dlg.result`.
|
|
634
|
+
"""
|
|
635
|
+
self._internal.show(position=position, modal=modal)
|
|
636
|
+
return self
|
|
637
|
+
|
|
638
|
+
@property
|
|
639
|
+
def result(self) -> FontChoice | None:
|
|
640
|
+
"""The selected font, or `None` if canceled.
|
|
641
|
+
|
|
642
|
+
Returns a `FontChoice` namedtuple with six attributes:
|
|
643
|
+
|
|
644
|
+
- `family` — font family name (str).
|
|
645
|
+
- `size` — point size (int).
|
|
646
|
+
- `weight` — `'normal'` or `'bold'`.
|
|
647
|
+
- `slant` — `'roman'` or `'italic'`.
|
|
648
|
+
- `underline` — `True` if underlined.
|
|
649
|
+
- `overstrike` — `True` if struck through.
|
|
650
|
+
"""
|
|
651
|
+
return self._internal.result
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
# ---------------------------------------------------------------------------
|
|
655
|
+
# FilterDialog — public wrapper
|
|
656
|
+
# ---------------------------------------------------------------------------
|
|
657
|
+
|
|
658
|
+
class FilterDialog:
|
|
659
|
+
"""A dialog for selecting multiple items from a list.
|
|
660
|
+
|
|
661
|
+
Displays a scrollable list of checkboxes. Optionally includes a search
|
|
662
|
+
box that narrows visible items and a "Select All" checkbox.
|
|
663
|
+
|
|
664
|
+
Args:
|
|
665
|
+
title: Dialog window title.
|
|
666
|
+
items: Items to display. Each item is a string or a dict with keys:
|
|
667
|
+
|
|
668
|
+
- `text` (str): Display label (required for dicts).
|
|
669
|
+
- `value` (Any): Value returned when selected. Defaults to `text`.
|
|
670
|
+
- `selected` (bool): Initial check state. Defaults to `False`.
|
|
671
|
+
enable_search: Include a search box that filters items by text.
|
|
672
|
+
Defaults to `False`.
|
|
673
|
+
enable_select_all: Include a "Select All" checkbox. Defaults to `False`.
|
|
674
|
+
parent: Parent widget. Defaults to the active root window.
|
|
675
|
+
"""
|
|
676
|
+
|
|
677
|
+
def __init__(
|
|
678
|
+
self,
|
|
679
|
+
*,
|
|
680
|
+
title: str = "",
|
|
681
|
+
items: list[str | dict[str, Any]] | None = None,
|
|
682
|
+
enable_search: bool = False,
|
|
683
|
+
enable_select_all: bool = False,
|
|
684
|
+
parent: Any = None,
|
|
685
|
+
) -> None:
|
|
686
|
+
self._title = title
|
|
687
|
+
self._items = items or []
|
|
688
|
+
self._enable_search = enable_search
|
|
689
|
+
self._enable_select_all = enable_select_all
|
|
690
|
+
self._parent = parent
|
|
691
|
+
self._result: list[Any] | None = None
|
|
692
|
+
|
|
693
|
+
def show(
|
|
694
|
+
self,
|
|
695
|
+
*,
|
|
696
|
+
position: tuple[int, int] | None = None,
|
|
697
|
+
modal: bool | None = None,
|
|
698
|
+
) -> "FilterDialog":
|
|
699
|
+
"""Display the dialog and block until it is closed.
|
|
700
|
+
|
|
701
|
+
Args:
|
|
702
|
+
position: Explicit `(x, y)` screen coordinates for the dialog.
|
|
703
|
+
Defaults to centered on the parent window.
|
|
704
|
+
modal: Override the default modality. `True` blocks the parent;
|
|
705
|
+
`False` shows a non-blocking dialog.
|
|
706
|
+
|
|
707
|
+
Returns:
|
|
708
|
+
`self` — allows chaining: `dlg = FilterDialog(...).show(); dlg.result`.
|
|
709
|
+
"""
|
|
710
|
+
dlg = _InternalFilterDialog(
|
|
711
|
+
master=self._parent,
|
|
712
|
+
title=self._title or "Filter",
|
|
713
|
+
items=self._items,
|
|
714
|
+
enable_search=self._enable_search,
|
|
715
|
+
enable_select_all=self._enable_select_all,
|
|
716
|
+
)
|
|
717
|
+
dlg.show(position=position, modal=modal)
|
|
718
|
+
self._result = dlg.result
|
|
719
|
+
return self
|
|
720
|
+
|
|
721
|
+
@property
|
|
722
|
+
def result(self) -> list[Any] | None:
|
|
723
|
+
"""List of selected values after closing, or `None` if canceled."""
|
|
724
|
+
return self._result
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
# ---------------------------------------------------------------------------
|
|
728
|
+
# Convenience functions — color, font, filter
|
|
729
|
+
# ---------------------------------------------------------------------------
|
|
730
|
+
|
|
731
|
+
def ask_color(
|
|
732
|
+
*,
|
|
733
|
+
title: str = "",
|
|
734
|
+
value: str | None = None,
|
|
735
|
+
parent: Any = None,
|
|
736
|
+
) -> ColorChoice | None:
|
|
737
|
+
"""Show a color chooser dialog.
|
|
738
|
+
|
|
739
|
+
Args:
|
|
740
|
+
title: Dialog window title.
|
|
741
|
+
value: Pre-selected color as a hex string (e.g. `'#ff0000'`). Defaults
|
|
742
|
+
to the current theme background color.
|
|
743
|
+
parent: Parent widget. Defaults to the active root window.
|
|
744
|
+
|
|
745
|
+
Returns:
|
|
746
|
+
A `ColorChoice` with `rgb`, `hsl`, and `hex` attributes, or
|
|
747
|
+
`None` if canceled.
|
|
748
|
+
"""
|
|
749
|
+
dlg = ColorChooserDialog(title=title, value=value, parent=parent)
|
|
750
|
+
dlg.show()
|
|
751
|
+
return dlg.result
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
def ask_font(
|
|
755
|
+
*,
|
|
756
|
+
title: str = "",
|
|
757
|
+
default_font: str = "body",
|
|
758
|
+
parent: Any = None,
|
|
759
|
+
) -> FontChoice | None:
|
|
760
|
+
"""Show a font selector dialog.
|
|
761
|
+
|
|
762
|
+
Args:
|
|
763
|
+
title: Dialog window title.
|
|
764
|
+
default_font: Font token to show initially (e.g. `'body'`, `'code'`,
|
|
765
|
+
`'heading-lg'`). Defaults to `'body'`. See :doc:`/reference/typography`.
|
|
766
|
+
parent: Parent widget. Defaults to the active root window.
|
|
767
|
+
|
|
768
|
+
Returns:
|
|
769
|
+
A `FontChoice` with `family`, `size`, `weight`, `slant`,
|
|
770
|
+
`underline`, and `overstrike` attributes, or `None` if canceled.
|
|
771
|
+
"""
|
|
772
|
+
dlg = FontDialog(title=title, default_font=default_font, parent=parent)
|
|
773
|
+
dlg.show()
|
|
774
|
+
return dlg.result
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
def ask_filter(
|
|
778
|
+
items: list[str | dict[str, Any]],
|
|
779
|
+
*,
|
|
780
|
+
title: str = "",
|
|
781
|
+
enable_search: bool = False,
|
|
782
|
+
enable_select_all: bool = False,
|
|
783
|
+
parent: Any = None,
|
|
784
|
+
) -> list[Any] | None:
|
|
785
|
+
"""Show a multi-select filter dialog.
|
|
786
|
+
|
|
787
|
+
Args:
|
|
788
|
+
items: Items to display. Each item is a string or a dict with keys:
|
|
789
|
+
|
|
790
|
+
- `text` (str): Display label (required for dicts).
|
|
791
|
+
- `value` (Any): Value returned when selected. Defaults to `text`.
|
|
792
|
+
- `selected` (bool): Initial check state. Defaults to `False`.
|
|
793
|
+
title: Dialog window title.
|
|
794
|
+
enable_search: Include a search box that filters items by text.
|
|
795
|
+
Defaults to `False`.
|
|
796
|
+
enable_select_all: Include a "Select All" checkbox. Defaults to `False`.
|
|
797
|
+
parent: Parent widget. Defaults to the active root window.
|
|
798
|
+
|
|
799
|
+
Returns:
|
|
800
|
+
A list of selected values, or `None` if canceled.
|
|
801
|
+
"""
|
|
802
|
+
dlg = FilterDialog(
|
|
803
|
+
title=title,
|
|
804
|
+
items=items,
|
|
805
|
+
enable_search=enable_search,
|
|
806
|
+
enable_select_all=enable_select_all,
|
|
807
|
+
parent=parent,
|
|
808
|
+
)
|
|
809
|
+
dlg.show()
|
|
810
|
+
return dlg.result
|
|
811
|
+
|
|
812
|
+
|
|
813
|
+
# File-system dialogs (native OS choosers) ----------------------------------
|
|
814
|
+
|
|
815
|
+
def _run_file_dialog(_name: str, parent: Any, **options: Any) -> Any:
|
|
816
|
+
"""Call a `tkinter.filedialog` function with cleaned options."""
|
|
817
|
+
import tkinter
|
|
818
|
+
from tkinter import filedialog
|
|
819
|
+
|
|
820
|
+
opts = {k: v for k, v in options.items() if v not in (None, "")}
|
|
821
|
+
master = parent if parent is not None else tkinter._default_root
|
|
822
|
+
if master is not None:
|
|
823
|
+
opts["parent"] = master
|
|
824
|
+
return getattr(filedialog, _name)(**opts)
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
def _coerce_file_types(file_types: Any) -> Any:
|
|
828
|
+
if not file_types:
|
|
829
|
+
return None
|
|
830
|
+
return [tuple(ft) for ft in file_types]
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
def ask_save_file(
|
|
834
|
+
*,
|
|
835
|
+
title: str = "",
|
|
836
|
+
initial_file: str = "",
|
|
837
|
+
initial_dir: str = "",
|
|
838
|
+
file_types: list[tuple[str, str]] | None = None,
|
|
839
|
+
default_extension: str = "",
|
|
840
|
+
parent: Any = None,
|
|
841
|
+
) -> str | None:
|
|
842
|
+
"""Show a native save dialog and return the chosen path.
|
|
843
|
+
|
|
844
|
+
Args:
|
|
845
|
+
title: Dialog window title.
|
|
846
|
+
initial_file: Suggested file name.
|
|
847
|
+
initial_dir: Directory to open in. Defaults to the last used location.
|
|
848
|
+
file_types: Selectable file types as `(label, pattern)` pairs, for
|
|
849
|
+
example `[('PNG image', '*.png'), ('All files', '*.*')]`.
|
|
850
|
+
default_extension: Extension appended when the user omits one (for
|
|
851
|
+
example `'.png'`).
|
|
852
|
+
parent: Parent widget. Defaults to the active root window.
|
|
853
|
+
|
|
854
|
+
Returns:
|
|
855
|
+
The chosen file path, or `None` if canceled.
|
|
856
|
+
"""
|
|
857
|
+
path = _run_file_dialog(
|
|
858
|
+
"asksaveasfilename",
|
|
859
|
+
parent,
|
|
860
|
+
title=title,
|
|
861
|
+
initialfile=initial_file,
|
|
862
|
+
initialdir=initial_dir,
|
|
863
|
+
filetypes=_coerce_file_types(file_types),
|
|
864
|
+
defaultextension=default_extension,
|
|
865
|
+
)
|
|
866
|
+
return path or None
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
def ask_open_file(
|
|
870
|
+
*,
|
|
871
|
+
title: str = "",
|
|
872
|
+
initial_dir: str = "",
|
|
873
|
+
file_types: list[tuple[str, str]] | None = None,
|
|
874
|
+
parent: Any = None,
|
|
875
|
+
) -> str | None:
|
|
876
|
+
"""Show a native open dialog for a single file and return its path.
|
|
877
|
+
|
|
878
|
+
Args:
|
|
879
|
+
title: Dialog window title.
|
|
880
|
+
initial_dir: Directory to open in. Defaults to the last used location.
|
|
881
|
+
file_types: Selectable file types as `(label, pattern)` pairs, for
|
|
882
|
+
example `[('Images', '*.png *.jpg'), ('All files', '*.*')]`.
|
|
883
|
+
parent: Parent widget. Defaults to the active root window.
|
|
884
|
+
|
|
885
|
+
Returns:
|
|
886
|
+
The chosen file path, or `None` if canceled.
|
|
887
|
+
"""
|
|
888
|
+
path = _run_file_dialog(
|
|
889
|
+
"askopenfilename",
|
|
890
|
+
parent,
|
|
891
|
+
title=title,
|
|
892
|
+
initialdir=initial_dir,
|
|
893
|
+
filetypes=_coerce_file_types(file_types),
|
|
894
|
+
)
|
|
895
|
+
return path or None
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
def ask_open_files(
|
|
899
|
+
*,
|
|
900
|
+
title: str = "",
|
|
901
|
+
initial_dir: str = "",
|
|
902
|
+
file_types: list[tuple[str, str]] | None = None,
|
|
903
|
+
parent: Any = None,
|
|
904
|
+
) -> list[str]:
|
|
905
|
+
"""Show a native open dialog allowing several files.
|
|
906
|
+
|
|
907
|
+
Args:
|
|
908
|
+
title: Dialog window title.
|
|
909
|
+
initial_dir: Directory to open in. Defaults to the last used location.
|
|
910
|
+
file_types: Selectable file types as `(label, pattern)` pairs.
|
|
911
|
+
parent: Parent widget. Defaults to the active root window.
|
|
912
|
+
|
|
913
|
+
Returns:
|
|
914
|
+
The chosen file paths, or an empty list if canceled.
|
|
915
|
+
"""
|
|
916
|
+
paths = _run_file_dialog(
|
|
917
|
+
"askopenfilenames",
|
|
918
|
+
parent,
|
|
919
|
+
title=title,
|
|
920
|
+
initialdir=initial_dir,
|
|
921
|
+
filetypes=_coerce_file_types(file_types),
|
|
922
|
+
)
|
|
923
|
+
return list(paths) if isinstance(paths, (list, tuple)) else []
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
def ask_directory(
|
|
927
|
+
*,
|
|
928
|
+
title: str = "",
|
|
929
|
+
initial_dir: str = "",
|
|
930
|
+
parent: Any = None,
|
|
931
|
+
) -> str | None:
|
|
932
|
+
"""Show a native folder chooser and return the chosen directory.
|
|
933
|
+
|
|
934
|
+
Args:
|
|
935
|
+
title: Dialog window title.
|
|
936
|
+
initial_dir: Directory to open in. Defaults to the last used location.
|
|
937
|
+
parent: Parent widget. Defaults to the active root window.
|
|
938
|
+
|
|
939
|
+
Returns:
|
|
940
|
+
The chosen directory path, or `None` if canceled.
|
|
941
|
+
"""
|
|
942
|
+
path = _run_file_dialog(
|
|
943
|
+
"askdirectory",
|
|
944
|
+
parent,
|
|
945
|
+
title=title,
|
|
946
|
+
initialdir=initial_dir,
|
|
947
|
+
)
|
|
948
|
+
return path or None
|