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