bootstack 0.1.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) hide show
  1. bootstack/__init__.py +249 -0
  2. bootstack/__main__.py +5 -0
  3. bootstack/api/__init__.py +127 -0
  4. bootstack/api/app.py +30 -0
  5. bootstack/api/constants.py +3 -0
  6. bootstack/api/data.py +23 -0
  7. bootstack/api/dialogs.py +44 -0
  8. bootstack/api/i18n.py +17 -0
  9. bootstack/api/localization.py +16 -0
  10. bootstack/api/menu.py +7 -0
  11. bootstack/api/style.py +23 -0
  12. bootstack/api/utils.py +24 -0
  13. bootstack/api/widgets.py +137 -0
  14. bootstack/assets/__init__.py +24 -0
  15. bootstack/assets/bootstack-transparent.png +0 -0
  16. bootstack/assets/bootstack.ico +0 -0
  17. bootstack/assets/bootstack.png +0 -0
  18. bootstack/assets/elements/__init__.py +0 -0
  19. bootstack/assets/elements/badge-pill.png +0 -0
  20. bootstack/assets/elements/badge-square.png +0 -0
  21. bootstack/assets/elements/border.png +0 -0
  22. bootstack/assets/elements/button-compact.png +0 -0
  23. bootstack/assets/elements/button-default.png +0 -0
  24. bootstack/assets/elements/button-group-horizontal-after-compact.png +0 -0
  25. bootstack/assets/elements/button-group-horizontal-after-default.png +0 -0
  26. bootstack/assets/elements/button-group-horizontal-before-compact.png +0 -0
  27. bootstack/assets/elements/button-group-horizontal-before-default.png +0 -0
  28. bootstack/assets/elements/button-group-horizontal-center-compact.png +0 -0
  29. bootstack/assets/elements/button-group-horizontal-center-default.png +0 -0
  30. bootstack/assets/elements/button-group-vertical-after-compact.png +0 -0
  31. bootstack/assets/elements/button-group-vertical-after-default.png +0 -0
  32. bootstack/assets/elements/button-group-vertical-before-compact.png +0 -0
  33. bootstack/assets/elements/button-group-vertical-before-default.png +0 -0
  34. bootstack/assets/elements/button-group-vertical-center-compact.png +0 -0
  35. bootstack/assets/elements/button-group-vertical-center-default.png +0 -0
  36. bootstack/assets/elements/checkbox-checked.png +0 -0
  37. bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  38. bootstack/assets/elements/checkbox-unchecked.png +0 -0
  39. bootstack/assets/elements/field.png +0 -0
  40. bootstack/assets/elements/input-after-compact.png +0 -0
  41. bootstack/assets/elements/input-after-default.png +0 -0
  42. bootstack/assets/elements/input-before-compact.png +0 -0
  43. bootstack/assets/elements/input-before-default.png +0 -0
  44. bootstack/assets/elements/input-compact.png +0 -0
  45. bootstack/assets/elements/input-default.png +0 -0
  46. bootstack/assets/elements/list-item-separated.png +0 -0
  47. bootstack/assets/elements/list-item.png +0 -0
  48. bootstack/assets/elements/manifest.toml +480 -0
  49. bootstack/assets/elements/menu-item.png +0 -0
  50. bootstack/assets/elements/nav-button-compact.png +0 -0
  51. bootstack/assets/elements/nav-button-default.png +0 -0
  52. bootstack/assets/elements/nav-icon-button-compact.png +0 -0
  53. bootstack/assets/elements/nav-icon-button-default.png +0 -0
  54. bootstack/assets/elements/notebook-client-border.png +0 -0
  55. bootstack/assets/elements/notebook-tab-active.png +0 -0
  56. bootstack/assets/elements/notebook-tab-bar.png +0 -0
  57. bootstack/assets/elements/notebook-tab-normal.png +0 -0
  58. bootstack/assets/elements/notebook-tab-pill.png +0 -0
  59. bootstack/assets/elements/progress-bar-horizontal-striped.png +0 -0
  60. bootstack/assets/elements/progress-bar-solid.png +0 -0
  61. bootstack/assets/elements/progress-bar-thin.png +0 -0
  62. bootstack/assets/elements/progress-bar-vertical-striped.png +0 -0
  63. bootstack/assets/elements/radio-selected.png +0 -0
  64. bootstack/assets/elements/radio-unselected.png +0 -0
  65. bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  66. bootstack/assets/elements/scrollbar-vertical.png +0 -0
  67. bootstack/assets/elements/slider-handle-focus.png +0 -0
  68. bootstack/assets/elements/slider-handle.png +0 -0
  69. bootstack/assets/elements/slider-track-horizontal.png +0 -0
  70. bootstack/assets/elements/slider-track-vertical.png +0 -0
  71. bootstack/assets/elements/switch-off.png +0 -0
  72. bootstack/assets/elements/switch-on.png +0 -0
  73. bootstack/assets/elements/tabs-bar-horizontal.png +0 -0
  74. bootstack/assets/elements/tabs-bar-vertical.png +0 -0
  75. bootstack/assets/elements/tabs-pill.png +0 -0
  76. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  77. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +853 -0
  78. bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +875 -0
  79. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  80. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +853 -0
  81. bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  82. bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +853 -0
  83. bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  84. bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +853 -0
  85. bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  86. bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +875 -0
  87. bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  88. bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +853 -0
  89. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  90. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +853 -0
  91. bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  92. bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +851 -0
  93. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  94. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +842 -0
  95. bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  96. bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +841 -0
  97. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  98. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +914 -0
  99. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  100. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +842 -0
  101. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  102. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +841 -0
  103. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  104. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +841 -0
  105. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  106. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +842 -0
  107. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  108. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +842 -0
  109. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  110. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +842 -0
  111. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  112. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +842 -0
  113. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  114. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +842 -0
  115. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  116. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +842 -0
  117. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  118. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +842 -0
  119. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  120. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +842 -0
  121. bootstack/assets/themes/__init__.py +0 -0
  122. bootstack/assets/themes/amber-dark.json +32 -0
  123. bootstack/assets/themes/amber-light.json +32 -0
  124. bootstack/assets/themes/aurora-dark.json +32 -0
  125. bootstack/assets/themes/aurora-light.json +32 -0
  126. bootstack/assets/themes/bootstrap-dark.json +32 -0
  127. bootstack/assets/themes/bootstrap-light.json +32 -0
  128. bootstack/assets/themes/classic-dark.json +32 -0
  129. bootstack/assets/themes/classic-light.json +32 -0
  130. bootstack/assets/themes/docs-dark.json +32 -0
  131. bootstack/assets/themes/docs-light.json +32 -0
  132. bootstack/assets/themes/forest-dark.json +32 -0
  133. bootstack/assets/themes/forest-light.json +32 -0
  134. bootstack/assets/themes/ocean-dark.json +32 -0
  135. bootstack/assets/themes/ocean-light.json +32 -0
  136. bootstack/assets/themes/rose-dark.json +32 -0
  137. bootstack/assets/themes/rose-light.json +32 -0
  138. bootstack/assets/widgets/__init__.py +0 -0
  139. bootstack/assets/widgets/badge-default.png +0 -0
  140. bootstack/assets/widgets/badge-pill.png +0 -0
  141. bootstack/assets/widgets/border.png +0 -0
  142. bootstack/assets/widgets/button-group-horizontal-after.png +0 -0
  143. bootstack/assets/widgets/button-group-horizontal-before.png +0 -0
  144. bootstack/assets/widgets/button-group-horizontal-center.png +0 -0
  145. bootstack/assets/widgets/button-group-vertical-after.png +0 -0
  146. bootstack/assets/widgets/button-group-vertical-before.png +0 -0
  147. bootstack/assets/widgets/button-group-vertical-center.png +0 -0
  148. bootstack/assets/widgets/button.png +0 -0
  149. bootstack/assets/widgets/checkbox-checked.png +0 -0
  150. bootstack/assets/widgets/checkbox-indeterminate.png +0 -0
  151. bootstack/assets/widgets/checkbox-unchecked.png +0 -0
  152. bootstack/assets/widgets/field.png +0 -0
  153. bootstack/assets/widgets/icon-button.png +0 -0
  154. bootstack/assets/widgets/input-inner.png +0 -0
  155. bootstack/assets/widgets/input-prefix.png +0 -0
  156. bootstack/assets/widgets/input-suffix.png +0 -0
  157. bootstack/assets/widgets/input.png +0 -0
  158. bootstack/assets/widgets/list-item-focus.png +0 -0
  159. bootstack/assets/widgets/list-item-separated.png +0 -0
  160. bootstack/assets/widgets/menu-item-separated.png +0 -0
  161. bootstack/assets/widgets/notebook-client-border.png +0 -0
  162. bootstack/assets/widgets/notebook-pill-active.png +0 -0
  163. bootstack/assets/widgets/notebook-pill-inactive.png +0 -0
  164. bootstack/assets/widgets/notebook-tab-active.png +0 -0
  165. bootstack/assets/widgets/notebook-tab-border.png +0 -0
  166. bootstack/assets/widgets/notebook-tab-normal.png +0 -0
  167. bootstack/assets/widgets/notebook-underline.png +0 -0
  168. bootstack/assets/widgets/progress-bar-horizontal-default.png +0 -0
  169. bootstack/assets/widgets/progress-bar-horizontal-striped.png +0 -0
  170. bootstack/assets/widgets/progress-bar-vertical-default.png +0 -0
  171. bootstack/assets/widgets/progress-bar-vertical-striped.png +0 -0
  172. bootstack/assets/widgets/progress-trough-horizontal.png +0 -0
  173. bootstack/assets/widgets/progress-trough-vertical.png +0 -0
  174. bootstack/assets/widgets/radio-selected.png +0 -0
  175. bootstack/assets/widgets/radio-unselected.png +0 -0
  176. bootstack/assets/widgets/scrollbar-horizontal-rounded.png +0 -0
  177. bootstack/assets/widgets/scrollbar-vertical-rounded.png +0 -0
  178. bootstack/assets/widgets/separator-horizontal.png +0 -0
  179. bootstack/assets/widgets/separator-vertical.png +0 -0
  180. bootstack/assets/widgets/slider-handle-focus.png +0 -0
  181. bootstack/assets/widgets/slider-handle.png +0 -0
  182. bootstack/assets/widgets/slider-track-horizontal.png +0 -0
  183. bootstack/assets/widgets/slider-track-vertical.png +0 -0
  184. bootstack/assets/widgets/switch-off.png +0 -0
  185. bootstack/assets/widgets/switch-on.png +0 -0
  186. bootstack/assets/widgets/tabs-bar-horizontal.png +0 -0
  187. bootstack/assets/widgets/tabs-bar-vertical.png +0 -0
  188. bootstack/assets/widgets/tabs-pill.png +0 -0
  189. bootstack/cli/__init__.py +124 -0
  190. bootstack/cli/__main__.py +6 -0
  191. bootstack/cli/add.py +439 -0
  192. bootstack/cli/build.py +115 -0
  193. bootstack/cli/config.py +287 -0
  194. bootstack/cli/demo.py +1267 -0
  195. bootstack/cli/doctor.py +195 -0
  196. bootstack/cli/list_cmd.py +71 -0
  197. bootstack/cli/promote.py +120 -0
  198. bootstack/cli/pyinstaller.py +246 -0
  199. bootstack/cli/run.py +99 -0
  200. bootstack/cli/start.py +105 -0
  201. bootstack/cli/templates/__init__.py +861 -0
  202. bootstack/constants.py +325 -0
  203. bootstack/core/__init__.py +34 -0
  204. bootstack/core/capabilities/__init__.py +45 -0
  205. bootstack/core/capabilities/after.py +103 -0
  206. bootstack/core/capabilities/bind.py +154 -0
  207. bootstack/core/capabilities/bindtags.py +112 -0
  208. bootstack/core/capabilities/busy.py +61 -0
  209. bootstack/core/capabilities/clipboard.py +88 -0
  210. bootstack/core/capabilities/focus.py +118 -0
  211. bootstack/core/capabilities/grab.py +65 -0
  212. bootstack/core/capabilities/grid.py +188 -0
  213. bootstack/core/capabilities/localization.py +231 -0
  214. bootstack/core/capabilities/pack.py +119 -0
  215. bootstack/core/capabilities/place.py +92 -0
  216. bootstack/core/capabilities/selection.py +136 -0
  217. bootstack/core/capabilities/signals.py +242 -0
  218. bootstack/core/capabilities/winfo.py +315 -0
  219. bootstack/core/colorutils.py +234 -0
  220. bootstack/core/exceptions.py +95 -0
  221. bootstack/core/images.py +283 -0
  222. bootstack/core/localization/README.md +90 -0
  223. bootstack/core/localization/__init__.py +13 -0
  224. bootstack/core/localization/intl_format.py +580 -0
  225. bootstack/core/localization/msgcat.py +425 -0
  226. bootstack/core/localization/specs.py +143 -0
  227. bootstack/core/mixins/__init__.py +1 -0
  228. bootstack/core/mixins/ttk_state.py +35 -0
  229. bootstack/core/mixins/widget.py +132 -0
  230. bootstack/core/publisher.py +147 -0
  231. bootstack/core/signals/README.md +112 -0
  232. bootstack/core/signals/__init__.py +8 -0
  233. bootstack/core/signals/integration.py +100 -0
  234. bootstack/core/signals/signal.py +317 -0
  235. bootstack/core/signals/types.py +4 -0
  236. bootstack/core/validation/__init__.py +5 -0
  237. bootstack/core/validation/types.py +13 -0
  238. bootstack/core/validation/validation_result.py +17 -0
  239. bootstack/core/validation/validation_rules.py +112 -0
  240. bootstack/core/variables.py +62 -0
  241. bootstack/datasource/README.md +607 -0
  242. bootstack/datasource/__init__.py +51 -0
  243. bootstack/datasource/base.py +474 -0
  244. bootstack/datasource/file_source.py +541 -0
  245. bootstack/datasource/memory_source.py +482 -0
  246. bootstack/datasource/sqlite_source.py +453 -0
  247. bootstack/datasource/types.py +259 -0
  248. bootstack/dialogs/__init__.py +56 -0
  249. bootstack/dialogs/colorchooser.py +674 -0
  250. bootstack/dialogs/colordropper.py +257 -0
  251. bootstack/dialogs/datedialog.py +404 -0
  252. bootstack/dialogs/dialog.py +514 -0
  253. bootstack/dialogs/filterdialog.py +358 -0
  254. bootstack/dialogs/fontdialog.py +339 -0
  255. bootstack/dialogs/formdialog.py +541 -0
  256. bootstack/dialogs/message.py +489 -0
  257. bootstack/dialogs/query.py +561 -0
  258. bootstack/py.typed +1 -0
  259. bootstack/runtime/__init__.py +3 -0
  260. bootstack/runtime/app.py +879 -0
  261. bootstack/runtime/base_window.py +786 -0
  262. bootstack/runtime/events.py +399 -0
  263. bootstack/runtime/menu.py +510 -0
  264. bootstack/runtime/shortcuts.py +423 -0
  265. bootstack/runtime/tk_patch.py +31 -0
  266. bootstack/runtime/toplevel.py +131 -0
  267. bootstack/runtime/utility.py +371 -0
  268. bootstack/runtime/visual_focus.py +228 -0
  269. bootstack/runtime/window_utilities.py +1043 -0
  270. bootstack/style/__init__.py +5498 -0
  271. bootstack/style/bootstyle.py +507 -0
  272. bootstack/style/bootstyle_builder_base.py +752 -0
  273. bootstack/style/bootstyle_builder_mixed.py +93 -0
  274. bootstack/style/bootstyle_builder_tk.py +109 -0
  275. bootstack/style/bootstyle_builder_ttk.py +354 -0
  276. bootstack/style/builders/__init__.py +51 -0
  277. bootstack/style/builders/badge.py +44 -0
  278. bootstack/style/builders/button.py +453 -0
  279. bootstack/style/builders/buttongroup.py +344 -0
  280. bootstack/style/builders/calendar.py +271 -0
  281. bootstack/style/builders/checkbutton.py +95 -0
  282. bootstack/style/builders/combobox.py +112 -0
  283. bootstack/style/builders/contextmenu.py +268 -0
  284. bootstack/style/builders/entry.py +83 -0
  285. bootstack/style/builders/expander.py +171 -0
  286. bootstack/style/builders/field.py +312 -0
  287. bootstack/style/builders/frame.py +27 -0
  288. bootstack/style/builders/label.py +28 -0
  289. bootstack/style/builders/labelframe.py +41 -0
  290. bootstack/style/builders/listview.py +267 -0
  291. bootstack/style/builders/menubar.py +74 -0
  292. bootstack/style/builders/menubutton.py +408 -0
  293. bootstack/style/builders/notebook.py +316 -0
  294. bootstack/style/builders/panedwindow.py +25 -0
  295. bootstack/style/builders/progressbar.py +71 -0
  296. bootstack/style/builders/radiobutton.py +68 -0
  297. bootstack/style/builders/scale.py +66 -0
  298. bootstack/style/builders/scrollbar.py +360 -0
  299. bootstack/style/builders/separator.py +45 -0
  300. bootstack/style/builders/sidenav.py +313 -0
  301. bootstack/style/builders/sizegrip.py +15 -0
  302. bootstack/style/builders/spinbox.py +119 -0
  303. bootstack/style/builders/switch.py +67 -0
  304. bootstack/style/builders/tabitem.py +205 -0
  305. bootstack/style/builders/toolbutton.py +260 -0
  306. bootstack/style/builders/tooltip.py +26 -0
  307. bootstack/style/builders/treeview.py +269 -0
  308. bootstack/style/builders/utils.py +404 -0
  309. bootstack/style/builders_tk/__init__.py +16 -0
  310. bootstack/style/builders_tk/defaults.py +229 -0
  311. bootstack/style/element.py +173 -0
  312. bootstack/style/style.py +499 -0
  313. bootstack/style/theme_provider.py +449 -0
  314. bootstack/style/tk_patch.py +5 -0
  315. bootstack/style/token_maps.py +42 -0
  316. bootstack/style/types.py +32 -0
  317. bootstack/style/typography.py +527 -0
  318. bootstack/style/utility.py +696 -0
  319. bootstack/themes/__init__.py +12 -0
  320. bootstack/themes/standard.py +415 -0
  321. bootstack/themes/user.py +45 -0
  322. bootstack/widgets/__init__.py +53 -0
  323. bootstack/widgets/composites/__init__.py +38 -0
  324. bootstack/widgets/composites/accordion.py +385 -0
  325. bootstack/widgets/composites/appshell.py +445 -0
  326. bootstack/widgets/composites/buttongroup.py +391 -0
  327. bootstack/widgets/composites/calendar.py +914 -0
  328. bootstack/widgets/composites/compositeframe.py +282 -0
  329. bootstack/widgets/composites/contextmenu.py +1754 -0
  330. bootstack/widgets/composites/dateentry.py +261 -0
  331. bootstack/widgets/composites/dropdownbutton.py +190 -0
  332. bootstack/widgets/composites/expander.py +508 -0
  333. bootstack/widgets/composites/field.py +448 -0
  334. bootstack/widgets/composites/floodgauge.py +434 -0
  335. bootstack/widgets/composites/form.py +983 -0
  336. bootstack/widgets/composites/labeledscale.py +209 -0
  337. bootstack/widgets/composites/list/__init__.py +15 -0
  338. bootstack/widgets/composites/list/listitem.py +733 -0
  339. bootstack/widgets/composites/list/listview.py +1507 -0
  340. bootstack/widgets/composites/menubar.py +303 -0
  341. bootstack/widgets/composites/meter.py +882 -0
  342. bootstack/widgets/composites/numericentry.py +183 -0
  343. bootstack/widgets/composites/pagestack.py +330 -0
  344. bootstack/widgets/composites/passwordentry.py +149 -0
  345. bootstack/widgets/composites/pathentry.py +223 -0
  346. bootstack/widgets/composites/radiogroup.py +466 -0
  347. bootstack/widgets/composites/scrolledtext.py +388 -0
  348. bootstack/widgets/composites/scrolledtext.pyi +186 -0
  349. bootstack/widgets/composites/scrollview.py +675 -0
  350. bootstack/widgets/composites/selectbox.py +544 -0
  351. bootstack/widgets/composites/sidenav/__init__.py +24 -0
  352. bootstack/widgets/composites/sidenav/group.py +485 -0
  353. bootstack/widgets/composites/sidenav/header.py +83 -0
  354. bootstack/widgets/composites/sidenav/item.py +413 -0
  355. bootstack/widgets/composites/sidenav/separator.py +51 -0
  356. bootstack/widgets/composites/sidenav/view.py +919 -0
  357. bootstack/widgets/composites/spinnerentry.py +232 -0
  358. bootstack/widgets/composites/tableview/__init__.py +5 -0
  359. bootstack/widgets/composites/tableview/tableview.py +2254 -0
  360. bootstack/widgets/composites/tableview/types.py +169 -0
  361. bootstack/widgets/composites/tabs/__init__.py +6 -0
  362. bootstack/widgets/composites/tabs/tabitem.py +372 -0
  363. bootstack/widgets/composites/tabs/tabs.py +478 -0
  364. bootstack/widgets/composites/tabs/tabview.py +352 -0
  365. bootstack/widgets/composites/textentry.py +90 -0
  366. bootstack/widgets/composites/timeentry.py +189 -0
  367. bootstack/widgets/composites/toast.py +364 -0
  368. bootstack/widgets/composites/togglegroup.py +382 -0
  369. bootstack/widgets/composites/toolbar.py +393 -0
  370. bootstack/widgets/composites/tooltip.py +404 -0
  371. bootstack/widgets/internal/__init__.py +0 -0
  372. bootstack/widgets/internal/wrapper_base.py +304 -0
  373. bootstack/widgets/mixins/__init__.py +25 -0
  374. bootstack/widgets/mixins/configure_mixin.py +186 -0
  375. bootstack/widgets/mixins/entry_mixin.py +70 -0
  376. bootstack/widgets/mixins/font_mixin.py +346 -0
  377. bootstack/widgets/mixins/icon_mixin.py +38 -0
  378. bootstack/widgets/mixins/localization_mixin.py +255 -0
  379. bootstack/widgets/mixins/signal_mixin.py +272 -0
  380. bootstack/widgets/mixins/validation_mixin.py +204 -0
  381. bootstack/widgets/parts/__init__.py +11 -0
  382. bootstack/widgets/parts/numberentry_part.py +345 -0
  383. bootstack/widgets/parts/spinnerentry_part.py +394 -0
  384. bootstack/widgets/parts/textentry_part.py +344 -0
  385. bootstack/widgets/primitives/__init__.py +55 -0
  386. bootstack/widgets/primitives/badge.py +44 -0
  387. bootstack/widgets/primitives/button.py +89 -0
  388. bootstack/widgets/primitives/card.py +66 -0
  389. bootstack/widgets/primitives/checkbutton.py +124 -0
  390. bootstack/widgets/primitives/checktoggle.py +53 -0
  391. bootstack/widgets/primitives/combobox.py +165 -0
  392. bootstack/widgets/primitives/entry.py +98 -0
  393. bootstack/widgets/primitives/frame.py +206 -0
  394. bootstack/widgets/primitives/gridframe.py +479 -0
  395. bootstack/widgets/primitives/label.py +95 -0
  396. bootstack/widgets/primitives/labelframe.py +63 -0
  397. bootstack/widgets/primitives/menubutton.py +118 -0
  398. bootstack/widgets/primitives/notebook.py +551 -0
  399. bootstack/widgets/primitives/optionmenu.py +248 -0
  400. bootstack/widgets/primitives/packframe.py +228 -0
  401. bootstack/widgets/primitives/panedwindow.py +58 -0
  402. bootstack/widgets/primitives/progressbar.py +95 -0
  403. bootstack/widgets/primitives/radiobutton.py +115 -0
  404. bootstack/widgets/primitives/radiotoggle.py +50 -0
  405. bootstack/widgets/primitives/scale.py +85 -0
  406. bootstack/widgets/primitives/scrollbar.py +56 -0
  407. bootstack/widgets/primitives/separator.py +56 -0
  408. bootstack/widgets/primitives/sizegrip.py +47 -0
  409. bootstack/widgets/primitives/spinbox.py +91 -0
  410. bootstack/widgets/primitives/switch.py +41 -0
  411. bootstack/widgets/primitives/treeview.py +77 -0
  412. bootstack/widgets/types.py +20 -0
  413. bootstack-0.1.0a1.dist-info/METADATA +196 -0
  414. bootstack-0.1.0a1.dist-info/RECORD +419 -0
  415. bootstack-0.1.0a1.dist-info/WHEEL +5 -0
  416. bootstack-0.1.0a1.dist-info/entry_points.txt +2 -0
  417. bootstack-0.1.0a1.dist-info/licenses/LICENSE +22 -0
  418. bootstack-0.1.0a1.dist-info/licenses/NOTICE +10 -0
  419. bootstack-0.1.0a1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,453 @@
1
+ """SQLite-backed data source implementation with persistence, filtering, and pagination.
2
+
3
+ Provides a database-backed data manager that supports:
4
+ - Persistent storage using SQLite
5
+ - All features of MemoryDataSource (pagination, filtering, sorting, CRUD)
6
+ - Efficient handling of large datasets
7
+ - SQL-native filtering and sorting
8
+ - Automatic schema inference
9
+ - Data persistence across application restarts
10
+
11
+ The SqliteDataSource is ideal for:
12
+ - Large datasets that don't fit comfortably in memory
13
+ - Applications requiring data persistence
14
+ - Scenarios needing SQL query capabilities
15
+ - Multi-user or multi-session data sharing
16
+
17
+ For in-memory, lightweight scenarios, consider MemoryDataSource instead.
18
+
19
+ Example:
20
+ ```python
21
+ ds = SqliteDataSource("mydata.db", page_size=50)
22
+ ds.set_data([{"name": "Alice", "age": 30}])
23
+ ds.set_filter("age >= 25")
24
+ page = ds.get_page(0)
25
+ ```
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import csv
31
+ import sqlite3
32
+ from typing import Any, Dict, List, Optional, Union, Sequence
33
+
34
+ from bootstack.datasource.base import BaseDataSource
35
+ from bootstack.datasource.types import Primitive, Record
36
+
37
+
38
+ class SqliteDataSource(BaseDataSource):
39
+ """SQLite-backed data manager with pagination, filtering, sorting, and CRUD operations.
40
+
41
+ Provides persistent storage using SQLite database with automatic schema inference
42
+ and SQL-native filtering/sorting. Supports all operations defined in DataSourceProtocol.
43
+
44
+ Args:
45
+ name: Database file path or ":memory:" for in-memory database (default: ":memory:")
46
+ page_size: Number of records per page (default: 10)
47
+
48
+ Attributes:
49
+ conn: SQLite database connection
50
+ page_size: Current page size setting
51
+
52
+ Example:
53
+ ```python
54
+ ds = SqliteDataSource("data.db", page_size=20)
55
+ ds.set_data([{"name": "Alice", "age": 30}])
56
+ ds.set_filter("age > 25")
57
+ page = ds.get_page(0)
58
+ ```
59
+
60
+ Note:
61
+ - The database connection persists for the lifetime of the object
62
+ - Close the connection explicitly with conn.close() if needed
63
+ - Schema is inferred from first record's data types
64
+ - 'id' field is automatically set as PRIMARY KEY
65
+ - 'selected' field is added automatically for selection tracking
66
+ """
67
+
68
+ def __init__(self, name: str = ":memory:", page_size: int = 10):
69
+ """Create SQLite datasource and set initial pagination state.
70
+
71
+ Args:
72
+ name: Database file path or ':memory:' for an in-memory database.
73
+ page_size: Number of records returned per page during pagination.
74
+ """
75
+ super().__init__(page_size)
76
+ self.conn = sqlite3.connect(name)
77
+ self.conn.row_factory = sqlite3.Row
78
+ self._table = "records"
79
+ self._where = ""
80
+ self._order_by = ""
81
+ self._columns = []
82
+
83
+ @staticmethod
84
+ def _quote_identifier(name: str) -> str:
85
+ """Safely quote an identifier (column/table) for SQLite."""
86
+ text = str(name).replace('"', '""')
87
+ return f'"{text}"'
88
+
89
+ def set_data(
90
+ self,
91
+ records: Union[Sequence[Primitive], Sequence[dict[str, Any]], Sequence[Sequence[Any]]],
92
+ column_keys: Optional[Sequence[str]] = None,
93
+ ) -> "SqliteDataSource":
94
+ """Load records into database, creating table with inferred schema.
95
+
96
+ Args:
97
+ records: Sequence of dicts, primitives, or row sequences.
98
+ column_keys: Optional column names when supplying row sequences (lists/tuples).
99
+
100
+ Returns:
101
+ Self for method chaining
102
+ """
103
+ if not records:
104
+ return self
105
+
106
+ # Apply fast, in-memory friendly pragmas when using ":memory:" to speed up bulk loads
107
+ try:
108
+ if self.conn.execute("PRAGMA database_list").fetchone()[2] == ":memory:":
109
+ self.conn.execute("PRAGMA synchronous = OFF")
110
+ self.conn.execute("PRAGMA journal_mode = MEMORY")
111
+ self.conn.execute("PRAGMA temp_store = MEMORY")
112
+ except Exception:
113
+ pass
114
+
115
+ # Normalize into a common structure: either dicts or row tuples with provided keys
116
+ first = records[0]
117
+ using_dicts = isinstance(first, dict)
118
+ # Turn primitives into dicts
119
+ if not using_dicts and not isinstance(first, (list, tuple)):
120
+ records = [dict(text=str(x)) for x in records]
121
+ using_dicts = True
122
+
123
+ if using_dicts:
124
+ # Ensure helper columns
125
+ for i, record in enumerate(records):
126
+ if "id" not in record:
127
+ record["id"] = i
128
+ if "selected" not in record:
129
+ record["selected"] = 0
130
+
131
+ self._columns = list(records[0].keys())
132
+ col_types = {col: self._infer_type(records[0][col]) for col in self._columns}
133
+ rows_to_insert = [tuple(row.get(col) for col in self._columns) for row in records]
134
+ else:
135
+ # Sequence rows with provided column keys
136
+ keys = list(column_keys or [])
137
+ if not keys:
138
+ keys = [str(i) for i in range(len(first))]
139
+ need_id = "id" not in keys
140
+ need_selected = "selected" not in keys
141
+ if need_id:
142
+ keys.append("id")
143
+ if need_selected:
144
+ keys.append("selected")
145
+ self._columns = keys
146
+
147
+ # Infer types from first row (pad to keys length)
148
+ padded_first = list(first) + [None] * (len(keys) - len(first))
149
+ if need_id and "id" in keys:
150
+ padded_first[keys.index("id")] = 0
151
+ if need_selected and "selected" in keys:
152
+ padded_first[keys.index("selected")] = 0
153
+ col_types = {col: self._infer_type(padded_first[idx]) for idx, col in enumerate(keys)}
154
+
155
+ rows_to_insert = []
156
+ id_idx = keys.index("id") if "id" in keys else None
157
+ sel_idx = keys.index("selected") if "selected" in keys else None
158
+ value_len = len(keys)
159
+ for i, row in enumerate(records):
160
+ base_values = list(row[: value_len]) + [""] * (value_len - len(row))
161
+ if id_idx is not None:
162
+ base_values[id_idx] = i
163
+ if sel_idx is not None:
164
+ base_values[sel_idx] = 0
165
+ rows_to_insert.append(tuple(base_values))
166
+
167
+ col_definitions = ", ".join(
168
+ f"{self._quote_identifier(col)} {col_types[col]}" + (" PRIMARY KEY" if col == "id" else "")
169
+ for col in self._columns
170
+ )
171
+ placeholders = ", ".join("?" for _ in self._columns)
172
+
173
+ # Recreate table and bulk insert in a single transaction for speed
174
+ original_row_factory = self.conn.row_factory
175
+ try:
176
+ self.conn.row_factory = None # avoid row wrapping overhead during inserts
177
+ with self.conn:
178
+ self.conn.execute(f"DROP TABLE IF EXISTS {self._table}")
179
+ self.conn.execute(f"CREATE TABLE {self._table} ({col_definitions})")
180
+ self.conn.executemany(f"INSERT INTO {self._table} VALUES ({placeholders})", rows_to_insert)
181
+ finally:
182
+ self.conn.row_factory = original_row_factory
183
+ return self
184
+
185
+ def set_filter(self, where_sql: str = ""):
186
+ """Apply SQL WHERE clause filter.
187
+
188
+ The fragment is interpolated into the query unmodified, so the caller
189
+ is responsible for ensuring it is trusted/author-controlled. Do not
190
+ pass strings built from end-user input — use parameterized queries
191
+ directly via `self.conn` instead.
192
+ """
193
+ self._where = where_sql
194
+
195
+ def set_sort(self, order_by_sql: str = ""):
196
+ """Apply SQL ORDER BY clause for sorting.
197
+
198
+ Same trust contract as `set_filter`: the fragment is interpolated
199
+ verbatim, so it must be author-controlled, not user input.
200
+ """
201
+ self._order_by = order_by_sql
202
+
203
+ def get_page(self, page: Optional[int] = None) -> List[Dict[str, Any]]:
204
+ """Get records for specified page."""
205
+ if page is not None:
206
+ self._page = page
207
+ offset = self._page * self.page_size
208
+
209
+ query = f"SELECT * FROM {self._table}"
210
+ if self._where:
211
+ query += f" WHERE {self._where}"
212
+ if self._order_by:
213
+ query += f" ORDER BY {self._order_by}"
214
+ query += f" LIMIT {self.page_size} OFFSET {offset}"
215
+
216
+ cursor = self.conn.execute(query)
217
+ return [dict(row) for row in cursor.fetchall()]
218
+
219
+ def next_page(self) -> List[Dict[str, Any]]:
220
+ """Advance to next page and return its records."""
221
+ self._page += 1
222
+ return self.get_page()
223
+
224
+ def prev_page(self) -> List[Dict[str, Any]]:
225
+ """Move to previous page and return its records."""
226
+ self._page = max(0, self._page - 1)
227
+ return self.get_page()
228
+
229
+ def has_next_page(self) -> bool:
230
+ """Check if more pages exist after current page."""
231
+ return (self._page + 1) * self.page_size < self.total_count()
232
+
233
+ def total_count(self) -> int:
234
+ """Get total number of records matching current filter."""
235
+ query = f"SELECT COUNT(*) FROM {self._table}"
236
+ if self._where:
237
+ query += f" WHERE {self._where}"
238
+ return self.conn.execute(query).fetchone()[0]
239
+
240
+ # === CRUD OPERATIONS ===
241
+
242
+ def create_record(self, record: Dict[str, Any]) -> int:
243
+ """Create new record and return its ID."""
244
+ if "id" not in record:
245
+ record["id"] = self._generate_new_id()
246
+
247
+ if "selected" not in record:
248
+ record["selected"] = 0
249
+
250
+ # Ensure table exists (handles empty datasources)
251
+ self._ensure_table_for_record(record)
252
+
253
+ keys = list(record.keys())
254
+ cols = ", ".join(self._quote_identifier(k) for k in keys)
255
+ placeholders = ", ".join("?" for _ in keys)
256
+ values = tuple(record[col] for col in keys)
257
+
258
+ with self.conn:
259
+ self.conn.execute(f"INSERT INTO {self._table} ({cols}) VALUES ({placeholders})", values)
260
+ return record["id"]
261
+
262
+ def read_record(self, record_id: Any) -> Optional[Dict[str, Any]]:
263
+ """Retrieve single record by ID."""
264
+ cursor = self.conn.execute(f"SELECT * FROM {self._table} WHERE id = ?", (record_id,))
265
+ row = cursor.fetchone()
266
+ return dict(row) if row else None
267
+
268
+ def update_record(self, record_id: Any, updates: Dict[str, Any]) -> bool:
269
+ """Update record fields by ID."""
270
+ if not updates:
271
+ return False
272
+ set_clause = ", ".join(f"{self._quote_identifier(k)} = ?" for k in updates)
273
+ values = tuple(updates.values()) + (record_id,)
274
+ with self.conn:
275
+ cur = self.conn.execute(f"UPDATE {self._table} SET {set_clause} WHERE id = ?", values)
276
+ return cur.rowcount > 0
277
+
278
+ def delete_record(self, record_id: Any) -> bool:
279
+ """Delete record by ID."""
280
+ with self.conn:
281
+ cur = self.conn.execute(f"DELETE FROM {self._table} WHERE id = ?", (record_id,))
282
+ return cur.rowcount > 0
283
+
284
+ def _generate_new_id(self) -> int:
285
+ """Generate next available integer ID."""
286
+ try:
287
+ cursor = self.conn.execute(f"SELECT MAX(id) FROM {self._table}")
288
+ max_id = cursor.fetchone()[0]
289
+ except Exception:
290
+ max_id = 0
291
+ return (max_id or 0) + 1
292
+
293
+ # ------------------------------------------------------------------ helpers
294
+ def _ensure_table_for_record(self, record: Dict[str, Any]) -> None:
295
+ """Create the table if it does not yet exist, inferring columns from record."""
296
+ try:
297
+ # Quick existence check
298
+ self.conn.execute(f"SELECT 1 FROM {self._table} LIMIT 1")
299
+ return
300
+ except Exception:
301
+ pass
302
+
303
+ cols = list(self._columns) if self._columns else list(record.keys())
304
+ if "id" not in cols:
305
+ cols.append("id")
306
+ if "selected" not in cols:
307
+ cols.append("selected")
308
+ self._columns = cols
309
+
310
+ col_types = {}
311
+ for c in cols:
312
+ if c == "id":
313
+ col_types[c] = "INTEGER"
314
+ elif c == "selected":
315
+ col_types[c] = "INTEGER"
316
+ else:
317
+ col_types[c] = self._infer_type(record.get(c))
318
+
319
+ col_definitions = ", ".join(
320
+ f"{self._quote_identifier(col)} {col_types[col]}" + (" PRIMARY KEY" if col == "id" else "")
321
+ for col in cols
322
+ )
323
+ with self.conn:
324
+ self.conn.execute(f"CREATE TABLE IF NOT EXISTS {self._table} ({col_definitions})")
325
+
326
+ # === SELECTION ====
327
+
328
+ def select_record(self, record_id: Any) -> bool:
329
+ """Mark record as selected."""
330
+ return self._set_selected_flag(record_id, 1)
331
+
332
+ def unselect_record(self, record_id: Any) -> bool:
333
+ """Mark record as unselected."""
334
+ return self._set_selected_flag(record_id, 0)
335
+
336
+ def select_all(self, current_page_only: bool = False) -> int:
337
+ """Select all records (optionally only current page)."""
338
+ self._ensure_selected_column()
339
+ if current_page_only:
340
+ ids = [row["id"] for row in self.get_page()]
341
+ if not ids:
342
+ return 0
343
+ placeholders = ", ".join("?" for _ in ids)
344
+ query = f"UPDATE {self._table} SET selected = 1 WHERE id IN ({placeholders})"
345
+ with self.conn:
346
+ cur = self.conn.execute(query, ids)
347
+ return cur.rowcount
348
+ else:
349
+ with self.conn:
350
+ cur = self.conn.execute(f"UPDATE {self._table} SET selected = 1")
351
+ return cur.rowcount
352
+
353
+ def unselect_all(self, current_page_only: bool = False) -> int:
354
+ """Unselect all records (optionally only current page)."""
355
+ self._ensure_selected_column()
356
+ if current_page_only:
357
+ ids = [row["id"] for row in self.get_page()]
358
+ if not ids:
359
+ return 0
360
+ placeholders = ", ".join("?" for _ in ids)
361
+ query = f"UPDATE {self._table} SET selected = 0 WHERE id IN ({placeholders})"
362
+ with self.conn:
363
+ cur = self.conn.execute(query, ids)
364
+ return cur.rowcount
365
+ else:
366
+ with self.conn:
367
+ cur = self.conn.execute(f"UPDATE {self._table} SET selected = 0")
368
+ return cur.rowcount
369
+
370
+ def get_selected(self, page: Optional[int] = None) -> List[Dict[str, Any]]:
371
+ """Get selected records, optionally paginated."""
372
+ self._ensure_selected_column()
373
+ query = f"SELECT * FROM {self._table} WHERE selected = 1"
374
+
375
+ if page is not None:
376
+ offset = page * self.page_size
377
+ query += f" LIMIT {self.page_size} OFFSET {offset}"
378
+
379
+ cursor = self.conn.execute(query)
380
+ return [dict(row) for row in cursor.fetchall()]
381
+
382
+ def _ensure_selected_column(self):
383
+ """Add 'selected' column to table if it doesn't exist."""
384
+ if "selected" not in self._columns:
385
+ with self.conn:
386
+ self.conn.execute(f"ALTER TABLE {self._table} ADD COLUMN selected INTEGER DEFAULT 0")
387
+ self._columns.append("selected")
388
+
389
+ def selected_count(self) -> int:
390
+ """Get total number of selected records."""
391
+ self._ensure_selected_column()
392
+ query = f"SELECT COUNT(*) FROM {self._table} WHERE selected = 1"
393
+ return self.conn.execute(query).fetchone()[0]
394
+
395
+ def _set_selected_flag(self, record_id: Any, flag: int) -> bool:
396
+ """Set selection flag for record by ID."""
397
+ if "selected" not in self._columns:
398
+ # Add selected column if it doesn't exist
399
+ with self.conn:
400
+ self.conn.execute(f"ALTER TABLE {self._table} ADD COLUMN selected INTEGER DEFAULT 0")
401
+ self._columns.append("selected")
402
+
403
+ with self.conn:
404
+ cur = self.conn.execute(f"UPDATE {self._table} SET selected = ? WHERE id = ?", (flag, record_id))
405
+ return cur.rowcount > 0
406
+
407
+ # === DATA EXPORT ===
408
+
409
+ def export_to_csv(self, filepath: str, include_all: bool = True):
410
+ """Export records to CSV file."""
411
+ self._ensure_selected_column()
412
+ query = f"SELECT * FROM {self._table}"
413
+ if not include_all:
414
+ query += " WHERE selected = 1"
415
+
416
+ cursor = self.conn.execute(query)
417
+ rows = cursor.fetchall()
418
+
419
+ if not rows:
420
+ return
421
+
422
+ with open(filepath, mode='w', newline='', encoding='utf-8') as f:
423
+ writer = csv.DictWriter(f, fieldnames=rows[0].keys())
424
+ writer.writeheader()
425
+ for row in rows:
426
+ writer.writerow(dict(row))
427
+
428
+ def get_page_from_index(self, start_index: int, count: int) -> List[Dict[str, Any]]:
429
+ """Get records by start index and count (respects filter/sort)."""
430
+ query = f"SELECT * FROM {self._table}"
431
+ if self._where:
432
+ query += f" WHERE {self._where}"
433
+ if self._order_by:
434
+ query += f" ORDER BY {self._order_by}"
435
+ query += f" LIMIT {count} OFFSET {start_index}"
436
+ cursor = self.conn.execute(query)
437
+ return [dict(row) for row in cursor.fetchall()]
438
+
439
+ def get_distinct_values(self, column: str, limit: int = 1000) -> List[Any]:
440
+ """Get distinct values for a column.
441
+
442
+ Args:
443
+ column: Column name to get distinct values from.
444
+ limit: Maximum number of distinct values to return.
445
+
446
+ Returns:
447
+ List of distinct values sorted alphabetically.
448
+ """
449
+ quoted_col = self._quote_identifier(column)
450
+ query = f"SELECT DISTINCT {quoted_col} FROM {self._table}"
451
+ query += f" ORDER BY {quoted_col} LIMIT {limit}"
452
+ cursor = self.conn.execute(query)
453
+ return [row[0] for row in cursor.fetchall()]