setiastrosuitepro 1.6.4__py3-none-any.whl → 1.7.1.post2__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 (132) hide show
  1. setiastro/images/TextureClarity.svg +56 -0
  2. setiastro/images/abeicon.svg +16 -0
  3. setiastro/images/acv_icon.png +0 -0
  4. setiastro/images/colorwheel.svg +97 -0
  5. setiastro/images/cosmic.svg +40 -0
  6. setiastro/images/cosmicsat.svg +24 -0
  7. setiastro/images/first_quarter.png +0 -0
  8. setiastro/images/full_moon.png +0 -0
  9. setiastro/images/graxpert.svg +19 -0
  10. setiastro/images/last_quarter.png +0 -0
  11. setiastro/images/linearfit.svg +32 -0
  12. setiastro/images/narrowbandnormalization.png +0 -0
  13. setiastro/images/new_moon.png +0 -0
  14. setiastro/images/pixelmath.svg +42 -0
  15. setiastro/images/planetarystacker.png +0 -0
  16. setiastro/images/waning_crescent_1.png +0 -0
  17. setiastro/images/waning_crescent_2.png +0 -0
  18. setiastro/images/waning_crescent_3.png +0 -0
  19. setiastro/images/waning_crescent_4.png +0 -0
  20. setiastro/images/waning_crescent_5.png +0 -0
  21. setiastro/images/waning_gibbous_1.png +0 -0
  22. setiastro/images/waning_gibbous_2.png +0 -0
  23. setiastro/images/waning_gibbous_3.png +0 -0
  24. setiastro/images/waning_gibbous_4.png +0 -0
  25. setiastro/images/waning_gibbous_5.png +0 -0
  26. setiastro/images/waxing_crescent_1.png +0 -0
  27. setiastro/images/waxing_crescent_2.png +0 -0
  28. setiastro/images/waxing_crescent_3.png +0 -0
  29. setiastro/images/waxing_crescent_4.png +0 -0
  30. setiastro/images/waxing_crescent_5.png +0 -0
  31. setiastro/images/waxing_gibbous_1.png +0 -0
  32. setiastro/images/waxing_gibbous_2.png +0 -0
  33. setiastro/images/waxing_gibbous_3.png +0 -0
  34. setiastro/images/waxing_gibbous_4.png +0 -0
  35. setiastro/images/waxing_gibbous_5.png +0 -0
  36. setiastro/qml/ResourceMonitor.qml +84 -82
  37. setiastro/saspro/__main__.py +20 -1
  38. setiastro/saspro/_generated/build_info.py +2 -2
  39. setiastro/saspro/abe.py +37 -4
  40. setiastro/saspro/aberration_ai.py +364 -33
  41. setiastro/saspro/aberration_ai_preset.py +29 -3
  42. setiastro/saspro/acv_exporter.py +379 -0
  43. setiastro/saspro/add_stars.py +33 -6
  44. setiastro/saspro/astrospike_python.py +45 -3
  45. setiastro/saspro/backgroundneutral.py +108 -40
  46. setiastro/saspro/blemish_blaster.py +4 -1
  47. setiastro/saspro/blink_comparator_pro.py +150 -55
  48. setiastro/saspro/clahe.py +4 -1
  49. setiastro/saspro/continuum_subtract.py +4 -1
  50. setiastro/saspro/convo.py +13 -7
  51. setiastro/saspro/cosmicclarity.py +129 -18
  52. setiastro/saspro/crop_dialog_pro.py +123 -7
  53. setiastro/saspro/curve_editor_pro.py +181 -64
  54. setiastro/saspro/curves_preset.py +249 -47
  55. setiastro/saspro/doc_manager.py +245 -15
  56. setiastro/saspro/exoplanet_detector.py +120 -28
  57. setiastro/saspro/frequency_separation.py +1158 -204
  58. setiastro/saspro/ghs_dialog_pro.py +81 -16
  59. setiastro/saspro/graxpert.py +1 -0
  60. setiastro/saspro/gui/main_window.py +706 -264
  61. setiastro/saspro/gui/mixins/dock_mixin.py +245 -24
  62. setiastro/saspro/gui/mixins/file_mixin.py +35 -16
  63. setiastro/saspro/gui/mixins/menu_mixin.py +35 -1
  64. setiastro/saspro/gui/mixins/theme_mixin.py +160 -14
  65. setiastro/saspro/gui/mixins/toolbar_mixin.py +499 -24
  66. setiastro/saspro/gui/mixins/update_mixin.py +138 -36
  67. setiastro/saspro/gui/mixins/view_mixin.py +42 -0
  68. setiastro/saspro/halobgon.py +4 -0
  69. setiastro/saspro/histogram.py +184 -8
  70. setiastro/saspro/image_combine.py +4 -0
  71. setiastro/saspro/image_peeker_pro.py +4 -0
  72. setiastro/saspro/imageops/narrowband_normalization.py +816 -0
  73. setiastro/saspro/imageops/serloader.py +1345 -0
  74. setiastro/saspro/imageops/starbasedwhitebalance.py +23 -52
  75. setiastro/saspro/imageops/stretch.py +582 -62
  76. setiastro/saspro/isophote.py +4 -0
  77. setiastro/saspro/layers.py +13 -9
  78. setiastro/saspro/layers_dock.py +183 -3
  79. setiastro/saspro/legacy/image_manager.py +154 -20
  80. setiastro/saspro/legacy/numba_utils.py +68 -48
  81. setiastro/saspro/legacy/xisf.py +240 -98
  82. setiastro/saspro/live_stacking.py +203 -82
  83. setiastro/saspro/luminancerecombine.py +228 -27
  84. setiastro/saspro/mask_creation.py +174 -15
  85. setiastro/saspro/mfdeconv.py +113 -35
  86. setiastro/saspro/mfdeconvcudnn.py +119 -70
  87. setiastro/saspro/mfdeconvsport.py +112 -35
  88. setiastro/saspro/morphology.py +4 -0
  89. setiastro/saspro/multiscale_decomp.py +81 -29
  90. setiastro/saspro/narrowband_normalization.py +1618 -0
  91. setiastro/saspro/numba_utils.py +72 -57
  92. setiastro/saspro/ops/commands.py +18 -18
  93. setiastro/saspro/ops/script_editor.py +10 -2
  94. setiastro/saspro/ops/scripts.py +122 -0
  95. setiastro/saspro/perfect_palette_picker.py +37 -3
  96. setiastro/saspro/plate_solver.py +84 -49
  97. setiastro/saspro/psf_viewer.py +119 -37
  98. setiastro/saspro/remove_green.py +1 -1
  99. setiastro/saspro/resources.py +73 -0
  100. setiastro/saspro/rgbalign.py +460 -12
  101. setiastro/saspro/selective_color.py +4 -1
  102. setiastro/saspro/ser_stack_config.py +82 -0
  103. setiastro/saspro/ser_stacker.py +2321 -0
  104. setiastro/saspro/ser_stacker_dialog.py +1838 -0
  105. setiastro/saspro/ser_tracking.py +206 -0
  106. setiastro/saspro/serviewer.py +1625 -0
  107. setiastro/saspro/sfcc.py +662 -216
  108. setiastro/saspro/shortcuts.py +171 -33
  109. setiastro/saspro/signature_insert.py +692 -33
  110. setiastro/saspro/stacking_suite.py +1347 -485
  111. setiastro/saspro/star_alignment.py +247 -123
  112. setiastro/saspro/star_spikes.py +4 -0
  113. setiastro/saspro/star_stretch.py +38 -3
  114. setiastro/saspro/stat_stretch.py +892 -129
  115. setiastro/saspro/subwindow.py +787 -363
  116. setiastro/saspro/supernovaasteroidhunter.py +1 -1
  117. setiastro/saspro/texture_clarity.py +593 -0
  118. setiastro/saspro/wavescale_hdr.py +4 -1
  119. setiastro/saspro/wavescalede.py +4 -1
  120. setiastro/saspro/whitebalance.py +84 -12
  121. setiastro/saspro/widgets/common_utilities.py +28 -21
  122. setiastro/saspro/widgets/resource_monitor.py +209 -111
  123. setiastro/saspro/widgets/spinboxes.py +10 -13
  124. setiastro/saspro/wimi.py +27 -656
  125. setiastro/saspro/wims.py +13 -3
  126. setiastro/saspro/xisf.py +101 -11
  127. {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.7.1.post2.dist-info}/METADATA +4 -2
  128. {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.7.1.post2.dist-info}/RECORD +132 -87
  129. {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.7.1.post2.dist-info}/WHEEL +0 -0
  130. {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.7.1.post2.dist-info}/entry_points.txt +0 -0
  131. {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.7.1.post2.dist-info}/licenses/LICENSE +0 -0
  132. {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.7.1.post2.dist-info}/licenses/license.txt +0 -0
@@ -493,7 +493,8 @@ class DraggableToolBar(QToolBar):
493
493
  cid = self._action_id(act)
494
494
  if cid:
495
495
  m.addSeparator()
496
- m.addAction(self.tr("Hide this icon"), lambda: self._set_action_hidden(act, True))
496
+ m.addAction(self.tr("Hide this icon"), lambda: self.window()._hide_action_to_hidden_toolbar(act))
497
+
497
498
 
498
499
  # (Optional) teach users about Alt+Drag:
499
500
  m.addSeparator()
@@ -502,7 +503,6 @@ class DraggableToolBar(QToolBar):
502
503
 
503
504
  m.exec(gpos)
504
505
 
505
-
506
506
  def contextMenuEvent(self, ev):
507
507
  # Right-click on empty toolbar area
508
508
  m = QMenu(self)
@@ -512,25 +512,29 @@ class DraggableToolBar(QToolBar):
512
512
  act_lock = m.addAction(self.tr("Lock Toolbar Icons"))
513
513
  act_lock.setCheckable(True)
514
514
  act_lock.setChecked(is_locked)
515
-
515
+
516
516
  def _toggle_lock(checked):
517
517
  self._set_locked(checked)
518
-
518
+
519
519
  act_lock.triggered.connect(_toggle_lock)
520
-
520
+
521
521
  m.addSeparator()
522
522
 
523
523
  # Submenu listing hidden actions for this toolbar
524
- hidden = self._load_hidden_set()
525
524
  sub = m.addMenu(self.tr("Show hidden…"))
526
525
 
527
- # Build list from actions that are currently invisible
526
+ mw = self.window()
527
+ tb_hidden = getattr(mw, "_hidden_toolbar", lambda: None)()
528
528
  any_hidden = False
529
- for act in self.actions():
530
- cid = self._action_id(act)
531
- if cid and (cid in hidden) and (not act.isVisible()):
529
+ if tb_hidden:
530
+ for act in tb_hidden.actions():
531
+ if act.isSeparator():
532
+ continue
532
533
  any_hidden = True
533
- sub.addAction(act.text() or cid, lambda a=act: self._set_action_hidden(a, False))
534
+ sub.addAction(
535
+ act.text() or (act.property("command_id") or act.objectName() or "item"),
536
+ lambda a=act: mw._unhide_action_from_hidden_toolbar(a)
537
+ )
534
538
 
535
539
  if not any_hidden:
536
540
  sub.setEnabled(False)
@@ -538,13 +542,25 @@ class DraggableToolBar(QToolBar):
538
542
  m.addSeparator()
539
543
  m.addAction(self.tr("Reset hidden icons"), self._reset_hidden_icons)
540
544
 
545
+ # ✅ NEW: Factory reset for all toolbars
546
+ m.addSeparator()
547
+ reset_all = m.addAction(self.tr("Reset ALL Toolbars (Factory Layout)"))
548
+ reset_all.setStatusTip(self.tr("Clears saved toolbar positions/orders/hidden state and restores defaults"))
549
+ reset_all.triggered.connect(lambda: getattr(self.window(), "_reset_all_toolbars_to_factory", lambda: None)())
550
+
541
551
  m.exec(ev.globalPos())
542
552
 
553
+
543
554
  def _reset_hidden_icons(self):
544
- # Show everything and clear hidden list
545
- for act in self.actions():
546
- act.setVisible(True)
547
- self._save_hidden_set(set())
555
+ mw = self.window()
556
+ tb_hidden = getattr(mw, "_hidden_toolbar", lambda: None)()
557
+ if not tb_hidden:
558
+ return
559
+ # copy list because we'll mutate
560
+ acts = [a for a in tb_hidden.actions() if not a.isSeparator()]
561
+ for a in acts:
562
+ mw._unhide_action_from_hidden_toolbar(a)
563
+
548
564
 
549
565
 
550
566
  _PRESET_UI_IDS = {
@@ -1024,17 +1040,14 @@ class ShortcutCanvas(QWidget):
1024
1040
  return False
1025
1041
  sw = self._top_subwindow_at(e.position().toPoint())
1026
1042
  if sw is None:
1027
- print("[ShortcutCanvas] _forward_command_drop: no subwindow under cursor", flush=True)
1028
- QApplication.processEvents()
1043
+
1029
1044
  return False
1030
1045
  try:
1031
1046
  raw = bytes(md.data(MIME_CMD))
1032
1047
  payload = _unpack_cmd_payload(raw) # your existing helper
1033
- print(f"[ShortcutCanvas] _forward_command_drop → subwin={sw}, payload={payload!r}", flush=True)
1034
- QApplication.processEvents()
1048
+
1035
1049
  except Exception as ex:
1036
- print(f"[ShortcutCanvas] _forward_command_drop: failed to unpack payload: {ex!r}", flush=True)
1037
- QApplication.processEvents()
1050
+
1038
1051
  return False
1039
1052
  self._mgr.apply_command_to_subwindow(sw, payload)
1040
1053
  e.acceptProposedAction()
@@ -1536,6 +1549,7 @@ class ShortcutManager:
1536
1549
 
1537
1550
  # ---- persistence (QSettings JSON blob) ----
1538
1551
  def save_shortcuts(self):
1552
+
1539
1553
  data = []
1540
1554
  for sid, w in list(self.widgets.items()):
1541
1555
  if _is_dead(w):
@@ -1836,6 +1850,7 @@ class ShortcutManager:
1836
1850
 
1837
1851
 
1838
1852
  def clear(self):
1853
+
1839
1854
  for sid, w in list(self.widgets.items()):
1840
1855
  try:
1841
1856
  if not _is_dead(w):
@@ -1910,50 +1925,173 @@ class ShortcutManager:
1910
1925
  # legacy single-remove (kept for callers)
1911
1926
  self.delete_by_id(sid, persist=True)
1912
1927
 
1913
-
1914
1928
  class _StatStretchPresetDialog(QDialog):
1929
+ """
1930
+ Preset editor for headless replay: command_id="stat_stretch"
1931
+
1932
+ Keys supported:
1933
+ target_median: float
1934
+ linked: bool
1935
+ normalize: bool
1936
+ apply_curves: bool
1937
+ curves_boost: float # 0..1
1938
+ blackpoint_sigma: float # 0..1 (matches your slider/100)
1939
+ no_black_clip: bool
1940
+ hdr_compress: bool
1941
+ hdr_amount: float # 0..1
1942
+ hdr_knee: float # 0..1
1943
+ luma_only: bool
1944
+ luma_mode: str # e.g. "rec709"
1945
+ luma_blend: float #0..1 (0=normal linked, 1=pure luma-only)
1946
+ """
1915
1947
  def __init__(self, parent=None, initial: dict | None = None):
1916
1948
  super().__init__(parent)
1917
1949
  self.setWindowTitle("Statistical Stretch — Preset")
1918
1950
  init = dict(initial or {})
1919
1951
 
1952
+ # --- Target median ---
1920
1953
  self.spin_target = QDoubleSpinBox()
1921
- self.spin_target.setRange(0.0, 1.0); self.spin_target.setDecimals(3)
1954
+ self.spin_target.setRange(0.0, 1.0)
1955
+ self.spin_target.setDecimals(3)
1922
1956
  self.spin_target.setSingleStep(0.01)
1923
1957
  self.spin_target.setValue(float(init.get("target_median", 0.25)))
1924
1958
 
1959
+ # --- Linked / Normalize ---
1925
1960
  self.chk_linked = QCheckBox("Linked RGB channels")
1926
1961
  self.chk_linked.setChecked(bool(init.get("linked", False)))
1927
1962
 
1928
1963
  self.chk_normalize = QCheckBox("Normalize to [0..1]")
1929
1964
  self.chk_normalize.setChecked(bool(init.get("normalize", False)))
1930
1965
 
1966
+ # --- Curves ---
1967
+ self.chk_curves = QCheckBox("Apply Curves")
1968
+ self.chk_curves.setChecked(bool(init.get("apply_curves", False)))
1969
+
1931
1970
  self.spin_curves = QDoubleSpinBox()
1932
- self.spin_curves.setRange(0.0, 1.0); self.spin_curves.setDecimals(2)
1971
+ self.spin_curves.setRange(0.0, 1.0)
1972
+ self.spin_curves.setDecimals(2)
1933
1973
  self.spin_curves.setSingleStep(0.05)
1934
- self.spin_curves.setValue(float(init.get("curves_boost", 0.0 if not init.get("apply_curves") else 0.20)))
1974
+ self.spin_curves.setValue(float(init.get("curves_boost", 0.0)))
1975
+ self.spin_curves.setEnabled(self.chk_curves.isChecked())
1976
+ self.chk_curves.toggled.connect(self.spin_curves.setEnabled)
1977
+
1978
+ # --- Blackpoint sigma ---
1979
+ self.spin_bp = QDoubleSpinBox()
1980
+ self.spin_bp.setRange(0.0, 1.0)
1981
+ self.spin_bp.setDecimals(2)
1982
+ self.spin_bp.setSingleStep(0.05)
1983
+ self.spin_bp.setValue(float(init.get("blackpoint_sigma", 0.0)))
1984
+
1985
+ self.chk_no_black_clip = QCheckBox("No black clip")
1986
+ self.chk_no_black_clip.setChecked(bool(init.get("no_black_clip", False)))
1987
+
1988
+ # --- HDR compress ---
1989
+ self.chk_hdr = QCheckBox("HDR compression")
1990
+ self.chk_hdr.setChecked(bool(init.get("hdr_compress", False)))
1991
+
1992
+ self.spin_hdr_amt = QDoubleSpinBox()
1993
+ self.spin_hdr_amt.setRange(0.0, 1.0)
1994
+ self.spin_hdr_amt.setDecimals(2)
1995
+ self.spin_hdr_amt.setSingleStep(0.05)
1996
+ self.spin_hdr_amt.setValue(float(init.get("hdr_amount", 0.0)))
1997
+
1998
+ self.spin_hdr_knee = QDoubleSpinBox()
1999
+ self.spin_hdr_knee.setRange(0.0, 1.0)
2000
+ self.spin_hdr_knee.setDecimals(2)
2001
+ self.spin_hdr_knee.setSingleStep(0.05)
2002
+ self.spin_hdr_knee.setValue(float(init.get("hdr_knee", 0.5)))
2003
+
2004
+ def _set_hdr_enabled(on: bool):
2005
+ on = bool(on)
2006
+ self.spin_hdr_amt.setEnabled(on)
2007
+ self.spin_hdr_knee.setEnabled(on)
2008
+
2009
+ _set_hdr_enabled(self.chk_hdr.isChecked())
2010
+ self.chk_hdr.toggled.connect(_set_hdr_enabled)
2011
+
2012
+ # --- Luma only ---
2013
+ self.chk_luma_only = QCheckBox("Luma-only mode")
2014
+ self.chk_luma_only.setChecked(bool(init.get("luma_only", False)))
2015
+
2016
+ self.cmb_luma = QComboBox()
2017
+ # keep in sync with your tool’s supported modes
2018
+ self.cmb_luma.addItems(["rec709", "avg", "hsp", "max"])
2019
+ init_mode = str(init.get("luma_mode", "rec709") or "rec709")
2020
+ idx = self.cmb_luma.findText(init_mode)
2021
+ if idx >= 0:
2022
+ self.cmb_luma.setCurrentIndex(idx)
2023
+
2024
+ self.spin_luma_blend = QDoubleSpinBox()
2025
+ self.spin_luma_blend.setRange(0.0, 1.0)
2026
+ self.spin_luma_blend.setDecimals(2)
2027
+ self.spin_luma_blend.setSingleStep(0.05)
2028
+ self.spin_luma_blend.setValue(float(init.get("luma_blend", 0.70)))
2029
+
2030
+ def _set_luma_enabled(on: bool):
2031
+ on = bool(on)
2032
+ self.cmb_luma.setEnabled(on)
2033
+ self.spin_luma_blend.setEnabled(on)
2034
+
2035
+ _set_luma_enabled(self.chk_luma_only.isChecked())
2036
+ self.chk_luma_only.toggled.connect(_set_luma_enabled)
2037
+
2038
+ # --- Layout ---
2039
+ form = QFormLayout()
1935
2040
 
1936
- form = QFormLayout(self)
1937
2041
  form.addRow("Target median:", self.spin_target)
1938
2042
  form.addRow("", self.chk_linked)
1939
2043
  form.addRow("", self.chk_normalize)
2044
+
2045
+ form.addRow("", QLabel("— Tone shaping —"))
2046
+ form.addRow("", self.chk_curves)
1940
2047
  form.addRow("Curves boost (0–1):", self.spin_curves)
1941
- form.addRow(QLabel("Curves are applied only if boost > 0."))
1942
2048
 
1943
- btns = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, parent=self)
1944
- btns.accepted.connect(self.accept); btns.rejected.connect(self.reject)
2049
+ form.addRow("", QLabel("— Blackpoint / HDR —"))
2050
+ form.addRow("Blackpoint σ (0–1):", self.spin_bp)
2051
+ form.addRow("", self.chk_no_black_clip)
2052
+
2053
+ form.addRow("", self.chk_hdr)
2054
+ form.addRow("HDR amount (0–1):", self.spin_hdr_amt)
2055
+ form.addRow("HDR knee (0–1):", self.spin_hdr_knee)
2056
+
2057
+ form.addRow("", QLabel("— Luma mode —"))
2058
+ form.addRow("", self.chk_luma_only)
2059
+ form.addRow("Luma mode:", self.cmb_luma)
2060
+ form.addRow("Luma blend (0–1):", self.spin_luma_blend)
2061
+ btns = QDialogButtonBox(
2062
+ QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel,
2063
+ parent=self
2064
+ )
2065
+ btns.accepted.connect(self.accept)
2066
+ btns.rejected.connect(self.reject)
1945
2067
  form.addRow(btns)
1946
2068
 
2069
+ self.setLayout(form)
2070
+
1947
2071
  def result_dict(self) -> dict:
1948
- boost = float(self.spin_curves.value())
2072
+ hdr_on = bool(self.chk_hdr.isChecked())
2073
+ curves_on = bool(self.chk_curves.isChecked())
2074
+ luma_on = bool(self.chk_luma_only.isChecked())
2075
+
1949
2076
  return {
1950
2077
  "target_median": float(self.spin_target.value()),
1951
2078
  "linked": bool(self.chk_linked.isChecked()),
1952
2079
  "normalize": bool(self.chk_normalize.isChecked()),
1953
- "apply_curves": bool(boost > 0.0),
1954
- "curves_boost": boost,
2080
+
2081
+ "apply_curves": curves_on,
2082
+ "curves_boost": float(self.spin_curves.value()) if curves_on else 0.0,
2083
+
2084
+ "blackpoint_sigma": float(self.spin_bp.value()),
2085
+ "no_black_clip": bool(self.chk_no_black_clip.isChecked()),
2086
+
2087
+ "hdr_compress": hdr_on,
2088
+ "hdr_amount": float(self.spin_hdr_amt.value()) if hdr_on else 0.0,
2089
+ "hdr_knee": float(self.spin_hdr_knee.value()) if hdr_on else 0.0,
2090
+
2091
+ "luma_only": luma_on,
2092
+ "luma_mode": str(self.cmb_luma.currentText()) if luma_on else "rec709",
2093
+ "luma_blend": float(self.spin_luma_blend.value()) if luma_on else 0.0,
1955
2094
  }
1956
-
1957
2095
 
1958
2096
  class _StarStretchPresetDialog(QDialog):
1959
2097
  def __init__(self, parent=None, initial: dict | None = None):