setiastrosuitepro 1.8.1.post2__py3-none-any.whl → 1.8.2__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 (33) hide show
  1. setiastro/images/finderchart.png +0 -0
  2. setiastro/saspro/__main__.py +29 -38
  3. setiastro/saspro/_generated/build_info.py +2 -2
  4. setiastro/saspro/abe.py +1 -1
  5. setiastro/saspro/blink_comparator_pro.py +3 -1
  6. setiastro/saspro/bright_stars.py +305 -0
  7. setiastro/saspro/continuum_subtract.py +2 -1
  8. setiastro/saspro/cosmicclarity_preset.py +2 -1
  9. setiastro/saspro/doc_manager.py +8 -0
  10. setiastro/saspro/exoplanet_detector.py +22 -17
  11. setiastro/saspro/finder_chart.py +1639 -0
  12. setiastro/saspro/gui/main_window.py +36 -14
  13. setiastro/saspro/gui/mixins/menu_mixin.py +2 -0
  14. setiastro/saspro/gui/mixins/toolbar_mixin.py +9 -1
  15. setiastro/saspro/legacy/image_manager.py +18 -4
  16. setiastro/saspro/legacy/xisf.py +3 -3
  17. setiastro/saspro/main_helpers.py +18 -0
  18. setiastro/saspro/memory_utils.py +18 -14
  19. setiastro/saspro/resources.py +175 -161
  20. setiastro/saspro/runtime_torch.py +51 -10
  21. setiastro/saspro/sfcc.py +5 -3
  22. setiastro/saspro/stacking_suite.py +4 -3
  23. setiastro/saspro/star_alignment.py +4 -2
  24. setiastro/saspro/texture_clarity.py +1 -1
  25. setiastro/saspro/widgets/image_utils.py +12 -4
  26. setiastro/saspro/wimi.py +2 -1
  27. setiastro/saspro/xisf.py +3 -3
  28. {setiastrosuitepro-1.8.1.post2.dist-info → setiastrosuitepro-1.8.2.dist-info}/METADATA +4 -4
  29. {setiastrosuitepro-1.8.1.post2.dist-info → setiastrosuitepro-1.8.2.dist-info}/RECORD +33 -30
  30. {setiastrosuitepro-1.8.1.post2.dist-info → setiastrosuitepro-1.8.2.dist-info}/WHEEL +0 -0
  31. {setiastrosuitepro-1.8.1.post2.dist-info → setiastrosuitepro-1.8.2.dist-info}/entry_points.txt +0 -0
  32. {setiastrosuitepro-1.8.1.post2.dist-info → setiastrosuitepro-1.8.2.dist-info}/licenses/LICENSE +0 -0
  33. {setiastrosuitepro-1.8.1.post2.dist-info → setiastrosuitepro-1.8.2.dist-info}/licenses/license.txt +0 -0
@@ -33,8 +33,8 @@ from urllib.parse import quote, quote_plus
33
33
  # ============================================================================
34
34
  import numpy as np
35
35
  import matplotlib
36
- from tifffile import imwrite
37
- from setiastro.saspro.xisf import XISF
36
+ # tifffile and XISF imports removed (unused in this file)
37
+
38
38
 
39
39
  # ============================================================================
40
40
  # Bootstrap Configuration (must run early)
@@ -93,18 +93,8 @@ from setiastro.saspro.widgets.common_utilities import (
93
93
  )
94
94
 
95
95
 
96
- # Reproject for WCS-based alignment
97
- try:
98
- from reproject import reproject_interp
99
- except ImportError:
100
- reproject_interp = None # fallback if not installed
96
+ # Reproject and OpenCV imports removed (unused or available via lazy_imports)
101
97
 
102
- # OpenCV for transform estimation & warping
103
- try:
104
- import cv2
105
- OPENCV_AVAILABLE = True
106
- except ImportError:
107
- OPENCV_AVAILABLE = False
108
98
 
109
99
 
110
100
 
@@ -196,7 +186,7 @@ from setiastro.saspro.resources import (
196
186
  colorwheel_path, font_path, csv_icon_path, spinner_path, wims_path, narrowbandnormalization_path,
197
187
  wimi_path, linearfit_path, debayer_path, aberration_path, acv_icon_path,
198
188
  functionbundles_path, viewbundles_path, selectivecolor_path, rgbalign_path, planetarystacker_path,
199
- background_path, script_icon_path, planetprojection_path,clonestampicon_path,
189
+ background_path, script_icon_path, planetprojection_path,clonestampicon_path, finderchart_path,
200
190
  )
201
191
 
202
192
  import faulthandler
@@ -4238,6 +4228,38 @@ class AstroSuiteProMainWindow(
4238
4228
  except Exception:
4239
4229
  pass
4240
4230
 
4231
+ def _doc_has_wcs(self, doc) -> bool:
4232
+ if doc is None:
4233
+ return False
4234
+ meta = getattr(doc, "metadata", None) or {}
4235
+ if meta.get("wcs") is not None:
4236
+ return True
4237
+
4238
+ hdr = meta.get("original_header") or meta.get("fits_header") or meta.get("header")
4239
+ if hdr is None:
4240
+ return False
4241
+
4242
+ try:
4243
+ keys = {str(k).upper() for k in hdr.keys()}
4244
+ except Exception:
4245
+ try:
4246
+ keys = {str(k).upper() for k in dict(hdr).keys()}
4247
+ except Exception:
4248
+ return False
4249
+
4250
+ return {"CTYPE1","CTYPE2","CRVAL1","CRVAL2"}.issubset(keys)
4251
+
4252
+
4253
+ def _open_finder_chart(self):
4254
+ doc = self._active_doc()
4255
+ if not self._doc_has_wcs(doc):
4256
+ QMessageBox.information(self, self.tr("Finder Chart"), self.tr("Active image has no astrometric solution (WCS). Plate solve first."))
4257
+ return
4258
+
4259
+ from setiastro.saspro.finder_chart import FinderChartDialog
4260
+ dlg = FinderChartDialog(doc=doc, settings=self.settings, parent=self)
4261
+ dlg.setWindowIcon(QIcon(finderchart_path))
4262
+ dlg.show()
4241
4263
 
4242
4264
  def _open_stellar_alignment(self):
4243
4265
  from setiastro.saspro.star_alignment import StellarAlignmentDialog
@@ -204,6 +204,7 @@ class MenuMixin:
204
204
 
205
205
  m_star.addAction(self.act_astrospike)
206
206
  m_star.addAction(self.act_exo_detector)
207
+
207
208
  m_star.addAction(self.act_image_peeker)
208
209
  m_star.addAction(self.act_isophote)
209
210
  m_star.addAction(self.act_live_stacking)
@@ -233,6 +234,7 @@ class MenuMixin:
233
234
  m_wim = mb.addMenu(self.tr("&What's In My..."))
234
235
  m_wim.addAction(self.act_whats_in_my_sky)
235
236
  m_wim.addAction(self.act_wimi)
237
+ m_wim.addAction(self.act_finder_chart)
236
238
 
237
239
  m_scripts = mb.addMenu(self.tr("&Scripts"))
238
240
  self.menu_scripts = m_scripts
@@ -36,7 +36,7 @@ from setiastro.saspro.resources import (
36
36
  nbtorgb_path, freqsep_path, multiscale_decomp_path, contsub_path, halo_path, cosmic_path,
37
37
  satellite_path, imagecombine_path, wims_path, wimi_path, linearfit_path,
38
38
  debayer_path, aberration_path, functionbundles_path, viewbundles_path, planetarystacker_path,
39
- selectivecolor_path, rgbalign_path, planetprojection_path, clonestampicon_path,
39
+ selectivecolor_path, rgbalign_path, planetprojection_path, clonestampicon_path, finderchart_path,
40
40
  )
41
41
 
42
42
  # Import shortcuts module
@@ -298,6 +298,7 @@ class ToolbarMixin:
298
298
  tb_star.addAction(self.act_planetary_stacker)
299
299
  tb_star.addAction(self.act_planet_projection)
300
300
  tb_star.addAction(self.act_plate_solve)
301
+
301
302
  tb_star.addAction(self.act_star_align)
302
303
  tb_star.addAction(self.act_star_register)
303
304
  tb_star.addAction(self.act_rgb_align)
@@ -332,6 +333,7 @@ class ToolbarMixin:
332
333
  tb_wim = DraggableToolBar(self.tr("What's In My..."), self)
333
334
  tb_wim.setObjectName("What's In My...")
334
335
  tb_wim.setSettingsKey("Toolbar/WhatsInMy")
336
+ tb_wim.addAction(self.act_finder_chart)
335
337
  self.addToolBar(Qt.ToolBarArea.TopToolBarArea, tb_wim)
336
338
 
337
339
  tb_wim.addAction(self.act_whats_in_my_sky)
@@ -1245,6 +1247,11 @@ class ToolbarMixin:
1245
1247
  self.act_rgb_align.setStatusTip(self.tr("Align R and B channels to G using astroalign (affine/homography/poly)"))
1246
1248
  self.act_rgb_align.triggered.connect(self._open_rgb_align)
1247
1249
 
1250
+ self.act_finder_chart = QAction(QIcon(finderchart_path), self.tr("Finder Chart..."), self)
1251
+ self.act_finder_chart.setIconVisibleInMenu(True)
1252
+ self.act_finder_chart.setStatusTip(self.tr("Show a finder chart for the active plate-solved image"))
1253
+ self.act_finder_chart.triggered.connect(self._open_finder_chart)
1254
+
1248
1255
  self.act_whats_in_my_sky = QAction(QIcon(wims_path), self.tr("What's In My Sky..."), self)
1249
1256
  self.act_whats_in_my_sky.setIconVisibleInMenu(True)
1250
1257
  self.act_whats_in_my_sky.setStatusTip(self.tr("Plan targets by altitude, transit time, and lunar separation"))
@@ -1446,6 +1453,7 @@ class ToolbarMixin:
1446
1453
  reg("astrospike", self.act_astrospike)
1447
1454
  reg("exo_detector", self.act_exo_detector)
1448
1455
  reg("isophote", self.act_isophote)
1456
+ reg("finder_chart", self.act_finder_chart)
1449
1457
  reg("rgb_align", self.act_rgb_align)
1450
1458
  reg("whats_in_my_sky", self.act_whats_in_my_sky)
1451
1459
  reg("whats_in_my_image", self.act_wimi)
@@ -242,8 +242,16 @@ class ImageManager(QObject):
242
242
  def set_image(self, new_image, metadata, step_name=None):
243
243
  slot = self.current_slot
244
244
  if self._images[slot] is not None:
245
+ # OPTIMIZATION: If we are setting the EXACT SAME image object (e.g. metadata update),
246
+ # do not deep-copy the image data to undo stack.
247
+ # This saves massive memory when just renaming a slot or changing WCS.
248
+ if new_image is self._images[slot]:
249
+ stored_img = self._images[slot] # Shallow copy / reference
250
+ else:
251
+ stored_img = self._images[slot].copy() # Deep copy for safety
252
+
245
253
  self._undo_stacks[slot].append(
246
- (self._images[slot].copy(), self._metadata[slot].copy(), step_name or "Unnamed Step")
254
+ (stored_img, self._metadata[slot].copy(), step_name or "Unnamed Step")
247
255
  )
248
256
  self._redo_stacks[slot].clear()
249
257
  print(f"ImageManager: Previous image in slot {slot} pushed to undo stack.")
@@ -1526,8 +1534,12 @@ def load_image(filename, max_retries=3, wait_seconds=3, return_metadata: bool =
1526
1534
  # 1) PixInsight astrometric solution (fallback only)
1527
1535
  try:
1528
1536
  if not all(k in hdr for k in ("CRPIX1","CRPIX2","CRVAL1","CRVAL2")):
1529
- ref_img = props['PCL:AstrometricSolution:ReferenceImageCoordinates']['value']
1530
- ref_sky = props['PCL:AstrometricSolution:ReferenceCelestialCoordinates']['value']
1537
+ p_img = props['PCL:AstrometricSolution:ReferenceImageCoordinates']
1538
+ p_sky = props['PCL:AstrometricSolution:ReferenceCelestialCoordinates']
1539
+
1540
+ # Resolve lazy properties (decode base64/binary)
1541
+ ref_img = xisf.resolve_property(p_img)
1542
+ ref_sky = xisf.resolve_property(p_sky)
1531
1543
 
1532
1544
  # Some files store extra values; only first two are CRPIX/CRVAL
1533
1545
  im0, im1 = float(ref_img[0]), float(ref_img[1])
@@ -1547,7 +1559,9 @@ def load_image(filename, max_retries=3, wait_seconds=3, return_metadata: bool =
1547
1559
  # 2) CD matrix (fallback only)
1548
1560
  try:
1549
1561
  if not all(k in hdr for k in ("CD1_1","CD1_2","CD2_1","CD2_2")):
1550
- lin = np.asarray(props['PCL:AstrometricSolution:LinearTransformationMatrix']['value'], float)
1562
+ p_mat = props['PCL:AstrometricSolution:LinearTransformationMatrix']
1563
+ lin = np.asarray(xisf.resolve_property(p_mat), float)
1564
+
1551
1565
  hdr['CD1_1'], hdr['CD1_2'] = float(lin[0,0]), float(lin[0,1])
1552
1566
  hdr['CD2_1'], hdr['CD2_2'] = float(lin[1,0]), float(lin[1,1])
1553
1567
  _filled |= {'CD1_1','CD1_2','CD2_1','CD2_2'}
@@ -1072,7 +1072,7 @@ class XISF:
1072
1072
  }
1073
1073
  try:
1074
1074
  return _dtypes[s]
1075
- except:
1075
+ except KeyError:
1076
1076
  raise NotImplementedError(f"sampleFormat {s} not implemented")
1077
1077
 
1078
1078
  # Return XISF data type from numpy dtype
@@ -1087,7 +1087,7 @@ class XISF:
1087
1087
  }
1088
1088
  try:
1089
1089
  return _sampleFormats[str(dtype)]
1090
- except:
1090
+ except KeyError:
1091
1091
  raise NotImplementedError(f"sampleFormat for {dtype} not implemented")
1092
1092
 
1093
1093
  @staticmethod
@@ -1121,7 +1121,7 @@ class XISF:
1121
1121
  }
1122
1122
  try:
1123
1123
  return _dtypes[type_prefix]
1124
- except:
1124
+ except KeyError:
1125
1125
  raise NotImplementedError(f"data type {type_name} not implemented")
1126
1126
 
1127
1127
  # __/ Auxiliary functions for compression/shuffling \________
@@ -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
  # ============================================================================
@@ -340,6 +340,7 @@ class Icons:
340
340
  DEBAYER = property(lambda self: _resource_path('debayer.png'))
341
341
  FUNCTION_BUNDLES = property(lambda self: _resource_path('functionbundle.png'))
342
342
  VIEW_BUNDLES = property(lambda self: _resource_path('viewbundle.png'))
343
+ FINDER_CHART = property(lambda self: _resource_path('finderchart.png'))
343
344
 
344
345
  # Singleton instances for easy access
345
346
  _icons_instance = None
@@ -396,157 +397,178 @@ def get_data_path(name: str) -> str:
396
397
  """
397
398
  return _resource_path(name)
398
399
 
400
+ # ---------------- Legacy compatibility (LAZY) ----------------
401
+ # These names match the original module-level variables used in older code.
402
+
403
+ _LEGACY_ICON_MAP = {
404
+ 'icon_path': 'astrosuitepro.png',
405
+ 'windowslogo_path': 'astrosuitepro.ico',
406
+ 'green_path': 'green.png',
407
+ 'neutral_path': 'neutral.png',
408
+ 'whitebalance_path': 'whitebalance.png',
409
+ 'texture_clarity_path': 'TextureClarity.svg',
410
+ 'morpho_path': 'morpho.png',
411
+ 'clahe_path': 'clahe.png',
412
+ 'starnet_path': 'starnet.png',
413
+ 'staradd_path': 'staradd.png',
414
+ 'LExtract_path': 'LExtract.png',
415
+ 'LInsert_path': 'LInsert.png',
416
+ 'slot0_path': 'slot0.png',
417
+ 'slot1_path': 'slot1.png',
418
+ 'slot2_path': 'slot2.png',
419
+ 'slot3_path': 'slot3.png',
420
+ 'slot4_path': 'slot4.png',
421
+ 'slot5_path': 'slot5.png',
422
+ 'slot6_path': 'slot6.png',
423
+ 'slot7_path': 'slot7.png',
424
+ 'slot8_path': 'slot8.png',
425
+ 'slot9_path': 'slot9.png',
426
+ 'acv_icon_path': 'acv_icon.png',
427
+
428
+ 'moon_new_path': 'new_moon.png',
429
+ 'moon_waxing_crescent_1_path': 'waxing_crescent_1.png',
430
+ 'moon_waxing_crescent_2_path': 'waxing_crescent_2.png',
431
+ 'moon_waxing_crescent_3_path': 'waxing_crescent_3.png',
432
+ 'moon_waxing_crescent_4_path': 'waxing_crescent_4.png',
433
+ 'moon_waxing_crescent_5_path': 'waxing_crescent_5.png',
434
+ 'moon_first_quarter_path': 'first_quarter.png',
435
+ 'moon_waxing_gibbous_1_path': 'waxing_gibbous_1.png',
436
+ 'moon_waxing_gibbous_2_path': 'waxing_gibbous_2.png',
437
+ 'moon_waxing_gibbous_3_path': 'waxing_gibbous_3.png',
438
+ 'moon_waxing_gibbous_4_path': 'waxing_gibbous_4.png',
439
+ 'moon_waxing_gibbous_5_path': 'waxing_gibbous_5.png',
440
+ 'moon_full_path': 'full_moon.png',
441
+ 'moon_waning_gibbous_1_path': 'waning_gibbous_1.png',
442
+ 'moon_waning_gibbous_2_path': 'waning_gibbous_2.png',
443
+ 'moon_waning_gibbous_3_path': 'waning_gibbous_3.png',
444
+ 'moon_waning_gibbous_4_path': 'waning_gibbous_4.png',
445
+ 'moon_waning_gibbous_5_path': 'waning_gibbous_5.png',
446
+ 'moon_last_quarter_path': 'last_quarter.png',
447
+ 'moon_waning_crescent_1_path': 'waning_crescent_1.png',
448
+ 'moon_waning_crescent_2_path': 'waning_crescent_2.png',
449
+ 'moon_waning_crescent_3_path': 'waning_crescent_3.png',
450
+ 'moon_waning_crescent_4_path': 'waning_crescent_4.png',
451
+ 'moon_waning_crescent_5_path': 'waning_crescent_5.png',
452
+
453
+ 'rgbcombo_path': 'rgbcombo.png',
454
+ 'rgbextract_path': 'rgbextract.png',
455
+ 'copyslot_path': 'copyslot.png',
456
+ 'graxperticon_path': 'graxpert.png',
457
+ 'cropicon_path': 'cropicon.png',
458
+ 'openfile_path': 'openfile.png',
459
+ 'abeicon_path': 'abeicon.png',
460
+ 'undoicon_path': 'undoicon.png',
461
+ 'redoicon_path': 'redoicon.png',
462
+ 'blastericon_path': 'blaster.png',
463
+ 'clonestampicon_path': 'clonestamp.png',
464
+ 'hdr_path': 'hdr.png',
465
+ 'invert_path': 'invert.png',
466
+ 'fliphorizontal_path': 'fliphorizontal.png',
467
+ 'flipvertical_path': 'flipvertical.png',
468
+ 'rotateclockwise_path': 'rotateclockwise.png',
469
+ 'rotatecounterclockwise_path': 'rotatecounterclockwise.png',
470
+ 'rotate180_path': 'rotate180.png',
471
+ 'rotatearbitrary_path': 'rotatearbitrary.png',
472
+ 'maskcreate_path': 'maskcreate.png',
473
+ 'maskapply_path': 'maskapply.png',
474
+ 'maskremove_path': 'maskremove.png',
475
+ 'pixelmath_path': 'pixelmath.png',
476
+ 'histogram_path': 'histogram.png',
477
+ 'mosaic_path': 'mosaic.png',
478
+ 'rescale_path': 'rescale.png',
479
+ 'staralign_path': 'staralign.png',
480
+ 'mask_path': 'maskapply.png',
481
+ 'platesolve_path': 'platesolve.png',
482
+ 'psf_path': 'psf.png',
483
+ 'supernova_path': 'supernova.png',
484
+ 'starregistration_path': 'starregistration.png',
485
+ 'stacking_path': 'stacking.png',
486
+ 'pedestal_icon_path': 'pedestal.png',
487
+ 'starspike_path': 'starspike.png',
488
+ 'astrospike_path': 'Astro_Spikes.png',
489
+ 'aperture_path': 'aperture.png',
490
+ 'jwstpupil_path': 'jwstpupil.png',
491
+ 'signature_icon_path': 'pen.png',
492
+ 'livestacking_path': 'livestacking.png',
493
+ 'hrdiagram_path': 'HRDiagram.png',
494
+ 'convoicon_path': 'convo.png',
495
+ 'spcc_icon_path': 'spcc.png',
496
+
497
+ 'exoicon_path': 'exoicon.png',
498
+ 'peeker_icon': 'gridicon.png',
499
+ 'dse_icon_path': 'dse.png',
500
+ 'isophote_path': 'isophote.png',
501
+ 'statstretch_path': 'statstretch.png',
502
+ 'starstretch_path': 'starstretch.png',
503
+ 'curves_path': 'curves.png',
504
+ 'disk_path': 'disk.png',
505
+ 'uhs_path': 'uhs.png',
506
+ 'blink_path': 'blink.png',
507
+ 'ppp_path': 'ppp.png',
508
+ 'nbtorgb_path': 'nbtorgb.png',
509
+ 'freqsep_path': 'freqsep.png',
510
+ 'multiscale_decomp_path': 'multiscale_decomp.png',
511
+ 'contsub_path': 'contsub.png',
512
+ 'halo_path': 'halo.png',
513
+ 'cosmic_path': 'cosmic.png',
514
+ 'satellite_path': 'cosmicsat.png',
515
+ 'imagecombine_path': 'imagecombine.png',
516
+ 'wrench_path': 'wrench_icon.png',
517
+ 'eye_icon_path': 'eye.png',
518
+ 'disk_icon_path': 'disk.png',
519
+ 'nuke_path': 'nuke.png',
520
+ 'hubble_path': 'hubble.png',
521
+ 'collage_path': 'collage.png',
522
+ 'annotated_path': 'annotated.png',
523
+ 'colorwheel_path': 'colorwheel.png',
524
+ 'narrowbandnormalization_path': 'narrowbandnormalization.png',
525
+ 'font_path': 'font.png',
526
+ 'csv_icon_path': 'cvs.png',
527
+ 'wims_path': 'wims.png',
528
+ 'wimi_path': 'wimi_icon_256x256.png',
529
+ 'linearfit_path': 'linearfit.png',
530
+ 'debayer_path': 'debayer.png',
531
+ 'aberration_path': 'aberration.png',
532
+ 'functionbundles_path': 'functionbundle.png',
533
+ 'planetarystacker_path': 'planetarystacker.png',
534
+ 'viewbundles_path': 'viewbundle.png',
535
+ 'selectivecolor_path': 'selectivecolor.png',
536
+ 'rgbalign_path': 'rgbalign.png',
537
+ 'background_path': 'background.png',
538
+ 'script_icon_path': 'script.png',
539
+ 'planetprojection_path': '3dplanet.png',
540
+ 'finderchart_path': 'finderchart.png',
541
+ }
542
+
543
+ _LEGACY_DATA_MAP = {
544
+ 'sasp_data_path': 'data/SASP_data.fits',
545
+ 'astrobin_filters_csv_path': 'data/catalogs/astrobin_filters.csv',
546
+ 'spinner_path': 'spinner.gif',
547
+ }
548
+
549
+ def __getattr__(name: str):
550
+ # --- legacy paths (lazy) ---
551
+ if name in _LEGACY_ICON_MAP:
552
+ return get_icon_path(_LEGACY_ICON_MAP[name])
553
+ if name in _LEGACY_DATA_MAP:
554
+ return get_data_path(_LEGACY_DATA_MAP[name])
555
+
556
+ # --- special exports ---
557
+ if name == 'background_startup_path':
558
+ return _resource_path('Background_startup.jpg')
559
+ if name == 'resource_monitor_qml':
560
+ return _resource_path(os.path.join("qml", "ResourceMonitor.qml"))
561
+
562
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
563
+
564
+ def __dir__():
565
+ return (
566
+ list(globals().keys())
567
+ + list(_LEGACY_ICON_MAP.keys())
568
+ + list(_LEGACY_DATA_MAP.keys())
569
+ + ['background_startup_path', 'resource_monitor_qml']
570
+ )
399
571
 
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
572
 
551
573
  class Resources:
552
574
  """
@@ -625,23 +647,15 @@ def model_path(filename: str) -> str:
625
647
  _assert_not_internal_models_path(p)
626
648
  return p
627
649
 
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
650
 
637
651
  # QML helper
638
652
  resource_monitor_qml = _resource_path(os.path.join("qml", "ResourceMonitor.qml"))
639
653
 
640
654
  # Export list for `from setiastro.saspro.resources import *`
641
655
  __all__ = [
642
- 'Icons', 'Resources',
656
+ 'Icons', 'Resources',
643
657
  'get_icons', 'get_resources',
644
658
  'get_icon_path', 'get_data_path',
659
+ 'resource_monitor_qml',
645
660
  'background_startup_path',
646
- ] + list(_legacy.keys())
647
-
661
+ ] + list(_LEGACY_ICON_MAP.keys()) + list(_LEGACY_DATA_MAP.keys())