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,195 @@
1
+ """bootstack doctor command - Validate project structure and environment."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import platform
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ from bootstack.cli.config import TtkbConfig, find_config
11
+
12
+
13
+ _OK = " [OK] "
14
+ _WARN = " [WARN] "
15
+ _FAIL = " [FAIL] "
16
+
17
+
18
+ def add_parser(subparsers: argparse._SubParsersAction) -> None:
19
+ """Add the 'doctor' subcommand parser."""
20
+ parser = subparsers.add_parser(
21
+ "doctor",
22
+ help="Diagnose project and environment health",
23
+ description=(
24
+ "Validate bootstack.toml, check the project layout, and report "
25
+ "environment versions relevant to building and running."
26
+ ),
27
+ )
28
+ parser.set_defaults(func=run_doctor)
29
+
30
+
31
+ def run_doctor(args: argparse.Namespace) -> None:
32
+ """Run the diagnostic checks."""
33
+ failures = 0
34
+ warnings = 0
35
+
36
+ # ---- Environment -------------------------------------------------------
37
+ print("Environment:")
38
+ print(f"{_OK}Python {platform.python_version()} ({sys.executable})")
39
+
40
+ tk_version, tk_error = _probe_tk()
41
+ if tk_version:
42
+ print(f"{_OK}Tcl/Tk {tk_version}")
43
+ else:
44
+ print(f"{_FAIL}Tcl/Tk: {tk_error}")
45
+ failures += 1
46
+
47
+ ttkb_version = _probe_bootstack()
48
+ print(f"{_OK}bootstack {ttkb_version}")
49
+
50
+ print()
51
+
52
+ # ---- Project -----------------------------------------------------------
53
+ config_path = find_config()
54
+ if config_path is None:
55
+ print("Project:")
56
+ print(f"{_WARN}No bootstack.toml found in current directory or parents.")
57
+ print(" (Run 'bootstack start <name>' to create a project, or 'cd' into one.)")
58
+ return
59
+
60
+ project_root = config_path.parent
61
+ print(f"Project: {project_root}")
62
+
63
+ try:
64
+ config = TtkbConfig.load(config_path)
65
+ except Exception as e:
66
+ print(f"{_FAIL}bootstack.toml failed to parse: {e}")
67
+ sys.exit(1)
68
+ print(f"{_OK}bootstack.toml parses ([app] name={config.app.name!r}, template={config.app.template!r})")
69
+
70
+ # Entry point exists
71
+ entry_path = project_root / config.app.entry
72
+ if entry_path.exists():
73
+ print(f"{_OK}entry point: {config.app.entry}")
74
+ else:
75
+ print(f"{_FAIL}entry point missing: {config.app.entry}")
76
+ failures += 1
77
+
78
+ # Layout matches template
79
+ if entry_path.parts and "src" in entry_path.parts:
80
+ idx = list(entry_path.parts).index("src")
81
+ if idx + 1 < len(entry_path.parts):
82
+ module_dir = project_root.joinpath(*entry_path.parts[: idx + 2])
83
+ failures += _check_template_layout(config.app.template, module_dir)
84
+ else:
85
+ # Non-src layout: skip the views/pages check
86
+ pass
87
+
88
+ # Packaging — always reported. If [build] exists we run the full check;
89
+ # otherwise we still flag a missing PyInstaller as a heads-up so users
90
+ # discover it before they reach 'bootstack build'.
91
+ if config.build is not None:
92
+ warnings += _check_build(config, project_root)
93
+ else:
94
+ warnings += _check_packaging_readiness()
95
+
96
+ # ---- Summary -----------------------------------------------------------
97
+ print()
98
+ if failures:
99
+ print(f"{failures} failure(s), {warnings} warning(s).")
100
+ sys.exit(1)
101
+ if warnings:
102
+ print(f"All checks passed with {warnings} warning(s).")
103
+ else:
104
+ print("All checks passed.")
105
+
106
+
107
+ def _check_template_layout(template: str, module_dir: Path) -> int:
108
+ """Verify the project's component directory matches its template type.
109
+
110
+ Returns the number of failures recorded.
111
+ """
112
+ expected = "pages" if template == "appshell" else "views"
113
+ other = "views" if template == "appshell" else "pages"
114
+
115
+ expected_dir = module_dir / expected
116
+ other_dir = module_dir / other
117
+
118
+ failures = 0
119
+ if expected_dir.exists():
120
+ print(f"{_OK}layout: found {expected}/ (matches template={template!r})")
121
+ else:
122
+ print(f"{_FAIL}layout: expected {expected_dir.relative_to(module_dir.parent.parent)}/ for template={template!r}")
123
+ failures += 1
124
+
125
+ if other_dir.exists():
126
+ print(f"{_WARN}layout: unexpected {other}/ directory for template={template!r}")
127
+ return failures
128
+
129
+
130
+ def _check_packaging_readiness() -> int:
131
+ """Report packaging-tool availability for un-promoted projects.
132
+
133
+ Never fails — just warns if PyInstaller is missing so users know
134
+ they have an install ahead of them before 'bootstack build' will work.
135
+ Returns the number of warnings.
136
+ """
137
+ print()
138
+ print("Packaging (project not yet promoted):")
139
+ try:
140
+ import PyInstaller # noqa: F401
141
+ print(f"{_OK}PyInstaller is available (run 'bootstack promote --pyinstaller' to enable builds)")
142
+ return 0
143
+ except ImportError:
144
+ print(f"{_WARN}PyInstaller is not installed")
145
+ print(" Install it before 'bootstack build': pip install pyinstaller")
146
+ return 1
147
+
148
+
149
+ def _check_build(config: TtkbConfig, project_root: Path) -> int:
150
+ """Verify build-time prerequisites. Returns the number of warnings."""
151
+ warnings = 0
152
+ backend = config.build.backend if config.build else None
153
+ print()
154
+ print(f"Build (backend={backend!r}):")
155
+
156
+ if backend == "pyinstaller":
157
+ spec = project_root / "build" / "pyinstaller" / "app.spec"
158
+ if spec.exists():
159
+ print(f"{_OK}spec file: {spec.relative_to(project_root)}")
160
+ else:
161
+ print(f"{_WARN}spec file missing — run 'bootstack promote --pyinstaller --force'")
162
+ warnings += 1
163
+ try:
164
+ import PyInstaller # noqa: F401
165
+ print(f"{_OK}PyInstaller is importable")
166
+ except ImportError:
167
+ print(f"{_WARN}PyInstaller is not installed (pip install pyinstaller)")
168
+ warnings += 1
169
+ else:
170
+ print(f"{_WARN}unknown build backend: {backend!r}")
171
+ warnings += 1
172
+
173
+ return warnings
174
+
175
+
176
+ def _probe_tk() -> tuple[str | None, str | None]:
177
+ """Return (version, None) on success, (None, error_message) on failure.
178
+
179
+ Uses `_tkinter` module constants directly so the probe does not have
180
+ to instantiate a Tk interpreter (bootstack monkey-patches
181
+ `Tk.__init__` to require an App, which would trip the probe).
182
+ """
183
+ try:
184
+ import _tkinter
185
+ return f"{_tkinter.TCL_VERSION} / Tk {_tkinter.TK_VERSION}", None
186
+ except Exception as e:
187
+ return None, str(e)
188
+
189
+
190
+ def _probe_bootstack() -> str:
191
+ try:
192
+ import bootstack
193
+ return getattr(bootstack, "__version__", "(unknown)")
194
+ except Exception:
195
+ return "(import failed)"
@@ -0,0 +1,71 @@
1
+ """bootstack list command - List available themes and other resources."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+
8
+
9
+ def add_parser(subparsers: argparse._SubParsersAction) -> None:
10
+ """Add the 'list' subcommand parser."""
11
+ parser = subparsers.add_parser(
12
+ "list",
13
+ help="List available resources",
14
+ description="List themes and other available resources.",
15
+ )
16
+ list_subparsers = parser.add_subparsers(dest="resource")
17
+
18
+ # bootstack list themes
19
+ themes_parser = list_subparsers.add_parser(
20
+ "themes",
21
+ help="List available themes",
22
+ )
23
+ themes_parser.set_defaults(func=run_list_themes)
24
+
25
+ parser.set_defaults(func=lambda args: parser.print_help())
26
+
27
+
28
+ def run_list_themes(args: argparse.Namespace) -> None:
29
+ """List available themes by reading theme JSON files from the package."""
30
+ from importlib import resources
31
+
32
+ themes: list[dict[str, str]] = []
33
+
34
+ for package in ("bootstack.assets.themes",):
35
+ try:
36
+ base = resources.files(package)
37
+ except (ModuleNotFoundError, FileNotFoundError):
38
+ continue
39
+ for item in base.iterdir():
40
+ if not item.name.endswith(".json"):
41
+ continue
42
+ try:
43
+ data = json.loads(item.read_text(encoding="utf-8"))
44
+ themes.append({
45
+ "name": data.get("name", item.name.removesuffix(".json")),
46
+ "display_name": data.get("display_name", ""),
47
+ "mode": data.get("mode", ""),
48
+ })
49
+ except (json.JSONDecodeError, OSError):
50
+ continue
51
+
52
+ if not themes:
53
+ print("No themes found.")
54
+ return
55
+
56
+ themes.sort(key=lambda t: (t["mode"], t["name"]))
57
+
58
+ # Print table
59
+ name_width = max(max(len(t["name"]) for t in themes), len("Name"))
60
+ display_width = max(
61
+ max(len(t["display_name"]) for t in themes), len("Display Name")
62
+ )
63
+
64
+ header = f" {'Name':<{name_width}} {'Display Name':<{display_width}} Mode"
65
+ print(header)
66
+ print(f" {'-' * name_width} {'-' * display_width} ----")
67
+
68
+ for t in themes:
69
+ print(f" {t['name']:<{name_width}} {t['display_name']:<{display_width}} {t['mode']}")
70
+
71
+ print(f"\n {len(themes)} themes available.")
@@ -0,0 +1,120 @@
1
+ """bootstack promote command - Upgrade project to packaging-ready."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from pathlib import Path
7
+
8
+ from bootstack.cli.config import (
9
+ BUILD_CONFIG_TEMPLATE,
10
+ TtkbConfig,
11
+ find_config,
12
+ )
13
+ from bootstack.cli.pyinstaller import generate_spec
14
+
15
+
16
+ def add_parser(subparsers: argparse._SubParsersAction) -> None:
17
+ """Add the 'promote' subcommand parser."""
18
+ parser = subparsers.add_parser(
19
+ "promote",
20
+ help="Upgrade project to packaging-ready",
21
+ description="Add build configuration and generate build files for distribution.",
22
+ )
23
+ parser.add_argument(
24
+ "--pyinstaller",
25
+ action="store_true",
26
+ help="Enable PyInstaller build support",
27
+ )
28
+ parser.add_argument(
29
+ "--force",
30
+ action="store_true",
31
+ help="Overwrite existing build configuration",
32
+ )
33
+ parser.set_defaults(func=run_promote)
34
+
35
+
36
+ def run_promote(args: argparse.Namespace) -> None:
37
+ """Execute the promote command."""
38
+ if not args.pyinstaller:
39
+ print("Error: Please specify a build backend.")
40
+ print(" bootstack promote --pyinstaller")
41
+ return
42
+
43
+ # Find project root
44
+ config_path = find_config()
45
+ if config_path is None:
46
+ print("Error: No bootstack.toml found in current directory or parents.")
47
+ print("Run 'bootstack start <appname>' to create a new project first.")
48
+ return
49
+
50
+ project_root = config_path.parent
51
+ config = TtkbConfig.load(config_path)
52
+
53
+ # Check if already promoted
54
+ if config.build is not None and not args.force:
55
+ print("Project already has build configuration.")
56
+ print("Use --force to overwrite.")
57
+ return
58
+
59
+ print(f"Promoting project '{config.app.name}' for PyInstaller...")
60
+
61
+ # Update bootstack.toml with build section
62
+ _add_build_section(config_path)
63
+
64
+ # Create build directory
65
+ build_dir = project_root / "build" / "pyinstaller"
66
+ build_dir.mkdir(parents=True, exist_ok=True)
67
+
68
+ # Reload config to get build settings
69
+ config = TtkbConfig.load(config_path)
70
+
71
+ # Generate .spec file
72
+ spec_path = build_dir / "app.spec"
73
+ generate_spec(config, project_root, spec_path)
74
+
75
+ print()
76
+ print("Project promoted successfully!")
77
+ print()
78
+ print("Generated files:")
79
+ print(f" - {spec_path.relative_to(project_root)}")
80
+ print()
81
+ print("Updated:")
82
+ print(f" - bootstack.toml (added [build] section)")
83
+ print()
84
+
85
+ # Warn early if PyInstaller isn't on this interpreter — `bootstack build`
86
+ # needs it but the promote step itself only writes config.
87
+ try:
88
+ import PyInstaller # noqa: F401
89
+ except ImportError:
90
+ print("Note: PyInstaller is not installed in this environment.")
91
+ print(" Install it before running 'bootstack build':")
92
+ print(" pip install pyinstaller")
93
+ print()
94
+
95
+ print("Next steps:")
96
+ print(" 1. (Optional) Edit bootstack.toml [build] section")
97
+ print(" 2. Run 'bootstack build' to create executable")
98
+
99
+
100
+ def _add_build_section(config_path: Path) -> None:
101
+ """Add [build] section to existing bootstack.toml."""
102
+ content = config_path.read_text(encoding="utf-8")
103
+
104
+ # Check if [build] section already exists
105
+ if "[build]" in content:
106
+ # Remove existing build section and everything after
107
+ lines = content.split("\n")
108
+ new_lines = []
109
+ skip = False
110
+ for line in lines:
111
+ if line.strip().startswith("[build"):
112
+ skip = True
113
+ if not skip:
114
+ new_lines.append(line)
115
+ content = "\n".join(new_lines).rstrip() + "\n"
116
+
117
+ # Append build section
118
+ content += BUILD_CONFIG_TEMPLATE
119
+
120
+ config_path.write_text(content, encoding="utf-8")
@@ -0,0 +1,246 @@
1
+ """PyInstaller spec file generation for bootstack projects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from bootstack.cli.config import TtkbConfig
10
+
11
+
12
+ # PyInstaller spec template that reads from bootstack.toml
13
+ SPEC_TEMPLATE = '''\
14
+ # -*- mode: python ; coding: utf-8 -*-
15
+ """
16
+ PyInstaller spec file for {app_name}.
17
+
18
+ This file is auto-generated by bootstack CLI.
19
+ Edit bootstack.toml to change build settings, then regenerate with:
20
+ bootstack promote --pyinstaller --force
21
+ """
22
+
23
+ import sys
24
+ from pathlib import Path
25
+
26
+ # PyInstaller exec's this spec with SPECPATH (directory of the .spec file)
27
+ # in the namespace. __file__ is not defined inside a spec.
28
+ PROJECT_ROOT = Path(SPECPATH).parent.parent
29
+ CONFIG_PATH = PROJECT_ROOT / "bootstack.toml"
30
+
31
+ # Ensure we can import bootstack CLI tools
32
+ try:
33
+ from bootstack.cli.config import TtkbConfig
34
+ except ImportError:
35
+ sys.path.insert(0, str(PROJECT_ROOT / "src"))
36
+ from bootstack.cli.config import TtkbConfig
37
+
38
+ if not CONFIG_PATH.exists():
39
+ raise FileNotFoundError(f"bootstack.toml not found at {{CONFIG_PATH}}")
40
+
41
+ config = TtkbConfig.load(CONFIG_PATH)
42
+
43
+ # Extract settings
44
+ APP_NAME = config.app.name
45
+ ENTRY_POINT = PROJECT_ROOT / config.app.entry
46
+ WINDOWED = config.build.windowed if config.build else True
47
+ ONEFILE = config.build.onefile if config.build else False
48
+
49
+ # Icon handling
50
+ ICON_PATH = None
51
+ if config.build and config.build.icon.path:
52
+ icon_candidate = PROJECT_ROOT / config.build.icon.path
53
+ if icon_candidate.exists():
54
+ ICON_PATH = str(icon_candidate)
55
+ else:
56
+ # Fall back to the bundled bootstack launch icon (the same artwork
57
+ # the runtime sets via wm_iconphoto, just rendered as a multi-size
58
+ # .ico for Windows). Users can override by setting [build.icon] path
59
+ # in bootstack.toml.
60
+ try:
61
+ import bootstack
62
+ default_icon = Path(bootstack.__file__).parent / "assets" / "bootstack.ico"
63
+ if default_icon.exists():
64
+ ICON_PATH = str(default_icon)
65
+ except Exception:
66
+ pass
67
+
68
+
69
+ # =============================================================================
70
+ # Collect data files
71
+ # =============================================================================
72
+
73
+ from glob import glob
74
+
75
+ datas = []
76
+
77
+ # Always include bootstack.toml
78
+ datas.append((str(CONFIG_PATH), "."))
79
+
80
+ # Include patterns from config
81
+ if config.build and config.build.datas.include:
82
+ for pattern in config.build.datas.include:
83
+ if pattern == "bootstack.toml":
84
+ continue # Already added
85
+ matches = glob(str(PROJECT_ROOT / pattern), recursive=True)
86
+ for match in matches:
87
+ match_path = Path(match)
88
+ if match_path.is_file():
89
+ rel_path = match_path.relative_to(PROJECT_ROOT)
90
+ dest_dir = str(rel_path.parent) if rel_path.parent != Path(".") else "."
91
+ datas.append((str(match_path), dest_dir))
92
+
93
+
94
+ # =============================================================================
95
+ # Collect package data (themes, fonts, icon glyphmaps, locales, etc.)
96
+ # =============================================================================
97
+
98
+ # PyInstaller's collect_data_files walks a package and grabs every non-.py
99
+ # file, returning (src, dest) tuples in the form `datas` expects. We use it
100
+ # here for bootstack's own assets, plus a downstream workaround:
101
+ #
102
+ # WORKAROUND (until upstream fixes ship):
103
+ # ttkbootstrap_icons ships a complete _pyinstaller/ hook directory, but
104
+ # its pyproject.toml does not declare the `pyinstaller40` entry point, so
105
+ # PyInstaller never discovers those hooks. ttkbootstrap_icons_bs also has
106
+ # no hook of its own. Both manifest at runtime as:
107
+ # ModuleNotFoundError: No module named 'ttkbootstrap_icons_bs.assets'
108
+ # We bundle their data files manually here so frozen apps actually work.
109
+ # Tracked at: https://github.com/israel-dryer/bootstack-icons/issues
110
+ # Once upstream lands the entry point + a `hook-ttkbootstrap_icons_bs.py`,
111
+ # the two icon-package entries below can be dropped — PyInstaller will
112
+ # pick them up automatically.
113
+ from PyInstaller.utils.hooks import collect_data_files
114
+
115
+ for _pkg in ("bootstack", "ttkbootstrap_icons", "ttkbootstrap_icons_bs"):
116
+ try:
117
+ datas += collect_data_files(_pkg)
118
+ except Exception as _e: # pragma: no cover
119
+ print(f"Warning: Could not collect data files from {{_pkg}}: {{_e}}")
120
+
121
+
122
+ # =============================================================================
123
+ # Hidden imports for bootstack
124
+ # =============================================================================
125
+
126
+ hiddenimports = [
127
+ "bootstack",
128
+ "bootstack.themes",
129
+ "bootstack.style",
130
+ "bootstack.widgets",
131
+ "ttkbootstrap_icons",
132
+ "ttkbootstrap_icons_bs",
133
+ "PIL",
134
+ "PIL._tkinter_finder",
135
+ "babel.numbers",
136
+ "babel.dates",
137
+ "dateutil",
138
+ "dateparser",
139
+ ]
140
+
141
+ # babel.bin is a CLI script package whose __init__.py calls exit(1) when
142
+ # imported without args; PyInstaller's binary-dependency analyzer
143
+ # imports it during package walking and crashes. Excluding it is safe
144
+ # because nothing in a bootstack app uses babel's command-line tools.
145
+ excludes = [
146
+ "babel.bin",
147
+ ]
148
+
149
+
150
+ # =============================================================================
151
+ # Analysis
152
+ # =============================================================================
153
+
154
+ a = Analysis(
155
+ [str(ENTRY_POINT)],
156
+ pathex=[str(PROJECT_ROOT / "src")],
157
+ binaries=[],
158
+ datas=datas,
159
+ hiddenimports=hiddenimports,
160
+ hookspath=[],
161
+ hooksconfig={{}},
162
+ runtime_hooks=[],
163
+ excludes=excludes,
164
+ noarchive=False,
165
+ optimize=0,
166
+ )
167
+
168
+
169
+ # =============================================================================
170
+ # Build
171
+ # =============================================================================
172
+
173
+ pyz = PYZ(a.pure)
174
+
175
+ if ONEFILE:
176
+ exe = EXE(
177
+ pyz,
178
+ a.scripts,
179
+ a.binaries,
180
+ a.datas,
181
+ [],
182
+ name=APP_NAME,
183
+ debug=False,
184
+ bootloader_ignore_signals=False,
185
+ strip=False,
186
+ upx=True,
187
+ upx_exclude=[],
188
+ runtime_tmpdir=None,
189
+ console=not WINDOWED,
190
+ disable_windowed_traceback=False,
191
+ argv_emulation=False,
192
+ target_arch=None,
193
+ codesign_identity=None,
194
+ entitlements_file=None,
195
+ icon=ICON_PATH,
196
+ )
197
+ else:
198
+ exe = EXE(
199
+ pyz,
200
+ a.scripts,
201
+ [],
202
+ exclude_binaries=True,
203
+ name=APP_NAME,
204
+ debug=False,
205
+ bootloader_ignore_signals=False,
206
+ strip=False,
207
+ upx=True,
208
+ console=not WINDOWED,
209
+ disable_windowed_traceback=False,
210
+ argv_emulation=False,
211
+ target_arch=None,
212
+ codesign_identity=None,
213
+ entitlements_file=None,
214
+ icon=ICON_PATH,
215
+ )
216
+
217
+ coll = COLLECT(
218
+ exe,
219
+ a.binaries,
220
+ a.datas,
221
+ strip=False,
222
+ upx=True,
223
+ upx_exclude=[],
224
+ name=APP_NAME,
225
+ )
226
+ '''
227
+
228
+
229
+ def generate_spec(
230
+ config: TtkbConfig,
231
+ project_root: Path,
232
+ output_path: Path,
233
+ ) -> None:
234
+ """Generate a PyInstaller spec file.
235
+
236
+ Args:
237
+ config: The project configuration.
238
+ project_root: Root directory of the project.
239
+ output_path: Path to write the spec file.
240
+ """
241
+ content = SPEC_TEMPLATE.format(
242
+ app_name=config.app.name,
243
+ )
244
+
245
+ output_path.parent.mkdir(parents=True, exist_ok=True)
246
+ output_path.write_text(content, encoding="utf-8")