setiastrosuitepro 1.6.1.post1__py3-none-any.whl → 1.6.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.
Files changed (127) hide show
  1. setiastro/images/Background_startup.jpg +0 -0
  2. setiastro/qml/ResourceMonitor.qml +126 -0
  3. setiastro/saspro/__main__.py +162 -25
  4. setiastro/saspro/_generated/build_info.py +2 -1
  5. setiastro/saspro/abe.py +62 -11
  6. setiastro/saspro/aberration_ai.py +3 -3
  7. setiastro/saspro/add_stars.py +5 -2
  8. setiastro/saspro/astrobin_exporter.py +3 -0
  9. setiastro/saspro/astrospike_python.py +3 -1
  10. setiastro/saspro/autostretch.py +4 -2
  11. setiastro/saspro/backgroundneutral.py +52 -10
  12. setiastro/saspro/batch_convert.py +3 -0
  13. setiastro/saspro/batch_renamer.py +3 -0
  14. setiastro/saspro/blemish_blaster.py +3 -0
  15. setiastro/saspro/cheat_sheet.py +50 -15
  16. setiastro/saspro/clahe.py +27 -1
  17. setiastro/saspro/comet_stacking.py +103 -38
  18. setiastro/saspro/convo.py +3 -0
  19. setiastro/saspro/copyastro.py +3 -0
  20. setiastro/saspro/cosmicclarity.py +70 -45
  21. setiastro/saspro/crop_dialog_pro.py +17 -0
  22. setiastro/saspro/curve_editor_pro.py +18 -0
  23. setiastro/saspro/debayer.py +3 -0
  24. setiastro/saspro/doc_manager.py +39 -16
  25. setiastro/saspro/fitsmodifier.py +3 -0
  26. setiastro/saspro/frequency_separation.py +8 -2
  27. setiastro/saspro/function_bundle.py +2 -0
  28. setiastro/saspro/generate_translations.py +715 -1
  29. setiastro/saspro/ghs_dialog_pro.py +3 -0
  30. setiastro/saspro/graxpert.py +3 -0
  31. setiastro/saspro/gui/main_window.py +272 -29
  32. setiastro/saspro/gui/mixins/dock_mixin.py +100 -1
  33. setiastro/saspro/gui/mixins/file_mixin.py +7 -0
  34. setiastro/saspro/gui/mixins/menu_mixin.py +28 -0
  35. setiastro/saspro/gui/statistics_dialog.py +47 -0
  36. setiastro/saspro/halobgon.py +29 -3
  37. setiastro/saspro/histogram.py +3 -0
  38. setiastro/saspro/history_explorer.py +2 -0
  39. setiastro/saspro/i18n.py +22 -10
  40. setiastro/saspro/image_combine.py +3 -0
  41. setiastro/saspro/image_peeker_pro.py +3 -0
  42. setiastro/saspro/imageops/stretch.py +5 -13
  43. setiastro/saspro/isophote.py +3 -0
  44. setiastro/saspro/legacy/numba_utils.py +64 -47
  45. setiastro/saspro/linear_fit.py +3 -0
  46. setiastro/saspro/live_stacking.py +13 -2
  47. setiastro/saspro/mask_creation.py +3 -0
  48. setiastro/saspro/mfdeconv.py +5 -0
  49. setiastro/saspro/morphology.py +30 -5
  50. setiastro/saspro/multiscale_decomp.py +3 -0
  51. setiastro/saspro/nbtorgb_stars.py +12 -2
  52. setiastro/saspro/numba_utils.py +148 -47
  53. setiastro/saspro/ops/scripts.py +77 -17
  54. setiastro/saspro/ops/settings.py +1 -43
  55. setiastro/saspro/perfect_palette_picker.py +1 -0
  56. setiastro/saspro/pixelmath.py +6 -2
  57. setiastro/saspro/plate_solver.py +1 -0
  58. setiastro/saspro/remove_green.py +18 -1
  59. setiastro/saspro/remove_stars.py +136 -162
  60. setiastro/saspro/resources.py +7 -0
  61. setiastro/saspro/rgb_combination.py +1 -0
  62. setiastro/saspro/rgbalign.py +4 -4
  63. setiastro/saspro/save_options.py +1 -0
  64. setiastro/saspro/sfcc.py +50 -8
  65. setiastro/saspro/signature_insert.py +3 -0
  66. setiastro/saspro/stacking_suite.py +630 -341
  67. setiastro/saspro/star_alignment.py +16 -1
  68. setiastro/saspro/star_spikes.py +116 -32
  69. setiastro/saspro/star_stretch.py +38 -1
  70. setiastro/saspro/stat_stretch.py +35 -3
  71. setiastro/saspro/subwindow.py +63 -2
  72. setiastro/saspro/supernovaasteroidhunter.py +3 -0
  73. setiastro/saspro/translations/all_source_strings.json +3654 -0
  74. setiastro/saspro/translations/ar_translations.py +3865 -0
  75. setiastro/saspro/translations/de_translations.py +16 -0
  76. setiastro/saspro/translations/es_translations.py +16 -0
  77. setiastro/saspro/translations/fr_translations.py +16 -0
  78. setiastro/saspro/translations/hi_translations.py +3571 -0
  79. setiastro/saspro/translations/integrate_translations.py +36 -0
  80. setiastro/saspro/translations/it_translations.py +16 -0
  81. setiastro/saspro/translations/ja_translations.py +16 -0
  82. setiastro/saspro/translations/pt_translations.py +16 -0
  83. setiastro/saspro/translations/ru_translations.py +2848 -0
  84. setiastro/saspro/translations/saspro_ar.qm +0 -0
  85. setiastro/saspro/translations/saspro_ar.ts +255 -0
  86. setiastro/saspro/translations/saspro_de.qm +0 -0
  87. setiastro/saspro/translations/saspro_de.ts +3 -3
  88. setiastro/saspro/translations/saspro_es.qm +0 -0
  89. setiastro/saspro/translations/saspro_es.ts +3 -3
  90. setiastro/saspro/translations/saspro_fr.qm +0 -0
  91. setiastro/saspro/translations/saspro_fr.ts +3 -3
  92. setiastro/saspro/translations/saspro_hi.qm +0 -0
  93. setiastro/saspro/translations/saspro_hi.ts +257 -0
  94. setiastro/saspro/translations/saspro_it.qm +0 -0
  95. setiastro/saspro/translations/saspro_it.ts +3 -3
  96. setiastro/saspro/translations/saspro_ja.qm +0 -0
  97. setiastro/saspro/translations/saspro_ja.ts +4 -4
  98. setiastro/saspro/translations/saspro_pt.qm +0 -0
  99. setiastro/saspro/translations/saspro_pt.ts +3 -3
  100. setiastro/saspro/translations/saspro_ru.qm +0 -0
  101. setiastro/saspro/translations/saspro_ru.ts +237 -0
  102. setiastro/saspro/translations/saspro_sw.qm +0 -0
  103. setiastro/saspro/translations/saspro_sw.ts +257 -0
  104. setiastro/saspro/translations/saspro_uk.qm +0 -0
  105. setiastro/saspro/translations/saspro_uk.ts +10771 -0
  106. setiastro/saspro/translations/saspro_zh.qm +0 -0
  107. setiastro/saspro/translations/saspro_zh.ts +3 -3
  108. setiastro/saspro/translations/sw_translations.py +3671 -0
  109. setiastro/saspro/translations/uk_translations.py +3700 -0
  110. setiastro/saspro/translations/zh_translations.py +16 -0
  111. setiastro/saspro/versioning.py +36 -5
  112. setiastro/saspro/view_bundle.py +3 -0
  113. setiastro/saspro/wavescale_hdr.py +22 -1
  114. setiastro/saspro/wavescalede.py +23 -1
  115. setiastro/saspro/whitebalance.py +39 -3
  116. setiastro/saspro/widgets/minigame/game.js +986 -0
  117. setiastro/saspro/widgets/minigame/index.html +53 -0
  118. setiastro/saspro/widgets/minigame/style.css +241 -0
  119. setiastro/saspro/widgets/resource_monitor.py +237 -0
  120. setiastro/saspro/widgets/wavelet_utils.py +52 -20
  121. setiastro/saspro/wimi.py +35 -15
  122. {setiastrosuitepro-1.6.1.post1.dist-info → setiastrosuitepro-1.6.2.dist-info}/METADATA +15 -4
  123. {setiastrosuitepro-1.6.1.post1.dist-info → setiastrosuitepro-1.6.2.dist-info}/RECORD +127 -104
  124. {setiastrosuitepro-1.6.1.post1.dist-info → setiastrosuitepro-1.6.2.dist-info}/WHEEL +0 -0
  125. {setiastrosuitepro-1.6.1.post1.dist-info → setiastrosuitepro-1.6.2.dist-info}/entry_points.txt +0 -0
  126. {setiastrosuitepro-1.6.1.post1.dist-info → setiastrosuitepro-1.6.2.dist-info}/licenses/LICENSE +0 -0
  127. {setiastrosuitepro-1.6.1.post1.dist-info → setiastrosuitepro-1.6.2.dist-info}/licenses/license.txt +0 -0
@@ -271,7 +271,16 @@ class CropDialogPro(QDialog):
271
271
  def __init__(self, parent, document):
272
272
  super().__init__(parent)
273
273
  self.setWindowTitle(self.tr("Crop Tool"))
274
+ self.setWindowFlag(Qt.WindowType.Window, True)
275
+ self.setWindowModality(Qt.WindowModality.NonModal)
276
+ self.setModal(False)
277
+ self._main = parent
274
278
  self.doc = document
279
+
280
+ # Connect to active document change signal
281
+ if hasattr(self._main, "currentDocumentChanged"):
282
+ self._main.currentDocumentChanged.connect(self._on_active_doc_changed)
283
+
275
284
  self._rect_item: Optional[ResizableRotatableRectItem] = None
276
285
  self._pix_item: Optional[QGraphicsPixmapItem] = None
277
286
  self._drawing = False
@@ -463,6 +472,14 @@ class CropDialogPro(QDialog):
463
472
  arr = arr[..., 0]
464
473
  return np.clip(arr, 0.0, 1.0)
465
474
 
475
+ def _on_active_doc_changed(self, doc):
476
+ """Called when user clicks a different image window."""
477
+ if doc is None or getattr(doc, "image", None) is None:
478
+ return
479
+ self.doc = doc
480
+ self._rect_item = None
481
+ self._load_from_doc()
482
+
466
483
  def _load_from_doc(self):
467
484
  self._full01 = self._img01_from_doc()
468
485
  self._orig_h, self._orig_w = self._full01.shape[:2]
@@ -1016,7 +1016,16 @@ class CurvesDialogPro(QDialog):
1016
1016
  def __init__(self, parent, document):
1017
1017
  super().__init__(parent)
1018
1018
  self.setWindowTitle(self.tr("Curves Editor"))
1019
+ self.setWindowFlag(Qt.WindowType.Window, True)
1020
+ self.setWindowModality(Qt.WindowModality.NonModal)
1021
+ self.setModal(False)
1022
+ self._main = parent
1019
1023
  self.doc = document
1024
+
1025
+ # Connect to active document change signal
1026
+ if hasattr(self._main, "currentDocumentChanged"):
1027
+ self._main.currentDocumentChanged.connect(self._on_active_doc_changed)
1028
+
1020
1029
  self._preview_img = None # downsampled float01
1021
1030
  self._full_img = None # full-res float01
1022
1031
  self._pix = None
@@ -1788,6 +1797,15 @@ class CurvesDialogPro(QDialog):
1788
1797
  self._rebuild_presets_menu()
1789
1798
 
1790
1799
 
1800
+ # ----- active document change -----
1801
+ def _on_active_doc_changed(self, doc):
1802
+ """Called when user clicks a different image window."""
1803
+ if doc is None or getattr(doc, "image", None) is None:
1804
+ return
1805
+ self.doc = doc
1806
+ self._load_from_doc()
1807
+ QTimer.singleShot(0, self._fit_after_load)
1808
+
1791
1809
  # ----- data -----
1792
1810
  def _load_from_doc(self):
1793
1811
  img = self.doc.image
@@ -402,6 +402,9 @@ class DebayerDialog(QDialog):
402
402
  def __init__(self, parent, doc_manager, active_doc):
403
403
  super().__init__(parent)
404
404
  self.setWindowTitle(self.tr("Debayer"))
405
+ self.setWindowFlag(Qt.WindowType.Window, True)
406
+ self.setWindowModality(Qt.WindowModality.NonModal)
407
+ self.setModal(False)
405
408
  self.dm = doc_manager
406
409
  self.doc = active_doc
407
410
  self.worker: Optional[_DebayerWorker] = None
@@ -2472,23 +2472,29 @@ class DocManager(QObject):
2472
2472
  If a Preview tab is selected on the active ImageSubWindow, return a cached
2473
2473
  _RoiViewDocument so tools and the Preview tab share the same instance.
2474
2474
  Otherwise return the real ImageDocument.
2475
+
2476
+ IMPORTANT: Always check the currently active MDI subwindow first,
2477
+ as that's what the user expects to be the "active" document.
2475
2478
  """
2476
- # Prefer cached (if set and still valid)
2477
- if self._active_doc is not None and self._active_doc in self._docs:
2478
- base_doc = self._active_doc
2479
- else:
2480
- base_doc = None
2481
- try:
2482
- if self._mdi is not None:
2483
- sw = self._mdi.activeSubWindow()
2484
- if sw is not None:
2485
- w = sw.widget()
2486
- base_doc = getattr(w, "document", None) or getattr(sw, "document", None)
2487
- if base_doc is not None:
2488
- self._active_doc = base_doc
2489
- except Exception:
2490
- pass
2491
- if base_doc is None:
2479
+ base_doc = None
2480
+
2481
+ # ALWAYS check the MDI active subwindow first - this is the source of truth
2482
+ try:
2483
+ if self._mdi is not None:
2484
+ sw = self._mdi.activeSubWindow()
2485
+ if sw is not None:
2486
+ w = sw.widget()
2487
+ base_doc = getattr(w, "document", None) or getattr(sw, "document", None)
2488
+ if base_doc is not None:
2489
+ self._active_doc = base_doc
2490
+ except Exception:
2491
+ pass
2492
+
2493
+ # Fallback to cached value only if MDI lookup failed
2494
+ if base_doc is None:
2495
+ if self._active_doc is not None and self._active_doc in self._docs:
2496
+ base_doc = self._active_doc
2497
+ else:
2492
2498
  base_doc = self._docs[-1] if self._docs else None
2493
2499
 
2494
2500
  # Non-image docs just pass through
@@ -2524,6 +2530,23 @@ class DocManager(QObject):
2524
2530
 
2525
2531
  # Prefer explicit doc if given; otherwise fall back to "active"
2526
2532
  view_doc = doc or self.get_active_document()
2533
+
2534
+ # DEBUG: Trace why LinearFit might fail
2535
+ # print(f"[DocManager] update_active_document target: {view_doc}, type: {type(view_doc).__name__}")
2536
+
2537
+ # NEW: Unwrap proxy objects (_DocProxy / LiveViewDocument)
2538
+ tname = type(view_doc).__name__
2539
+ if "LiveViewDocument" in tname:
2540
+ try:
2541
+ view_doc = view_doc._current()
2542
+ except Exception:
2543
+ pass
2544
+ elif "_DocProxy" in tname:
2545
+ try:
2546
+ view_doc = view_doc._target()
2547
+ except Exception:
2548
+ pass
2549
+
2527
2550
  if view_doc is None:
2528
2551
  raise RuntimeError("No active document")
2529
2552
 
@@ -606,6 +606,9 @@ class BatchFITSHeaderDialog(QDialog):
606
606
  def __init__(self, parent=None, preset_keyword: str = "", preset_value: str = "", preset_comment: str = ""):
607
607
  super().__init__(parent)
608
608
  self.setWindowTitle(self.tr("Batch Modify FITS Headers"))
609
+ self.setWindowFlag(Qt.WindowType.Window, True)
610
+ self.setWindowModality(Qt.WindowModality.NonModal)
611
+ self.setModal(False)
609
612
  self.resize(520, 220)
610
613
 
611
614
  v = QVBoxLayout(self)
@@ -708,10 +708,16 @@ class FrequencySeperationTab(QWidget):
708
708
 
709
709
  # channel reconcile
710
710
  if rch == 1 and ch == 3:
711
- # convert RGB→mono (luma or average; we’ll use average)
711
+ # convert RGB→mono (use weighted luma for consistency, or mean if desired. Original was mean)
712
712
  a = a.mean(axis=2).astype(np.float32)
713
713
  elif rch == 3 and ch == 1:
714
- a = np.repeat(a[..., None], 3, axis=2).astype(np.float32)
714
+ # Broadcast mono to 3 channels without copying
715
+ # (H,W,1) -> (H,W,3) via broadcasted view if consumer allows,
716
+ # but usually downstream (like subtraction) handles broadcasting fine.
717
+ # If explicit physical layout is needed, we must check usage.
718
+ # Here: used for subtraction (OK) and preview (OK).
719
+ # We return a view using broadcast_to or striding tricks.
720
+ a = np.broadcast_to(a, (ah, aw, 3))
715
721
 
716
722
  return a
717
723
 
@@ -334,6 +334,8 @@ class FunctionBundleDialog(QDialog):
334
334
  super().__init__(parent)
335
335
  _pin_on_top_mac(self)
336
336
  self.setWindowTitle("Function Bundles")
337
+ self.setWindowFlag(Qt.WindowType.Window, True)
338
+ self.setWindowModality(Qt.WindowModality.NonModal)
337
339
  self.setModal(False)
338
340
  self.resize(920, 560)
339
341
  self.setAcceptDrops(True)