setiastrosuitepro 1.8.1.post2__py3-none-any.whl → 1.8.3__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.
Files changed (40) hide show
  1. setiastro/images/finderchart.png +0 -0
  2. setiastro/images/magnitude.png +0 -0
  3. setiastro/saspro/__main__.py +29 -38
  4. setiastro/saspro/_generated/build_info.py +2 -2
  5. setiastro/saspro/abe.py +1 -1
  6. setiastro/saspro/backgroundneutral.py +54 -16
  7. setiastro/saspro/blink_comparator_pro.py +3 -1
  8. setiastro/saspro/bright_stars.py +305 -0
  9. setiastro/saspro/continuum_subtract.py +2 -1
  10. setiastro/saspro/cosmicclarity_preset.py +2 -1
  11. setiastro/saspro/doc_manager.py +8 -0
  12. setiastro/saspro/exoplanet_detector.py +22 -17
  13. setiastro/saspro/finder_chart.py +1650 -0
  14. setiastro/saspro/gui/main_window.py +131 -17
  15. setiastro/saspro/gui/mixins/header_mixin.py +40 -15
  16. setiastro/saspro/gui/mixins/menu_mixin.py +3 -0
  17. setiastro/saspro/gui/mixins/toolbar_mixin.py +16 -1
  18. setiastro/saspro/imageops/stretch.py +1 -1
  19. setiastro/saspro/legacy/image_manager.py +18 -4
  20. setiastro/saspro/legacy/xisf.py +3 -3
  21. setiastro/saspro/magnitude_tool.py +1724 -0
  22. setiastro/saspro/main_helpers.py +18 -0
  23. setiastro/saspro/memory_utils.py +18 -14
  24. setiastro/saspro/remove_stars.py +13 -30
  25. setiastro/saspro/resources.py +177 -161
  26. setiastro/saspro/runtime_torch.py +71 -10
  27. setiastro/saspro/sfcc.py +86 -77
  28. setiastro/saspro/stacking_suite.py +4 -3
  29. setiastro/saspro/star_alignment.py +4 -2
  30. setiastro/saspro/texture_clarity.py +1 -1
  31. setiastro/saspro/torch_rejection.py +59 -28
  32. setiastro/saspro/widgets/image_utils.py +12 -4
  33. setiastro/saspro/wimi.py +2 -1
  34. setiastro/saspro/xisf.py +3 -3
  35. {setiastrosuitepro-1.8.1.post2.dist-info → setiastrosuitepro-1.8.3.dist-info}/METADATA +4 -4
  36. {setiastrosuitepro-1.8.1.post2.dist-info → setiastrosuitepro-1.8.3.dist-info}/RECORD +40 -35
  37. {setiastrosuitepro-1.8.1.post2.dist-info → setiastrosuitepro-1.8.3.dist-info}/WHEEL +0 -0
  38. {setiastrosuitepro-1.8.1.post2.dist-info → setiastrosuitepro-1.8.3.dist-info}/entry_points.txt +0 -0
  39. {setiastrosuitepro-1.8.1.post2.dist-info → setiastrosuitepro-1.8.3.dist-info}/licenses/LICENSE +0 -0
  40. {setiastrosuitepro-1.8.1.post2.dist-info → setiastrosuitepro-1.8.3.dist-info}/licenses/license.txt +0 -0
@@ -1,3 +1,4 @@
1
+
1
2
  # pro/main_helpers.py
2
3
  """
3
4
  Helper functions extracted from the main module.
@@ -7,12 +8,15 @@ Contains utility functions used throughout the main window:
7
8
  - Document name/type detection
8
9
  - Widget safety checks
9
10
  - WCS/FITS header utilities
11
+ - UI responsiveness helpers
10
12
  """
11
13
 
12
14
  import os
15
+ import time
13
16
  from typing import Optional, Tuple
14
17
 
15
18
  from PyQt6 import sip
19
+ from PyQt6.QtWidgets import QApplication
16
20
 
17
21
  from setiastro.saspro.file_utils import (
18
22
  _normalize_ext,
@@ -23,6 +27,20 @@ from setiastro.saspro.file_utils import (
23
27
  )
24
28
 
25
29
 
30
+ def non_blocking_sleep(duration_sec: float):
31
+ """
32
+ Sleep for duration_sec seconds while keeping the UI responsive.
33
+ Uses QApplication.processEvents() to process pending events.
34
+ """
35
+ end_time = time.time() + duration_sec
36
+ while time.time() < end_time:
37
+ QApplication.processEvents()
38
+ # Sleep a tiny bit to avoid 100% CPU usage
39
+ sleep_time = min(0.05, end_time - time.time())
40
+ if sleep_time > 0:
41
+ time.sleep(sleep_time)
42
+
43
+
26
44
  def safe_join_dir_and_name(directory: str, basename: str) -> str:
27
45
  """
28
46
  Join directory + sanitized basename.
@@ -128,30 +128,34 @@ class LRUDict(OrderedDict):
128
128
  When maxsize is exceeded, oldest items are evicted.
129
129
  Thread-safe for basic operations.
130
130
  """
131
- __slots__ = ('maxsize',)
131
+ __slots__ = ('maxsize', '_lock')
132
132
 
133
133
  def __init__(self, maxsize: int = 500):
134
134
  super().__init__()
135
135
  self.maxsize = maxsize
136
+ self._lock = threading.RLock()
136
137
 
137
138
  def __getitem__(self, key):
138
- # Move to end on access (most recently used)
139
- self.move_to_end(key)
140
- return super().__getitem__(key)
141
-
142
- def get(self, key, default=None):
143
- if key in self:
139
+ with self._lock:
140
+ # Move to end on access (most recently used)
144
141
  self.move_to_end(key)
145
142
  return super().__getitem__(key)
146
- return default
143
+
144
+ def get(self, key, default=None):
145
+ with self._lock:
146
+ if key in self:
147
+ self.move_to_end(key)
148
+ return super().__getitem__(key)
149
+ return default
147
150
 
148
151
  def __setitem__(self, key, value):
149
- if key in self:
150
- self.move_to_end(key)
151
- super().__setitem__(key, value)
152
- # Evict oldest if over limit
153
- while len(self) > self.maxsize:
154
- self.popitem(last=False) # Remove oldest
152
+ with self._lock:
153
+ if key in self:
154
+ self.move_to_end(key)
155
+ super().__setitem__(key, value)
156
+ # Evict oldest if over limit
157
+ while len(self) > self.maxsize:
158
+ self.popitem(last=False) # Remove oldest
155
159
 
156
160
 
157
161
  # ============================================================================
@@ -34,7 +34,6 @@ from setiastro.saspro.widgets.image_utils import extract_mask_from_document as _
34
34
  _MAD_NORM = 1.4826
35
35
 
36
36
  # --------- deterministic, invertible stretch used for StarNet ----------
37
- # ---------- Siril-like MTF (linked) pre-stretch for StarNet ----------
38
37
  def _robust_peak_sigma(gray: np.ndarray) -> tuple[float, float]:
39
38
  gray = gray.astype(np.float32, copy=False)
40
39
  med = float(np.median(gray))
@@ -61,18 +60,6 @@ def _mtf_apply(x: np.ndarray, shadows: float, midtones: float, highlights: float
61
60
  return np.clip(y, 0.0, 1.0).astype(np.float32, copy=False)
62
61
 
63
62
  def _mtf_inverse(y: np.ndarray, shadows: float, midtones: float, highlights: float) -> np.ndarray:
64
- """
65
- Pseudoinverse of MTF, matching Siril's MTF_pseudoinverse() implementation.
66
-
67
- C reference:
68
-
69
- float MTF_pseudoinverse(float y, struct mtf_params params) {
70
- return ((((params.shadows + params.highlights) * params.midtones
71
- - params.shadows) * y - params.shadows * params.midtones
72
- + params.shadows)
73
- / ((2 * params.midtones - 1) * y - params.midtones + 1));
74
- }
75
- """
76
63
  s = float(shadows)
77
64
  m = float(midtones)
78
65
  h = float(highlights)
@@ -107,7 +94,7 @@ def _mtf_params_linked(img_rgb01: np.ndarray, shadowclip_sigma: float = -2.8, ta
107
94
  s = peak + shadowclip_sigma * sigma
108
95
  # keep [0..1) with margin
109
96
  s = float(np.clip(s, gray.min(), max(gray.max() - 1e-6, 0.0)))
110
- h = 1.0 # Siril effectively normalizes to <=1 before 16-bit TIFF
97
+ h = 1.0
111
98
  # solve for midtones m so that mtf(xp(peak)) = targetbg
112
99
  x = (peak - s) / max(h - s, 1e-8)
113
100
  x = float(np.clip(x, 1e-6, 1.0 - 1e-6))
@@ -136,14 +123,14 @@ def _mtf_params_unlinked(img_rgb01: np.ndarray,
136
123
  shadows_clipping: float = -2.8,
137
124
  targetbg: float = 0.25) -> dict:
138
125
  """
139
- Siril-style per-channel MTF parameter estimation, matching
126
+ per-channel MTF parameter estimation, matching
140
127
  find_unlinked_midtones_balance_default() / find_unlinked_midtones_balance().
141
128
 
142
129
  Works on float32 data assumed in [0,1].
143
130
  Returns dict with arrays: {'s': (C,), 'm': (C,), 'h': (C,)}.
144
131
  """
145
132
  """
146
- Siril-style per-channel MTF parameter estimation, matching
133
+ per-channel MTF parameter estimation, matching
147
134
  find_unlinked_midtones_balance_default() / find_unlinked_midtones_balance().
148
135
 
149
136
  Works on float32 data assumed in [0,1].
@@ -221,8 +208,6 @@ def _mtf_params_unlinked(img_rgb01: np.ndarray,
221
208
 
222
209
  def _mtf_scalar(x: float, m: float, lo: float = 0.0, hi: float = 1.0) -> float:
223
210
  """
224
- Scalar midtones transfer function matching the PixInsight / Siril spec.
225
-
226
211
  For x in [lo, hi], rescale to [0,1] and apply:
227
212
 
228
213
  M(x; m) = (m - 1) * xp / ((2*m - 1)*xp - m)
@@ -250,7 +235,7 @@ def _mtf_scalar(x: float, m: float, lo: float = 0.0, hi: float = 1.0) -> float:
250
235
  return 0.5
251
236
 
252
237
  y = num / den
253
- # clamp to [0,1] as PI/Siril do
238
+
254
239
  if y < 0.0:
255
240
  y = 0.0
256
241
  elif y > 1.0:
@@ -308,10 +293,8 @@ def _get_setting_any(settings, keys: tuple[str, ...], default: str = "") -> str:
308
293
 
309
294
  def starnet_starless_from_array(arr_rgb01: np.ndarray, settings, *, tmp_prefix="comet") -> np.ndarray:
310
295
  """
311
- Siril-style MTF round-trip for 32-bit data:
312
-
313
296
  1) Normalize to [0,1] (preserving overall scale separately)
314
- 2) Compute unlinked MTF params per channel (Siril auto-stretch)
297
+ 2) Compute unlinked MTF params per channel
315
298
  3) Apply unlinked MTF -> 16-bit TIFF for StarNet
316
299
  4) StarNet -> read starless 16-bit TIFF
317
300
  5) Apply per-channel MTF pseudoinverse with SAME params
@@ -351,7 +334,7 @@ def starnet_starless_from_array(arr_rgb01: np.ndarray, settings, *, tmp_prefix="
351
334
  xin = (x_in / scale_factor) if scale_factor > 1.0 else x_in
352
335
  xin = np.clip(xin, 0.0, 1.0)
353
336
 
354
- # --- Siril-style unlinked MTF params + pre-stretch ---
337
+
355
338
  mtf_params = _mtf_params_unlinked(xin, shadows_clipping=-2.8, targetbg=0.25)
356
339
  x_for_starnet = _apply_mtf_unlinked_rgb(xin, mtf_params).astype(np.float32, copy=False)
357
340
 
@@ -384,7 +367,7 @@ def starnet_starless_from_array(arr_rgb01: np.ndarray, settings, *, tmp_prefix="
384
367
  starless_s = np.repeat(starless_s, 3, axis=2)
385
368
  starless_s = np.clip(starless_s.astype(np.float32, copy=False), 0.0, 1.0)
386
369
 
387
- # --- Apply Siril-style pseudoinverse MTF with SAME params ---
370
+
388
371
  starless_lin01 = _invert_mtf_unlinked_rgb(starless_s, mtf_params)
389
372
 
390
373
  # Restore original scale if we normalized earlier
@@ -610,7 +593,7 @@ def _run_starnet(main, doc):
610
593
  input_image_path = os.path.join(starnet_dir, "imagetoremovestars.tif")
611
594
  output_image_path = os.path.join(starnet_dir, "starless.tif")
612
595
 
613
- # --- Prepare input for StarNet (Siril-style unlinked MTF pre-stretch for linear data) ---
596
+
614
597
  img_for_starnet = processing_norm
615
598
  if is_linear:
616
599
  mtf_params = _mtf_params_unlinked(processing_norm, shadows_clipping=-2.8, targetbg=0.25)
@@ -619,7 +602,7 @@ def _run_starnet(main, doc):
619
602
  # 🔐 Stash EXACT params for inverse step later
620
603
  try:
621
604
  setattr(main, "_starnet_stat_meta", {
622
- "scheme": "siril_mtf",
605
+ "scheme": "stretch_mtf",
623
606
  "s": np.asarray(mtf_params["s"], dtype=np.float32),
624
607
  "m": np.asarray(mtf_params["m"], dtype=np.float32),
625
608
  "h": np.asarray(mtf_params["h"], dtype=np.float32),
@@ -825,12 +808,12 @@ def _on_starnet_finished(main, doc, return_code, dialog, input_path, output_path
825
808
 
826
809
  # ---- Inversion back to the document’s domain ----
827
810
  if did_stretch:
828
- # Prefer the new Siril-style MTF meta if present
811
+
829
812
  meta = getattr(main, "_starnet_stat_meta", None)
830
813
  mtf_params_legacy = getattr(main, "_starnet_last_mtf_params", None)
831
814
 
832
- if isinstance(meta, dict) and meta.get("scheme") == "siril_mtf":
833
- dialog.append_text("Unstretching (Siril-style MTF pseudoinverse)...\n")
815
+ if isinstance(meta, dict) and meta.get("scheme") == "stretch_mtf":
816
+ dialog.append_text("Unstretching (MTF pseudoinverse)...\n")
834
817
  try:
835
818
  s_vec = np.asarray(meta.get("s"), dtype=np.float32)
836
819
  m_vec = np.asarray(meta.get("m"), dtype=np.float32)
@@ -845,7 +828,7 @@ def _on_starnet_finished(main, doc, return_code, dialog, input_path, output_path
845
828
 
846
829
  starless_rgb = np.clip(inv, 0.0, 1.0)
847
830
  except Exception as e:
848
- dialog.append_text(f"⚠️ Siril-style MTF inverse failed: {e}\n")
831
+ dialog.append_text(f"⚠️ MTF inverse failed: {e}\n")
849
832
 
850
833
  elif isinstance(meta, dict) and meta.get("scheme") == "statstretch":
851
834
  # Back-compat: statistical round-trip with bp/m0
@@ -302,6 +302,7 @@ class Icons:
302
302
 
303
303
  # Color
304
304
  SPCC = property(lambda self: _resource_path('spcc.png'))
305
+ MAGNITUDE = property(lambda self: _resource_path('magnitude.png'))
305
306
  DSE = property(lambda self: _resource_path('dse.png'))
306
307
  COLOR_WHEEL = property(lambda self: _resource_path('colorwheel.png'))
307
308
  SELECTIVE_COLOR = property(lambda self: _resource_path('selectivecolor.png'))
@@ -340,6 +341,7 @@ class Icons:
340
341
  DEBAYER = property(lambda self: _resource_path('debayer.png'))
341
342
  FUNCTION_BUNDLES = property(lambda self: _resource_path('functionbundle.png'))
342
343
  VIEW_BUNDLES = property(lambda self: _resource_path('viewbundle.png'))
344
+ FINDER_CHART = property(lambda self: _resource_path('finderchart.png'))
343
345
 
344
346
  # Singleton instances for easy access
345
347
  _icons_instance = None
@@ -396,157 +398,179 @@ def get_data_path(name: str) -> str:
396
398
  """
397
399
  return _resource_path(name)
398
400
 
401
+ # ---------------- Legacy compatibility (LAZY) ----------------
402
+ # These names match the original module-level variables used in older code.
403
+
404
+ _LEGACY_ICON_MAP = {
405
+ 'icon_path': 'astrosuitepro.png',
406
+ 'windowslogo_path': 'astrosuitepro.ico',
407
+ 'green_path': 'green.png',
408
+ 'neutral_path': 'neutral.png',
409
+ 'whitebalance_path': 'whitebalance.png',
410
+ 'texture_clarity_path': 'TextureClarity.svg',
411
+ 'morpho_path': 'morpho.png',
412
+ 'clahe_path': 'clahe.png',
413
+ 'starnet_path': 'starnet.png',
414
+ 'staradd_path': 'staradd.png',
415
+ 'LExtract_path': 'LExtract.png',
416
+ 'LInsert_path': 'LInsert.png',
417
+ 'slot0_path': 'slot0.png',
418
+ 'slot1_path': 'slot1.png',
419
+ 'slot2_path': 'slot2.png',
420
+ 'slot3_path': 'slot3.png',
421
+ 'slot4_path': 'slot4.png',
422
+ 'slot5_path': 'slot5.png',
423
+ 'slot6_path': 'slot6.png',
424
+ 'slot7_path': 'slot7.png',
425
+ 'slot8_path': 'slot8.png',
426
+ 'slot9_path': 'slot9.png',
427
+ 'acv_icon_path': 'acv_icon.png',
428
+
429
+ 'moon_new_path': 'new_moon.png',
430
+ 'moon_waxing_crescent_1_path': 'waxing_crescent_1.png',
431
+ 'moon_waxing_crescent_2_path': 'waxing_crescent_2.png',
432
+ 'moon_waxing_crescent_3_path': 'waxing_crescent_3.png',
433
+ 'moon_waxing_crescent_4_path': 'waxing_crescent_4.png',
434
+ 'moon_waxing_crescent_5_path': 'waxing_crescent_5.png',
435
+ 'moon_first_quarter_path': 'first_quarter.png',
436
+ 'moon_waxing_gibbous_1_path': 'waxing_gibbous_1.png',
437
+ 'moon_waxing_gibbous_2_path': 'waxing_gibbous_2.png',
438
+ 'moon_waxing_gibbous_3_path': 'waxing_gibbous_3.png',
439
+ 'moon_waxing_gibbous_4_path': 'waxing_gibbous_4.png',
440
+ 'moon_waxing_gibbous_5_path': 'waxing_gibbous_5.png',
441
+ 'moon_full_path': 'full_moon.png',
442
+ 'moon_waning_gibbous_1_path': 'waning_gibbous_1.png',
443
+ 'moon_waning_gibbous_2_path': 'waning_gibbous_2.png',
444
+ 'moon_waning_gibbous_3_path': 'waning_gibbous_3.png',
445
+ 'moon_waning_gibbous_4_path': 'waning_gibbous_4.png',
446
+ 'moon_waning_gibbous_5_path': 'waning_gibbous_5.png',
447
+ 'moon_last_quarter_path': 'last_quarter.png',
448
+ 'moon_waning_crescent_1_path': 'waning_crescent_1.png',
449
+ 'moon_waning_crescent_2_path': 'waning_crescent_2.png',
450
+ 'moon_waning_crescent_3_path': 'waning_crescent_3.png',
451
+ 'moon_waning_crescent_4_path': 'waning_crescent_4.png',
452
+ 'moon_waning_crescent_5_path': 'waning_crescent_5.png',
453
+
454
+ 'rgbcombo_path': 'rgbcombo.png',
455
+ 'rgbextract_path': 'rgbextract.png',
456
+ 'copyslot_path': 'copyslot.png',
457
+ 'graxperticon_path': 'graxpert.png',
458
+ 'cropicon_path': 'cropicon.png',
459
+ 'openfile_path': 'openfile.png',
460
+ 'abeicon_path': 'abeicon.png',
461
+ 'undoicon_path': 'undoicon.png',
462
+ 'redoicon_path': 'redoicon.png',
463
+ 'blastericon_path': 'blaster.png',
464
+ 'clonestampicon_path': 'clonestamp.png',
465
+ 'hdr_path': 'hdr.png',
466
+ 'invert_path': 'invert.png',
467
+ 'fliphorizontal_path': 'fliphorizontal.png',
468
+ 'flipvertical_path': 'flipvertical.png',
469
+ 'rotateclockwise_path': 'rotateclockwise.png',
470
+ 'rotatecounterclockwise_path': 'rotatecounterclockwise.png',
471
+ 'rotate180_path': 'rotate180.png',
472
+ 'rotatearbitrary_path': 'rotatearbitrary.png',
473
+ 'maskcreate_path': 'maskcreate.png',
474
+ 'maskapply_path': 'maskapply.png',
475
+ 'maskremove_path': 'maskremove.png',
476
+ 'pixelmath_path': 'pixelmath.png',
477
+ 'histogram_path': 'histogram.png',
478
+ 'mosaic_path': 'mosaic.png',
479
+ 'rescale_path': 'rescale.png',
480
+ 'staralign_path': 'staralign.png',
481
+ 'mask_path': 'maskapply.png',
482
+ 'platesolve_path': 'platesolve.png',
483
+ 'psf_path': 'psf.png',
484
+ 'supernova_path': 'supernova.png',
485
+ 'starregistration_path': 'starregistration.png',
486
+ 'stacking_path': 'stacking.png',
487
+ 'pedestal_icon_path': 'pedestal.png',
488
+ 'starspike_path': 'starspike.png',
489
+ 'astrospike_path': 'Astro_Spikes.png',
490
+ 'aperture_path': 'aperture.png',
491
+ 'jwstpupil_path': 'jwstpupil.png',
492
+ 'signature_icon_path': 'pen.png',
493
+ 'livestacking_path': 'livestacking.png',
494
+ 'hrdiagram_path': 'HRDiagram.png',
495
+ 'convoicon_path': 'convo.png',
496
+ 'spcc_icon_path': 'spcc.png',
497
+
498
+ 'exoicon_path': 'exoicon.png',
499
+ 'peeker_icon': 'gridicon.png',
500
+ 'dse_icon_path': 'dse.png',
501
+ 'isophote_path': 'isophote.png',
502
+ 'statstretch_path': 'statstretch.png',
503
+ 'starstretch_path': 'starstretch.png',
504
+ 'curves_path': 'curves.png',
505
+ 'disk_path': 'disk.png',
506
+ 'uhs_path': 'uhs.png',
507
+ 'blink_path': 'blink.png',
508
+ 'ppp_path': 'ppp.png',
509
+ 'nbtorgb_path': 'nbtorgb.png',
510
+ 'freqsep_path': 'freqsep.png',
511
+ 'multiscale_decomp_path': 'multiscale_decomp.png',
512
+ 'contsub_path': 'contsub.png',
513
+ 'halo_path': 'halo.png',
514
+ 'cosmic_path': 'cosmic.png',
515
+ 'satellite_path': 'cosmicsat.png',
516
+ 'imagecombine_path': 'imagecombine.png',
517
+ 'wrench_path': 'wrench_icon.png',
518
+ 'eye_icon_path': 'eye.png',
519
+ 'disk_icon_path': 'disk.png',
520
+ 'nuke_path': 'nuke.png',
521
+ 'hubble_path': 'hubble.png',
522
+ 'collage_path': 'collage.png',
523
+ 'annotated_path': 'annotated.png',
524
+ 'colorwheel_path': 'colorwheel.png',
525
+ 'narrowbandnormalization_path': 'narrowbandnormalization.png',
526
+ 'font_path': 'font.png',
527
+ 'csv_icon_path': 'cvs.png',
528
+ 'wims_path': 'wims.png',
529
+ 'wimi_path': 'wimi_icon_256x256.png',
530
+ 'linearfit_path': 'linearfit.png',
531
+ 'debayer_path': 'debayer.png',
532
+ 'aberration_path': 'aberration.png',
533
+ 'functionbundles_path': 'functionbundle.png',
534
+ 'planetarystacker_path': 'planetarystacker.png',
535
+ 'viewbundles_path': 'viewbundle.png',
536
+ 'selectivecolor_path': 'selectivecolor.png',
537
+ 'rgbalign_path': 'rgbalign.png',
538
+ 'background_path': 'background.png',
539
+ 'script_icon_path': 'script.png',
540
+ 'planetprojection_path': '3dplanet.png',
541
+ 'finderchart_path': 'finderchart.png',
542
+ 'magnitude_path': 'magnitude.png',
543
+ }
544
+
545
+ _LEGACY_DATA_MAP = {
546
+ 'sasp_data_path': 'data/SASP_data.fits',
547
+ 'astrobin_filters_csv_path': 'data/catalogs/astrobin_filters.csv',
548
+ 'spinner_path': 'spinner.gif',
549
+ }
550
+
551
+ def __getattr__(name: str):
552
+ # --- legacy paths (lazy) ---
553
+ if name in _LEGACY_ICON_MAP:
554
+ return get_icon_path(_LEGACY_ICON_MAP[name])
555
+ if name in _LEGACY_DATA_MAP:
556
+ return get_data_path(_LEGACY_DATA_MAP[name])
557
+
558
+ # --- special exports ---
559
+ if name == 'background_startup_path':
560
+ return _resource_path('Background_startup.jpg')
561
+ if name == 'resource_monitor_qml':
562
+ return _resource_path(os.path.join("qml", "ResourceMonitor.qml"))
563
+
564
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
565
+
566
+ def __dir__():
567
+ return (
568
+ list(globals().keys())
569
+ + list(_LEGACY_ICON_MAP.keys())
570
+ + list(_LEGACY_DATA_MAP.keys())
571
+ + ['background_startup_path', 'resource_monitor_qml']
572
+ )
399
573
 
400
- # Legacy compatibility: export paths as module-level variables
401
- # These match the original variable names in setiastrosuitepro.py
402
- def _init_legacy_paths():
403
- """Initialize legacy path variables for backward compatibility."""
404
- return {
405
- 'icon_path': get_icon_path('astrosuitepro.png'),
406
- 'windowslogo_path': get_icon_path('astrosuitepro.ico'),
407
- 'green_path': get_icon_path('green.png'),
408
- 'neutral_path': get_icon_path('neutral.png'),
409
- 'whitebalance_path': get_icon_path('whitebalance.png'),
410
- 'texture_clarity_path': get_icon_path('TextureClarity.svg'),
411
- 'morpho_path': get_icon_path('morpho.png'),
412
- 'clahe_path': get_icon_path('clahe.png'),
413
- 'starnet_path': get_icon_path('starnet.png'),
414
- 'staradd_path': get_icon_path('staradd.png'),
415
- 'LExtract_path': get_icon_path('LExtract.png'),
416
- 'LInsert_path': get_icon_path('LInsert.png'),
417
- 'slot0_path': get_icon_path('slot0.png'),
418
- 'slot1_path': get_icon_path('slot1.png'),
419
- 'slot2_path': get_icon_path('slot2.png'),
420
- 'slot3_path': get_icon_path('slot3.png'),
421
- 'slot4_path': get_icon_path('slot4.png'),
422
- 'slot5_path': get_icon_path('slot5.png'),
423
- 'slot6_path': get_icon_path('slot6.png'),
424
- 'slot7_path': get_icon_path('slot7.png'),
425
- 'slot8_path': get_icon_path('slot8.png'),
426
- 'slot9_path': get_icon_path('slot9.png'),
427
- 'acv_icon_path': get_icon_path('acv_icon.png'),
428
-
429
- 'moon_new_path': get_icon_path('new_moon.png'),
430
- 'moon_waxing_crescent_1_path': get_icon_path('waxing_crescent_1.png'),
431
- 'moon_waxing_crescent_2_path': get_icon_path('waxing_crescent_2.png'),
432
- 'moon_waxing_crescent_3_path': get_icon_path('waxing_crescent_3.png'),
433
- 'moon_waxing_crescent_4_path': get_icon_path('waxing_crescent_4.png'),
434
- 'moon_waxing_crescent_5_path': get_icon_path('waxing_crescent_5.png'),
435
-
436
- 'moon_first_quarter_path': get_icon_path('first_quarter.png'),
437
-
438
- 'moon_waxing_gibbous_1_path': get_icon_path('waxing_gibbous_1.png'),
439
- 'moon_waxing_gibbous_2_path': get_icon_path('waxing_gibbous_2.png'),
440
- 'moon_waxing_gibbous_3_path': get_icon_path('waxing_gibbous_3.png'),
441
- 'moon_waxing_gibbous_4_path': get_icon_path('waxing_gibbous_4.png'),
442
- 'moon_waxing_gibbous_5_path': get_icon_path('waxing_gibbous_5.png'),
443
-
444
- 'moon_full_path': get_icon_path('full_moon.png'),
445
-
446
- 'moon_waning_gibbous_1_path': get_icon_path('waning_gibbous_1.png'),
447
- 'moon_waning_gibbous_2_path': get_icon_path('waning_gibbous_2.png'),
448
- 'moon_waning_gibbous_3_path': get_icon_path('waning_gibbous_3.png'),
449
- 'moon_waning_gibbous_4_path': get_icon_path('waning_gibbous_4.png'),
450
- 'moon_waning_gibbous_5_path': get_icon_path('waning_gibbous_5.png'),
451
-
452
- 'moon_last_quarter_path': get_icon_path('last_quarter.png'),
453
-
454
- 'moon_waning_crescent_1_path': get_icon_path('waning_crescent_1.png'),
455
- 'moon_waning_crescent_2_path': get_icon_path('waning_crescent_2.png'),
456
- 'moon_waning_crescent_3_path': get_icon_path('waning_crescent_3.png'),
457
- 'moon_waning_crescent_4_path': get_icon_path('waning_crescent_4.png'),
458
- 'moon_waning_crescent_5_path': get_icon_path('waning_crescent_5.png'),
459
-
460
- 'rgbcombo_path': get_icon_path('rgbcombo.png'),
461
- 'rgbextract_path': get_icon_path('rgbextract.png'),
462
- 'copyslot_path': get_icon_path('copyslot.png'),
463
- 'graxperticon_path': get_icon_path('graxpert.png'),
464
- 'cropicon_path': get_icon_path('cropicon.png'),
465
- 'openfile_path': get_icon_path('openfile.png'),
466
- 'abeicon_path': get_icon_path('abeicon.png'),
467
- 'undoicon_path': get_icon_path('undoicon.png'),
468
- 'redoicon_path': get_icon_path('redoicon.png'),
469
- 'blastericon_path': get_icon_path('blaster.png'),
470
- 'clonestampicon_path': get_icon_path('clonestamp.png'),
471
- 'hdr_path': get_icon_path('hdr.png'),
472
- 'invert_path': get_icon_path('invert.png'),
473
- 'fliphorizontal_path': get_icon_path('fliphorizontal.png'),
474
- 'flipvertical_path': get_icon_path('flipvertical.png'),
475
- 'rotateclockwise_path': get_icon_path('rotateclockwise.png'),
476
- 'rotatecounterclockwise_path': get_icon_path('rotatecounterclockwise.png'),
477
- 'rotate180_path': get_icon_path('rotate180.png'),
478
- 'rotatearbitrary_path': get_icon_path('rotatearbitrary.png'),
479
- 'maskcreate_path': get_icon_path('maskcreate.png'),
480
- 'maskapply_path': get_icon_path('maskapply.png'),
481
- 'maskremove_path': get_icon_path('maskremove.png'),
482
- 'pixelmath_path': get_icon_path('pixelmath.png'),
483
- 'histogram_path': get_icon_path('histogram.png'),
484
- 'mosaic_path': get_icon_path('mosaic.png'),
485
- 'rescale_path': get_icon_path('rescale.png'),
486
- 'staralign_path': get_icon_path('staralign.png'),
487
- 'mask_path': get_icon_path('maskapply.png'),
488
- 'platesolve_path': get_icon_path('platesolve.png'),
489
- 'psf_path': get_icon_path('psf.png'),
490
- 'supernova_path': get_icon_path('supernova.png'),
491
- 'starregistration_path': get_icon_path('starregistration.png'),
492
- 'stacking_path': get_icon_path('stacking.png'),
493
- 'pedestal_icon_path': get_icon_path('pedestal.png'),
494
- 'starspike_path': get_icon_path('starspike.png'),
495
- 'astrospike_path': get_icon_path('Astro_Spikes.png'),
496
- 'aperture_path': get_icon_path('aperture.png'),
497
- 'jwstpupil_path': get_icon_path('jwstpupil.png'),
498
- 'signature_icon_path': get_icon_path('pen.png'),
499
- 'livestacking_path': get_icon_path('livestacking.png'),
500
- 'hrdiagram_path': get_icon_path('HRDiagram.png'),
501
- 'convoicon_path': get_icon_path('convo.png'),
502
- 'spcc_icon_path': get_icon_path('spcc.png'),
503
- 'sasp_data_path': get_data_path('data/SASP_data.fits'),
504
- 'exoicon_path': get_icon_path('exoicon.png'),
505
- 'peeker_icon': get_icon_path('gridicon.png'),
506
- 'dse_icon_path': get_icon_path('dse.png'),
507
- 'astrobin_filters_csv_path': get_data_path('data/catalogs/astrobin_filters.csv'),
508
- 'isophote_path': get_icon_path('isophote.png'),
509
- 'statstretch_path': get_icon_path('statstretch.png'),
510
- 'starstretch_path': get_icon_path('starstretch.png'),
511
- 'curves_path': get_icon_path('curves.png'),
512
- 'disk_path': get_icon_path('disk.png'),
513
- 'uhs_path': get_icon_path('uhs.png'),
514
- 'blink_path': get_icon_path('blink.png'),
515
- 'ppp_path': get_icon_path('ppp.png'),
516
- 'nbtorgb_path': get_icon_path('nbtorgb.png'),
517
- 'freqsep_path': get_icon_path('freqsep.png'),
518
- 'multiscale_decomp_path': get_icon_path('multiscale_decomp.png'),
519
- 'contsub_path': get_icon_path('contsub.png'),
520
- 'halo_path': get_icon_path('halo.png'),
521
- 'cosmic_path': get_icon_path('cosmic.png'),
522
- 'satellite_path': get_icon_path('cosmicsat.png'),
523
- 'imagecombine_path': get_icon_path('imagecombine.png'),
524
- 'wrench_path': get_icon_path('wrench_icon.png'),
525
- 'eye_icon_path': get_icon_path('eye.png'),
526
- 'disk_icon_path': get_icon_path('disk.png'),
527
- 'nuke_path': get_icon_path('nuke.png'),
528
- 'hubble_path': get_icon_path('hubble.png'),
529
- 'collage_path': get_icon_path('collage.png'),
530
- 'annotated_path': get_icon_path('annotated.png'),
531
- 'colorwheel_path': get_icon_path('colorwheel.png'),
532
- 'narrowbandnormalization_path': get_icon_path('narrowbandnormalization.png'),
533
- 'font_path': get_icon_path('font.png'),
534
- 'csv_icon_path': get_icon_path('cvs.png'),
535
- 'spinner_path': get_data_path('spinner.gif'),
536
- 'wims_path': get_icon_path('wims.png'),
537
- 'wimi_path': get_icon_path('wimi_icon_256x256.png'),
538
- 'linearfit_path': get_icon_path('linearfit.png'),
539
- 'debayer_path': get_icon_path('debayer.png'),
540
- 'aberration_path': get_icon_path('aberration.png'),
541
- 'functionbundles_path': get_icon_path('functionbundle.png'),
542
- 'planetarystacker_path': get_icon_path('planetarystacker.png'),
543
- 'viewbundles_path': get_icon_path('viewbundle.png'),
544
- 'selectivecolor_path': get_icon_path('selectivecolor.png'),
545
- 'rgbalign_path': get_icon_path('rgbalign.png'),
546
- 'background_path': get_icon_path('background.png'),
547
- 'script_icon_path': get_icon_path('script.png'),
548
- 'planetprojection_path': get_icon_path('3dplanet.png'),
549
- }
550
574
 
551
575
  class Resources:
552
576
  """
@@ -625,23 +649,15 @@ def model_path(filename: str) -> str:
625
649
  _assert_not_internal_models_path(p)
626
650
  return p
627
651
 
628
- # Export all legacy paths as module-level variables
629
- _legacy = _init_legacy_paths()
630
- globals().update(_legacy)
631
-
632
-
633
- # Background for startup
634
- background_startup_path = _resource_path('Background_startup.jpg')
635
- _legacy['background_startup_path'] = background_startup_path
636
652
 
637
653
  # QML helper
638
654
  resource_monitor_qml = _resource_path(os.path.join("qml", "ResourceMonitor.qml"))
639
655
 
640
656
  # Export list for `from setiastro.saspro.resources import *`
641
657
  __all__ = [
642
- 'Icons', 'Resources',
658
+ 'Icons', 'Resources',
643
659
  'get_icons', 'get_resources',
644
660
  'get_icon_path', 'get_data_path',
661
+ 'resource_monitor_qml',
645
662
  'background_startup_path',
646
- ] + list(_legacy.keys())
647
-
663
+ ] + list(_LEGACY_ICON_MAP.keys()) + list(_LEGACY_DATA_MAP.keys())