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,540 @@
1
+ # pro/whitebalance.py
2
+ from __future__ import annotations
3
+
4
+ import numpy as np
5
+ from typing import Dict, Tuple, Optional
6
+
7
+ from PyQt6.QtCore import Qt, QTimer
8
+ from PyQt6.QtGui import QIcon, QImage, QPixmap
9
+ from PyQt6.QtWidgets import (
10
+ QDialog, QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QWidget, QGroupBox,
11
+ QGridLayout, QSlider, QCheckBox, QPushButton, QMessageBox, QDoubleSpinBox
12
+ )
13
+
14
+ # imageops
15
+ from setiastro.saspro.imageops.starbasedwhitebalance import apply_star_based_white_balance
16
+ from setiastro.saspro.imageops.stretch import stretch_color_image
17
+
18
+ # Shared utilities
19
+ from setiastro.saspro.widgets.image_utils import (
20
+ to_float01 as _to_float01,
21
+ extract_mask_from_document as _active_mask_array_from_doc
22
+ )
23
+
24
+ from matplotlib import pyplot as plt # NEW
25
+ from matplotlib.patches import Circle # NEW
26
+ from matplotlib.ticker import MaxNLocator # NEW
27
+ # ----------------------------
28
+ # Core WB implementations
29
+ # ----------------------------
30
+ def plot_star_color_ratios_comparison(raw_pixels: np.ndarray, after_pixels: np.ndarray):
31
+ """
32
+ Replicates the SASv2 diagnostic plot: star color ratios before/after WB,
33
+ with an RGB background grid, best-fit line, and neutral markers.
34
+ Expects Nx3 arrays of star RGB samples in [0,1] (or any common scale).
35
+ """
36
+ def compute_ratios(pixels: np.ndarray):
37
+ eps = 1e-8
38
+ rb = pixels[:, 0] / (pixels[:, 2] + eps) # R/B
39
+ gb = pixels[:, 1] / (pixels[:, 2] + eps) # G/B
40
+ return rb, gb
41
+
42
+ rb_before, gb_before = compute_ratios(raw_pixels)
43
+ rb_after, gb_after = compute_ratios(after_pixels)
44
+
45
+ # Optional: keep only finite points
46
+ def _finite(x, y):
47
+ m = np.isfinite(x) & np.isfinite(y)
48
+ return x[m], y[m]
49
+ rb_before, gb_before = _finite(rb_before, gb_before)
50
+ rb_after, gb_after = _finite(rb_after, gb_after)
51
+
52
+ # Plot bounds + background grid
53
+ rmin, rmax = 0.5, 2.0
54
+ gmin, gmax = 0.5, 2.0
55
+ res = 200
56
+
57
+ rb_vals = np.linspace(rmin, rmax, res)
58
+ gb_vals = np.linspace(gmin, gmax, res)
59
+ rb_grid, gb_grid = np.meshgrid(rb_vals, gb_vals)
60
+ rgb_image = np.stack([rb_grid, gb_grid, np.ones_like(rb_grid)], axis=-1)
61
+ rgb_image /= np.maximum(rgb_image.max(axis=2, keepdims=True), 1e-8)
62
+
63
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6), sharex=True, sharey=True)
64
+
65
+ def plot_panel(ax, rb_data, gb_data, title):
66
+ ax.imshow(rgb_image, extent=(rmin, rmax, gmin, gmax), origin='lower', aspect='auto')
67
+ ax.scatter(rb_data, gb_data, alpha=0.6, edgecolors='k', label="Stars")
68
+
69
+ if rb_data.size >= 2:
70
+ m, b = np.polyfit(rb_data, gb_data, 1)
71
+ xs = np.linspace(rmin, rmax, 100)
72
+ ax.plot(xs, m * xs + b, 'r--', label=f"Best Fit\ny = {m:.2f}x + {b:.2f}")
73
+
74
+ ax.axhline(1.0, color='gray', linestyle=':', linewidth=1)
75
+ ax.axvline(1.0, color='gray', linestyle=':', linewidth=1)
76
+ ax.add_patch(Circle((1.0, 1.0), 0.2, fill=False, edgecolor='blue', linestyle='--', linewidth=1.5))
77
+ ax.text(1.03, 1.17, "Neutral Region", color='blue', fontsize=9)
78
+
79
+ ax.set_xlim(rmin, rmax); ax.set_ylim(gmin, gmax)
80
+ ax.set_title(f"{title} White Balance")
81
+ ax.set_xlabel("Red / Blue Ratio"); ax.set_ylabel("Green / Blue Ratio")
82
+ ax.xaxis.set_major_locator(MaxNLocator(integer=True))
83
+ ax.yaxis.set_major_locator(MaxNLocator(integer=True))
84
+ ax.grid(True); ax.legend()
85
+
86
+ plot_panel(ax1, rb_before, gb_before, "Before")
87
+ plot_panel(ax2, rb_after, gb_after, "After")
88
+
89
+ plt.suptitle("Star Color Ratios with RGB Mapping", fontsize=14)
90
+ plt.tight_layout()
91
+ plt.show(block=False)
92
+
93
+ def apply_manual_white_balance(img: np.ndarray, r_gain: float, g_gain: float, b_gain: float) -> np.ndarray:
94
+ """Simple per-channel gain, clipped to [0,1]."""
95
+ if img.ndim != 3 or img.shape[2] != 3:
96
+ raise ValueError("Manual WB requires RGB image.")
97
+ out = _to_float01(img).copy()
98
+ gains = np.array([r_gain, g_gain, b_gain], dtype=np.float32).reshape((1, 1, 3))
99
+ out = np.clip(out * gains, 0.0, 1.0)
100
+ return out.astype(np.float32, copy=False)
101
+
102
+
103
+ def apply_auto_white_balance(img: np.ndarray) -> np.ndarray:
104
+ """
105
+ Gray-world auto WB: scale each channel so its mean equals the overall mean.
106
+ """
107
+ if img.ndim != 3 or img.shape[2] != 3:
108
+ raise ValueError("Auto WB requires RGB image.")
109
+ rgb = _to_float01(img)
110
+ means = np.mean(rgb, axis=(0, 1))
111
+ overall = float(np.mean(means))
112
+ means = np.where(means <= 1e-8, 1e-8, means)
113
+ scale = (overall / means).reshape((1, 1, 3))
114
+ out = np.clip(rgb * scale, 0.0, 1.0)
115
+ return out.astype(np.float32, copy=False)
116
+
117
+
118
+ # --------------------------------
119
+ # Headless entry point for DnD
120
+ # --------------------------------
121
+ def apply_white_balance_to_doc(doc, preset: Optional[Dict] = None):
122
+ """
123
+ Preset schema:
124
+ {
125
+ "mode": "star" | "manual" | "auto", # default "star"
126
+ # star mode:
127
+ "threshold": float (default 50),
128
+ "reuse_cached_sources": bool (default True),
129
+ # manual mode:
130
+ "r_gain": float (default 1.0), "g_gain": float (default 1.0), "b_gain": float (default 1.0)
131
+ }
132
+ """
133
+ import numpy as np
134
+
135
+ p = dict(preset or {})
136
+ mode = (p.get("mode") or "star").lower()
137
+
138
+ base = np.asarray(doc.image).astype(np.float32, copy=False)
139
+ if base.ndim != 3 or base.shape[2] != 3:
140
+ raise ValueError("White Balance requires an RGB image.")
141
+
142
+ base_n = _to_float01(base)
143
+
144
+ try:
145
+ if mode == "manual":
146
+ r = float(p.get("r_gain", 1.0))
147
+ g = float(p.get("g_gain", 1.0))
148
+ b = float(p.get("b_gain", 1.0))
149
+ out = apply_manual_white_balance(base_n, r, g, b)
150
+
151
+ elif mode == "auto":
152
+ out = apply_auto_white_balance(base_n)
153
+
154
+ else: # "star"
155
+ thr = float(p.get("threshold", 50.0))
156
+ reuse = bool(p.get("reuse_cached_sources", True))
157
+ out, _count, _overlay = apply_star_based_white_balance(
158
+ base_n, threshold=thr, autostretch=False,
159
+ reuse_cached_sources=reuse, return_star_colors=False
160
+ )
161
+ except Exception as e:
162
+ # Fallback: if SEP missing or star detection fails, try Auto WB
163
+ if mode == "star":
164
+ try:
165
+ out = apply_auto_white_balance(base_n)
166
+ except Exception:
167
+ raise e
168
+ else:
169
+ raise
170
+
171
+ # Destination-mask blend (if any)
172
+ m = _active_mask_array_from_doc(doc)
173
+ if m is not None:
174
+ if out.ndim == 3:
175
+ m3 = np.repeat(m[..., None], 3, axis=2).astype(np.float32, copy=False)
176
+ else:
177
+ m3 = m.astype(np.float32, copy=False)
178
+ base_for_blend = _to_float01(np.asarray(doc.image).astype(np.float32, copy=False))
179
+ out = base_for_blend * (1.0 - m3) + out * m3
180
+
181
+ doc.apply_edit(
182
+ out.astype(np.float32, copy=False),
183
+ metadata={"step_name": "White Balance", "preset": p},
184
+ step_name="White Balance",
185
+ )
186
+
187
+
188
+ # -------------------------
189
+ # Interactive dialog (UI)
190
+ # -------------------------
191
+ class WhiteBalanceDialog(QDialog):
192
+ def __init__(self, parent, doc, icon: QIcon | None = None):
193
+ super().__init__(parent)
194
+ self._main = parent
195
+ self.doc = doc
196
+ self._active_doc_conn = False
197
+ if hasattr(self._main, "currentDocumentChanged"):
198
+ try:
199
+ self._main.currentDocumentChanged.connect(self._on_active_doc_changed)
200
+ self._active_doc_conn = True
201
+ except Exception:
202
+ self._active_doc_conn = False
203
+ if icon:
204
+ self.setWindowIcon(icon)
205
+ self.setWindowTitle(self.tr("White Balance"))
206
+ self.setWindowFlag(Qt.WindowType.Window, True)
207
+ self.setWindowModality(Qt.WindowModality.NonModal)
208
+ self.setModal(False)
209
+ try:
210
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
211
+ except Exception:
212
+ pass # older PyQt6 versions
213
+ self.resize(900, 600)
214
+
215
+ self._build_ui()
216
+ self._wire_events()
217
+
218
+ # default to Star-Based, like SASv2
219
+ self.type_combo.setCurrentText("Star-Based")
220
+ self._update_mode_widgets()
221
+ # kick off a first detection preview
222
+ QTimer.singleShot(200, self._update_star_preview)
223
+ self.finished.connect(lambda *_: self._cleanup())
224
+
225
+
226
+ # ---- UI construction ------------------------------------------------
227
+ def _build_ui(self):
228
+ self.main_layout = QVBoxLayout(self)
229
+
230
+ # Type selector
231
+ row = QHBoxLayout()
232
+ row.addWidget(QLabel(self.tr("White Balance Type:")))
233
+ self.type_combo = QComboBox()
234
+ self.type_combo.addItems([self.tr("Star-Based"), self.tr("Manual"), self.tr("Auto")])
235
+ row.addWidget(self.type_combo); row.addStretch()
236
+ self.main_layout.addLayout(row)
237
+
238
+ # Manual group
239
+ self.manual_group = QGroupBox(self.tr("Manual Gains"))
240
+ g = QGridLayout(self.manual_group)
241
+ self.r_spin = QDoubleSpinBox(); self._cfg_gain(self.r_spin, 1.0)
242
+ self.g_spin = QDoubleSpinBox(); self._cfg_gain(self.g_spin, 1.0)
243
+ self.b_spin = QDoubleSpinBox(); self._cfg_gain(self.b_spin, 1.0)
244
+ g.addWidget(QLabel(self.tr("Red gain:")), 0, 0); g.addWidget(self.r_spin, 0, 1)
245
+ g.addWidget(QLabel(self.tr("Green gain:")), 1, 0); g.addWidget(self.g_spin, 1, 1)
246
+ g.addWidget(QLabel(self.tr("Blue gain:")), 2, 0); g.addWidget(self.b_spin, 2, 1)
247
+ self.main_layout.addWidget(self.manual_group)
248
+
249
+ # Star-based controls + preview
250
+ self.star_group = QGroupBox(self.tr("Star-Based Settings"))
251
+ sg = QVBoxLayout(self.star_group)
252
+ # threshold slider
253
+ thr_row = QHBoxLayout()
254
+ thr_row.addWidget(QLabel(self.tr("Detection threshold (σ):")))
255
+ self.thr_slider = QSlider(Qt.Orientation.Horizontal)
256
+ self.thr_slider.setMinimum(1); self.thr_slider.setMaximum(100)
257
+ self.thr_slider.setValue(50); self.thr_slider.setTickInterval(10)
258
+ self.thr_slider.setTickPosition(QSlider.TickPosition.TicksBelow)
259
+ self.thr_label = QLabel("50")
260
+ thr_row.addWidget(self.thr_slider); thr_row.addWidget(self.thr_label)
261
+ sg.addLayout(thr_row)
262
+
263
+ self.chk_reuse = QCheckBox(self.tr("Reuse cached star detections")); self.chk_reuse.setChecked(True)
264
+ sg.addWidget(self.chk_reuse)
265
+
266
+ self.chk_autostretch_overlay = QCheckBox(self.tr("Autostretch overlay preview")); self.chk_autostretch_overlay.setChecked(True)
267
+ sg.addWidget(self.chk_autostretch_overlay)
268
+
269
+ # star count + image preview
270
+ self.star_count = QLabel(self.tr("Detecting stars..."))
271
+ sg.addWidget(self.star_count)
272
+
273
+ self.preview = QLabel(); self.preview.setMinimumSize(640, 360)
274
+ self.preview.setStyleSheet("border: 1px solid #333;")
275
+ sg.addWidget(self.preview)
276
+ self.main_layout.addWidget(self.star_group)
277
+
278
+ # Buttons
279
+ btn_row = QHBoxLayout()
280
+ btn_row.addStretch()
281
+ self.btn_apply = QPushButton(self.tr("Apply"))
282
+ self.btn_cancel = QPushButton(self.tr("Cancel"))
283
+ btn_row.addWidget(self.btn_apply)
284
+ btn_row.addWidget(self.btn_cancel)
285
+ self.main_layout.addLayout(btn_row)
286
+
287
+ self.setLayout(self.main_layout)
288
+
289
+ # debounce timer for star preview
290
+ self._debounce = QTimer(self); self._debounce.setSingleShot(True); self._debounce.setInterval(600)
291
+ self._debounce.timeout.connect(self._update_star_preview)
292
+
293
+ def _cfg_gain(self, box: QDoubleSpinBox, val: float):
294
+ box.setRange(0.5, 1.5); box.setDecimals(3); box.setSingleStep(0.01); box.setValue(val)
295
+
296
+ # ---- events ---------------------------------------------------------
297
+ def _wire_events(self):
298
+ self.type_combo.currentTextChanged.connect(self._update_mode_widgets)
299
+ self.btn_cancel.clicked.connect(self.reject)
300
+ self.btn_apply.clicked.connect(self._on_apply)
301
+
302
+ self.thr_slider.valueChanged.connect(lambda v: (self.thr_label.setText(str(v)), self._debounce.start()))
303
+ self.chk_autostretch_overlay.toggled.connect(lambda _=None: self._debounce.start())
304
+ self.chk_reuse.toggled.connect(lambda _=None: self._debounce.start())
305
+
306
+ def _update_mode_widgets(self):
307
+ t = self.type_combo.currentText()
308
+ self.manual_group.setVisible(t == "Manual")
309
+ self.star_group.setVisible(t == "Star-Based")
310
+
311
+ # ---- active document change ------------------------------------
312
+ def _on_active_doc_changed(self, doc):
313
+ """Called when user clicks a different image window."""
314
+ if doc is None or getattr(doc, "image", None) is None:
315
+ return
316
+ self.doc = doc
317
+ # Force fresh star detection by temporarily disabling cache
318
+ old_reuse = self.chk_reuse.isChecked()
319
+ self.chk_reuse.setChecked(False)
320
+ self._update_star_preview()
321
+ self.chk_reuse.setChecked(old_reuse)
322
+
323
+ # ---- preview --------------------------------------------------------
324
+ def _update_star_preview(self):
325
+ if self.type_combo.currentText() != "Star-Based":
326
+ return
327
+ try:
328
+ img = _to_float01(np.asarray(self.doc.image))
329
+ thr = float(self.thr_slider.value())
330
+ reuse = bool(self.chk_reuse.isChecked())
331
+ auto = bool(self.chk_autostretch_overlay.isChecked())
332
+
333
+ _, count, overlay = apply_star_based_white_balance(
334
+ img, threshold=thr, autostretch=auto,
335
+ reuse_cached_sources=reuse, return_star_colors=False
336
+ )
337
+ self.star_count.setText(self.tr("Detected {0} stars.").format(count))
338
+ # to pixmap
339
+ overlay8 = np.ascontiguousarray(np.clip(overlay * 255.0, 0, 255).astype(np.uint8))
340
+ h, w, _ = overlay8.shape
341
+ qimg = QImage(overlay8.data, w, h, 3 * w, QImage.Format.Format_RGB888)
342
+
343
+ # Make sure the numpy buffer stays alive until QPixmap is created
344
+ pm = QPixmap.fromImage(qimg.copy()).scaled(
345
+ self.preview.width(), self.preview.height(),
346
+ Qt.AspectRatioMode.KeepAspectRatio,
347
+ Qt.TransformationMode.SmoothTransformation
348
+ )
349
+ self.preview.setPixmap(pm)
350
+ except Exception as e:
351
+ self.star_count.setText(self.tr("Detection failed."))
352
+ self.preview.clear()
353
+
354
+ # ---- apply ----------------------------------------------------------
355
+ def _on_apply(self):
356
+ mode = self.type_combo.currentText()
357
+
358
+ # Find the main window that carries the replay machinery
359
+ main = self.parent()
360
+ while main is not None and not hasattr(main, "replay_last_action_on_base"):
361
+ main = main.parent()
362
+
363
+ def _record_preset_for_replay(preset: dict):
364
+ """Helper to stash the WB preset on the main window for Replay Last Action."""
365
+ if main is None:
366
+ return
367
+ try:
368
+ main._last_headless_command = {
369
+ "command_id": "white_balance",
370
+ "preset": preset,
371
+ }
372
+ if hasattr(main, "_log"):
373
+ mode_str = str(preset.get("mode", "star")).lower()
374
+ if mode_str == "manual":
375
+ r = float(preset.get("r_gain", 1.0))
376
+ g = float(preset.get("g_gain", 1.0))
377
+ b = float(preset.get("b_gain", 1.0))
378
+ main._log(
379
+ f"[Replay] Recorded White Balance preset "
380
+ f"(mode=manual, R={r:.3f}, G={g:.3f}, B={b:.3f})"
381
+ )
382
+ elif mode_str == "auto":
383
+ main._log("[Replay] Recorded White Balance preset (mode=auto)")
384
+ else:
385
+ thr = float(preset.get("threshold", 50.0))
386
+ reuse = bool(preset.get("reuse_cached_sources", True))
387
+ main._log(
388
+ f"[Replay] Recorded White Balance preset "
389
+ f"(mode=star, threshold={thr:.1f}, "
390
+ f"reuse={'yes' if reuse else 'no'})"
391
+ )
392
+ except Exception:
393
+ # Logging/recording must never break the dialog
394
+ pass
395
+
396
+ try:
397
+ if mode == "Manual":
398
+ preset = {
399
+ "mode": "manual",
400
+ "r_gain": float(self.r_spin.value()),
401
+ "g_gain": float(self.g_spin.value()),
402
+ "b_gain": float(self.b_spin.value()),
403
+ }
404
+
405
+ # Record for Replay Last Action
406
+ _record_preset_for_replay(preset)
407
+
408
+ # Use the headless helper so doc metadata is consistent
409
+ apply_white_balance_to_doc(self.doc, preset)
410
+ # Dialog stays open - refresh document for next operation
411
+ self._finish_and_close()
412
+ return
413
+
414
+ elif mode == "Auto":
415
+ preset = {"mode": "auto"}
416
+
417
+ # Record for Replay Last Action
418
+ _record_preset_for_replay(preset)
419
+
420
+ apply_white_balance_to_doc(self.doc, preset)
421
+ # Dialog stays open - refresh document for next operation
422
+ self._finish_and_close()
423
+ return
424
+
425
+ else: # --- Star-Based: compute here so we can plot like SASv2 ---
426
+ thr = float(self.thr_slider.value())
427
+ reuse = bool(self.chk_reuse.isChecked())
428
+
429
+ preset = {
430
+ "mode": "star",
431
+ "threshold": thr,
432
+ "reuse_cached_sources": reuse,
433
+ }
434
+
435
+ base = _to_float01(
436
+ np.asarray(self.doc.image).astype(np.float32, copy=False)
437
+ )
438
+
439
+ # Ask for star colors so we can plot
440
+ result = apply_star_based_white_balance(
441
+ base,
442
+ threshold=thr,
443
+ autostretch=False,
444
+ reuse_cached_sources=reuse,
445
+ return_star_colors=True,
446
+ )
447
+
448
+ # Expected: (out, count, overlay, raw_colors, after_colors)
449
+ if len(result) < 5:
450
+ raise RuntimeError(
451
+ "Star-based WB did not return star color arrays. "
452
+ "Ensure return_star_colors=True is supported."
453
+ )
454
+
455
+ out, star_count, _overlay, raw_colors, after_colors = result
456
+
457
+ # Optional destination-mask blend, same as headless path
458
+ m = _active_mask_array_from_doc(self.doc)
459
+ if m is not None:
460
+ m3 = np.repeat(m[..., None], 3, axis=2).astype(np.float32, copy=False)
461
+ base_for_blend = _to_float01(
462
+ np.asarray(self.doc.image).astype(np.float32, copy=False)
463
+ )
464
+ out = base_for_blend * (1.0 - m3) + out * m3
465
+
466
+ # Commit to the document, including the preset in metadata
467
+ self.doc.apply_edit(
468
+ out.astype(np.float32, copy=False),
469
+ metadata={"step_name": "White Balance", "preset": preset},
470
+ step_name="White Balance",
471
+ )
472
+
473
+ # Record for Replay Last Action (using the same preset we just stored in metadata)
474
+ _record_preset_for_replay(preset)
475
+
476
+ # 🔬 Show the same diagnostic plot SASv2 shows
477
+ if (
478
+ raw_colors is not None
479
+ and after_colors is not None
480
+ and raw_colors.size
481
+ and after_colors.size
482
+ ):
483
+ plot_star_color_ratios_comparison(raw_colors, after_colors)
484
+
485
+ QMessageBox.information(
486
+ self,
487
+ self.tr("White Balance"),
488
+ self.tr("Star-Based WB applied.\nDetected {0} stars.").format(int(star_count)),
489
+ )
490
+ # Dialog stays open - refresh document for next operation
491
+ self._finish_and_close()
492
+ return
493
+
494
+ except Exception as e:
495
+ QMessageBox.critical(self, self.tr("White Balance"), self.tr("Failed to apply White Balance:\n{0}").format(e))
496
+
497
+ def _refresh_document_from_active(self):
498
+ """
499
+ Refresh the dialog's document reference to the currently active document.
500
+ This allows reusing the same dialog on different images.
501
+ """
502
+ try:
503
+ main = self.parent()
504
+ if main and hasattr(main, "_active_doc"):
505
+ new_doc = main._active_doc()
506
+ if new_doc is not None and new_doc is not self.doc:
507
+ self.doc = new_doc
508
+ except Exception:
509
+ pass
510
+
511
+ def _finish_and_close(self):
512
+ """
513
+ Close this dialog after a successful apply.
514
+ Use accept() so it behaves like a successful completion.
515
+ """
516
+ try:
517
+ self.accept()
518
+ except Exception:
519
+ self.close()
520
+
521
+ def _cleanup(self):
522
+ # Disconnect active-doc signal so the main window doesn't keep us alive
523
+ try:
524
+ if self._active_doc_conn and hasattr(self._main, "currentDocumentChanged"):
525
+ self._main.currentDocumentChanged.disconnect(self._on_active_doc_changed)
526
+ except Exception:
527
+ pass
528
+ self._active_doc_conn = False
529
+
530
+ # Stop debounce timer
531
+ try:
532
+ if getattr(self, "_debounce", None) is not None:
533
+ self._debounce.stop()
534
+ except Exception:
535
+ pass
536
+
537
+
538
+ def closeEvent(self, ev):
539
+ self._cleanup()
540
+ super().closeEvent(ev)
@@ -0,0 +1,48 @@
1
+ # pro/widgets/__init__.py
2
+ """
3
+ Shared UI widgets for Seti Astro Suite Pro.
4
+
5
+ This package contains reusable UI components to avoid code duplication.
6
+ """
7
+
8
+ from setiastro.saspro.widgets.spinboxes import CustomSpinBox, CustomDoubleSpinBox
9
+ from setiastro.saspro.widgets.graphics_views import ZoomableGraphicsView
10
+ from setiastro.saspro.widgets.preview_dialogs import ImagePreviewDialog
11
+ from setiastro.saspro.widgets.image_utils import (
12
+ numpy_to_qimage,
13
+ numpy_to_qpixmap,
14
+ qimage_to_numpy,
15
+ create_preview_image,
16
+ normalize_image
17
+ )
18
+ from setiastro.saspro.widgets.common_utilities import (
19
+ AboutDialog,
20
+ ProjectSaveWorker,
21
+ DECOR_GLYPHS,
22
+ strip_ui_decorations,
23
+ _strip_ui_decorations,
24
+ install_crash_handlers,
25
+ get_version,
26
+ get_build_timestamp,
27
+ )
28
+
29
+ __all__ = [
30
+ 'CustomSpinBox',
31
+ 'CustomDoubleSpinBox',
32
+ 'ZoomableGraphicsView',
33
+ 'ImagePreviewDialog',
34
+ 'numpy_to_qimage',
35
+ 'numpy_to_qpixmap',
36
+ 'qimage_to_numpy',
37
+ 'create_preview_image',
38
+ 'normalize_image',
39
+ # Common utilities
40
+ 'AboutDialog',
41
+ 'ProjectSaveWorker',
42
+ 'DECOR_GLYPHS',
43
+ 'strip_ui_decorations',
44
+ '_strip_ui_decorations',
45
+ 'install_crash_handlers',
46
+ 'get_version',
47
+ 'get_build_timestamp',
48
+ ]