setiastrosuitepro 1.7.1.post2__py3-none-any.whl → 1.7.4__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.
- setiastro/images/3dplanet.png +0 -0
- setiastro/saspro/__init__.py +20 -8
- setiastro/saspro/__main__.py +349 -290
- setiastro/saspro/_generated/build_info.py +2 -2
- setiastro/saspro/abe.py +4 -4
- setiastro/saspro/autostretch.py +29 -18
- setiastro/saspro/doc_manager.py +4 -1
- setiastro/saspro/gui/main_window.py +46 -7
- setiastro/saspro/gui/mixins/file_mixin.py +6 -2
- setiastro/saspro/gui/mixins/menu_mixin.py +1 -0
- setiastro/saspro/gui/mixins/toolbar_mixin.py +9 -2
- setiastro/saspro/imageops/serloader.py +101 -17
- setiastro/saspro/layers.py +186 -10
- setiastro/saspro/layers_dock.py +198 -5
- setiastro/saspro/legacy/image_manager.py +10 -4
- setiastro/saspro/legacy/numba_utils.py +301 -119
- setiastro/saspro/numba_utils.py +998 -270
- setiastro/saspro/ops/settings.py +6 -6
- setiastro/saspro/pixelmath.py +1 -1
- setiastro/saspro/planetprojection.py +4059 -0
- setiastro/saspro/resources.py +2 -0
- setiastro/saspro/save_options.py +45 -13
- setiastro/saspro/ser_stack_config.py +21 -1
- setiastro/saspro/ser_stacker.py +8 -2
- setiastro/saspro/ser_stacker_dialog.py +37 -10
- setiastro/saspro/ser_tracking.py +57 -35
- setiastro/saspro/serviewer.py +164 -16
- setiastro/saspro/sfcc.py +14 -8
- setiastro/saspro/stacking_suite.py +292 -111
- setiastro/saspro/subwindow.py +64 -36
- setiastro/saspro/translations/all_source_strings.json +2 -2
- setiastro/saspro/translations/ar_translations.py +3 -3
- setiastro/saspro/translations/de_translations.py +2 -2
- setiastro/saspro/translations/es_translations.py +2 -2
- setiastro/saspro/translations/fr_translations.py +2 -2
- setiastro/saspro/translations/hi_translations.py +2 -2
- setiastro/saspro/translations/it_translations.py +2 -2
- setiastro/saspro/translations/ja_translations.py +2 -2
- setiastro/saspro/translations/pt_translations.py +2 -2
- setiastro/saspro/translations/ru_translations.py +2 -2
- setiastro/saspro/translations/saspro_ar.ts +2 -2
- setiastro/saspro/translations/saspro_de.ts +4 -4
- setiastro/saspro/translations/saspro_es.ts +2 -2
- setiastro/saspro/translations/saspro_fr.ts +2 -2
- setiastro/saspro/translations/saspro_hi.ts +2 -2
- setiastro/saspro/translations/saspro_it.ts +4 -4
- setiastro/saspro/translations/saspro_ja.ts +2 -2
- setiastro/saspro/translations/saspro_pt.ts +2 -2
- setiastro/saspro/translations/saspro_ru.ts +2 -2
- setiastro/saspro/translations/saspro_sw.ts +2 -2
- setiastro/saspro/translations/saspro_uk.ts +2 -2
- setiastro/saspro/translations/saspro_zh.ts +2 -2
- setiastro/saspro/translations/sw_translations.py +2 -2
- setiastro/saspro/translations/uk_translations.py +2 -2
- setiastro/saspro/translations/zh_translations.py +2 -2
- setiastro/saspro/window_shelf.py +62 -1
- {setiastrosuitepro-1.7.1.post2.dist-info → setiastrosuitepro-1.7.4.dist-info}/METADATA +1 -1
- {setiastrosuitepro-1.7.1.post2.dist-info → setiastrosuitepro-1.7.4.dist-info}/RECORD +62 -60
- {setiastrosuitepro-1.7.1.post2.dist-info → setiastrosuitepro-1.7.4.dist-info}/entry_points.txt +1 -1
- {setiastrosuitepro-1.7.1.post2.dist-info → setiastrosuitepro-1.7.4.dist-info}/WHEEL +0 -0
- {setiastrosuitepro-1.7.1.post2.dist-info → setiastrosuitepro-1.7.4.dist-info}/licenses/LICENSE +0 -0
- {setiastrosuitepro-1.7.1.post2.dist-info → setiastrosuitepro-1.7.4.dist-info}/licenses/license.txt +0 -0
setiastro/saspro/__main__.py
CHANGED
|
@@ -12,6 +12,27 @@ called via the `main()` function when invoked as an entry point.
|
|
|
12
12
|
import sys
|
|
13
13
|
import os
|
|
14
14
|
|
|
15
|
+
_STARTUP_PROFILE = os.environ.get("SASPRO_STARTUP_PROFILE", "").strip().lower() in ("1","true","yes","on")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _is_wayland_session() -> bool:
|
|
19
|
+
# Best-effort detection that does NOT require a QApplication instance
|
|
20
|
+
xdg = (os.environ.get("XDG_SESSION_TYPE") or "").lower()
|
|
21
|
+
if xdg == "wayland":
|
|
22
|
+
return True
|
|
23
|
+
# QT can still run wayland even if XDG_SESSION_TYPE isn't set
|
|
24
|
+
if os.environ.get("WAYLAND_DISPLAY"):
|
|
25
|
+
return True
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
def _allow_window_opacity_effects() -> bool:
|
|
29
|
+
# User override: allow opacity effects even on Wayland if they want
|
|
30
|
+
if os.environ.get("SASPRO_ALLOW_OPACITY", "").strip().lower() in ("1","true","yes","on"):
|
|
31
|
+
return True
|
|
32
|
+
# Default: disable opacity effects on Wayland
|
|
33
|
+
return not _is_wayland_session()
|
|
34
|
+
|
|
35
|
+
|
|
15
36
|
from pathlib import Path
|
|
16
37
|
|
|
17
38
|
if sys.platform.startswith("win"):
|
|
@@ -24,7 +45,7 @@ if sys.platform.startswith("win"):
|
|
|
24
45
|
pass
|
|
25
46
|
os.environ["PATH"] = str(internal) + os.pathsep + os.environ.get("PATH", "")
|
|
26
47
|
|
|
27
|
-
|
|
48
|
+
|
|
28
49
|
|
|
29
50
|
# ---- Linux Qt stability guard (must run BEFORE any PyQt6 import) ----
|
|
30
51
|
# Default behavior: DO NOT override Wayland.
|
|
@@ -41,6 +62,8 @@ if sys.platform.startswith("linux"):
|
|
|
41
62
|
os.environ.setdefault("QT_OPENGL", "software")
|
|
42
63
|
os.environ.setdefault("LIBGL_ALWAYS_SOFTWARE", "1")
|
|
43
64
|
|
|
65
|
+
from PyQt6.QtCore import QCoreApplication
|
|
66
|
+
|
|
44
67
|
# Global variables for splash screen and app
|
|
45
68
|
_splash = None
|
|
46
69
|
_app = None
|
|
@@ -48,6 +71,10 @@ _app = None
|
|
|
48
71
|
# Flag to track if splash was initialized
|
|
49
72
|
_splash_initialized = False
|
|
50
73
|
|
|
74
|
+
# Flag to ensure heavy imports/bootstrap happens only once
|
|
75
|
+
_imports_bootstrapped = False
|
|
76
|
+
|
|
77
|
+
|
|
51
78
|
from setiastro.saspro.versioning import get_app_version
|
|
52
79
|
_EARLY_VERSION = get_app_version("setiastrosuitepro")
|
|
53
80
|
|
|
@@ -413,18 +440,31 @@ def _init_splash():
|
|
|
413
440
|
self.close()
|
|
414
441
|
self.deleteLater()
|
|
415
442
|
|
|
416
|
-
def start_fade_out(self):
|
|
417
|
-
|
|
443
|
+
def start_fade_out(self, on_finished=None):
|
|
444
|
+
if not _allow_window_opacity_effects():
|
|
445
|
+
self.finish()
|
|
446
|
+
if callable(on_finished):
|
|
447
|
+
on_finished()
|
|
448
|
+
return
|
|
449
|
+
|
|
418
450
|
self._anim = QPropertyAnimation(self, b"windowOpacity")
|
|
419
451
|
self._anim.setDuration(1000)
|
|
420
452
|
self._anim.setStartValue(1.0)
|
|
421
453
|
self._anim.setEndValue(0.0)
|
|
422
454
|
self._anim.setEasingCurve(QEasingCurve.Type.OutQuad)
|
|
423
|
-
|
|
455
|
+
|
|
456
|
+
def _done():
|
|
457
|
+
self.finish()
|
|
458
|
+
if callable(on_finished):
|
|
459
|
+
on_finished()
|
|
460
|
+
|
|
461
|
+
self._anim.finished.connect(_done)
|
|
424
462
|
self._anim.start()
|
|
425
463
|
|
|
426
464
|
def start_fade_in(self):
|
|
427
|
-
|
|
465
|
+
if not _allow_window_opacity_effects():
|
|
466
|
+
self.setWindowOpacity(1.0)
|
|
467
|
+
return
|
|
428
468
|
self.setWindowOpacity(0.0)
|
|
429
469
|
self._anim = QPropertyAnimation(self, b"windowOpacity")
|
|
430
470
|
self._anim.setDuration(800)
|
|
@@ -462,11 +502,6 @@ def _init_splash():
|
|
|
462
502
|
_splash_initialized = True
|
|
463
503
|
|
|
464
504
|
|
|
465
|
-
# Initialize splash immediately before any heavy imports
|
|
466
|
-
# This ensures the splash is visible while PyTorch, NumPy, etc. are loading
|
|
467
|
-
_init_splash()
|
|
468
|
-
|
|
469
|
-
|
|
470
505
|
# =============================================================================
|
|
471
506
|
# Now proceed with all the heavy imports (splash is visible)
|
|
472
507
|
# =============================================================================
|
|
@@ -478,271 +513,279 @@ def _update_splash(msg: str, progress: int):
|
|
|
478
513
|
_splash.setMessage(msg)
|
|
479
514
|
_splash.setProgress(progress)
|
|
480
515
|
|
|
481
|
-
|
|
516
|
+
def _bootstrap_imports():
|
|
517
|
+
"""
|
|
518
|
+
Heavy imports + runtime bootstrap.
|
|
519
|
+
This must NOT run at module import time, only when main() is called.
|
|
520
|
+
"""
|
|
521
|
+
global _imports_bootstrapped
|
|
522
|
+
if _imports_bootstrapped:
|
|
523
|
+
return
|
|
524
|
+
_imports_bootstrapped = True
|
|
525
|
+
|
|
526
|
+
# Make sure splash exists before we start doing heavy work
|
|
527
|
+
if not _splash_initialized:
|
|
528
|
+
_init_splash()
|
|
482
529
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
530
|
+
_update_splash(QCoreApplication.translate("Splash", "Loading PyTorch runtime..."), 5)
|
|
531
|
+
|
|
532
|
+
from setiastro.saspro.runtime_torch import (
|
|
533
|
+
add_runtime_to_sys_path,
|
|
534
|
+
_ban_shadow_torch_paths,
|
|
535
|
+
_purge_bad_torch_from_sysmodules,
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
add_runtime_to_sys_path(status_cb=lambda *_: None)
|
|
539
|
+
_ban_shadow_torch_paths(status_cb=lambda *_: None)
|
|
540
|
+
_purge_bad_torch_from_sysmodules(status_cb=lambda *_: None)
|
|
541
|
+
|
|
542
|
+
_update_splash(QCoreApplication.translate("Splash", "Loading standard libraries..."), 10)
|
|
543
|
+
|
|
544
|
+
# ----------------------------------------
|
|
545
|
+
# Standard library imports (consolidated)
|
|
546
|
+
# ----------------------------------------
|
|
547
|
+
import importlib
|
|
548
|
+
import json
|
|
549
|
+
import logging
|
|
550
|
+
import math
|
|
551
|
+
import multiprocessing
|
|
552
|
+
import os
|
|
553
|
+
import re
|
|
554
|
+
import sys
|
|
555
|
+
import threading
|
|
556
|
+
import time
|
|
557
|
+
import traceback
|
|
558
|
+
import warnings
|
|
559
|
+
import webbrowser
|
|
560
|
+
|
|
561
|
+
from collections import defaultdict
|
|
562
|
+
from datetime import datetime
|
|
563
|
+
from decimal import getcontext
|
|
564
|
+
from io import BytesIO
|
|
565
|
+
from itertools import combinations
|
|
566
|
+
from math import isnan
|
|
567
|
+
from pathlib import Path
|
|
568
|
+
from typing import Dict, List, Optional, Set, Tuple
|
|
569
|
+
from urllib.parse import quote, quote_plus
|
|
488
570
|
|
|
489
|
-
|
|
490
|
-
_ban_shadow_torch_paths(status_cb=lambda *_: None)
|
|
491
|
-
_purge_bad_torch_from_sysmodules(status_cb=lambda *_: None)
|
|
571
|
+
_update_splash(QCoreApplication.translate("Splash", "Loading NumPy..."), 15)
|
|
492
572
|
|
|
493
|
-
|
|
573
|
+
# ----------------------------------------
|
|
574
|
+
# Third-party imports
|
|
575
|
+
# ----------------------------------------
|
|
576
|
+
import numpy as np
|
|
494
577
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
import
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
import
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
#
|
|
526
|
-
#
|
|
527
|
-
import numpy as np
|
|
528
|
-
|
|
529
|
-
_update_splash(QCoreApplication.translate("Splash", "Loading image libraries..."), 20)
|
|
530
|
-
from tifffile import imwrite
|
|
531
|
-
from setiastro.saspro.xisf import XISF
|
|
532
|
-
|
|
533
|
-
_update_splash(QCoreApplication.translate("Splash", "Configuring matplotlib..."), 25)
|
|
534
|
-
from setiastro.saspro.config_bootstrap import ensure_mpl_config_dir
|
|
535
|
-
_MPL_CFG_DIR = ensure_mpl_config_dir()
|
|
536
|
-
|
|
537
|
-
# Apply metadata patches for frozen builds
|
|
538
|
-
from setiastro.saspro.metadata_patcher import apply_metadata_patches
|
|
539
|
-
apply_metadata_patches()
|
|
540
|
-
# ----------------------------------------
|
|
541
|
-
|
|
542
|
-
warnings.filterwarnings(
|
|
543
|
-
"ignore",
|
|
544
|
-
message=r"Call to deprecated function \(or staticmethod\) _destroy\.",
|
|
545
|
-
category=DeprecationWarning,
|
|
546
|
-
)
|
|
547
|
-
|
|
548
|
-
os.environ['LIGHTKURVE_STYLE'] = 'default'
|
|
549
|
-
|
|
550
|
-
# ----------------------------------------
|
|
551
|
-
# Matplotlib configuration
|
|
552
|
-
# ----------------------------------------
|
|
553
|
-
import matplotlib
|
|
554
|
-
matplotlib.use("QtAgg")
|
|
555
|
-
|
|
556
|
-
# Configure stdout encoding
|
|
557
|
-
if (sys.stdout is not None) and (hasattr(sys.stdout, "reconfigure")):
|
|
558
|
-
sys.stdout.reconfigure(encoding='utf-8')
|
|
559
|
-
|
|
560
|
-
# --- Lazy imports for heavy dependencies (performance optimization) ---
|
|
561
|
-
# photutils: loaded on first use
|
|
562
|
-
_photutils_isophote = None
|
|
563
|
-
def _get_photutils_isophote():
|
|
564
|
-
"""Lazy loader for photutils.isophote module."""
|
|
578
|
+
_update_splash(QCoreApplication.translate("Splash", "Loading image libraries..."), 20)
|
|
579
|
+
from tifffile import imwrite
|
|
580
|
+
from setiastro.saspro.xisf import XISF
|
|
581
|
+
|
|
582
|
+
_update_splash(QCoreApplication.translate("Splash", "Configuring matplotlib..."), 25)
|
|
583
|
+
from setiastro.saspro.config_bootstrap import ensure_mpl_config_dir
|
|
584
|
+
_MPL_CFG_DIR = ensure_mpl_config_dir()
|
|
585
|
+
|
|
586
|
+
# Apply metadata patches for frozen builds
|
|
587
|
+
from setiastro.saspro.metadata_patcher import apply_metadata_patches
|
|
588
|
+
apply_metadata_patches()
|
|
589
|
+
|
|
590
|
+
warnings.filterwarnings(
|
|
591
|
+
"ignore",
|
|
592
|
+
message=r"Call to deprecated function \(or staticmethod\) _destroy\.",
|
|
593
|
+
category=DeprecationWarning,
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
os.environ['LIGHTKURVE_STYLE'] = 'default'
|
|
597
|
+
|
|
598
|
+
# ----------------------------------------
|
|
599
|
+
# Matplotlib configuration
|
|
600
|
+
# ----------------------------------------
|
|
601
|
+
import matplotlib
|
|
602
|
+
matplotlib.use("QtAgg")
|
|
603
|
+
|
|
604
|
+
# Configure stdout encoding
|
|
605
|
+
if (sys.stdout is not None) and (hasattr(sys.stdout, "reconfigure")):
|
|
606
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
607
|
+
|
|
608
|
+
# --- Lazy imports for heavy dependencies (performance optimization) ---
|
|
609
|
+
# photutils: loaded on first use
|
|
565
610
|
global _photutils_isophote
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
_lightkurve_module = None
|
|
591
|
-
def get_lightkurve():
|
|
592
|
-
"""Lazy loader for lightkurve module."""
|
|
611
|
+
_photutils_isophote = None
|
|
612
|
+
|
|
613
|
+
def _get_photutils_isophote():
|
|
614
|
+
global _photutils_isophote
|
|
615
|
+
if _photutils_isophote is None:
|
|
616
|
+
try:
|
|
617
|
+
from photutils import isophote as _isophote_module
|
|
618
|
+
_photutils_isophote = _isophote_module
|
|
619
|
+
except Exception:
|
|
620
|
+
_photutils_isophote = False
|
|
621
|
+
return _photutils_isophote if _photutils_isophote else None
|
|
622
|
+
|
|
623
|
+
def get_Ellipse():
|
|
624
|
+
mod = _get_photutils_isophote()
|
|
625
|
+
return mod.Ellipse if mod else None
|
|
626
|
+
|
|
627
|
+
def get_EllipseGeometry():
|
|
628
|
+
mod = _get_photutils_isophote()
|
|
629
|
+
return mod.EllipseGeometry if mod else None
|
|
630
|
+
|
|
631
|
+
def get_build_ellipse_model():
|
|
632
|
+
mod = _get_photutils_isophote()
|
|
633
|
+
return mod.build_ellipse_model if mod else None
|
|
634
|
+
|
|
593
635
|
global _lightkurve_module
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
try:
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
except ImportError:
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
from PyQt6.
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
from setiastro.saspro.
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
636
|
+
_lightkurve_module = None
|
|
637
|
+
|
|
638
|
+
def get_lightkurve():
|
|
639
|
+
global _lightkurve_module
|
|
640
|
+
if _lightkurve_module is None:
|
|
641
|
+
try:
|
|
642
|
+
import lightkurve as _lk
|
|
643
|
+
_lk.MPLSTYLE = None
|
|
644
|
+
_lightkurve_module = _lk
|
|
645
|
+
except Exception:
|
|
646
|
+
_lightkurve_module = False
|
|
647
|
+
return _lightkurve_module if _lightkurve_module else None
|
|
648
|
+
|
|
649
|
+
_update_splash(QCoreApplication.translate("Splash", "Loading UI utilities..."), 30)
|
|
650
|
+
|
|
651
|
+
from setiastro.saspro.widgets.common_utilities import (
|
|
652
|
+
AboutDialog,
|
|
653
|
+
ProjectSaveWorker as _ProjectSaveWorker,
|
|
654
|
+
DECOR_GLYPHS,
|
|
655
|
+
_strip_ui_decorations,
|
|
656
|
+
install_crash_handlers,
|
|
657
|
+
)
|
|
658
|
+
|
|
659
|
+
_update_splash(QCoreApplication.translate("Splash", "Loading reproject library..."), 35)
|
|
660
|
+
|
|
661
|
+
try:
|
|
662
|
+
from reproject import reproject_interp
|
|
663
|
+
except ImportError:
|
|
664
|
+
reproject_interp = None
|
|
665
|
+
|
|
666
|
+
_update_splash(QCoreApplication.translate("Splash", "Loading OpenCV..."), 40)
|
|
667
|
+
|
|
668
|
+
try:
|
|
669
|
+
import cv2
|
|
670
|
+
OPENCV_AVAILABLE = True
|
|
671
|
+
except ImportError:
|
|
672
|
+
OPENCV_AVAILABLE = False
|
|
673
|
+
|
|
674
|
+
_update_splash(QCoreApplication.translate("Splash", "Loading PyQt6 components..."), 45)
|
|
675
|
+
|
|
676
|
+
from PyQt6 import sip
|
|
677
|
+
|
|
678
|
+
from PyQt6.QtWidgets import (QDialog, QApplication, QMainWindow, QWidget, QHBoxLayout, QFileDialog, QMessageBox, QSizePolicy, QToolBar, QPushButton, QAbstractItemDelegate,
|
|
679
|
+
QLineEdit, QMenu, QListWidget, QListWidgetItem, QSplashScreen, QDockWidget, QListView, QCompleter, QMdiArea, QMdiSubWindow, QWidgetAction, QAbstractItemView,
|
|
680
|
+
QInputDialog, QVBoxLayout, QLabel, QCheckBox, QProgressBar, QProgressDialog, QGraphicsItem, QTabWidget, QTableWidget, QHeaderView, QTableWidgetItem, QToolButton, QPlainTextEdit
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
from PyQt6.QtGui import (QPixmap, QColor, QIcon, QKeySequence, QShortcut, QGuiApplication, QStandardItemModel, QStandardItem, QAction, QPalette, QBrush, QActionGroup, QDesktopServices, QFont, QTextCursor
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
from PyQt6.QtCore import (Qt, pyqtSignal, QTimer, QSize, QSignalBlocker, QModelIndex, QThread, QUrl, QSettings, QEvent, QByteArray, QObject,
|
|
687
|
+
QPropertyAnimation, QEasingCurve
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
|
692
|
+
|
|
693
|
+
global BUILD_TIMESTAMP
|
|
694
|
+
try:
|
|
695
|
+
from setiastro.saspro._generated.build_info import BUILD_TIMESTAMP
|
|
696
|
+
except Exception:
|
|
697
|
+
BUILD_TIMESTAMP = "dev"
|
|
698
|
+
|
|
699
|
+
_update_splash(QCoreApplication.translate("Splash", "Loading resources..."), 50)
|
|
700
|
+
|
|
701
|
+
from setiastro.saspro.resources import (
|
|
702
|
+
icon_path, windowslogo_path, green_path, neutral_path, whitebalance_path,
|
|
703
|
+
morpho_path, clahe_path, starnet_path, staradd_path, LExtract_path,
|
|
704
|
+
LInsert_path, slot0_path, slot1_path, slot2_path, slot3_path, slot4_path,
|
|
705
|
+
rgbcombo_path, rgbextract_path, copyslot_path, graxperticon_path,
|
|
706
|
+
cropicon_path, openfile_path, abeicon_path, undoicon_path, redoicon_path,
|
|
707
|
+
blastericon_path, hdr_path, invert_path, fliphorizontal_path,
|
|
708
|
+
flipvertical_path, rotateclockwise_path, rotatecounterclockwise_path,
|
|
709
|
+
rotate180_path, maskcreate_path, maskapply_path, maskremove_path,
|
|
710
|
+
slot5_path, slot6_path, slot7_path, slot8_path, slot9_path, pixelmath_path,
|
|
711
|
+
histogram_path, mosaic_path, rescale_path, staralign_path, mask_path,
|
|
712
|
+
platesolve_path, psf_path, supernova_path, starregistration_path,
|
|
713
|
+
stacking_path, pedestal_icon_path, starspike_path, aperture_path,
|
|
714
|
+
jwstpupil_path, signature_icon_path, livestacking_path, hrdiagram_path,
|
|
715
|
+
convoicon_path, spcc_icon_path, sasp_data_path, exoicon_path, peeker_icon,
|
|
716
|
+
dse_icon_path, astrobin_filters_csv_path, isophote_path, statstretch_path,
|
|
717
|
+
starstretch_path, curves_path, disk_path, uhs_path, blink_path, ppp_path,
|
|
718
|
+
nbtorgb_path, freqsep_path, contsub_path, halo_path, cosmic_path,
|
|
719
|
+
satellite_path, imagecombine_path, wrench_path, eye_icon_path,
|
|
720
|
+
disk_icon_path, nuke_path, hubble_path, collage_path, annotated_path,
|
|
721
|
+
colorwheel_path, font_path, csv_icon_path, spinner_path, wims_path,
|
|
722
|
+
wimi_path, linearfit_path, debayer_path, aberration_path,
|
|
723
|
+
functionbundles_path, viewbundles_path, selectivecolor_path, rgbalign_path,
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
_update_splash(QCoreApplication.translate("Splash", "Configuring Qt message handler..."), 55)
|
|
727
|
+
|
|
728
|
+
from PyQt6.QtCore import qInstallMessageHandler, QtMsgType
|
|
729
|
+
|
|
730
|
+
def _qt_msg_handler(mode, ctx, msg):
|
|
731
|
+
lvl = {
|
|
732
|
+
QtMsgType.QtDebugMsg: logging.DEBUG,
|
|
733
|
+
QtMsgType.QtInfoMsg: logging.INFO,
|
|
734
|
+
QtMsgType.QtWarningMsg: logging.WARNING,
|
|
735
|
+
QtMsgType.QtCriticalMsg: logging.ERROR,
|
|
736
|
+
QtMsgType.QtFatalMsg: logging.CRITICAL,
|
|
737
|
+
}.get(mode, logging.ERROR)
|
|
738
|
+
logging.log(lvl, "Qt: %s (%s:%s)", msg, getattr(ctx, "file", "?"), getattr(ctx, "line", -1))
|
|
739
|
+
|
|
740
|
+
qInstallMessageHandler(_qt_msg_handler)
|
|
741
|
+
|
|
742
|
+
_update_splash(QCoreApplication.translate("Splash", "Loading MDI widgets..."), 60)
|
|
743
|
+
|
|
744
|
+
from setiastro.saspro.mdi_widgets import (
|
|
745
|
+
MdiArea, ViewLinkController, ConsoleListWidget, QtLogStream, _DocProxy,
|
|
746
|
+
ROLE_ACTION as _ROLE_ACTION,
|
|
747
|
+
)
|
|
748
|
+
|
|
749
|
+
from setiastro.saspro.main_helpers import (
|
|
750
|
+
safe_join_dir_and_name as _safe_join_dir_and_name,
|
|
751
|
+
normalize_save_path_chosen_filter as _normalize_save_path_chosen_filter,
|
|
752
|
+
display_name as _display_name,
|
|
753
|
+
best_doc_name as _best_doc_name,
|
|
754
|
+
doc_looks_like_table as _doc_looks_like_table,
|
|
755
|
+
is_alive as _is_alive,
|
|
756
|
+
safe_widget as _safe_widget,
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
from setiastro.saspro.file_utils import (
|
|
760
|
+
_normalize_ext,
|
|
761
|
+
_sanitize_filename,
|
|
762
|
+
_exts_from_filter,
|
|
763
|
+
REPLACE_SPACES_WITH_UNDERSCORES as _REPLACE_SPACES_WITH_UNDERSCORES,
|
|
764
|
+
WIN_RESERVED_NAMES as _WIN_RESERVED,
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
_update_splash(QCoreApplication.translate("Splash", "Loading main window module..."), 65)
|
|
768
|
+
|
|
769
|
+
from setiastro.saspro.gui.main_window import AstroSuiteProMainWindow
|
|
770
|
+
|
|
771
|
+
_update_splash(QCoreApplication.translate("Splash", "Modules loaded, finalizing..."), 70)
|
|
772
|
+
|
|
773
|
+
# Export things main() already relies on as globals (min disruption)
|
|
774
|
+
globals().update({
|
|
775
|
+
"logging": logging,
|
|
776
|
+
"multiprocessing": multiprocessing,
|
|
777
|
+
"warnings": warnings,
|
|
778
|
+
"QIcon": QIcon,
|
|
779
|
+
"QMessageBox": QMessageBox,
|
|
780
|
+
"QPropertyAnimation": QPropertyAnimation,
|
|
781
|
+
"QEasingCurve": QEasingCurve,
|
|
782
|
+
"windowslogo_path": windowslogo_path,
|
|
783
|
+
"icon_path": icon_path,
|
|
784
|
+
"install_crash_handlers": install_crash_handlers,
|
|
785
|
+
"AstroSuiteProMainWindow": AstroSuiteProMainWindow,
|
|
786
|
+
"BUILD_TIMESTAMP": BUILD_TIMESTAMP,
|
|
787
|
+
})
|
|
788
|
+
|
|
746
789
|
|
|
747
790
|
|
|
748
791
|
def main():
|
|
@@ -755,11 +798,13 @@ def main():
|
|
|
755
798
|
- When running as a module: python -m setiastro.saspro
|
|
756
799
|
"""
|
|
757
800
|
global _splash, _app, _splash_initialized
|
|
801
|
+
from PyQt6.QtCore import QTimer
|
|
758
802
|
|
|
759
803
|
# Initialize splash if not already done
|
|
760
804
|
if not _splash_initialized:
|
|
761
805
|
_init_splash()
|
|
762
|
-
|
|
806
|
+
|
|
807
|
+
_bootstrap_imports()
|
|
763
808
|
# Update splash with build info now that we have VERSION and BUILD_TIMESTAMP
|
|
764
809
|
if _splash:
|
|
765
810
|
_splash.setBuildInfo(VERSION, BUILD_TIMESTAMP)
|
|
@@ -874,7 +919,16 @@ def main():
|
|
|
874
919
|
version=VERSION,
|
|
875
920
|
build_timestamp=BUILD_TIMESTAMP,
|
|
876
921
|
)
|
|
877
|
-
|
|
922
|
+
|
|
923
|
+
def _kick_updates_after_splash():
|
|
924
|
+
try:
|
|
925
|
+
win.raise_()
|
|
926
|
+
win.activateWindow()
|
|
927
|
+
if win.settings.value("updates/check_on_startup", True, type=bool):
|
|
928
|
+
QTimer.singleShot(1000, win.check_for_updates_startup)
|
|
929
|
+
except Exception:
|
|
930
|
+
pass
|
|
931
|
+
|
|
878
932
|
if _splash:
|
|
879
933
|
_splash.setMessage(QCoreApplication.translate("Splash", "Showing main window..."))
|
|
880
934
|
_splash.setProgress(95)
|
|
@@ -882,29 +936,34 @@ def main():
|
|
|
882
936
|
# --- Smooth Transition: App Fade In + Splash Fade Out ---
|
|
883
937
|
# MITIGATION: Prevent "White Flash" on startup
|
|
884
938
|
# 1. Force a dark background immediately so if opacity lags, it's dark not white
|
|
885
|
-
win.setStyleSheet("QMainWindow { background-color: #0F0F19; }")
|
|
886
|
-
# 2. Ensure native window handle exists so setWindowOpacity works immediately
|
|
939
|
+
win.setStyleSheet("QMainWindow { background-color: #0F0F19; }")
|
|
887
940
|
win.winId()
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
941
|
+
|
|
942
|
+
if _allow_window_opacity_effects():
|
|
943
|
+
win.setWindowOpacity(0.0)
|
|
944
|
+
|
|
891
945
|
win.show()
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
946
|
+
|
|
947
|
+
if _allow_window_opacity_effects():
|
|
948
|
+
anim_app = QPropertyAnimation(win, b"windowOpacity")
|
|
949
|
+
anim_app.setDuration(1200)
|
|
950
|
+
anim_app.setStartValue(0.0)
|
|
951
|
+
anim_app.setEndValue(1.0)
|
|
952
|
+
anim_app.setEasingCurve(QEasingCurve.Type.OutQuad)
|
|
953
|
+
|
|
954
|
+
def _on_fade_in_finished():
|
|
955
|
+
win.setStyleSheet("")
|
|
956
|
+
if hasattr(win, "on_fade_in_complete"):
|
|
957
|
+
win.on_fade_in_complete()
|
|
958
|
+
|
|
959
|
+
anim_app.finished.connect(_on_fade_in_finished)
|
|
960
|
+
anim_app.start()
|
|
961
|
+
else:
|
|
962
|
+
# No opacity animation on Wayland: just show immediately and clear the temp stylesheet
|
|
902
963
|
win.setStyleSheet("")
|
|
903
964
|
if hasattr(win, "on_fade_in_complete"):
|
|
904
965
|
win.on_fade_in_complete()
|
|
905
|
-
|
|
906
|
-
anim_app.finished.connect(_on_fade_in_finished)
|
|
907
|
-
anim_app.start()
|
|
966
|
+
|
|
908
967
|
|
|
909
968
|
# Start background Numba warmup after UI is visible
|
|
910
969
|
try:
|
|
@@ -924,7 +983,7 @@ def main():
|
|
|
924
983
|
|
|
925
984
|
# 2. Animate Splash Fade Out
|
|
926
985
|
# Note: We do NOT use finish() directly here. The animation calls it when done.
|
|
927
|
-
_splash.start_fade_out()
|
|
986
|
+
_splash.start_fade_out(on_finished=_kick_updates_after_splash)
|
|
928
987
|
|
|
929
988
|
# NOTE: We keep a reference to _splash (global) so it doesn't get GC'd during animation.
|
|
930
989
|
# It will deleteLater() itself.
|