setiastrosuitepro 1.6.2.post1__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 (367) 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/andromedatry.png +0 -0
  24. setiastro/images/andromedatry_satellited.png +0 -0
  25. setiastro/images/annotated.png +0 -0
  26. setiastro/images/aperture.png +0 -0
  27. setiastro/images/astrosuite.ico +0 -0
  28. setiastro/images/astrosuite.png +0 -0
  29. setiastro/images/astrosuitepro.icns +0 -0
  30. setiastro/images/astrosuitepro.ico +0 -0
  31. setiastro/images/astrosuitepro.png +0 -0
  32. setiastro/images/background.png +0 -0
  33. setiastro/images/background2.png +0 -0
  34. setiastro/images/benchmark.png +0 -0
  35. setiastro/images/big_moon_stabilizer_timeline.png +0 -0
  36. setiastro/images/big_moon_stabilizer_timeline_clean.png +0 -0
  37. setiastro/images/blaster.png +0 -0
  38. setiastro/images/blink.png +0 -0
  39. setiastro/images/clahe.png +0 -0
  40. setiastro/images/collage.png +0 -0
  41. setiastro/images/colorwheel.png +0 -0
  42. setiastro/images/contsub.png +0 -0
  43. setiastro/images/convo.png +0 -0
  44. setiastro/images/copyslot.png +0 -0
  45. setiastro/images/cosmic.png +0 -0
  46. setiastro/images/cosmicsat.png +0 -0
  47. setiastro/images/crop1.png +0 -0
  48. setiastro/images/cropicon.png +0 -0
  49. setiastro/images/curves.png +0 -0
  50. setiastro/images/cvs.png +0 -0
  51. setiastro/images/debayer.png +0 -0
  52. setiastro/images/denoise_cnn_custom.png +0 -0
  53. setiastro/images/denoise_cnn_graph.png +0 -0
  54. setiastro/images/disk.png +0 -0
  55. setiastro/images/dse.png +0 -0
  56. setiastro/images/exoicon.png +0 -0
  57. setiastro/images/eye.png +0 -0
  58. setiastro/images/fliphorizontal.png +0 -0
  59. setiastro/images/flipvertical.png +0 -0
  60. setiastro/images/font.png +0 -0
  61. setiastro/images/freqsep.png +0 -0
  62. setiastro/images/functionbundle.png +0 -0
  63. setiastro/images/graxpert.png +0 -0
  64. setiastro/images/green.png +0 -0
  65. setiastro/images/gridicon.png +0 -0
  66. setiastro/images/halo.png +0 -0
  67. setiastro/images/hdr.png +0 -0
  68. setiastro/images/histogram.png +0 -0
  69. setiastro/images/hubble.png +0 -0
  70. setiastro/images/imagecombine.png +0 -0
  71. setiastro/images/invert.png +0 -0
  72. setiastro/images/isophote.png +0 -0
  73. setiastro/images/isophote_demo_figure.png +0 -0
  74. setiastro/images/isophote_demo_image.png +0 -0
  75. setiastro/images/isophote_demo_model.png +0 -0
  76. setiastro/images/isophote_demo_residual.png +0 -0
  77. setiastro/images/jwstpupil.png +0 -0
  78. setiastro/images/linearfit.png +0 -0
  79. setiastro/images/livestacking.png +0 -0
  80. setiastro/images/mask.png +0 -0
  81. setiastro/images/maskapply.png +0 -0
  82. setiastro/images/maskcreate.png +0 -0
  83. setiastro/images/maskremove.png +0 -0
  84. setiastro/images/morpho.png +0 -0
  85. setiastro/images/mosaic.png +0 -0
  86. setiastro/images/multiscale_decomp.png +0 -0
  87. setiastro/images/nbtorgb.png +0 -0
  88. setiastro/images/neutral.png +0 -0
  89. setiastro/images/nuke.png +0 -0
  90. setiastro/images/openfile.png +0 -0
  91. setiastro/images/pedestal.png +0 -0
  92. setiastro/images/pen.png +0 -0
  93. setiastro/images/pixelmath.png +0 -0
  94. setiastro/images/platesolve.png +0 -0
  95. setiastro/images/ppp.png +0 -0
  96. setiastro/images/pro.png +0 -0
  97. setiastro/images/project.png +0 -0
  98. setiastro/images/psf.png +0 -0
  99. setiastro/images/redo.png +0 -0
  100. setiastro/images/redoicon.png +0 -0
  101. setiastro/images/rescale.png +0 -0
  102. setiastro/images/rgbalign.png +0 -0
  103. setiastro/images/rgbcombo.png +0 -0
  104. setiastro/images/rgbextract.png +0 -0
  105. setiastro/images/rotate180.png +0 -0
  106. setiastro/images/rotateclockwise.png +0 -0
  107. setiastro/images/rotatecounterclockwise.png +0 -0
  108. setiastro/images/satellite.png +0 -0
  109. setiastro/images/script.png +0 -0
  110. setiastro/images/selectivecolor.png +0 -0
  111. setiastro/images/simbad.png +0 -0
  112. setiastro/images/slot0.png +0 -0
  113. setiastro/images/slot1.png +0 -0
  114. setiastro/images/slot2.png +0 -0
  115. setiastro/images/slot3.png +0 -0
  116. setiastro/images/slot4.png +0 -0
  117. setiastro/images/slot5.png +0 -0
  118. setiastro/images/slot6.png +0 -0
  119. setiastro/images/slot7.png +0 -0
  120. setiastro/images/slot8.png +0 -0
  121. setiastro/images/slot9.png +0 -0
  122. setiastro/images/spcc.png +0 -0
  123. setiastro/images/spin_precession_vs_lunar_distance.png +0 -0
  124. setiastro/images/spinner.gif +0 -0
  125. setiastro/images/stacking.png +0 -0
  126. setiastro/images/staradd.png +0 -0
  127. setiastro/images/staralign.png +0 -0
  128. setiastro/images/starnet.png +0 -0
  129. setiastro/images/starregistration.png +0 -0
  130. setiastro/images/starspike.png +0 -0
  131. setiastro/images/starstretch.png +0 -0
  132. setiastro/images/statstretch.png +0 -0
  133. setiastro/images/supernova.png +0 -0
  134. setiastro/images/uhs.png +0 -0
  135. setiastro/images/undoicon.png +0 -0
  136. setiastro/images/upscale.png +0 -0
  137. setiastro/images/viewbundle.png +0 -0
  138. setiastro/images/whitebalance.png +0 -0
  139. setiastro/images/wimi_icon_256x256.png +0 -0
  140. setiastro/images/wimilogo.png +0 -0
  141. setiastro/images/wims.png +0 -0
  142. setiastro/images/wrench_icon.png +0 -0
  143. setiastro/images/xisfliberator.png +0 -0
  144. setiastro/qml/ResourceMonitor.qml +126 -0
  145. setiastro/saspro/__init__.py +20 -0
  146. setiastro/saspro/__main__.py +945 -0
  147. setiastro/saspro/_generated/__init__.py +7 -0
  148. setiastro/saspro/_generated/build_info.py +3 -0
  149. setiastro/saspro/abe.py +1346 -0
  150. setiastro/saspro/abe_preset.py +196 -0
  151. setiastro/saspro/aberration_ai.py +694 -0
  152. setiastro/saspro/aberration_ai_preset.py +224 -0
  153. setiastro/saspro/accel_installer.py +218 -0
  154. setiastro/saspro/accel_workers.py +30 -0
  155. setiastro/saspro/add_stars.py +624 -0
  156. setiastro/saspro/astrobin_exporter.py +1010 -0
  157. setiastro/saspro/astrospike.py +153 -0
  158. setiastro/saspro/astrospike_python.py +1841 -0
  159. setiastro/saspro/autostretch.py +198 -0
  160. setiastro/saspro/backgroundneutral.py +602 -0
  161. setiastro/saspro/batch_convert.py +328 -0
  162. setiastro/saspro/batch_renamer.py +522 -0
  163. setiastro/saspro/blemish_blaster.py +491 -0
  164. setiastro/saspro/blink_comparator_pro.py +2926 -0
  165. setiastro/saspro/bundles.py +61 -0
  166. setiastro/saspro/bundles_dock.py +114 -0
  167. setiastro/saspro/cheat_sheet.py +213 -0
  168. setiastro/saspro/clahe.py +368 -0
  169. setiastro/saspro/comet_stacking.py +1442 -0
  170. setiastro/saspro/common_tr.py +107 -0
  171. setiastro/saspro/config.py +38 -0
  172. setiastro/saspro/config_bootstrap.py +40 -0
  173. setiastro/saspro/config_manager.py +316 -0
  174. setiastro/saspro/continuum_subtract.py +1617 -0
  175. setiastro/saspro/convo.py +1400 -0
  176. setiastro/saspro/convo_preset.py +414 -0
  177. setiastro/saspro/copyastro.py +190 -0
  178. setiastro/saspro/cosmicclarity.py +1589 -0
  179. setiastro/saspro/cosmicclarity_preset.py +407 -0
  180. setiastro/saspro/crop_dialog_pro.py +973 -0
  181. setiastro/saspro/crop_preset.py +189 -0
  182. setiastro/saspro/curve_editor_pro.py +2562 -0
  183. setiastro/saspro/curves_preset.py +375 -0
  184. setiastro/saspro/debayer.py +673 -0
  185. setiastro/saspro/debug_utils.py +29 -0
  186. setiastro/saspro/dnd_mime.py +35 -0
  187. setiastro/saspro/doc_manager.py +2664 -0
  188. setiastro/saspro/exoplanet_detector.py +2166 -0
  189. setiastro/saspro/file_utils.py +284 -0
  190. setiastro/saspro/fitsmodifier.py +748 -0
  191. setiastro/saspro/fix_bom.py +32 -0
  192. setiastro/saspro/free_torch_memory.py +48 -0
  193. setiastro/saspro/frequency_separation.py +1349 -0
  194. setiastro/saspro/function_bundle.py +1596 -0
  195. setiastro/saspro/generate_translations.py +3092 -0
  196. setiastro/saspro/ghs_dialog_pro.py +663 -0
  197. setiastro/saspro/ghs_preset.py +284 -0
  198. setiastro/saspro/graxpert.py +637 -0
  199. setiastro/saspro/graxpert_preset.py +287 -0
  200. setiastro/saspro/gui/__init__.py +0 -0
  201. setiastro/saspro/gui/main_window.py +8810 -0
  202. setiastro/saspro/gui/mixins/__init__.py +33 -0
  203. setiastro/saspro/gui/mixins/dock_mixin.py +362 -0
  204. setiastro/saspro/gui/mixins/file_mixin.py +450 -0
  205. setiastro/saspro/gui/mixins/geometry_mixin.py +403 -0
  206. setiastro/saspro/gui/mixins/header_mixin.py +441 -0
  207. setiastro/saspro/gui/mixins/mask_mixin.py +421 -0
  208. setiastro/saspro/gui/mixins/menu_mixin.py +389 -0
  209. setiastro/saspro/gui/mixins/theme_mixin.py +367 -0
  210. setiastro/saspro/gui/mixins/toolbar_mixin.py +1457 -0
  211. setiastro/saspro/gui/mixins/update_mixin.py +309 -0
  212. setiastro/saspro/gui/mixins/view_mixin.py +435 -0
  213. setiastro/saspro/gui/statistics_dialog.py +47 -0
  214. setiastro/saspro/halobgon.py +488 -0
  215. setiastro/saspro/header_viewer.py +448 -0
  216. setiastro/saspro/headless_utils.py +88 -0
  217. setiastro/saspro/histogram.py +756 -0
  218. setiastro/saspro/history_explorer.py +941 -0
  219. setiastro/saspro/i18n.py +168 -0
  220. setiastro/saspro/image_combine.py +417 -0
  221. setiastro/saspro/image_peeker_pro.py +1604 -0
  222. setiastro/saspro/imageops/__init__.py +37 -0
  223. setiastro/saspro/imageops/mdi_snap.py +292 -0
  224. setiastro/saspro/imageops/scnr.py +36 -0
  225. setiastro/saspro/imageops/starbasedwhitebalance.py +210 -0
  226. setiastro/saspro/imageops/stretch.py +236 -0
  227. setiastro/saspro/isophote.py +1182 -0
  228. setiastro/saspro/layers.py +208 -0
  229. setiastro/saspro/layers_dock.py +714 -0
  230. setiastro/saspro/lazy_imports.py +193 -0
  231. setiastro/saspro/legacy/__init__.py +2 -0
  232. setiastro/saspro/legacy/image_manager.py +2226 -0
  233. setiastro/saspro/legacy/numba_utils.py +3676 -0
  234. setiastro/saspro/legacy/xisf.py +1071 -0
  235. setiastro/saspro/linear_fit.py +537 -0
  236. setiastro/saspro/live_stacking.py +1841 -0
  237. setiastro/saspro/log_bus.py +5 -0
  238. setiastro/saspro/logging_config.py +460 -0
  239. setiastro/saspro/luminancerecombine.py +309 -0
  240. setiastro/saspro/main_helpers.py +201 -0
  241. setiastro/saspro/mask_creation.py +931 -0
  242. setiastro/saspro/masks_core.py +56 -0
  243. setiastro/saspro/mdi_widgets.py +353 -0
  244. setiastro/saspro/memory_utils.py +666 -0
  245. setiastro/saspro/metadata_patcher.py +75 -0
  246. setiastro/saspro/mfdeconv.py +3831 -0
  247. setiastro/saspro/mfdeconv_earlystop.py +71 -0
  248. setiastro/saspro/mfdeconvcudnn.py +3263 -0
  249. setiastro/saspro/mfdeconvsport.py +2382 -0
  250. setiastro/saspro/minorbodycatalog.py +567 -0
  251. setiastro/saspro/morphology.py +407 -0
  252. setiastro/saspro/multiscale_decomp.py +1293 -0
  253. setiastro/saspro/nbtorgb_stars.py +541 -0
  254. setiastro/saspro/numba_utils.py +3145 -0
  255. setiastro/saspro/numba_warmup.py +141 -0
  256. setiastro/saspro/ops/__init__.py +9 -0
  257. setiastro/saspro/ops/command_help_dialog.py +623 -0
  258. setiastro/saspro/ops/command_runner.py +217 -0
  259. setiastro/saspro/ops/commands.py +1594 -0
  260. setiastro/saspro/ops/script_editor.py +1102 -0
  261. setiastro/saspro/ops/scripts.py +1473 -0
  262. setiastro/saspro/ops/settings.py +637 -0
  263. setiastro/saspro/parallel_utils.py +554 -0
  264. setiastro/saspro/pedestal.py +121 -0
  265. setiastro/saspro/perfect_palette_picker.py +1071 -0
  266. setiastro/saspro/pipeline.py +110 -0
  267. setiastro/saspro/pixelmath.py +1604 -0
  268. setiastro/saspro/plate_solver.py +2445 -0
  269. setiastro/saspro/project_io.py +797 -0
  270. setiastro/saspro/psf_utils.py +136 -0
  271. setiastro/saspro/psf_viewer.py +549 -0
  272. setiastro/saspro/pyi_rthook_astroquery.py +95 -0
  273. setiastro/saspro/remove_green.py +331 -0
  274. setiastro/saspro/remove_stars.py +1599 -0
  275. setiastro/saspro/remove_stars_preset.py +404 -0
  276. setiastro/saspro/resources.py +501 -0
  277. setiastro/saspro/rgb_combination.py +208 -0
  278. setiastro/saspro/rgb_extract.py +19 -0
  279. setiastro/saspro/rgbalign.py +723 -0
  280. setiastro/saspro/runtime_imports.py +7 -0
  281. setiastro/saspro/runtime_torch.py +754 -0
  282. setiastro/saspro/save_options.py +73 -0
  283. setiastro/saspro/selective_color.py +1552 -0
  284. setiastro/saspro/sfcc.py +1472 -0
  285. setiastro/saspro/shortcuts.py +3043 -0
  286. setiastro/saspro/signature_insert.py +1102 -0
  287. setiastro/saspro/stacking_suite.py +18470 -0
  288. setiastro/saspro/star_alignment.py +7435 -0
  289. setiastro/saspro/star_alignment_preset.py +329 -0
  290. setiastro/saspro/star_metrics.py +49 -0
  291. setiastro/saspro/star_spikes.py +765 -0
  292. setiastro/saspro/star_stretch.py +507 -0
  293. setiastro/saspro/stat_stretch.py +538 -0
  294. setiastro/saspro/status_log_dock.py +78 -0
  295. setiastro/saspro/subwindow.py +3328 -0
  296. setiastro/saspro/supernovaasteroidhunter.py +1719 -0
  297. setiastro/saspro/swap_manager.py +99 -0
  298. setiastro/saspro/torch_backend.py +89 -0
  299. setiastro/saspro/torch_rejection.py +434 -0
  300. setiastro/saspro/translations/all_source_strings.json +3654 -0
  301. setiastro/saspro/translations/ar_translations.py +3865 -0
  302. setiastro/saspro/translations/de_translations.py +3749 -0
  303. setiastro/saspro/translations/es_translations.py +3939 -0
  304. setiastro/saspro/translations/fr_translations.py +3858 -0
  305. setiastro/saspro/translations/hi_translations.py +3571 -0
  306. setiastro/saspro/translations/integrate_translations.py +270 -0
  307. setiastro/saspro/translations/it_translations.py +3678 -0
  308. setiastro/saspro/translations/ja_translations.py +3601 -0
  309. setiastro/saspro/translations/pt_translations.py +3869 -0
  310. setiastro/saspro/translations/ru_translations.py +2848 -0
  311. setiastro/saspro/translations/saspro_ar.qm +0 -0
  312. setiastro/saspro/translations/saspro_ar.ts +255 -0
  313. setiastro/saspro/translations/saspro_de.qm +0 -0
  314. setiastro/saspro/translations/saspro_de.ts +253 -0
  315. setiastro/saspro/translations/saspro_es.qm +0 -0
  316. setiastro/saspro/translations/saspro_es.ts +12520 -0
  317. setiastro/saspro/translations/saspro_fr.qm +0 -0
  318. setiastro/saspro/translations/saspro_fr.ts +12514 -0
  319. setiastro/saspro/translations/saspro_hi.qm +0 -0
  320. setiastro/saspro/translations/saspro_hi.ts +257 -0
  321. setiastro/saspro/translations/saspro_it.qm +0 -0
  322. setiastro/saspro/translations/saspro_it.ts +12520 -0
  323. setiastro/saspro/translations/saspro_ja.qm +0 -0
  324. setiastro/saspro/translations/saspro_ja.ts +257 -0
  325. setiastro/saspro/translations/saspro_pt.qm +0 -0
  326. setiastro/saspro/translations/saspro_pt.ts +257 -0
  327. setiastro/saspro/translations/saspro_ru.qm +0 -0
  328. setiastro/saspro/translations/saspro_ru.ts +237 -0
  329. setiastro/saspro/translations/saspro_sw.qm +0 -0
  330. setiastro/saspro/translations/saspro_sw.ts +257 -0
  331. setiastro/saspro/translations/saspro_uk.qm +0 -0
  332. setiastro/saspro/translations/saspro_uk.ts +10771 -0
  333. setiastro/saspro/translations/saspro_zh.qm +0 -0
  334. setiastro/saspro/translations/saspro_zh.ts +12520 -0
  335. setiastro/saspro/translations/sw_translations.py +3671 -0
  336. setiastro/saspro/translations/uk_translations.py +3700 -0
  337. setiastro/saspro/translations/zh_translations.py +3675 -0
  338. setiastro/saspro/versioning.py +77 -0
  339. setiastro/saspro/view_bundle.py +1558 -0
  340. setiastro/saspro/wavescale_hdr.py +645 -0
  341. setiastro/saspro/wavescale_hdr_preset.py +101 -0
  342. setiastro/saspro/wavescalede.py +680 -0
  343. setiastro/saspro/wavescalede_preset.py +230 -0
  344. setiastro/saspro/wcs_update.py +374 -0
  345. setiastro/saspro/whitebalance.py +492 -0
  346. setiastro/saspro/widgets/__init__.py +48 -0
  347. setiastro/saspro/widgets/common_utilities.py +306 -0
  348. setiastro/saspro/widgets/graphics_views.py +122 -0
  349. setiastro/saspro/widgets/image_utils.py +518 -0
  350. setiastro/saspro/widgets/minigame/game.js +986 -0
  351. setiastro/saspro/widgets/minigame/index.html +53 -0
  352. setiastro/saspro/widgets/minigame/style.css +241 -0
  353. setiastro/saspro/widgets/preview_dialogs.py +280 -0
  354. setiastro/saspro/widgets/resource_monitor.py +237 -0
  355. setiastro/saspro/widgets/spinboxes.py +275 -0
  356. setiastro/saspro/widgets/themed_buttons.py +13 -0
  357. setiastro/saspro/widgets/wavelet_utils.py +331 -0
  358. setiastro/saspro/wimi.py +7996 -0
  359. setiastro/saspro/wims.py +578 -0
  360. setiastro/saspro/window_shelf.py +185 -0
  361. setiastro/saspro/xisf.py +1123 -0
  362. setiastrosuitepro-1.6.2.post1.dist-info/METADATA +278 -0
  363. setiastrosuitepro-1.6.2.post1.dist-info/RECORD +367 -0
  364. setiastrosuitepro-1.6.2.post1.dist-info/WHEEL +4 -0
  365. setiastrosuitepro-1.6.2.post1.dist-info/entry_points.txt +6 -0
  366. setiastrosuitepro-1.6.2.post1.dist-info/licenses/LICENSE +674 -0
  367. setiastrosuitepro-1.6.2.post1.dist-info/licenses/license.txt +2580 -0
@@ -0,0 +1,945 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Seti Astro Suite Pro - Main Entry Point Module
4
+
5
+ This module contains the main application entry point logic.
6
+ It can be executed directly via `python -m setiastro.saspro` or
7
+ called via the `main()` function when invoked as an entry point.
8
+ """
9
+
10
+ # Show splash screen IMMEDIATELY before any heavy imports
11
+
12
+ import sys
13
+ import os
14
+ from PyQt6.QtCore import QCoreApplication
15
+
16
+ # ---- Linux Qt stability guard (must run BEFORE any PyQt6 import) ----
17
+ # Default behavior: DO NOT override Wayland.
18
+ # If a user needs the "safe" path, they can opt-in by setting:
19
+ # SASPRO_QT_SAFE=1
20
+ #
21
+ # This avoids punishing all Wayland users for one bad driver/Qt stack.
22
+ if sys.platform.startswith("linux"):
23
+ if os.environ.get("SASPRO_QT_SAFE", "").strip() in ("1", "true", "yes", "on"):
24
+ # Prefer X11/xcb unless user explicitly set a platform plugin
25
+ os.environ.setdefault("QT_QPA_PLATFORM", "xcb")
26
+
27
+ # Prefer software GL unless user explicitly set something else
28
+ os.environ.setdefault("QT_OPENGL", "software")
29
+ os.environ.setdefault("LIBGL_ALWAYS_SOFTWARE", "1")
30
+
31
+ # Global variables for splash screen and app
32
+ _splash = None
33
+ _app = None
34
+
35
+ # Flag to track if splash was initialized
36
+ _splash_initialized = False
37
+
38
+ from setiastro.saspro.versioning import get_app_version
39
+ _EARLY_VERSION = get_app_version("setiastrosuitepro")
40
+
41
+ VERSION = _EARLY_VERSION
42
+
43
+ def _init_splash():
44
+ """Initialize the splash screen. Safe to call multiple times."""
45
+ global _splash, _app, _splash_initialized
46
+
47
+ if _splash_initialized:
48
+ return
49
+
50
+ # Minimal imports for splash screen
51
+ from PyQt6.QtWidgets import QApplication, QWidget
52
+ from PyQt6.QtCore import Qt, QCoreApplication, QRect, QPropertyAnimation, QEasingCurve
53
+ import time
54
+ from PyQt6.QtGui import QGuiApplication, QIcon, QPixmap, QColor, QPainter, QFont, QLinearGradient
55
+
56
+
57
+ # If we're forcing software OpenGL, do it *before* QApplication is created.
58
+ if sys.platform.startswith("linux"):
59
+ if os.environ.get("SASPRO_QT_SAFE", "").strip() in ("1", "true", "yes", "on"):
60
+ if os.environ.get("QT_OPENGL", "").lower() == "software":
61
+ try:
62
+ QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_UseSoftwareOpenGL, True)
63
+ except Exception:
64
+ pass
65
+
66
+ # Set application attributes before creating QApplication
67
+ try:
68
+ QGuiApplication.setHighDpiScaleFactorRoundingPolicy(
69
+ Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
70
+ )
71
+ except Exception:
72
+ pass
73
+
74
+ QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_DontShowIconsInMenus, False)
75
+ QCoreApplication.setOrganizationName("SetiAstro")
76
+ QCoreApplication.setOrganizationDomain("setiastrosuite.pro")
77
+ QCoreApplication.setApplicationName("Seti Astro Suite Pro")
78
+
79
+ # Create QApplication
80
+ _app = QApplication(sys.argv)
81
+
82
+ if sys.platform.startswith("linux"):
83
+ try:
84
+ print("Qt platform:", _app.platformName())
85
+ print("XDG_SESSION_TYPE:", os.environ.get("XDG_SESSION_TYPE"))
86
+ print("QT_QPA_PLATFORM:", os.environ.get("QT_QPA_PLATFORM"))
87
+ print("QT_OPENGL:", os.environ.get("QT_OPENGL"))
88
+ except Exception:
89
+ pass
90
+
91
+ # Determine icon paths early
92
+ # Determine icon paths early
93
+ def _find_icon_path():
94
+ """Legacy fallback if resources import fails."""
95
+ if hasattr(sys, '_MEIPASS'):
96
+ base = sys._MEIPASS
97
+ else:
98
+ try:
99
+ import setiastro
100
+ package_dir = os.path.dirname(os.path.abspath(setiastro.__file__))
101
+ package_parent = os.path.dirname(package_dir)
102
+ images_dir_installed = os.path.join(package_parent, 'images')
103
+ if os.path.exists(images_dir_installed):
104
+ base = package_parent
105
+ else:
106
+ base = os.path.dirname(
107
+ os.path.dirname(
108
+ os.path.dirname(
109
+ os.path.dirname(os.path.abspath(__file__))
110
+ )
111
+ )
112
+ )
113
+ except (ImportError, AttributeError):
114
+ base = os.path.dirname(
115
+ os.path.dirname(
116
+ os.path.dirname(
117
+ os.path.dirname(os.path.abspath(__file__))
118
+ )
119
+ )
120
+ )
121
+
122
+ candidates = [
123
+ os.path.join(base, "images", "astrosuitepro.png"),
124
+ os.path.join(base, "images", "astrosuitepro.ico"),
125
+ os.path.join(base, "images", "astrosuite.png"),
126
+ os.path.join(base, "images", "astrosuite.ico"),
127
+ ]
128
+ for p in candidates:
129
+ if os.path.exists(p):
130
+ return p
131
+ return "" # nothing found
132
+
133
+ # NEW: Prefer centralized resources resolver
134
+ try:
135
+ from setiastro.saspro.resources import icon_path, background_startup_path
136
+ _early_icon_path = icon_path
137
+ if not os.path.exists(_early_icon_path):
138
+ # fall back to legacy search if for some reason this is missing
139
+ _early_icon_path = _find_icon_path()
140
+
141
+ # Load startup background path
142
+ _startup_bg_path = background_startup_path
143
+ if not os.path.exists(_startup_bg_path):
144
+ _startup_bg_path = None
145
+
146
+ except Exception:
147
+ _early_icon_path = _find_icon_path()
148
+ _startup_bg_path = None
149
+
150
+
151
+ # =========================================================================
152
+ # PhotoshopStyleSplash - Custom splash screen widget
153
+ # =========================================================================
154
+ class _EarlySplash(QWidget):
155
+ """
156
+ A modern, Photoshop-style splash screen shown immediately on startup.
157
+ """
158
+ def __init__(self, logo_path: str):
159
+ super().__init__()
160
+ self._version = _EARLY_VERSION
161
+ self._build = ""
162
+ self.current_message = QCoreApplication.translate("Splash", "Starting...")
163
+ self.progress_value = 0
164
+
165
+ # Window setup
166
+ self.setWindowFlags(
167
+ Qt.WindowType.SplashScreen |
168
+ Qt.WindowType.FramelessWindowHint |
169
+ Qt.WindowType.WindowStaysOnTopHint
170
+ )
171
+ self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, False)
172
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
173
+
174
+ # Splash dimensions
175
+ self.splash_width = 600
176
+ self.splash_height = 400
177
+ self.setFixedSize(self.splash_width, self.splash_height)
178
+
179
+ # Center on screen
180
+ screen = QGuiApplication.primaryScreen()
181
+ if screen:
182
+ screen_geo = screen.availableGeometry()
183
+ x = (screen_geo.width() - self.splash_width) // 2 + screen_geo.x()
184
+ y = (screen_geo.height() - self.splash_height) // 2 + screen_geo.y()
185
+ self.move(x, y)
186
+
187
+ # Load and scale logo
188
+ self.logo_pixmap = self._load_logo(logo_path)
189
+
190
+ # Load background image
191
+ self.bg_image_pixmap = QPixmap()
192
+ if _startup_bg_path:
193
+ self.bg_image_pixmap = QPixmap(_startup_bg_path)
194
+
195
+ # Fonts
196
+ self.title_font = QFont("Segoe UI", 28, QFont.Weight.Bold)
197
+ self.subtitle_font = QFont("Segoe UI", 11)
198
+ self.message_font = QFont("Segoe UI", 9)
199
+ self.copyright_font = QFont("Segoe UI", 8)
200
+
201
+ def _load_logo(self, path: str) -> QPixmap:
202
+ """Load the logo and scale appropriately."""
203
+ if not path or not os.path.exists(path):
204
+ return QPixmap()
205
+
206
+ ext = os.path.splitext(path)[1].lower()
207
+ if ext == ".ico":
208
+ ic = QIcon(path)
209
+ pm = ic.pixmap(256, 256)
210
+ if pm.isNull():
211
+ pm = QPixmap(path)
212
+ else:
213
+ pm = QPixmap(path)
214
+ if pm.isNull():
215
+ pm = QIcon(path).pixmap(256, 256)
216
+
217
+ if not pm.isNull():
218
+ pm = pm.scaled(
219
+ 180, 180,
220
+ Qt.AspectRatioMode.KeepAspectRatio,
221
+ Qt.TransformationMode.SmoothTransformation
222
+ )
223
+ return pm
224
+
225
+ def setMessage(self, message: str):
226
+ """Update the loading message."""
227
+ self.current_message = message
228
+ self.repaint()
229
+ if _app:
230
+ _app.processEvents()
231
+
232
+ def setProgress(self, value: int):
233
+ """Update progress (0-100) with smooth animation."""
234
+ target = max(0, min(100, value))
235
+ start = self.progress_value
236
+
237
+ # If jumping backwards or small change, just set it
238
+ if target <= start or (target - start) < 1:
239
+ self.progress_value = target
240
+ self.repaint()
241
+ if _app: _app.processEvents()
242
+ return
243
+
244
+ # Animate forward
245
+ steps = 15 # number of frames for the slide
246
+ # We want the total slide to take ~100-150ms max to feel responsive but smooth
247
+ dt = 0.005 # 5ms per frame
248
+
249
+ for i in range(1, steps + 1):
250
+ # Ease out interpolator
251
+ t = i / steps
252
+ # Quadratic ease out: f(t) = -t*(t-2)
253
+ factor = -t * (t - 2)
254
+
255
+ cur = start + (target - start) * factor
256
+ self.progress_value = cur
257
+ self.repaint()
258
+ if _app: _app.processEvents()
259
+ time.sleep(dt)
260
+
261
+ self.progress_value = target
262
+ self.repaint()
263
+ if _app:
264
+ _app.processEvents()
265
+
266
+ def setBuildInfo(self, version: str, build: str):
267
+ """Update version and build info once available."""
268
+ self._version = _EARLY_VERSION
269
+ self._build = build
270
+ self.repaint()
271
+
272
+ def paintEvent(self, event):
273
+ """Custom paint for the splash screen."""
274
+ painter = QPainter(self)
275
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
276
+ painter.setRenderHint(QPainter.RenderHint.TextAntialiasing)
277
+
278
+ w, h = self.splash_width, self.splash_height
279
+
280
+ # --- Background gradient (deep space theme) ---
281
+ gradient = QLinearGradient(0, 0, 0, h)
282
+ gradient.setColorAt(0.0, QColor(15, 15, 25))
283
+ gradient.setColorAt(0.5, QColor(25, 25, 45))
284
+ gradient.setColorAt(1.0, QColor(10, 10, 20))
285
+ painter.fillRect(0, 0, w, h, gradient)
286
+
287
+ # --- Background Image (Centered with Fade Out) ---
288
+ if not self.bg_image_pixmap.isNull():
289
+ # Create a temporary pixmap to handle the masking
290
+ temp = QPixmap(w, h)
291
+ temp.fill(Qt.GlobalColor.transparent)
292
+
293
+ ptmp = QPainter(temp)
294
+ ptmp.setRenderHint(QPainter.RenderHint.Antialiasing)
295
+ ptmp.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)
296
+
297
+ # Scale image to cover the entire splash screen
298
+ scaled = self.bg_image_pixmap.scaled(
299
+ w, h,
300
+ Qt.AspectRatioMode.KeepAspectRatioByExpanding,
301
+ Qt.TransformationMode.SmoothTransformation
302
+ )
303
+
304
+ # Center the image
305
+ sx = (w - scaled.width()) // 2
306
+ sy = (h - scaled.height()) // 2
307
+ ptmp.drawPixmap(sx, sy, scaled)
308
+
309
+ # Apply Fade Out Mask (Gradient Alpha)
310
+ ptmp.setCompositionMode(QPainter.CompositionMode.CompositionMode_DestinationIn)
311
+ fade_gradient = QLinearGradient(0, 0, 0, h)
312
+ # Keep top half fully visible (subject to global opacity)
313
+ fade_gradient.setColorAt(0.0, QColor(0, 0, 0, 255))
314
+ fade_gradient.setColorAt(0.5, QColor(0, 0, 0, 255))
315
+ # Fade out completely at the bottom
316
+ fade_gradient.setColorAt(1.0, QColor(0, 0, 0, 0))
317
+ ptmp.fillRect(0, 0, w, h, fade_gradient)
318
+ ptmp.end()
319
+
320
+ # Draw combined result with 50% opacity
321
+ painter.save()
322
+ painter.setOpacity(0.25)
323
+ painter.drawPixmap(0, 0, temp)
324
+ painter.restore()
325
+
326
+ # --- Subtle border ---
327
+ painter.setPen(QColor(60, 60, 80))
328
+ painter.drawRect(0, 0, w - 1, h - 1)
329
+
330
+ # --- Logo (centered upper area) ---
331
+ if not self.logo_pixmap.isNull():
332
+ logo_x = (w - self.logo_pixmap.width()) // 2
333
+ logo_y = 40
334
+ painter.drawPixmap(logo_x, logo_y, self.logo_pixmap)
335
+
336
+ # --- Title ---
337
+ painter.setFont(self.title_font)
338
+ painter.setPen(QColor(255, 255, 255))
339
+ title_rect = QRect(0, 230, w, 40)
340
+ painter.drawText(title_rect, Qt.AlignmentFlag.AlignCenter, "Seti Astro Suite Pro")
341
+
342
+ # --- Subtitle with version ---
343
+ painter.setFont(self.subtitle_font)
344
+ painter.setPen(QColor(180, 180, 200))
345
+ subtitle_text = QCoreApplication.translate("Splash", "Version {0}").format(self._version)
346
+
347
+ if self._build:
348
+ if self._build == "dev":
349
+ # No build_info → running from source checkout
350
+ subtitle_text += QCoreApplication.translate("Splash", " • Running locally from source code")
351
+ else:
352
+ subtitle_text += QCoreApplication.translate("Splash", " • Build {0}").format(self._build)
353
+
354
+ subtitle_rect = QRect(0, 270, w, 25)
355
+ painter.drawText(subtitle_rect, Qt.AlignmentFlag.AlignCenter, subtitle_text)
356
+
357
+ # --- Progress bar ---
358
+ bar_margin = 50
359
+ bar_height = 4
360
+ bar_y = h - 70
361
+ bar_width = w - (bar_margin * 2)
362
+
363
+ painter.setPen(Qt.PenStyle.NoPen)
364
+ painter.setBrush(QColor(40, 40, 60))
365
+ painter.drawRoundedRect(bar_margin, bar_y, bar_width, bar_height, 2, 2)
366
+
367
+ if self.progress_value > 0:
368
+ fill_width = int(bar_width * self.progress_value / 100)
369
+ bar_gradient = QLinearGradient(bar_margin, 0, bar_margin + bar_width, 0)
370
+ bar_gradient.setColorAt(0.0, QColor(80, 140, 220))
371
+ bar_gradient.setColorAt(1.0, QColor(140, 180, 255))
372
+ painter.setBrush(bar_gradient)
373
+ painter.drawRoundedRect(bar_margin, bar_y, fill_width, bar_height, 2, 2)
374
+
375
+ # --- Loading message ---
376
+ painter.setFont(self.message_font)
377
+ painter.setPen(QColor(150, 150, 180))
378
+ msg_rect = QRect(bar_margin, bar_y + 10, bar_width, 20)
379
+ painter.drawText(msg_rect, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter,
380
+ self.current_message)
381
+
382
+ # --- Copyright ---
383
+ painter.setFont(self.copyright_font)
384
+ painter.setPen(QColor(100, 100, 130))
385
+ copyright_text = "© 2024-2025 Franklin Marek (Seti Astro) • All Rights Reserved"
386
+ copyright_rect = QRect(0, h - 30, w, 20)
387
+ painter.drawText(copyright_rect, Qt.AlignmentFlag.AlignCenter, copyright_text)
388
+
389
+ painter.end()
390
+
391
+ def finish(self):
392
+ """Hide and cleanup the splash."""
393
+ self.hide()
394
+ self.close()
395
+ self.deleteLater()
396
+
397
+ def start_fade_out(self):
398
+ """Smoothly fade out the splash screen."""
399
+ self._anim = QPropertyAnimation(self, b"windowOpacity")
400
+ self._anim.setDuration(1000)
401
+ self._anim.setStartValue(1.0)
402
+ self._anim.setEndValue(0.0)
403
+ self._anim.setEasingCurve(QEasingCurve.Type.OutQuad)
404
+ self._anim.finished.connect(self.finish)
405
+ self._anim.start()
406
+
407
+ def start_fade_in(self):
408
+ """Smoothly fade in the splash screen."""
409
+ self.setWindowOpacity(0.0)
410
+ self._anim = QPropertyAnimation(self, b"windowOpacity")
411
+ self._anim.setDuration(800)
412
+ self._anim.setStartValue(0.0)
413
+ self._anim.setEndValue(1.0)
414
+ self._anim.setEasingCurve(QEasingCurve.Type.InQuad)
415
+ self._anim.start()
416
+
417
+ # --- Show splash IMMEDIATELY ---
418
+ _splash = _EarlySplash(_early_icon_path)
419
+ _splash.start_fade_in()
420
+ _splash.show()
421
+
422
+ # Block briefly to allow fade-in to progress smoothly before heavy imports start
423
+ # We use a busy loop with processEvents to keep the UI responsive during fade
424
+ t_start = time.time()
425
+ while time.time() - t_start < 0.85: # slightly longer than animation
426
+ _app.processEvents()
427
+ if _splash.windowOpacity() >= 0.99:
428
+ break
429
+ time.sleep(0.01)
430
+
431
+ _splash.setMessage(QCoreApplication.translate("Splash", "Initializing Python runtime..."))
432
+ _splash.setProgress(2)
433
+ _app.processEvents()
434
+
435
+ # Load translation BEFORE any other widgets are created
436
+ try:
437
+ from setiastro.saspro.i18n import load_language, get_translations_dir
438
+ ok = load_language(app=_app)
439
+ except Exception as e:
440
+ print("i18n load failed:", repr(e))
441
+
442
+
443
+ _splash_initialized = True
444
+
445
+
446
+ # Initialize splash immediately before any heavy imports
447
+ # This ensures the splash is visible while PyTorch, NumPy, etc. are loading
448
+ _init_splash()
449
+
450
+
451
+ # =============================================================================
452
+ # Now proceed with all the heavy imports (splash is visible)
453
+ # =============================================================================
454
+
455
+ # Helper to update splash during imports
456
+ def _update_splash(msg: str, progress: int):
457
+ global _splash
458
+ if _splash is not None:
459
+ _splash.setMessage(msg)
460
+ _splash.setProgress(progress)
461
+
462
+ _update_splash(QCoreApplication.translate("Splash", "Loading PyTorch runtime..."), 5)
463
+
464
+ from setiastro.saspro.runtime_torch import (
465
+ add_runtime_to_sys_path,
466
+ _ban_shadow_torch_paths,
467
+ _purge_bad_torch_from_sysmodules,
468
+ )
469
+
470
+ add_runtime_to_sys_path(status_cb=lambda *_: None)
471
+ _ban_shadow_torch_paths(status_cb=lambda *_: None)
472
+ _purge_bad_torch_from_sysmodules(status_cb=lambda *_: None)
473
+
474
+ _update_splash(QCoreApplication.translate("Splash", "Loading standard libraries..."), 10)
475
+
476
+ # ----------------------------------------
477
+ # Standard library imports (consolidated)
478
+ # ----------------------------------------
479
+ import importlib
480
+ import json
481
+ import logging
482
+ import math
483
+ import multiprocessing
484
+ import os
485
+ import re
486
+ import sys
487
+ import threading
488
+ import time
489
+ import traceback
490
+ import warnings
491
+ import webbrowser
492
+
493
+ from collections import defaultdict
494
+ from datetime import datetime
495
+ from decimal import getcontext
496
+ from io import BytesIO
497
+ from itertools import combinations
498
+ from math import isnan
499
+ from pathlib import Path
500
+ from typing import Dict, List, Optional, Set, Tuple
501
+ from urllib.parse import quote, quote_plus
502
+
503
+ _update_splash(QCoreApplication.translate("Splash", "Loading NumPy..."), 15)
504
+
505
+ # ----------------------------------------
506
+ # Third-party imports
507
+ # ----------------------------------------
508
+ import numpy as np
509
+
510
+ _update_splash(QCoreApplication.translate("Splash", "Loading image libraries..."), 20)
511
+ from tifffile import imwrite
512
+ from setiastro.saspro.xisf import XISF
513
+
514
+ _update_splash(QCoreApplication.translate("Splash", "Configuring matplotlib..."), 25)
515
+ from setiastro.saspro.config_bootstrap import ensure_mpl_config_dir
516
+ _MPL_CFG_DIR = ensure_mpl_config_dir()
517
+
518
+ # Apply metadata patches for frozen builds
519
+ from setiastro.saspro.metadata_patcher import apply_metadata_patches
520
+ apply_metadata_patches()
521
+ # ----------------------------------------
522
+
523
+ warnings.filterwarnings(
524
+ "ignore",
525
+ message=r"Call to deprecated function \(or staticmethod\) _destroy\.",
526
+ category=DeprecationWarning,
527
+ )
528
+
529
+ os.environ['LIGHTKURVE_STYLE'] = 'default'
530
+
531
+ # ----------------------------------------
532
+ # Matplotlib configuration
533
+ # ----------------------------------------
534
+ import matplotlib
535
+ matplotlib.use("QtAgg")
536
+
537
+ # Configure stdout encoding
538
+ if (sys.stdout is not None) and (hasattr(sys.stdout, "reconfigure")):
539
+ sys.stdout.reconfigure(encoding='utf-8')
540
+
541
+ # --- Lazy imports for heavy dependencies (performance optimization) ---
542
+ # photutils: loaded on first use
543
+ _photutils_isophote = None
544
+ def _get_photutils_isophote():
545
+ """Lazy loader for photutils.isophote module."""
546
+ global _photutils_isophote
547
+ if _photutils_isophote is None:
548
+ try:
549
+ from photutils import isophote as _isophote_module
550
+ _photutils_isophote = _isophote_module
551
+ except Exception:
552
+ _photutils_isophote = False # Mark as failed
553
+ return _photutils_isophote if _photutils_isophote else None
554
+
555
+ def get_Ellipse():
556
+ """Get photutils.isophote.Ellipse, loading lazily."""
557
+ mod = _get_photutils_isophote()
558
+ return mod.Ellipse if mod else None
559
+
560
+ def get_EllipseGeometry():
561
+ """Get photutils.isophote.EllipseGeometry, loading lazily."""
562
+ mod = _get_photutils_isophote()
563
+ return mod.EllipseGeometry if mod else None
564
+
565
+ def get_build_ellipse_model():
566
+ """Get photutils.isophote.build_ellipse_model, loading lazily."""
567
+ mod = _get_photutils_isophote()
568
+ return mod.build_ellipse_model if mod else None
569
+
570
+ # lightkurve: loaded on first use
571
+ _lightkurve_module = None
572
+ def get_lightkurve():
573
+ """Lazy loader for lightkurve module."""
574
+ global _lightkurve_module
575
+ if _lightkurve_module is None:
576
+ try:
577
+ import lightkurve as _lk
578
+ _lk.MPLSTYLE = None
579
+ _lightkurve_module = _lk
580
+ except Exception:
581
+ _lightkurve_module = False # Mark as failed
582
+ return _lightkurve_module if _lightkurve_module else None
583
+ # --- End lazy imports ---
584
+
585
+ _update_splash(QCoreApplication.translate("Splash", "Loading UI utilities..."), 30)
586
+
587
+ # Shared UI utilities (avoiding code duplication)
588
+ from setiastro.saspro.widgets.common_utilities import (
589
+ AboutDialog,
590
+ ProjectSaveWorker as _ProjectSaveWorker,
591
+ DECOR_GLYPHS,
592
+ _strip_ui_decorations,
593
+ install_crash_handlers,
594
+ )
595
+
596
+ _update_splash(QCoreApplication.translate("Splash", "Loading reproject library..."), 35)
597
+
598
+ # Reproject for WCS-based alignment
599
+ try:
600
+ from reproject import reproject_interp
601
+ except ImportError:
602
+ reproject_interp = None # fallback if not installed
603
+
604
+ _update_splash(QCoreApplication.translate("Splash", "Loading OpenCV..."), 40)
605
+
606
+ # OpenCV for transform estimation & warping
607
+ try:
608
+ import cv2
609
+ OPENCV_AVAILABLE = True
610
+ except ImportError:
611
+ OPENCV_AVAILABLE = False
612
+
613
+
614
+ _update_splash(QCoreApplication.translate("Splash", "Loading PyQt6 components..."), 45)
615
+
616
+ #################################
617
+ # PyQt6 Imports
618
+ #################################
619
+ from PyQt6 import sip
620
+
621
+ # ----- QtWidgets -----
622
+ from PyQt6.QtWidgets import (QDialog, QApplication, QMainWindow, QWidget, QHBoxLayout, QFileDialog, QMessageBox, QSizePolicy, QToolBar, QPushButton, QAbstractItemDelegate,
623
+ QLineEdit, QMenu, QListWidget, QListWidgetItem, QSplashScreen, QDockWidget, QListView, QCompleter, QMdiArea, QMdiSubWindow, QWidgetAction, QAbstractItemView,
624
+ QInputDialog, QVBoxLayout, QLabel, QCheckBox, QProgressBar, QProgressDialog, QGraphicsItem, QTabWidget, QTableWidget, QHeaderView, QTableWidgetItem, QToolButton, QPlainTextEdit
625
+ )
626
+
627
+ # ----- QtGui -----
628
+ from PyQt6.QtGui import (QPixmap, QColor, QIcon, QKeySequence, QShortcut, QGuiApplication, QStandardItemModel, QStandardItem, QAction, QPalette, QBrush, QActionGroup, QDesktopServices, QFont, QTextCursor
629
+ )
630
+
631
+ # ----- QtCore -----
632
+ from PyQt6.QtCore import (Qt, pyqtSignal, QCoreApplication, QTimer, QSize, QSignalBlocker, QModelIndex, QThread, QUrl, QSettings, QEvent, QByteArray, QObject,
633
+ QPropertyAnimation, QEasingCurve
634
+ )
635
+
636
+ from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
637
+
638
+
639
+ try:
640
+ from setiastro.saspro._generated.build_info import BUILD_TIMESTAMP
641
+ except Exception:
642
+ # No generated build info → running from local source checkout
643
+ BUILD_TIMESTAMP = "dev"
644
+
645
+
646
+
647
+ _update_splash(QCoreApplication.translate("Splash", "Loading resources..."), 50)
648
+
649
+ # Icon paths are now centralized in setiastro.saspro.resources module
650
+ from setiastro.saspro.resources import (
651
+ icon_path, windowslogo_path, green_path, neutral_path, whitebalance_path,
652
+ morpho_path, clahe_path, starnet_path, staradd_path, LExtract_path,
653
+ LInsert_path, slot0_path, slot1_path, slot2_path, slot3_path, slot4_path,
654
+ rgbcombo_path, rgbextract_path, copyslot_path, graxperticon_path,
655
+ cropicon_path, openfile_path, abeicon_path, undoicon_path, redoicon_path,
656
+ blastericon_path, hdr_path, invert_path, fliphorizontal_path,
657
+ flipvertical_path, rotateclockwise_path, rotatecounterclockwise_path,
658
+ rotate180_path, maskcreate_path, maskapply_path, maskremove_path,
659
+ slot5_path, slot6_path, slot7_path, slot8_path, slot9_path, pixelmath_path,
660
+ histogram_path, mosaic_path, rescale_path, staralign_path, mask_path,
661
+ platesolve_path, psf_path, supernova_path, starregistration_path,
662
+ stacking_path, pedestal_icon_path, starspike_path, aperture_path,
663
+ jwstpupil_path, signature_icon_path, livestacking_path, hrdiagram_path,
664
+ convoicon_path, spcc_icon_path, sasp_data_path, exoicon_path, peeker_icon,
665
+ dse_icon_path, astrobin_filters_csv_path, isophote_path, statstretch_path,
666
+ starstretch_path, curves_path, disk_path, uhs_path, blink_path, ppp_path,
667
+ nbtorgb_path, freqsep_path, contsub_path, halo_path, cosmic_path,
668
+ satellite_path, imagecombine_path, wrench_path, eye_icon_path,
669
+ disk_icon_path, nuke_path, hubble_path, collage_path, annotated_path,
670
+ colorwheel_path, font_path, csv_icon_path, spinner_path, wims_path,
671
+ wimi_path, linearfit_path, debayer_path, aberration_path,
672
+ functionbundles_path, viewbundles_path, selectivecolor_path, rgbalign_path,
673
+ )
674
+
675
+
676
+ _update_splash(QCoreApplication.translate("Splash", "Configuring Qt message handler..."), 55)
677
+
678
+ from PyQt6.QtCore import qInstallMessageHandler, QtMsgType
679
+
680
+ def _qt_msg_handler(mode, ctx, msg):
681
+ lvl = {
682
+ QtMsgType.QtDebugMsg: logging.DEBUG,
683
+ QtMsgType.QtInfoMsg: logging.INFO,
684
+ QtMsgType.QtWarningMsg: logging.WARNING,
685
+ QtMsgType.QtCriticalMsg: logging.ERROR,
686
+ QtMsgType.QtFatalMsg: logging.CRITICAL,
687
+ }.get(mode, logging.ERROR)
688
+ logging.log(lvl, "Qt: %s (%s:%s)", msg, getattr(ctx, "file", "?"), getattr(ctx, "line", -1))
689
+
690
+ qInstallMessageHandler(_qt_msg_handler)
691
+
692
+ _update_splash(QCoreApplication.translate("Splash", "Loading MDI widgets..."), 60)
693
+
694
+ # MDI widgets imported from setiastro.saspro.mdi_widgets
695
+ from setiastro.saspro.mdi_widgets import (
696
+ MdiArea, ViewLinkController, ConsoleListWidget, QtLogStream, _DocProxy,
697
+ ROLE_ACTION as _ROLE_ACTION,
698
+ )
699
+
700
+ # Helper functions imported from setiastro.saspro.main_helpers
701
+ from setiastro.saspro.main_helpers import (
702
+ safe_join_dir_and_name as _safe_join_dir_and_name,
703
+ normalize_save_path_chosen_filter as _normalize_save_path_chosen_filter,
704
+ display_name as _display_name,
705
+ best_doc_name as _best_doc_name,
706
+ doc_looks_like_table as _doc_looks_like_table,
707
+ is_alive as _is_alive,
708
+ safe_widget as _safe_widget,
709
+ )
710
+
711
+ # AboutDialog, DECOR_GLYPHS, _strip_ui_decorations imported from setiastro.saspro.widgets.common_utilities
712
+
713
+ # File utilities imported from setiastro.saspro.file_utils
714
+ from setiastro.saspro.file_utils import (
715
+ _normalize_ext,
716
+ _sanitize_filename,
717
+ _exts_from_filter,
718
+ REPLACE_SPACES_WITH_UNDERSCORES as _REPLACE_SPACES_WITH_UNDERSCORES,
719
+ WIN_RESERVED_NAMES as _WIN_RESERVED,
720
+ )
721
+
722
+ _update_splash(QCoreApplication.translate("Splash", "Loading main window module..."), 65)
723
+
724
+ from setiastro.saspro.gui.main_window import AstroSuiteProMainWindow
725
+
726
+ _update_splash(QCoreApplication.translate("Splash", "Modules loaded, finalizing..."), 70)
727
+
728
+
729
+ def main():
730
+ """
731
+ Main entry point for Seti Astro Suite Pro.
732
+
733
+ This function can be called from:
734
+ - The package entry point (setiastrosuitepro command)
735
+ - Direct import and call
736
+ - When running as a module: python -m setiastro.saspro
737
+ """
738
+ global _splash, _app, _splash_initialized
739
+
740
+ # Initialize splash if not already done
741
+ if not _splash_initialized:
742
+ _init_splash()
743
+
744
+ # Update splash with build info now that we have VERSION and BUILD_TIMESTAMP
745
+ if _splash:
746
+ _splash.setBuildInfo(VERSION, BUILD_TIMESTAMP)
747
+ _splash.setMessage(QCoreApplication.translate("Splash", "Setting up logging..."))
748
+ _splash.setProgress(72)
749
+
750
+ # --- Logging (catch unhandled exceptions to a file) ---
751
+ import tempfile
752
+ from pathlib import Path
753
+
754
+ # Cross-platform log file location
755
+ def get_log_file_path():
756
+ """Get appropriate log file path for the current platform."""
757
+
758
+ if hasattr(sys, '_MEIPASS'):
759
+ # Running in PyInstaller bundle - use platform-appropriate user directory
760
+ if sys.platform.startswith('win'):
761
+ # Windows: %APPDATA%\SetiAstroSuitePro\logs\
762
+ log_dir = Path(os.path.expandvars('%APPDATA%')) / 'SetiAstroSuitePro' / 'logs'
763
+ elif sys.platform.startswith('darwin'):
764
+ # macOS: ~/Library/Logs/SetiAstroSuitePro/
765
+ log_dir = Path.home() / 'Library' / 'Logs' / 'SetiAstroSuitePro'
766
+ else:
767
+ # Linux: ~/.local/share/SetiAstroSuitePro/logs/
768
+ log_dir = Path.home() / '.local' / 'share' / 'SetiAstroSuitePro' / 'logs'
769
+
770
+ # Create directory if it doesn't exist
771
+ try:
772
+ log_dir.mkdir(parents=True, exist_ok=True)
773
+ log_file = log_dir / 'saspro.log'
774
+ except (OSError, PermissionError):
775
+ # Fallback to temp directory if user directory fails
776
+ log_file = Path(tempfile.gettempdir()) / 'saspro.log'
777
+ else:
778
+ # Development mode - use logs folder in project
779
+ log_dir = Path('logs')
780
+ try:
781
+ log_dir.mkdir(parents=True, exist_ok=True)
782
+ log_file = log_dir / 'saspro.log'
783
+ except (OSError, PermissionError):
784
+ log_file = Path('saspro.log')
785
+
786
+ return str(log_file)
787
+
788
+ # Configure logging with cross-platform path
789
+ log_file_path = get_log_file_path()
790
+
791
+ try:
792
+ logging.basicConfig(
793
+ filename=log_file_path,
794
+ level=logging.INFO,
795
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
796
+ filemode='a' # Append mode
797
+ )
798
+ logging.info(f"Logging to: {log_file_path}")
799
+ logging.info(f"Platform: {sys.platform}")
800
+ logging.info(f"PyInstaller bundle: {hasattr(sys, '_MEIPASS')}")
801
+ except Exception as e:
802
+ # Ultimate fallback - console only logging
803
+ logging.basicConfig(
804
+ level=logging.INFO,
805
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
806
+ handlers=[logging.StreamHandler(sys.stdout)]
807
+ )
808
+ print(f"Warning: Could not write to log file {log_file_path}: {e}")
809
+ print("Using console-only logging")
810
+
811
+
812
+ # Setup crash handlers and app icon
813
+ if _splash:
814
+ _splash.setMessage(QCoreApplication.translate("Splash", "Installing crash handlers..."))
815
+ _splash.setProgress(75)
816
+ install_crash_handlers(_app)
817
+ _app.setWindowIcon(QIcon(windowslogo_path if os.path.exists(windowslogo_path) else icon_path))
818
+
819
+ # --- Windows exe / multiprocessing friendly ---
820
+ if _splash:
821
+ _splash.setMessage(QCoreApplication.translate("Splash", "Configuring multiprocessing..."))
822
+ _splash.setProgress(78)
823
+ try:
824
+ multiprocessing.freeze_support()
825
+ try:
826
+ multiprocessing.set_start_method("spawn", force=True)
827
+ except RuntimeError:
828
+ # Already set in this interpreter
829
+ pass
830
+ except Exception:
831
+ logging.exception("Multiprocessing init failed (continuing).")
832
+
833
+ try:
834
+ if _splash:
835
+ _splash.setMessage(QCoreApplication.translate("Splash", "Loading image manager..."))
836
+ _splash.setProgress(80)
837
+ from setiastro.saspro.legacy.image_manager import ImageManager
838
+
839
+ if _splash:
840
+ _splash.setMessage(QCoreApplication.translate("Splash", "Suppressing warnings..."))
841
+ _splash.setProgress(82)
842
+ from matplotlib import MatplotlibDeprecationWarning
843
+ warnings.filterwarnings("ignore", category=MatplotlibDeprecationWarning)
844
+
845
+ if _splash:
846
+ _splash.setMessage(QCoreApplication.translate("Splash", "Creating image manager..."))
847
+ _splash.setProgress(85)
848
+ imgr = ImageManager(max_slots=100)
849
+
850
+ if _splash:
851
+ _splash.setMessage(QCoreApplication.translate("Splash", "Building main window..."))
852
+ _splash.setProgress(90)
853
+ win = AstroSuiteProMainWindow(
854
+ image_manager=imgr,
855
+ version=VERSION,
856
+ build_timestamp=BUILD_TIMESTAMP,
857
+ )
858
+
859
+ if _splash:
860
+ _splash.setMessage(QCoreApplication.translate("Splash", "Showing main window..."))
861
+ _splash.setProgress(95)
862
+
863
+ # --- Smooth Transition: App Fade In + Splash Fade Out ---
864
+ # MITIGATION: Prevent "White Flash" on startup
865
+ # 1. Force a dark background immediately so if opacity lags, it's dark not white
866
+ win.setStyleSheet("QMainWindow { background-color: #0F0F19; }")
867
+ # 2. Ensure native window handle exists so setWindowOpacity works immediately
868
+ win.winId()
869
+ # 3. Set opacity to 0
870
+ win.setWindowOpacity(0.0)
871
+
872
+ win.show()
873
+
874
+ # 1. Animate Main Window Fade In
875
+ anim_app = QPropertyAnimation(win, b"windowOpacity")
876
+ anim_app.setDuration(1200)
877
+ anim_app.setStartValue(0.0)
878
+ anim_app.setEndValue(1.0)
879
+ anim_app.setEasingCurve(QEasingCurve.Type.OutQuad)
880
+
881
+ # Cleanup temp stylesheet upon completion to avoid interfering with ThemeMixin
882
+ def _on_fade_in_finished():
883
+ win.setStyleSheet("")
884
+ if hasattr(win, "on_fade_in_complete"):
885
+ win.on_fade_in_complete()
886
+
887
+ anim_app.finished.connect(_on_fade_in_finished)
888
+ anim_app.start()
889
+
890
+ # Start background Numba warmup after UI is visible
891
+ try:
892
+ from setiastro.saspro.numba_warmup import start_background_warmup
893
+ start_background_warmup()
894
+ except Exception:
895
+ pass # Non-critical if warmup fails
896
+
897
+ if _splash:
898
+ _splash.setMessage(QCoreApplication.translate("Splash", "Ready!"))
899
+ _splash.setProgress(100)
900
+ _app.processEvents()
901
+
902
+ # Small delay to ensure "Ready!" is seen briefly before fade starts
903
+ import time
904
+ time.sleep(0.1)
905
+
906
+ # 2. Animate Splash Fade Out
907
+ # Note: We do NOT use finish() directly here. The animation calls it when done.
908
+ _splash.start_fade_out()
909
+
910
+ # NOTE: We keep a reference to _splash (global) so it doesn't get GC'd during animation.
911
+ # It will deleteLater() itself.
912
+
913
+ if BUILD_TIMESTAMP == "dev":
914
+ build_label = "running from local source code"
915
+ else:
916
+ build_label = f"build {BUILD_TIMESTAMP}"
917
+
918
+ print(f"Seti Astro Suite Pro v{VERSION} ({build_label}) up and running!")
919
+ sys.exit(_app.exec())
920
+
921
+ except Exception:
922
+ import traceback
923
+ if _splash:
924
+ try:
925
+ _splash.hide()
926
+ _splash.close()
927
+ _splash.deleteLater()
928
+ except Exception:
929
+ pass
930
+ tb = traceback.format_exc()
931
+ logging.error("Unhandled exception occurred\n%s", tb)
932
+ msg = QMessageBox(None)
933
+ msg.setIcon(QMessageBox.Icon.Critical)
934
+ msg.setWindowTitle(QCoreApplication.translate("Main", "Application Error"))
935
+ msg.setText(QCoreApplication.translate("Main", "An unexpected error occurred."))
936
+ msg.setInformativeText(tb.splitlines()[-1] if tb else "See details.")
937
+ msg.setDetailedText(tb)
938
+ msg.setStandardButtons(QMessageBox.StandardButton.Ok)
939
+ msg.exec()
940
+ sys.exit(1)
941
+
942
+
943
+ # When run as a module, execute main()
944
+ if __name__ == "__main__":
945
+ main()