setiastrosuitepro 1.6.7__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.

Potentially problematic release.


This version of setiastrosuitepro might be problematic. Click here for more details.

Files changed (394) hide show
  1. setiastro/__init__.py +2 -0
  2. setiastro/data/SASP_data.fits +0 -0
  3. setiastro/data/catalogs/List_of_Galaxies_with_Distances_Gly.csv +488 -0
  4. setiastro/data/catalogs/astrobin_filters.csv +890 -0
  5. setiastro/data/catalogs/astrobin_filters_page1_local.csv +51 -0
  6. setiastro/data/catalogs/cali2.csv +63 -0
  7. setiastro/data/catalogs/cali2color.csv +65 -0
  8. setiastro/data/catalogs/celestial_catalog - original.csv +16471 -0
  9. setiastro/data/catalogs/celestial_catalog.csv +24031 -0
  10. setiastro/data/catalogs/detected_stars.csv +24784 -0
  11. setiastro/data/catalogs/fits_header_data.csv +46 -0
  12. setiastro/data/catalogs/test.csv +8 -0
  13. setiastro/data/catalogs/updated_celestial_catalog.csv +16471 -0
  14. setiastro/images/Astro_Spikes.png +0 -0
  15. setiastro/images/Background_startup.jpg +0 -0
  16. setiastro/images/HRDiagram.png +0 -0
  17. setiastro/images/LExtract.png +0 -0
  18. setiastro/images/LInsert.png +0 -0
  19. setiastro/images/Oxygenation-atm-2.svg.png +0 -0
  20. setiastro/images/RGB080604.png +0 -0
  21. setiastro/images/abeicon.png +0 -0
  22. setiastro/images/aberration.png +0 -0
  23. setiastro/images/acv_icon.png +0 -0
  24. setiastro/images/andromedatry.png +0 -0
  25. setiastro/images/andromedatry_satellited.png +0 -0
  26. setiastro/images/annotated.png +0 -0
  27. setiastro/images/aperture.png +0 -0
  28. setiastro/images/astrosuite.ico +0 -0
  29. setiastro/images/astrosuite.png +0 -0
  30. setiastro/images/astrosuitepro.icns +0 -0
  31. setiastro/images/astrosuitepro.ico +0 -0
  32. setiastro/images/astrosuitepro.png +0 -0
  33. setiastro/images/background.png +0 -0
  34. setiastro/images/background2.png +0 -0
  35. setiastro/images/benchmark.png +0 -0
  36. setiastro/images/big_moon_stabilizer_timeline.png +0 -0
  37. setiastro/images/big_moon_stabilizer_timeline_clean.png +0 -0
  38. setiastro/images/blaster.png +0 -0
  39. setiastro/images/blink.png +0 -0
  40. setiastro/images/clahe.png +0 -0
  41. setiastro/images/collage.png +0 -0
  42. setiastro/images/colorwheel.png +0 -0
  43. setiastro/images/contsub.png +0 -0
  44. setiastro/images/convo.png +0 -0
  45. setiastro/images/copyslot.png +0 -0
  46. setiastro/images/cosmic.png +0 -0
  47. setiastro/images/cosmicsat.png +0 -0
  48. setiastro/images/crop1.png +0 -0
  49. setiastro/images/cropicon.png +0 -0
  50. setiastro/images/curves.png +0 -0
  51. setiastro/images/cvs.png +0 -0
  52. setiastro/images/debayer.png +0 -0
  53. setiastro/images/denoise_cnn_custom.png +0 -0
  54. setiastro/images/denoise_cnn_graph.png +0 -0
  55. setiastro/images/disk.png +0 -0
  56. setiastro/images/dse.png +0 -0
  57. setiastro/images/exoicon.png +0 -0
  58. setiastro/images/eye.png +0 -0
  59. setiastro/images/first_quarter.png +0 -0
  60. setiastro/images/fliphorizontal.png +0 -0
  61. setiastro/images/flipvertical.png +0 -0
  62. setiastro/images/font.png +0 -0
  63. setiastro/images/freqsep.png +0 -0
  64. setiastro/images/full_moon.png +0 -0
  65. setiastro/images/functionbundle.png +0 -0
  66. setiastro/images/graxpert.png +0 -0
  67. setiastro/images/green.png +0 -0
  68. setiastro/images/gridicon.png +0 -0
  69. setiastro/images/halo.png +0 -0
  70. setiastro/images/hdr.png +0 -0
  71. setiastro/images/histogram.png +0 -0
  72. setiastro/images/hubble.png +0 -0
  73. setiastro/images/imagecombine.png +0 -0
  74. setiastro/images/invert.png +0 -0
  75. setiastro/images/isophote.png +0 -0
  76. setiastro/images/isophote_demo_figure.png +0 -0
  77. setiastro/images/isophote_demo_image.png +0 -0
  78. setiastro/images/isophote_demo_model.png +0 -0
  79. setiastro/images/isophote_demo_residual.png +0 -0
  80. setiastro/images/jwstpupil.png +0 -0
  81. setiastro/images/last_quarter.png +0 -0
  82. setiastro/images/linearfit.png +0 -0
  83. setiastro/images/livestacking.png +0 -0
  84. setiastro/images/mask.png +0 -0
  85. setiastro/images/maskapply.png +0 -0
  86. setiastro/images/maskcreate.png +0 -0
  87. setiastro/images/maskremove.png +0 -0
  88. setiastro/images/morpho.png +0 -0
  89. setiastro/images/mosaic.png +0 -0
  90. setiastro/images/multiscale_decomp.png +0 -0
  91. setiastro/images/nbtorgb.png +0 -0
  92. setiastro/images/neutral.png +0 -0
  93. setiastro/images/new_moon.png +0 -0
  94. setiastro/images/nuke.png +0 -0
  95. setiastro/images/openfile.png +0 -0
  96. setiastro/images/pedestal.png +0 -0
  97. setiastro/images/pen.png +0 -0
  98. setiastro/images/pixelmath.png +0 -0
  99. setiastro/images/platesolve.png +0 -0
  100. setiastro/images/ppp.png +0 -0
  101. setiastro/images/pro.png +0 -0
  102. setiastro/images/project.png +0 -0
  103. setiastro/images/psf.png +0 -0
  104. setiastro/images/redo.png +0 -0
  105. setiastro/images/redoicon.png +0 -0
  106. setiastro/images/rescale.png +0 -0
  107. setiastro/images/rgbalign.png +0 -0
  108. setiastro/images/rgbcombo.png +0 -0
  109. setiastro/images/rgbextract.png +0 -0
  110. setiastro/images/rotate180.png +0 -0
  111. setiastro/images/rotatearbitrary.png +0 -0
  112. setiastro/images/rotateclockwise.png +0 -0
  113. setiastro/images/rotatecounterclockwise.png +0 -0
  114. setiastro/images/satellite.png +0 -0
  115. setiastro/images/script.png +0 -0
  116. setiastro/images/selectivecolor.png +0 -0
  117. setiastro/images/simbad.png +0 -0
  118. setiastro/images/slot0.png +0 -0
  119. setiastro/images/slot1.png +0 -0
  120. setiastro/images/slot2.png +0 -0
  121. setiastro/images/slot3.png +0 -0
  122. setiastro/images/slot4.png +0 -0
  123. setiastro/images/slot5.png +0 -0
  124. setiastro/images/slot6.png +0 -0
  125. setiastro/images/slot7.png +0 -0
  126. setiastro/images/slot8.png +0 -0
  127. setiastro/images/slot9.png +0 -0
  128. setiastro/images/spcc.png +0 -0
  129. setiastro/images/spin_precession_vs_lunar_distance.png +0 -0
  130. setiastro/images/spinner.gif +0 -0
  131. setiastro/images/stacking.png +0 -0
  132. setiastro/images/staradd.png +0 -0
  133. setiastro/images/staralign.png +0 -0
  134. setiastro/images/starnet.png +0 -0
  135. setiastro/images/starregistration.png +0 -0
  136. setiastro/images/starspike.png +0 -0
  137. setiastro/images/starstretch.png +0 -0
  138. setiastro/images/statstretch.png +0 -0
  139. setiastro/images/supernova.png +0 -0
  140. setiastro/images/uhs.png +0 -0
  141. setiastro/images/undoicon.png +0 -0
  142. setiastro/images/upscale.png +0 -0
  143. setiastro/images/viewbundle.png +0 -0
  144. setiastro/images/waning_crescent_1.png +0 -0
  145. setiastro/images/waning_crescent_2.png +0 -0
  146. setiastro/images/waning_crescent_3.png +0 -0
  147. setiastro/images/waning_crescent_4.png +0 -0
  148. setiastro/images/waning_crescent_5.png +0 -0
  149. setiastro/images/waning_gibbous_1.png +0 -0
  150. setiastro/images/waning_gibbous_2.png +0 -0
  151. setiastro/images/waning_gibbous_3.png +0 -0
  152. setiastro/images/waning_gibbous_4.png +0 -0
  153. setiastro/images/waning_gibbous_5.png +0 -0
  154. setiastro/images/waxing_crescent_1.png +0 -0
  155. setiastro/images/waxing_crescent_2.png +0 -0
  156. setiastro/images/waxing_crescent_3.png +0 -0
  157. setiastro/images/waxing_crescent_4.png +0 -0
  158. setiastro/images/waxing_crescent_5.png +0 -0
  159. setiastro/images/waxing_gibbous_1.png +0 -0
  160. setiastro/images/waxing_gibbous_2.png +0 -0
  161. setiastro/images/waxing_gibbous_3.png +0 -0
  162. setiastro/images/waxing_gibbous_4.png +0 -0
  163. setiastro/images/waxing_gibbous_5.png +0 -0
  164. setiastro/images/whitebalance.png +0 -0
  165. setiastro/images/wimi_icon_256x256.png +0 -0
  166. setiastro/images/wimilogo.png +0 -0
  167. setiastro/images/wims.png +0 -0
  168. setiastro/images/wrench_icon.png +0 -0
  169. setiastro/images/xisfliberator.png +0 -0
  170. setiastro/qml/ResourceMonitor.qml +128 -0
  171. setiastro/saspro/__init__.py +20 -0
  172. setiastro/saspro/__main__.py +964 -0
  173. setiastro/saspro/_generated/__init__.py +7 -0
  174. setiastro/saspro/_generated/build_info.py +3 -0
  175. setiastro/saspro/abe.py +1379 -0
  176. setiastro/saspro/abe_preset.py +196 -0
  177. setiastro/saspro/aberration_ai.py +910 -0
  178. setiastro/saspro/aberration_ai_preset.py +224 -0
  179. setiastro/saspro/accel_installer.py +218 -0
  180. setiastro/saspro/accel_workers.py +30 -0
  181. setiastro/saspro/acv_exporter.py +379 -0
  182. setiastro/saspro/add_stars.py +627 -0
  183. setiastro/saspro/astrobin_exporter.py +1010 -0
  184. setiastro/saspro/astrospike.py +153 -0
  185. setiastro/saspro/astrospike_python.py +1841 -0
  186. setiastro/saspro/autostretch.py +198 -0
  187. setiastro/saspro/backgroundneutral.py +639 -0
  188. setiastro/saspro/batch_convert.py +328 -0
  189. setiastro/saspro/batch_renamer.py +522 -0
  190. setiastro/saspro/blemish_blaster.py +494 -0
  191. setiastro/saspro/blink_comparator_pro.py +3149 -0
  192. setiastro/saspro/bundles.py +61 -0
  193. setiastro/saspro/bundles_dock.py +114 -0
  194. setiastro/saspro/cheat_sheet.py +213 -0
  195. setiastro/saspro/clahe.py +371 -0
  196. setiastro/saspro/comet_stacking.py +1442 -0
  197. setiastro/saspro/common_tr.py +107 -0
  198. setiastro/saspro/config.py +38 -0
  199. setiastro/saspro/config_bootstrap.py +40 -0
  200. setiastro/saspro/config_manager.py +316 -0
  201. setiastro/saspro/continuum_subtract.py +1620 -0
  202. setiastro/saspro/convo.py +1403 -0
  203. setiastro/saspro/convo_preset.py +414 -0
  204. setiastro/saspro/copyastro.py +190 -0
  205. setiastro/saspro/cosmicclarity.py +1593 -0
  206. setiastro/saspro/cosmicclarity_preset.py +407 -0
  207. setiastro/saspro/crop_dialog_pro.py +1005 -0
  208. setiastro/saspro/crop_preset.py +189 -0
  209. setiastro/saspro/curve_editor_pro.py +2608 -0
  210. setiastro/saspro/curves_preset.py +375 -0
  211. setiastro/saspro/debayer.py +673 -0
  212. setiastro/saspro/debug_utils.py +29 -0
  213. setiastro/saspro/dnd_mime.py +35 -0
  214. setiastro/saspro/doc_manager.py +2727 -0
  215. setiastro/saspro/exoplanet_detector.py +2258 -0
  216. setiastro/saspro/file_utils.py +284 -0
  217. setiastro/saspro/fitsmodifier.py +748 -0
  218. setiastro/saspro/fix_bom.py +32 -0
  219. setiastro/saspro/free_torch_memory.py +48 -0
  220. setiastro/saspro/frequency_separation.py +1352 -0
  221. setiastro/saspro/function_bundle.py +1596 -0
  222. setiastro/saspro/generate_translations.py +3092 -0
  223. setiastro/saspro/ghs_dialog_pro.py +728 -0
  224. setiastro/saspro/ghs_preset.py +284 -0
  225. setiastro/saspro/graxpert.py +638 -0
  226. setiastro/saspro/graxpert_preset.py +287 -0
  227. setiastro/saspro/gui/__init__.py +0 -0
  228. setiastro/saspro/gui/main_window.py +8928 -0
  229. setiastro/saspro/gui/mixins/__init__.py +33 -0
  230. setiastro/saspro/gui/mixins/dock_mixin.py +375 -0
  231. setiastro/saspro/gui/mixins/file_mixin.py +450 -0
  232. setiastro/saspro/gui/mixins/geometry_mixin.py +503 -0
  233. setiastro/saspro/gui/mixins/header_mixin.py +441 -0
  234. setiastro/saspro/gui/mixins/mask_mixin.py +421 -0
  235. setiastro/saspro/gui/mixins/menu_mixin.py +391 -0
  236. setiastro/saspro/gui/mixins/theme_mixin.py +367 -0
  237. setiastro/saspro/gui/mixins/toolbar_mixin.py +1824 -0
  238. setiastro/saspro/gui/mixins/update_mixin.py +323 -0
  239. setiastro/saspro/gui/mixins/view_mixin.py +477 -0
  240. setiastro/saspro/gui/statistics_dialog.py +47 -0
  241. setiastro/saspro/halobgon.py +492 -0
  242. setiastro/saspro/header_viewer.py +448 -0
  243. setiastro/saspro/headless_utils.py +88 -0
  244. setiastro/saspro/histogram.py +760 -0
  245. setiastro/saspro/history_explorer.py +941 -0
  246. setiastro/saspro/i18n.py +168 -0
  247. setiastro/saspro/image_combine.py +421 -0
  248. setiastro/saspro/image_peeker_pro.py +1608 -0
  249. setiastro/saspro/imageops/__init__.py +37 -0
  250. setiastro/saspro/imageops/mdi_snap.py +292 -0
  251. setiastro/saspro/imageops/scnr.py +36 -0
  252. setiastro/saspro/imageops/starbasedwhitebalance.py +210 -0
  253. setiastro/saspro/imageops/stretch.py +236 -0
  254. setiastro/saspro/isophote.py +1186 -0
  255. setiastro/saspro/layers.py +208 -0
  256. setiastro/saspro/layers_dock.py +714 -0
  257. setiastro/saspro/lazy_imports.py +193 -0
  258. setiastro/saspro/legacy/__init__.py +2 -0
  259. setiastro/saspro/legacy/image_manager.py +2360 -0
  260. setiastro/saspro/legacy/numba_utils.py +3676 -0
  261. setiastro/saspro/legacy/xisf.py +1213 -0
  262. setiastro/saspro/linear_fit.py +537 -0
  263. setiastro/saspro/live_stacking.py +1854 -0
  264. setiastro/saspro/log_bus.py +5 -0
  265. setiastro/saspro/logging_config.py +460 -0
  266. setiastro/saspro/luminancerecombine.py +510 -0
  267. setiastro/saspro/main_helpers.py +201 -0
  268. setiastro/saspro/mask_creation.py +1090 -0
  269. setiastro/saspro/masks_core.py +56 -0
  270. setiastro/saspro/mdi_widgets.py +353 -0
  271. setiastro/saspro/memory_utils.py +666 -0
  272. setiastro/saspro/metadata_patcher.py +75 -0
  273. setiastro/saspro/mfdeconv.py +3909 -0
  274. setiastro/saspro/mfdeconv_earlystop.py +71 -0
  275. setiastro/saspro/mfdeconvcudnn.py +3312 -0
  276. setiastro/saspro/mfdeconvsport.py +2459 -0
  277. setiastro/saspro/minorbodycatalog.py +567 -0
  278. setiastro/saspro/morphology.py +411 -0
  279. setiastro/saspro/multiscale_decomp.py +1751 -0
  280. setiastro/saspro/nbtorgb_stars.py +541 -0
  281. setiastro/saspro/numba_utils.py +3145 -0
  282. setiastro/saspro/numba_warmup.py +141 -0
  283. setiastro/saspro/ops/__init__.py +9 -0
  284. setiastro/saspro/ops/command_help_dialog.py +623 -0
  285. setiastro/saspro/ops/command_runner.py +217 -0
  286. setiastro/saspro/ops/commands.py +1594 -0
  287. setiastro/saspro/ops/script_editor.py +1105 -0
  288. setiastro/saspro/ops/scripts.py +1476 -0
  289. setiastro/saspro/ops/settings.py +637 -0
  290. setiastro/saspro/parallel_utils.py +554 -0
  291. setiastro/saspro/pedestal.py +121 -0
  292. setiastro/saspro/perfect_palette_picker.py +1105 -0
  293. setiastro/saspro/pipeline.py +110 -0
  294. setiastro/saspro/pixelmath.py +1604 -0
  295. setiastro/saspro/plate_solver.py +2480 -0
  296. setiastro/saspro/project_io.py +797 -0
  297. setiastro/saspro/psf_utils.py +136 -0
  298. setiastro/saspro/psf_viewer.py +631 -0
  299. setiastro/saspro/pyi_rthook_astroquery.py +95 -0
  300. setiastro/saspro/remove_green.py +331 -0
  301. setiastro/saspro/remove_stars.py +1599 -0
  302. setiastro/saspro/remove_stars_preset.py +446 -0
  303. setiastro/saspro/resources.py +570 -0
  304. setiastro/saspro/rgb_combination.py +208 -0
  305. setiastro/saspro/rgb_extract.py +19 -0
  306. setiastro/saspro/rgbalign.py +727 -0
  307. setiastro/saspro/runtime_imports.py +7 -0
  308. setiastro/saspro/runtime_torch.py +754 -0
  309. setiastro/saspro/save_options.py +73 -0
  310. setiastro/saspro/selective_color.py +1614 -0
  311. setiastro/saspro/sfcc.py +1530 -0
  312. setiastro/saspro/shortcuts.py +3125 -0
  313. setiastro/saspro/signature_insert.py +1106 -0
  314. setiastro/saspro/stacking_suite.py +19069 -0
  315. setiastro/saspro/star_alignment.py +7383 -0
  316. setiastro/saspro/star_alignment_preset.py +329 -0
  317. setiastro/saspro/star_metrics.py +49 -0
  318. setiastro/saspro/star_spikes.py +769 -0
  319. setiastro/saspro/star_stretch.py +542 -0
  320. setiastro/saspro/stat_stretch.py +554 -0
  321. setiastro/saspro/status_log_dock.py +78 -0
  322. setiastro/saspro/subwindow.py +3523 -0
  323. setiastro/saspro/supernovaasteroidhunter.py +1719 -0
  324. setiastro/saspro/swap_manager.py +134 -0
  325. setiastro/saspro/torch_backend.py +89 -0
  326. setiastro/saspro/torch_rejection.py +434 -0
  327. setiastro/saspro/translations/all_source_strings.json +4726 -0
  328. setiastro/saspro/translations/ar_translations.py +4096 -0
  329. setiastro/saspro/translations/de_translations.py +3728 -0
  330. setiastro/saspro/translations/es_translations.py +4169 -0
  331. setiastro/saspro/translations/fr_translations.py +4090 -0
  332. setiastro/saspro/translations/hi_translations.py +3803 -0
  333. setiastro/saspro/translations/integrate_translations.py +271 -0
  334. setiastro/saspro/translations/it_translations.py +4728 -0
  335. setiastro/saspro/translations/ja_translations.py +3834 -0
  336. setiastro/saspro/translations/pt_translations.py +3847 -0
  337. setiastro/saspro/translations/ru_translations.py +3082 -0
  338. setiastro/saspro/translations/saspro_ar.qm +0 -0
  339. setiastro/saspro/translations/saspro_ar.ts +16019 -0
  340. setiastro/saspro/translations/saspro_de.qm +0 -0
  341. setiastro/saspro/translations/saspro_de.ts +14548 -0
  342. setiastro/saspro/translations/saspro_es.qm +0 -0
  343. setiastro/saspro/translations/saspro_es.ts +16202 -0
  344. setiastro/saspro/translations/saspro_fr.qm +0 -0
  345. setiastro/saspro/translations/saspro_fr.ts +15870 -0
  346. setiastro/saspro/translations/saspro_hi.qm +0 -0
  347. setiastro/saspro/translations/saspro_hi.ts +14855 -0
  348. setiastro/saspro/translations/saspro_it.qm +0 -0
  349. setiastro/saspro/translations/saspro_it.ts +19046 -0
  350. setiastro/saspro/translations/saspro_ja.qm +0 -0
  351. setiastro/saspro/translations/saspro_ja.ts +14980 -0
  352. setiastro/saspro/translations/saspro_pt.qm +0 -0
  353. setiastro/saspro/translations/saspro_pt.ts +15024 -0
  354. setiastro/saspro/translations/saspro_ru.qm +0 -0
  355. setiastro/saspro/translations/saspro_ru.ts +11835 -0
  356. setiastro/saspro/translations/saspro_sw.qm +0 -0
  357. setiastro/saspro/translations/saspro_sw.ts +15237 -0
  358. setiastro/saspro/translations/saspro_uk.qm +0 -0
  359. setiastro/saspro/translations/saspro_uk.ts +15248 -0
  360. setiastro/saspro/translations/saspro_zh.qm +0 -0
  361. setiastro/saspro/translations/saspro_zh.ts +15289 -0
  362. setiastro/saspro/translations/sw_translations.py +3897 -0
  363. setiastro/saspro/translations/uk_translations.py +3929 -0
  364. setiastro/saspro/translations/zh_translations.py +3910 -0
  365. setiastro/saspro/versioning.py +77 -0
  366. setiastro/saspro/view_bundle.py +1558 -0
  367. setiastro/saspro/wavescale_hdr.py +648 -0
  368. setiastro/saspro/wavescale_hdr_preset.py +101 -0
  369. setiastro/saspro/wavescalede.py +683 -0
  370. setiastro/saspro/wavescalede_preset.py +230 -0
  371. setiastro/saspro/wcs_update.py +374 -0
  372. setiastro/saspro/whitebalance.py +540 -0
  373. setiastro/saspro/widgets/__init__.py +48 -0
  374. setiastro/saspro/widgets/common_utilities.py +306 -0
  375. setiastro/saspro/widgets/graphics_views.py +122 -0
  376. setiastro/saspro/widgets/image_utils.py +518 -0
  377. setiastro/saspro/widgets/minigame/game.js +991 -0
  378. setiastro/saspro/widgets/minigame/index.html +53 -0
  379. setiastro/saspro/widgets/minigame/style.css +241 -0
  380. setiastro/saspro/widgets/preview_dialogs.py +280 -0
  381. setiastro/saspro/widgets/resource_monitor.py +313 -0
  382. setiastro/saspro/widgets/spinboxes.py +290 -0
  383. setiastro/saspro/widgets/themed_buttons.py +13 -0
  384. setiastro/saspro/widgets/wavelet_utils.py +331 -0
  385. setiastro/saspro/wimi.py +7367 -0
  386. setiastro/saspro/wims.py +588 -0
  387. setiastro/saspro/window_shelf.py +185 -0
  388. setiastro/saspro/xisf.py +1213 -0
  389. setiastrosuitepro-1.6.7.dist-info/METADATA +279 -0
  390. setiastrosuitepro-1.6.7.dist-info/RECORD +394 -0
  391. setiastrosuitepro-1.6.7.dist-info/WHEEL +4 -0
  392. setiastrosuitepro-1.6.7.dist-info/entry_points.txt +6 -0
  393. setiastrosuitepro-1.6.7.dist-info/licenses/LICENSE +674 -0
  394. setiastrosuitepro-1.6.7.dist-info/licenses/license.txt +2580 -0
@@ -0,0 +1,313 @@
1
+ # src/setiastro/saspro/widgets/resource_monitor.py
2
+ from __future__ import annotations
3
+ import os
4
+ import psutil
5
+ from PyQt6.QtCore import Qt, QUrl, QTimer, QObject, pyqtProperty, pyqtSignal, QThread
6
+ from PyQt6.QtWidgets import QWidget, QVBoxLayout, QFrame
7
+ from PyQt6.QtQuickWidgets import QQuickWidget
8
+ import time
9
+ import subprocess
10
+ from setiastro.saspro.memory_utils import get_memory_usage_mb
11
+ from setiastro.saspro.resources import _get_base_path
12
+
13
+
14
+ class GPUWorker(QThread):
15
+ resultReady = pyqtSignal(float)
16
+
17
+ def __init__(self, has_nvidia: bool, parent=None):
18
+ super().__init__(parent)
19
+ self._has_nvidia = has_nvidia
20
+
21
+ # cache + throttle (Windows PowerShell is expensive)
22
+ self._last_win_poll = 0.0
23
+ self._cached_win_val = 0.0
24
+
25
+ self._last_emit = 0.0
26
+ self._last_emitted_val = None
27
+
28
+ def _startupinfo_hidden(self):
29
+ if os.name != "nt":
30
+ return None
31
+ si = subprocess.STARTUPINFO()
32
+ si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
33
+ si.wShowWindow = 0
34
+ return si
35
+
36
+ def _get_windows_gpu_load(self) -> float:
37
+ if os.name != "nt":
38
+ return 0.0
39
+
40
+ now = time.monotonic()
41
+
42
+ # THROTTLE: run this at most once every 1.5 seconds
43
+ if (now - self._last_win_poll) < 1.5:
44
+ return self._cached_win_val
45
+
46
+ self._last_win_poll = now
47
+
48
+ try:
49
+ # Use explicit powershell.exe and make it non-interactive + hidden
50
+ cmd = [
51
+ "powershell.exe",
52
+ "-NoProfile",
53
+ "-NonInteractive",
54
+ "-ExecutionPolicy", "Bypass",
55
+ "-Command",
56
+ (
57
+ "$groups = Get-CimInstance Win32_PerfFormattedData_GPUPerformanceCounters_GPUEngine "
58
+ "-ErrorAction SilentlyContinue | "
59
+ "Group-Object -Property { $_.Name -replace '^pid_\\d+_', '' }; "
60
+ "$res_list = $groups | ForEach-Object { ($_.Group | Measure-Object -Property UtilizationPercentage -Sum).Sum }; "
61
+ "$max_val = ($res_list | Measure-Object -Maximum).Maximum; "
62
+ "if ($max_val) { [math]::Round($max_val, 1) } else { 0 }"
63
+ ),
64
+ ]
65
+
66
+ out = subprocess.check_output(
67
+ cmd,
68
+ startupinfo=self._startupinfo_hidden(),
69
+ timeout=1.0, # IMPORTANT: don’t allow 5s hangs
70
+ stderr=subprocess.DEVNULL, # keep it quiet
71
+ )
72
+ val_str = out.decode("utf-8", errors="ignore").strip()
73
+
74
+ val = float(val_str.replace(",", ".")) if val_str else 0.0
75
+ self._cached_win_val = val
76
+ return val
77
+ except Exception:
78
+ # keep last known value instead of spamming 0.0
79
+ return self._cached_win_val
80
+
81
+ def _get_gpu_load(self) -> float:
82
+ nv_val = 0.0
83
+ win_val = 0.0
84
+
85
+ # NVIDIA (fast, keep it)
86
+ if self._has_nvidia:
87
+ try:
88
+ out = subprocess.check_output(
89
+ ["nvidia-smi", "--query-gpu=utilization.gpu", "--format=csv,noheader,nounits"],
90
+ startupinfo=self._startupinfo_hidden(),
91
+ timeout=0.6,
92
+ stderr=subprocess.DEVNULL,
93
+ )
94
+ line = out.decode("utf-8", errors="ignore").strip().split("\n")[0]
95
+ nv_val = float(line)
96
+ except Exception:
97
+ pass
98
+
99
+ # Windows integrated (slow, throttled)
100
+ if os.name == "nt":
101
+ win_val = self._get_windows_gpu_load()
102
+
103
+ return max(nv_val, win_val)
104
+
105
+ def run(self):
106
+ while not self.isInterruptionRequested():
107
+ try:
108
+ val = self._get_gpu_load()
109
+
110
+ # Optional: emit only if value changed a bit, or once per 250ms max
111
+ now = time.monotonic()
112
+ if (
113
+ self._last_emitted_val is None
114
+ or abs(val - self._last_emitted_val) >= 1.0
115
+ or (now - self._last_emit) >= 0.5
116
+ ):
117
+ self._last_emit = now
118
+ self._last_emitted_val = val
119
+ self.resultReady.emit(val)
120
+
121
+ self.msleep(250)
122
+ except Exception:
123
+ self.msleep(1000)
124
+
125
+ class ResourceBackend(QObject):
126
+ """Backend logic for the QML Resource Monitor."""
127
+
128
+ cpuChanged = pyqtSignal()
129
+ ramChanged = pyqtSignal()
130
+ gpuChanged = pyqtSignal()
131
+ appRamChanged = pyqtSignal()
132
+
133
+ def __init__(self, parent=None):
134
+ super().__init__(parent)
135
+ self._cpu = 0.0
136
+ self._ram = 0.0
137
+ self._gpu = 0.0
138
+ self._app_ram_val = 0.0
139
+ self._app_ram_str = "0 MB"
140
+
141
+ # Check if nvidia-smi is reachable once
142
+ has_nvidia = False
143
+ try:
144
+ import shutil
145
+ if shutil.which("nvidia-smi"):
146
+ has_nvidia = True
147
+ except Exception:
148
+ pass
149
+
150
+ # Start Background GPU Worker
151
+ self._gpu_worker = GPUWorker(has_nvidia, self)
152
+ self._gpu_worker.resultReady.connect(self._on_gpu_measured)
153
+ self._gpu_worker.start()
154
+
155
+ # Timer for CPU/RAM updates (250ms as requested)
156
+ self._timer = QTimer(self)
157
+ self._timer.setInterval(250)
158
+ self._timer.timeout.connect(self._update_stats)
159
+ self._timer.start()
160
+
161
+ def _on_gpu_measured(self, val: float):
162
+ self._gpu = val
163
+ self.gpuChanged.emit()
164
+
165
+ @pyqtProperty(float, notify=cpuChanged)
166
+ def cpuUsage(self):
167
+ return self._cpu
168
+
169
+ @pyqtProperty(float, notify=ramChanged)
170
+ def ramUsage(self):
171
+ return self._ram
172
+
173
+ @pyqtProperty(float, notify=gpuChanged)
174
+ def gpuUsage(self):
175
+ return self._gpu
176
+
177
+ @pyqtProperty(str, notify=appRamChanged)
178
+ def appRamString(self):
179
+ return self._app_ram_str
180
+
181
+ def _update_stats(self):
182
+ # 1. CPU
183
+ try:
184
+ self._cpu = psutil.cpu_percent(interval=None)
185
+ except Exception:
186
+ self._cpu = 0.0
187
+
188
+ # 2. System RAM
189
+ try:
190
+ vm = psutil.virtual_memory()
191
+ self._ram = vm.percent
192
+ except Exception:
193
+ self._ram = 0.0
194
+
195
+ # 3. App RAM
196
+ try:
197
+ mb = get_memory_usage_mb()
198
+ self._app_ram_val = mb
199
+ self._app_ram_str = f"{int(mb)} MB"
200
+ except Exception:
201
+ self._app_ram_str = "? MB"
202
+
203
+ self.cpuChanged.emit()
204
+ self.ramChanged.emit()
205
+ self.appRamChanged.emit()
206
+
207
+ def stop(self):
208
+ """Explicitly stop background threads."""
209
+ if hasattr(self, "_gpu_worker") and self._gpu_worker.isRunning():
210
+ self._gpu_worker.requestInterruption()
211
+ self._gpu_worker.quit()
212
+ self._gpu_worker.wait(1000)
213
+
214
+ def __del__(self):
215
+ self.stop()
216
+
217
+
218
+ class SystemMonitorWidget(QQuickWidget):
219
+ """
220
+ The QQuickWidget hosting the QML content.
221
+ """
222
+ def __init__(self, parent=None):
223
+ super().__init__(parent)
224
+
225
+ self.setResizeMode(QQuickWidget.ResizeMode.SizeRootObjectToView)
226
+ self.setAttribute(Qt.WidgetAttribute.WA_AlwaysStackOnTop, False)
227
+ self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, True)
228
+ self.setClearColor(Qt.GlobalColor.transparent)
229
+ self._qml_push_pending = False
230
+
231
+ # Connect Backend
232
+ self.backend = ResourceBackend(self)
233
+ self.rootContext().setContextProperty("backend", self.backend)
234
+
235
+ # We need to manually wire property updates because we are binding to root properties in QML
236
+ # Actually, simpler pattern: QML file reads from an object we inject.
237
+ # Let's adjust QML slightly to bind to `backend.cpuUsage` etc. if we can,
238
+ # OR we leave QML as having properties and we set them from Python.
239
+ #
240
+ # Better approach for Py+QML:
241
+ # Inject `backend` into context, modify QML to use `backend.cpuUsage`.
242
+ # But since I already wrote QML with root properties, I will just set them directly
243
+ # or update the QML file. Updating QML is cleaner.
244
+ #
245
+ # For now, let's keep QML independent and binding via setProperty?
246
+ # No, properly: context property is best.
247
+ #
248
+ # Let's re-write the QML loading part to use a safer 'initialProperties' approach or just signal/slots.
249
+ #
250
+ # EASIEST: QML binds to `root.cpuUsage`. Python sets `root.cpuUsage`.
251
+
252
+
253
+ # Load QML
254
+ qml_path = os.path.join(_get_base_path(), "qml", "ResourceMonitor.qml")
255
+ self.setSource(QUrl.fromLocalFile(qml_path))
256
+
257
+ def _schedule_qml_push(self):
258
+ if self._qml_push_pending:
259
+ return
260
+ self._qml_push_pending = True
261
+ QTimer.singleShot(0, self._push_data_to_qml_coalesced)
262
+
263
+ def _push_data_to_qml_coalesced(self):
264
+ self._qml_push_pending = False
265
+ self._push_data_to_qml()
266
+
267
+
268
+ def _push_data_to_qml(self):
269
+ root = self.rootObject()
270
+ if root:
271
+ root.setProperty("cpuUsage", self.backend.cpuUsage)
272
+ root.setProperty("ramUsage", self.backend.ramUsage)
273
+ root.setProperty("gpuUsage", self.backend.gpuUsage)
274
+ root.setProperty("appRamString", self.backend.appRamString)
275
+
276
+ # --- Drag & Drop Support ---
277
+ def mousePressEvent(self, event):
278
+ if event.button() == Qt.MouseButton.LeftButton:
279
+ # Wayland-friendly: ask compositor to move the window
280
+ wh = self.windowHandle()
281
+ if wh is not None:
282
+ try:
283
+ # Works best for frameless overlays on Wayland
284
+ wh.startSystemMove()
285
+ event.accept()
286
+ return
287
+ except Exception:
288
+ pass
289
+
290
+ # Fallback (Windows/X11): manual move tracking
291
+ self._drag_start_pos = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
292
+ event.accept()
293
+ return
294
+
295
+ super().mousePressEvent(event)
296
+
297
+ def mouseMoveEvent(self, event):
298
+ if event.buttons() & Qt.MouseButton.LeftButton:
299
+ if hasattr(self, "_drag_start_pos"):
300
+ self.move(event.globalPosition().toPoint() - self._drag_start_pos)
301
+ event.accept()
302
+ else:
303
+ super().mouseMoveEvent(event)
304
+
305
+ def mouseReleaseEvent(self, event):
306
+ if event.button() == Qt.MouseButton.LeftButton:
307
+ from PyQt6.QtCore import QSettings
308
+ settings = QSettings("SetiAstro", "SetiAstroSuitePro")
309
+ pos = self.pos()
310
+ settings.setValue("ui/resource_monitor_pos_x", pos.x())
311
+ settings.setValue("ui/resource_monitor_pos_y", pos.y())
312
+ event.accept()
313
+ super().mouseReleaseEvent(event)
@@ -0,0 +1,290 @@
1
+ # pro/widgets/spinboxes.py
2
+ """
3
+ Custom spinbox widgets for Seti Astro Suite Pro.
4
+
5
+ Provides enhanced spinbox widgets with consistent styling and behavior.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from PyQt6.QtCore import Qt, pyqtSignal
10
+ from PyQt6.QtWidgets import (
11
+ QWidget, QHBoxLayout, QVBoxLayout, QLineEdit, QToolButton
12
+ )
13
+ from PyQt6.QtGui import QIntValidator, QDoubleValidator
14
+
15
+
16
+ class CustomSpinBox(QWidget):
17
+ """
18
+ A custom integer spin box widget with up/down buttons.
19
+
20
+ Emits valueChanged(int) when the value changes.
21
+
22
+ Usage:
23
+ spin = CustomSpinBox(minimum=0, maximum=100, initial=50, step=1)
24
+ spin.valueChanged.connect(my_handler)
25
+ """
26
+ valueChanged = pyqtSignal(int)
27
+
28
+ def __init__(
29
+ self,
30
+ minimum: int = 0,
31
+ maximum: int = 100,
32
+ initial: int = 0,
33
+ step: int = 1,
34
+ parent: QWidget | None = None
35
+ ):
36
+ super().__init__(parent)
37
+ self.minimum = minimum
38
+ self.maximum = maximum
39
+ self.step = step
40
+ self._value = int(initial)
41
+
42
+ # Line edit for value display/entry
43
+ self.lineEdit = QLineEdit(str(initial))
44
+ self.lineEdit.setAlignment(Qt.AlignmentFlag.AlignRight)
45
+ self.lineEdit.setValidator(QIntValidator(self.minimum, self.maximum, self))
46
+ self.lineEdit.editingFinished.connect(self._on_editing_finished)
47
+
48
+ # Up/down buttons
49
+ self.upButton = QToolButton()
50
+ self.upButton.setText("▲")
51
+ self.upButton.setAutoRepeat(True)
52
+ self.upButton.setAutoRepeatInterval(50)
53
+ self.upButton.setAutoRepeatDelay(300)
54
+ self.upButton.clicked.connect(self._increase_value)
55
+
56
+ self.downButton = QToolButton()
57
+ self.downButton.setText("▼")
58
+ self.downButton.setAutoRepeat(True)
59
+ self.downButton.setAutoRepeatInterval(50)
60
+ self.downButton.setAutoRepeatDelay(300)
61
+ self.downButton.clicked.connect(self._decrease_value)
62
+
63
+ # Layout buttons vertically
64
+ button_layout = QVBoxLayout()
65
+ button_layout.addWidget(self.upButton)
66
+ button_layout.addWidget(self.downButton)
67
+ button_layout.setSpacing(0)
68
+ button_layout.setContentsMargins(0, 0, 0, 0)
69
+
70
+ # Main horizontal layout
71
+ main_layout = QHBoxLayout()
72
+ main_layout.addWidget(self.lineEdit)
73
+ main_layout.addLayout(button_layout)
74
+ main_layout.setSpacing(0)
75
+ main_layout.setContentsMargins(0, 0, 0, 0)
76
+ self.setLayout(main_layout)
77
+
78
+ self._update_button_states()
79
+
80
+ def value(self) -> int:
81
+ """Qt-style getter."""
82
+ return int(self._value)
83
+
84
+ def setValue(self, val: int) -> None:
85
+ val = int(val)
86
+ val = max(int(self.minimum), min(int(self.maximum), val))
87
+ if val != self._value:
88
+ self._value = val
89
+ self.lineEdit.setText(str(val))
90
+ self.valueChanged.emit(val)
91
+ self._update_button_states()
92
+
93
+ def setMinimum(self, minimum: int) -> None:
94
+ """Set the minimum value."""
95
+ self.minimum = minimum
96
+ self.lineEdit.setValidator(QIntValidator(self.minimum, self.maximum, self))
97
+ if self._value < minimum:
98
+ self.setValue(minimum)
99
+ self._update_button_states()
100
+
101
+ def setMaximum(self, maximum: int) -> None:
102
+ """Set the maximum value."""
103
+ self.maximum = maximum
104
+ self.lineEdit.setValidator(QIntValidator(self.minimum, self.maximum, self))
105
+ if self._value > maximum:
106
+ self.setValue(maximum)
107
+ self._update_button_states()
108
+
109
+ def setRange(self, minimum: int, maximum: int) -> None:
110
+ """Set both minimum and maximum values."""
111
+ self.minimum = minimum
112
+ self.maximum = maximum
113
+ self.lineEdit.setValidator(QIntValidator(self.minimum, self.maximum, self))
114
+ self.setValue(max(minimum, min(maximum, self._value)))
115
+
116
+ def setSingleStep(self, step: int) -> None:
117
+ """Set the step value for up/down buttons."""
118
+ self.step = step
119
+
120
+ def _on_editing_finished(self) -> None:
121
+ """Handle manual text entry."""
122
+ try:
123
+ val = int(self.lineEdit.text())
124
+ self.setValue(val)
125
+ except ValueError:
126
+ self.lineEdit.setText(str(self._value))
127
+
128
+ def _increase_value(self) -> None:
129
+ """Increase value by step."""
130
+ self.setValue(self._value + self.step)
131
+
132
+ def _decrease_value(self) -> None:
133
+ """Decrease value by step."""
134
+ self.setValue(self._value - self.step)
135
+
136
+ def value(self) -> int:
137
+ """
138
+ Qt-compatible getter (QSpinBox uses value()).
139
+
140
+ Note: we also have @property value for convenience,
141
+ but code that expects QSpinBox calls value().
142
+ """
143
+ return self._value
144
+
145
+ def _update_button_states(self) -> None:
146
+ self.upButton.setEnabled(self._value < self.maximum)
147
+ self.downButton.setEnabled(self._value > self.minimum)
148
+
149
+
150
+ class CustomDoubleSpinBox(QWidget):
151
+ """
152
+ A custom double (float) spin box widget with up/down buttons.
153
+
154
+ Emits valueChanged(float) when the value changes.
155
+
156
+ Usage:
157
+ spin = CustomDoubleSpinBox(minimum=0.0, maximum=1.0, initial=0.5, step=0.1, decimals=2)
158
+ spin.valueChanged.connect(my_handler)
159
+ """
160
+ valueChanged = pyqtSignal(float)
161
+
162
+ def __init__(
163
+ self,
164
+ minimum: float = 0.0,
165
+ maximum: float = 100.0,
166
+ initial: float = 0.0,
167
+ step: float = 1.0,
168
+ decimals: int = 2,
169
+ parent: QWidget | None = None
170
+ ):
171
+ super().__init__(parent)
172
+ self.minimum = minimum
173
+ self.maximum = maximum
174
+ self.step = step
175
+ self.decimals = decimals
176
+ self._value = float(initial)
177
+
178
+ # Line edit for value display/entry
179
+ self.lineEdit = QLineEdit(f"{initial:.{decimals}f}")
180
+ self.lineEdit.setAlignment(Qt.AlignmentFlag.AlignRight)
181
+ self.lineEdit.setValidator(QDoubleValidator(self.minimum, self.maximum, decimals, self))
182
+ self.lineEdit.editingFinished.connect(self._on_editing_finished)
183
+
184
+ # Up/down buttons
185
+ self.upButton = QToolButton()
186
+ self.upButton.setText("▲")
187
+ self.upButton.setAutoRepeat(True)
188
+ self.upButton.setAutoRepeatInterval(50)
189
+ self.upButton.setAutoRepeatDelay(300)
190
+ self.upButton.clicked.connect(self._increase_value)
191
+
192
+ self.downButton = QToolButton()
193
+ self.downButton.setText("▼")
194
+ self.downButton.setAutoRepeat(True)
195
+ self.downButton.setAutoRepeatInterval(50)
196
+ self.downButton.setAutoRepeatDelay(300)
197
+ self.downButton.clicked.connect(self._decrease_value)
198
+
199
+ # Layout buttons vertically
200
+ button_layout = QVBoxLayout()
201
+ button_layout.addWidget(self.upButton)
202
+ button_layout.addWidget(self.downButton)
203
+ button_layout.setSpacing(0)
204
+ button_layout.setContentsMargins(0, 0, 0, 0)
205
+
206
+ # Main horizontal layout
207
+ main_layout = QHBoxLayout()
208
+ main_layout.addWidget(self.lineEdit)
209
+ main_layout.addLayout(button_layout)
210
+ main_layout.setSpacing(0)
211
+ main_layout.setContentsMargins(0, 0, 0, 0)
212
+ self.setLayout(main_layout)
213
+
214
+ self._update_button_states()
215
+
216
+ def value(self) -> float:
217
+ """Qt-style getter."""
218
+ return float(self._value)
219
+
220
+ def setValue(self, val: float) -> None:
221
+ val = float(val)
222
+ val = max(float(self.minimum), min(float(self.maximum), val))
223
+ if abs(val - self._value) > 1e-10:
224
+ self._value = val
225
+ self.lineEdit.setText(f"{val:.{self.decimals}f}")
226
+ self.valueChanged.emit(val)
227
+ self._update_button_states()
228
+
229
+ def setMinimum(self, minimum: float) -> None:
230
+ """Set the minimum value."""
231
+ self.minimum = minimum
232
+ self.lineEdit.setValidator(QDoubleValidator(self.minimum, self.maximum, self.decimals, self))
233
+ if self._value < minimum:
234
+ self.setValue(minimum)
235
+ self._update_button_states()
236
+
237
+ def value(self) -> float:
238
+ """
239
+ Qt-compatible getter (QDoubleSpinBox uses value()).
240
+
241
+ Note: we also have @property value for convenience,
242
+ but code that expects QDoubleSpinBox calls value().
243
+ """
244
+ return self._value
245
+
246
+ def setMaximum(self, maximum: float) -> None:
247
+ """Set the maximum value."""
248
+ self.maximum = maximum
249
+ self.lineEdit.setValidator(QDoubleValidator(self.minimum, self.maximum, self.decimals, self))
250
+ if self._value > maximum:
251
+ self.setValue(maximum)
252
+ self._update_button_states()
253
+
254
+ def setRange(self, minimum: float, maximum: float) -> None:
255
+ """Set both minimum and maximum values."""
256
+ self.minimum = minimum
257
+ self.maximum = maximum
258
+ self.lineEdit.setValidator(QDoubleValidator(self.minimum, self.maximum, self.decimals, self))
259
+ self.setValue(max(minimum, min(maximum, self._value)))
260
+
261
+ def setSingleStep(self, step: float) -> None:
262
+ """Set the step value for up/down buttons."""
263
+ self.step = step
264
+
265
+ def setDecimals(self, decimals: int) -> None:
266
+ """Set the number of decimal places."""
267
+ self.decimals = decimals
268
+ self.lineEdit.setText(f"{self._value:.{decimals}f}")
269
+ self.lineEdit.setValidator(QDoubleValidator(self.minimum, self.maximum, decimals, self))
270
+
271
+ def _on_editing_finished(self) -> None:
272
+ """Handle manual text entry."""
273
+ try:
274
+ val = float(self.lineEdit.text())
275
+ self.setValue(val)
276
+ except ValueError:
277
+ self.lineEdit.setText(f"{self._value:.{self.decimals}f}")
278
+
279
+ def _increase_value(self) -> None:
280
+ """Increase value by step."""
281
+ self.setValue(self._value + self.step)
282
+
283
+ def _decrease_value(self) -> None:
284
+ """Decrease value by step."""
285
+ self.setValue(self._value - self.step)
286
+
287
+ def _update_button_states(self) -> None:
288
+ """Enable/disable buttons at limits."""
289
+ self.upButton.setEnabled(self._value < self.maximum - 1e-10)
290
+ self.downButton.setEnabled(self._value > self.minimum + 1e-10)
@@ -0,0 +1,13 @@
1
+ # pro/widgets/themed_buttons.py
2
+ from __future__ import annotations
3
+ from PyQt6.QtGui import QIcon
4
+ from PyQt6.QtWidgets import QToolButton
5
+
6
+ def themed_toolbtn(icon_name: str, tip: str, *, on_click=None) -> QToolButton:
7
+ b = QToolButton()
8
+ b.setIcon(QIcon.fromTheme(icon_name))
9
+ b.setToolTip(tip)
10
+ b.setAutoRaise(True) # nice flat toolbar look
11
+ if on_click is not None:
12
+ b.clicked.connect(on_click)
13
+ return b