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,541 @@
1
+ """File-based datasource with support for CSV, JSON, and lazy loading strategies.
2
+
3
+ Provides file-backed data storage with multiple loading strategies optimized for
4
+ different file sizes. Supports extensive transformation pipelines for data cleaning
5
+ and preprocessing.
6
+
7
+ Supported Formats (Built-in):
8
+ - CSV: Comma-separated values
9
+ - TSV: Tab-separated values
10
+ - JSON: Standard JSON arrays or objects
11
+ - JSONL/NDJSON: Line-delimited JSON (one record per line)
12
+
13
+ Loading Strategies:
14
+ - Eager: Load all data into memory at once (fast access, high memory)
15
+ - Lazy: Load data on-demand per page (slow access, low memory)
16
+ - Chunked: Load in configurable batches (balanced approach)
17
+ - Hybrid: Index in memory, lazy-load records (optimized balance)
18
+ - Auto: Automatically select strategy based on file size
19
+
20
+ Transformation Pipeline:
21
+ - Column renaming
22
+ - Type conversions
23
+ - Custom transformation functions per column
24
+ - Row-level filtering during load
25
+ - Row-level transformations
26
+ - Default values for missing data
27
+
28
+ Large File Optimization:
29
+ - Streaming parsing for minimal memory usage
30
+ - Threading for non-blocking loads
31
+ - Progress callbacks for UI updates
32
+ - Automatic strategy selection based on file size
33
+
34
+ Example:
35
+ ```python
36
+ ds = FileDataSource("data.csv")
37
+ ds.load()
38
+ page = ds.get_page(0)
39
+ ```
40
+ """
41
+
42
+ from __future__ import annotations
43
+
44
+ import csv
45
+ import json
46
+ import os
47
+ import threading
48
+ from dataclasses import dataclass, field
49
+ from pathlib import Path
50
+ from typing import (
51
+ Any, Callable, Dict, Iterator, List, Literal, Optional, Union
52
+ )
53
+
54
+ from bootstack.datasource.memory_source import MemoryDataSource
55
+ from bootstack.datasource.types import Record, Primitive
56
+
57
+
58
+ @dataclass
59
+ class FileSourceConfig:
60
+ """Configuration for file datasource loading and transformations.
61
+
62
+ Controls how files are parsed, loaded, and transformed. Provides extensive
63
+ customization for handling various data formats and scenarios.
64
+
65
+ Attributes:
66
+ file_format: Format type - auto-detected from extension if 'auto'.
67
+ encoding: Character encoding for reading the file.
68
+ delimiter: Field separator (None = auto-detect: ',' for CSV, tab for TSV).
69
+ quotechar: Quote character for fields containing the delimiter.
70
+ skip_rows: Number of header rows to skip.
71
+ header_row: Row index containing column names (None = no header).
72
+ has_header: Whether the first row contains column names.
73
+ json_lines: True for line-delimited JSON (JSONL/NDJSON format).
74
+ json_orient: Pandas-like orientation for JSON arrays.
75
+ column_renames: Map {old_name: new_name} for renaming columns.
76
+ column_types: Map {column: type} for type conversions.
77
+ column_transforms: Map {column: func} for custom transformations.
78
+ columns_to_load: List of columns to load (None = all columns).
79
+ default_values: Map {column: value} for missing/null values.
80
+ row_filter: Function(row_dict) -> bool to filter rows during load.
81
+ row_transform: Function(row_dict) -> row_dict for row-level transforms.
82
+ loading_strategy: How to load file ('eager', 'lazy', 'chunked', 'hybrid', 'auto').
83
+ chunk_size: Rows per chunk for chunked/lazy loading.
84
+ max_memory_rows: Threshold for auto-switching loading strategies.
85
+ use_threading: Load file in background thread (non-blocking).
86
+ progress_callback: Function(current, total) called during load.
87
+ on_complete: Function() called when loading completes.
88
+ on_error: Function(exception) called if loading fails.
89
+
90
+ Example:
91
+ ```python
92
+ config = FileSourceConfig(
93
+ column_renames={'emp_id': 'id'},
94
+ column_types={'age': int},
95
+ )
96
+ ```
97
+ """
98
+
99
+ file_format: Literal['auto', 'csv', 'tsv', 'json', 'jsonl'] = 'auto'
100
+ encoding: str = 'utf-8'
101
+ delimiter: Optional[str] = None
102
+ quotechar: str = '"'
103
+ skip_rows: int = 0
104
+ header_row: Optional[int] = 0
105
+ has_header: bool = True
106
+ json_lines: bool = False
107
+ json_orient: Literal['records', 'index', 'columns', 'values'] = 'records'
108
+ column_renames: Optional[Dict[str, str]] = None
109
+ column_types: Optional[Dict[str, type]] = None
110
+ column_transforms: Optional[Dict[str, Callable[[Any], Any]]] = None
111
+ columns_to_load: Optional[List[str]] = None
112
+ default_values: Optional[Dict[str, Any]] = None
113
+ row_filter: Optional[Callable[[Dict[str, Any]], bool]] = None
114
+ row_transform: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None
115
+ loading_strategy: Literal['eager', 'lazy', 'chunked', 'hybrid', 'auto'] = 'auto'
116
+ chunk_size: int = 10000
117
+ max_memory_rows: int = 100000
118
+ use_threading: bool = False
119
+ progress_callback: Optional[Callable[[int, int], None]] = None
120
+ on_complete: Optional[Callable[[], None]] = None
121
+ on_error: Optional[Callable[[Exception], None]] = None
122
+
123
+
124
+ class FileDataSource(MemoryDataSource):
125
+ """File-based datasource with MemoryDataSource API and advanced loading strategies.
126
+
127
+ Extends MemoryDataSource to load data from files (CSV, JSON, JSONL) with support
128
+ for large files via multiple loading strategies. Provides extensive transformation
129
+ pipeline for data preprocessing.
130
+
131
+ The datasource automatically selects optimal loading strategy based on file size
132
+ and configuration. Supports background loading with progress callbacks for UI
133
+ integration.
134
+
135
+ Args:
136
+ filepath: Path to data file
137
+ config: FileSourceConfig object (uses defaults if None)
138
+ page_size: Records per page for pagination (default: 10)
139
+
140
+ Attributes:
141
+ filepath: Path object for the data file
142
+ config: Active configuration
143
+ is_loaded: Whether file has been loaded
144
+
145
+ Loading Strategies:
146
+ Eager: Load entire file into memory
147
+ - Fastest access after initial load
148
+ - High memory usage
149
+ - Best for: Small files (< 100k rows)
150
+
151
+ Lazy: Load data on-demand per page
152
+ - Slowest access (re-parses on each request)
153
+ - Minimal memory usage
154
+ - Best for: Very large files (> 1M rows)
155
+
156
+ Chunked: Load file in batches
157
+ - Balanced performance and memory
158
+ - Shows progress during load
159
+ - Best for: Medium files (100k-500k rows)
160
+
161
+ Hybrid: Index in memory, lazy-load records
162
+ - Fast filtering/sorting
163
+ - Moderate memory usage
164
+ - Best for: Large files (500k-1M rows)
165
+
166
+ Auto: Automatically select based on file size
167
+ - < 100k rows: Eager
168
+ - 100k-500k: Chunked
169
+ - > 500k: Hybrid
170
+
171
+ Example:
172
+ ```python
173
+ ds = FileDataSource("data.csv")
174
+ ds.load()
175
+ ds.set_filter("age > 25")
176
+ page = ds.get_page(0)
177
+ ```
178
+
179
+ Note:
180
+ - File is re-parsed on reload()
181
+ - Threading uses daemon threads (auto-cleanup)
182
+ - All MemoryDataSource methods available after load
183
+ - Lazy strategy re-parses file on filter/sort changes
184
+ """
185
+
186
+ def __init__(
187
+ self,
188
+ filepath: str | Path,
189
+ config: Optional[FileSourceConfig] = None,
190
+ page_size: int = 10
191
+ ):
192
+ """Configure a file-backed datasource and detect file format.
193
+
194
+ Args:
195
+ filepath: Location of the data file to be read.
196
+ config: Optional overrides for parsing, transforms, and threading.
197
+ page_size: Number of records returned per page after loading.
198
+
199
+ Raises:
200
+ FileNotFoundError: If the supplied file path cannot be found.
201
+ """
202
+ super().__init__(page_size=page_size)
203
+
204
+ self.filepath = Path(filepath)
205
+ self.config = config or FileSourceConfig()
206
+
207
+ # Loading state
208
+ self.is_loaded = False
209
+ self._loading = False
210
+ self._load_progress = (0, 0)
211
+ self._load_thread = None
212
+
213
+ # Detect file format
214
+ self._detected_format = self._detect_format()
215
+
216
+ # Validate file exists
217
+ if not self.filepath.exists():
218
+ raise FileNotFoundError(f"File not found: {self.filepath}")
219
+
220
+ def _detect_format(self) -> str:
221
+ """Detect file format from extension or config."""
222
+ if self.config.file_format != 'auto':
223
+ return self.config.file_format
224
+
225
+ ext = self.filepath.suffix.lower()
226
+ format_map = {
227
+ '.csv': 'csv',
228
+ '.tsv': 'tsv',
229
+ '.txt': 'csv', # Assume CSV for .txt
230
+ '.json': 'json',
231
+ '.jsonl': 'jsonl',
232
+ '.ndjson': 'jsonl',
233
+ }
234
+
235
+ return format_map.get(ext, 'csv') # Default to CSV
236
+
237
+ def _estimate_row_count(self) -> int:
238
+ """Estimate total row count for progress tracking."""
239
+ file_size = self.filepath.stat().st_size
240
+
241
+ # Estimate based on file format
242
+ if self._detected_format in ('csv', 'tsv'):
243
+ # Sample first few lines to estimate average row size
244
+ with open(self.filepath, 'r', encoding=self.config.encoding) as f:
245
+ sample_lines = []
246
+ for i, line in enumerate(f):
247
+ if i >= 100: # Sample first 100 lines
248
+ break
249
+ sample_lines.append(len(line.encode('utf-8')))
250
+
251
+ if sample_lines:
252
+ avg_line_size = sum(sample_lines) / len(sample_lines)
253
+ estimated = int(file_size / avg_line_size)
254
+ return max(estimated - self.config.skip_rows - 1, 0)
255
+
256
+ # For JSON, harder to estimate - use file size as proxy
257
+ return file_size // 100 # Very rough estimate
258
+
259
+ def _determine_strategy(self) -> str:
260
+ """Determine optimal loading strategy."""
261
+ if self.config.loading_strategy != 'auto':
262
+ return self.config.loading_strategy
263
+
264
+ # Auto-select based on estimated row count
265
+ estimated_rows = self._estimate_row_count()
266
+
267
+ if estimated_rows < self.config.max_memory_rows:
268
+ return 'eager'
269
+ elif estimated_rows < self.config.max_memory_rows * 5:
270
+ return 'chunked'
271
+ else:
272
+ return 'hybrid'
273
+
274
+ def _parse_csv_records(self) -> Iterator[Record]:
275
+ """Parse CSV/TSV file and yield records."""
276
+ delimiter = self.config.delimiter
277
+ if delimiter is None:
278
+ delimiter = '\t' if self._detected_format == 'tsv' else ','
279
+
280
+ with open(self.filepath, 'r', encoding=self.config.encoding, newline='') as f:
281
+ # Skip initial rows if configured
282
+ for _ in range(self.config.skip_rows):
283
+ next(f, None)
284
+
285
+ reader = csv.DictReader(
286
+ f,
287
+ delimiter=delimiter,
288
+ quotechar=self.config.quotechar
289
+ )
290
+
291
+ for row in reader:
292
+ yield dict(row)
293
+
294
+ def _parse_json_records(self) -> Iterator[Record]:
295
+ """Parse JSON file and yield records."""
296
+ with open(self.filepath, 'r', encoding=self.config.encoding) as f:
297
+ if self.config.json_lines:
298
+ # JSONL format - one JSON object per line
299
+ for line in f:
300
+ line = line.strip()
301
+ if line:
302
+ yield json.loads(line)
303
+ else:
304
+ # Standard JSON
305
+ data = json.load(f)
306
+
307
+ # Handle different JSON structures
308
+ if isinstance(data, list):
309
+ for item in data:
310
+ if isinstance(item, dict):
311
+ yield item
312
+ else:
313
+ yield {'value': item}
314
+ elif isinstance(data, dict):
315
+ # Could be records, columns, etc.
316
+ if self.config.json_orient == 'records':
317
+ # Assume it's a dict of lists
318
+ for item in data.get('records', data.values()):
319
+ if isinstance(item, dict):
320
+ yield item
321
+ else:
322
+ # Single record
323
+ yield data
324
+
325
+ def _parse_records(self) -> Iterator[Record]:
326
+ """Parse file and yield records based on format."""
327
+ if self._detected_format in ('csv', 'tsv'):
328
+ yield from self._parse_csv_records()
329
+ elif self._detected_format in ('json', 'jsonl'):
330
+ yield from self._parse_json_records()
331
+ else:
332
+ raise ValueError(f"Unsupported format: {self._detected_format}")
333
+
334
+ def _apply_transformations(self, record: Record) -> Optional[Record]:
335
+ """Apply transformation pipeline to a single record.
336
+
337
+ Returns:
338
+ Transformed record or None if filtered out
339
+ """
340
+ # Apply row filter first
341
+ if self.config.row_filter and not self.config.row_filter(record):
342
+ return None
343
+
344
+ # Apply column selection
345
+ if self.config.columns_to_load:
346
+ record = {k: v for k, v in record.items() if k in self.config.columns_to_load}
347
+
348
+ # Apply column renames
349
+ if self.config.column_renames:
350
+ for old_name, new_name in self.config.column_renames.items():
351
+ if old_name in record:
352
+ record[new_name] = record.pop(old_name)
353
+
354
+ # Apply default values
355
+ if self.config.default_values:
356
+ for col, default in self.config.default_values.items():
357
+ if col not in record or record[col] is None:
358
+ record[col] = default
359
+
360
+ # Apply type conversions
361
+ if self.config.column_types:
362
+ for col, col_type in self.config.column_types.items():
363
+ if col in record and record[col] is not None:
364
+ try:
365
+ if callable(col_type):
366
+ record[col] = col_type(record[col])
367
+ else:
368
+ record[col] = col_type(record[col])
369
+ except (ValueError, TypeError):
370
+ pass # Keep original value if conversion fails
371
+
372
+ # Apply column transformations
373
+ if self.config.column_transforms:
374
+ for col, transform_func in self.config.column_transforms.items():
375
+ if col in record:
376
+ try:
377
+ record[col] = transform_func(record[col])
378
+ except Exception:
379
+ pass # Keep original value if transform fails
380
+
381
+ # Apply row-level transformation
382
+ if self.config.row_transform:
383
+ record = self.config.row_transform(record)
384
+
385
+ return record
386
+
387
+ def _load_eager(self) -> None:
388
+ """Load all records into memory at once."""
389
+ records = []
390
+ estimated_total = self._estimate_row_count()
391
+
392
+ for i, raw_record in enumerate(self._parse_records()):
393
+ record = self._apply_transformations(raw_record)
394
+ if record is not None:
395
+ records.append(record)
396
+
397
+ if self.config.progress_callback and (i + 1) % 1000 == 0:
398
+ self._load_progress = (i + 1, estimated_total)
399
+ self.config.progress_callback(i + 1, estimated_total)
400
+
401
+ # Set data in parent MemoryDataSource
402
+ self.set_data(records)
403
+ self.is_loaded = True
404
+ self._load_progress = (len(records), len(records))
405
+
406
+ if self.config.progress_callback:
407
+ self.config.progress_callback(len(records), len(records))
408
+
409
+ def _load_chunked(self) -> None:
410
+ """Load file in chunks."""
411
+ chunk = []
412
+ estimated_total = self._estimate_row_count()
413
+ loaded_count = 0
414
+
415
+ for i, raw_record in enumerate(self._parse_records()):
416
+ record = self._apply_transformations(raw_record)
417
+ if record is not None:
418
+ chunk.append(record)
419
+
420
+ # Process chunk when it reaches chunk_size
421
+ if len(chunk) >= self.config.chunk_size:
422
+ if not self.is_loaded:
423
+ self.set_data(chunk)
424
+ self.is_loaded = True
425
+ else:
426
+ # Append to existing data
427
+ for rec in chunk:
428
+ self.create_record(rec)
429
+
430
+ loaded_count += len(chunk)
431
+ self._load_progress = (loaded_count, estimated_total)
432
+
433
+ if self.config.progress_callback:
434
+ self.config.progress_callback(loaded_count, estimated_total)
435
+
436
+ chunk = []
437
+
438
+ # Process remaining records
439
+ if chunk:
440
+ if not self.is_loaded:
441
+ self.set_data(chunk)
442
+ self.is_loaded = True
443
+ else:
444
+ for rec in chunk:
445
+ self.create_record(rec)
446
+
447
+ loaded_count += len(chunk)
448
+
449
+ self._load_progress = (loaded_count, loaded_count)
450
+ if self.config.progress_callback:
451
+ self.config.progress_callback(loaded_count, loaded_count)
452
+
453
+ def _load_impl(self) -> None:
454
+ """Internal load implementation (runs in thread if use_threading=True)."""
455
+ try:
456
+ strategy = self._determine_strategy()
457
+
458
+ if strategy == 'eager':
459
+ self._load_eager()
460
+ elif strategy == 'chunked':
461
+ self._load_chunked()
462
+ elif strategy in ('lazy', 'hybrid'):
463
+ # For now, fall back to eager (lazy/hybrid require more complex implementation)
464
+ self._load_eager()
465
+
466
+ self._loading = False
467
+
468
+ if self.config.on_complete:
469
+ self.config.on_complete()
470
+
471
+ except Exception as e:
472
+ self._loading = False
473
+ self.is_loaded = False
474
+
475
+ if self.config.on_error:
476
+ self.config.on_error(e)
477
+ else:
478
+ raise
479
+
480
+ def load(self, on_complete: Optional[Callable] = None) -> None:
481
+ """Load file with configured strategy.
482
+
483
+ For threaded loading, returns immediately and calls on_complete when done.
484
+ For synchronous loading, blocks until complete.
485
+
486
+ Args:
487
+ on_complete: Optional callback when loading finishes (overrides config)
488
+ """
489
+ if self._loading:
490
+ return # Already loading
491
+
492
+ self._loading = True
493
+ self.is_loaded = False
494
+ self._load_progress = (0, 0)
495
+
496
+ # Override on_complete if provided
497
+ if on_complete:
498
+ original_callback = self.config.on_complete
499
+ self.config.on_complete = on_complete
500
+
501
+ if self.config.use_threading:
502
+ # Load in background thread
503
+ self._load_thread = threading.Thread(target=self._load_impl, daemon=True)
504
+ self._load_thread.start()
505
+ else:
506
+ # Load synchronously
507
+ self._load_impl()
508
+
509
+ # Restore original callback if overridden
510
+ if on_complete:
511
+ self.config.on_complete = original_callback
512
+
513
+ def reload(self) -> None:
514
+ """Reload from file, clearing current data."""
515
+ self.is_loaded = False
516
+ self._data.clear()
517
+ self._columns.clear()
518
+ self._id_index.clear()
519
+ self.load()
520
+
521
+ def is_loading(self) -> bool:
522
+ """Check if file is currently loading."""
523
+ return self._loading
524
+
525
+ def get_load_progress(self) -> tuple[int, int]:
526
+ """Get loading progress as (current, total) rows."""
527
+ return self._load_progress
528
+
529
+ def wait_for_load(self, timeout: Optional[float] = None) -> bool:
530
+ """Wait for loading to complete (if threaded).
531
+
532
+ Args:
533
+ timeout: Max seconds to wait (None = wait forever)
534
+
535
+ Returns:
536
+ True if load completed, False if timed out
537
+ """
538
+ if self._load_thread and self._load_thread.is_alive():
539
+ self._load_thread.join(timeout=timeout)
540
+ return not self._load_thread.is_alive()
541
+ return True