setiastrosuitepro 1.6.4__py3-none-any.whl → 1.6.12__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.
- setiastro/images/abeicon.svg +16 -0
- setiastro/images/acv_icon.png +0 -0
- setiastro/images/colorwheel.svg +97 -0
- setiastro/images/cosmic.svg +40 -0
- setiastro/images/cosmicsat.svg +24 -0
- setiastro/images/first_quarter.png +0 -0
- setiastro/images/full_moon.png +0 -0
- setiastro/images/graxpert.svg +19 -0
- setiastro/images/last_quarter.png +0 -0
- setiastro/images/linearfit.svg +32 -0
- setiastro/images/new_moon.png +0 -0
- setiastro/images/pixelmath.svg +42 -0
- setiastro/images/waning_crescent_1.png +0 -0
- setiastro/images/waning_crescent_2.png +0 -0
- setiastro/images/waning_crescent_3.png +0 -0
- setiastro/images/waning_crescent_4.png +0 -0
- setiastro/images/waning_crescent_5.png +0 -0
- setiastro/images/waning_gibbous_1.png +0 -0
- setiastro/images/waning_gibbous_2.png +0 -0
- setiastro/images/waning_gibbous_3.png +0 -0
- setiastro/images/waning_gibbous_4.png +0 -0
- setiastro/images/waning_gibbous_5.png +0 -0
- setiastro/images/waxing_crescent_1.png +0 -0
- setiastro/images/waxing_crescent_2.png +0 -0
- setiastro/images/waxing_crescent_3.png +0 -0
- setiastro/images/waxing_crescent_4.png +0 -0
- setiastro/images/waxing_crescent_5.png +0 -0
- setiastro/images/waxing_gibbous_1.png +0 -0
- setiastro/images/waxing_gibbous_2.png +0 -0
- setiastro/images/waxing_gibbous_3.png +0 -0
- setiastro/images/waxing_gibbous_4.png +0 -0
- setiastro/images/waxing_gibbous_5.png +0 -0
- setiastro/qml/ResourceMonitor.qml +84 -82
- setiastro/saspro/__main__.py +20 -1
- setiastro/saspro/_generated/build_info.py +2 -2
- setiastro/saspro/abe.py +37 -4
- setiastro/saspro/aberration_ai.py +237 -21
- setiastro/saspro/acv_exporter.py +379 -0
- setiastro/saspro/add_stars.py +33 -6
- setiastro/saspro/backgroundneutral.py +108 -40
- setiastro/saspro/blemish_blaster.py +4 -1
- setiastro/saspro/blink_comparator_pro.py +74 -24
- setiastro/saspro/clahe.py +4 -1
- setiastro/saspro/continuum_subtract.py +4 -1
- setiastro/saspro/convo.py +13 -7
- setiastro/saspro/cosmicclarity.py +129 -18
- setiastro/saspro/crop_dialog_pro.py +123 -7
- setiastro/saspro/curve_editor_pro.py +109 -42
- setiastro/saspro/doc_manager.py +245 -15
- setiastro/saspro/exoplanet_detector.py +120 -28
- setiastro/saspro/frequency_separation.py +1158 -204
- setiastro/saspro/ghs_dialog_pro.py +81 -16
- setiastro/saspro/graxpert.py +1 -0
- setiastro/saspro/gui/main_window.py +429 -228
- setiastro/saspro/gui/mixins/dock_mixin.py +245 -24
- setiastro/saspro/gui/mixins/menu_mixin.py +27 -1
- setiastro/saspro/gui/mixins/theme_mixin.py +160 -14
- setiastro/saspro/gui/mixins/toolbar_mixin.py +384 -18
- setiastro/saspro/gui/mixins/update_mixin.py +138 -36
- setiastro/saspro/gui/mixins/view_mixin.py +42 -0
- setiastro/saspro/halobgon.py +4 -0
- setiastro/saspro/histogram.py +5 -1
- setiastro/saspro/image_combine.py +4 -0
- setiastro/saspro/image_peeker_pro.py +4 -0
- setiastro/saspro/imageops/starbasedwhitebalance.py +23 -52
- setiastro/saspro/imageops/stretch.py +582 -62
- setiastro/saspro/isophote.py +4 -0
- setiastro/saspro/layers.py +13 -9
- setiastro/saspro/layers_dock.py +183 -3
- setiastro/saspro/legacy/image_manager.py +154 -20
- setiastro/saspro/legacy/numba_utils.py +67 -47
- setiastro/saspro/legacy/xisf.py +240 -98
- setiastro/saspro/live_stacking.py +180 -79
- setiastro/saspro/luminancerecombine.py +228 -27
- setiastro/saspro/mask_creation.py +174 -15
- setiastro/saspro/mfdeconv.py +113 -35
- setiastro/saspro/mfdeconvcudnn.py +119 -70
- setiastro/saspro/mfdeconvsport.py +112 -35
- setiastro/saspro/morphology.py +4 -0
- setiastro/saspro/multiscale_decomp.py +51 -12
- setiastro/saspro/numba_utils.py +72 -57
- setiastro/saspro/ops/commands.py +18 -18
- setiastro/saspro/ops/script_editor.py +10 -2
- setiastro/saspro/ops/scripts.py +122 -0
- setiastro/saspro/perfect_palette_picker.py +37 -3
- setiastro/saspro/plate_solver.py +84 -49
- setiastro/saspro/psf_viewer.py +119 -37
- setiastro/saspro/resources.py +67 -0
- setiastro/saspro/rgbalign.py +4 -0
- setiastro/saspro/selective_color.py +4 -1
- setiastro/saspro/sfcc.py +364 -152
- setiastro/saspro/shortcuts.py +160 -29
- setiastro/saspro/signature_insert.py +692 -33
- setiastro/saspro/stacking_suite.py +1331 -484
- setiastro/saspro/star_alignment.py +247 -123
- setiastro/saspro/star_spikes.py +4 -0
- setiastro/saspro/star_stretch.py +38 -3
- setiastro/saspro/stat_stretch.py +743 -128
- setiastro/saspro/subwindow.py +786 -360
- setiastro/saspro/supernovaasteroidhunter.py +1 -1
- setiastro/saspro/wavescale_hdr.py +4 -1
- setiastro/saspro/wavescalede.py +4 -1
- setiastro/saspro/whitebalance.py +84 -12
- setiastro/saspro/widgets/common_utilities.py +28 -21
- setiastro/saspro/widgets/resource_monitor.py +109 -59
- setiastro/saspro/widgets/spinboxes.py +10 -13
- setiastro/saspro/wimi.py +27 -656
- setiastro/saspro/wims.py +13 -3
- setiastro/saspro/xisf.py +101 -11
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/METADATA +2 -1
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/RECORD +115 -82
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/WHEEL +0 -0
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/entry_points.txt +0 -0
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/licenses/LICENSE +0 -0
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/licenses/license.txt +0 -0
setiastro/saspro/plate_solver.py
CHANGED
|
@@ -116,16 +116,15 @@ def _status_popup_update(text: str):
|
|
|
116
116
|
_STATUS_POPUP.update_text(text)
|
|
117
117
|
|
|
118
118
|
def _status_popup_close():
|
|
119
|
-
"""Hide (but do not destroy) the singleton status popup if it exists."""
|
|
120
119
|
global _STATUS_POPUP
|
|
120
|
+
if _STATUS_POPUP is None:
|
|
121
|
+
return
|
|
121
122
|
try:
|
|
122
|
-
|
|
123
|
-
_STATUS_POPUP.hide()
|
|
124
|
-
# keep instance for reuse (fast re-open)
|
|
123
|
+
_STATUS_POPUP.hide()
|
|
125
124
|
except Exception:
|
|
126
|
-
# Completely safe to ignore; worst case the popup was already gone.
|
|
127
125
|
pass
|
|
128
126
|
|
|
127
|
+
|
|
129
128
|
def _sleep_ui(ms: int):
|
|
130
129
|
"""Non-blocking sleep that keeps the UI responsive."""
|
|
131
130
|
loop = QEventLoop()
|
|
@@ -137,54 +136,38 @@ def _with_events():
|
|
|
137
136
|
QApplication.processEvents()
|
|
138
137
|
|
|
139
138
|
def _set_status_ui(parent, text: str):
|
|
140
|
-
"""
|
|
141
|
-
Update dialog/main-window status or batch log; if neither exists (headless),
|
|
142
|
-
show/update a small modeless popup. Always pumps events for responsiveness.
|
|
143
|
-
"""
|
|
144
139
|
try:
|
|
145
140
|
updated_any = False
|
|
146
141
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
target =
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
142
|
+
target = None
|
|
143
|
+
if hasattr(parent, "status") and isinstance(getattr(parent, "status"), QLabel):
|
|
144
|
+
target = parent.status
|
|
145
|
+
if target is None and hasattr(parent, "findChild"):
|
|
146
|
+
target = parent.findChild(QLabel, "status_label")
|
|
147
|
+
if target is not None:
|
|
148
|
+
target.setText(text)
|
|
149
|
+
updated_any = True
|
|
150
|
+
|
|
151
|
+
logw = getattr(parent, "log", None)
|
|
152
|
+
if logw and hasattr(logw, "append"):
|
|
153
|
+
tr_status = QCoreApplication.translate("PlateSolver", "Status:")
|
|
154
|
+
if text and (text.startswith("Status:") or text.startswith(tr_status) or text.startswith("▶") or text.startswith("✔") or text.startswith("❌")):
|
|
155
|
+
logw.append(text)
|
|
158
156
|
updated_any = True
|
|
159
157
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if logw and hasattr(logw, "append"):
|
|
163
|
-
tr_status = QCoreApplication.translate("PlateSolver", "Status:")
|
|
164
|
-
if text and (text.startswith("Status:") or text.startswith(tr_status) or text.startswith("▶") or text.startswith("✔") or text.startswith("❌")):
|
|
165
|
-
logw.append(text)
|
|
166
|
-
updated_any = True
|
|
167
|
-
|
|
168
|
-
# If we couldn't update any inline widget, use the headless popup.
|
|
169
|
-
if not updated_any:
|
|
170
|
-
_status_popup_open(parent, text)
|
|
171
|
-
else:
|
|
172
|
-
# If inline widgets exist and popup is visible, keep it quiet.
|
|
173
|
-
_status_popup_update(text)
|
|
174
|
-
|
|
175
|
-
QApplication.processEvents()
|
|
176
|
-
|
|
177
|
-
if isinstance(parent, QWidget):
|
|
178
|
-
QTimer.singleShot(0, _do)
|
|
158
|
+
if not updated_any:
|
|
159
|
+
_status_popup_open(parent, text)
|
|
179
160
|
else:
|
|
180
|
-
|
|
161
|
+
_status_popup_update(text)
|
|
162
|
+
|
|
163
|
+
QApplication.processEvents()
|
|
181
164
|
except Exception:
|
|
182
|
-
# Last-resort popup if even the above failed
|
|
183
165
|
try:
|
|
184
166
|
_status_popup_open(parent, text)
|
|
185
167
|
except Exception:
|
|
186
168
|
pass
|
|
187
169
|
|
|
170
|
+
|
|
188
171
|
def _wait_process(proc: QProcess, timeout_ms: int, parent=None) -> bool:
|
|
189
172
|
"""
|
|
190
173
|
Incrementally wait for a QProcess while pumping UI events so the dialog stays responsive.
|
|
@@ -234,10 +217,53 @@ def _get_solvefield_exe(settings) -> str:
|
|
|
234
217
|
return cand[0] # may be empty (used to decide web vs. local)
|
|
235
218
|
|
|
236
219
|
def _get_astrometry_api_key(settings) -> str:
|
|
237
|
-
|
|
220
|
+
"""
|
|
221
|
+
Canonical key: 'api/astrometry_key' (matches SettingsDialog).
|
|
222
|
+
Also check older legacy keys for backward compatibility.
|
|
223
|
+
"""
|
|
224
|
+
if settings is None:
|
|
225
|
+
return ""
|
|
226
|
+
|
|
227
|
+
# ✅ canonical
|
|
228
|
+
key = settings.value("api/astrometry_key", "", type=str) or ""
|
|
229
|
+
key = key.strip()
|
|
230
|
+
if key:
|
|
231
|
+
return key
|
|
232
|
+
|
|
233
|
+
# 🔁 legacy fallbacks (if you ever stored them differently)
|
|
234
|
+
for k in (
|
|
235
|
+
"api/astrometry", # old guess
|
|
236
|
+
"astrometry/api_key",
|
|
237
|
+
"astrometry/key",
|
|
238
|
+
"astrometry_key",
|
|
239
|
+
"plate_solver/astrometry_key",
|
|
240
|
+
):
|
|
241
|
+
v = settings.value(k, "", type=str) or ""
|
|
242
|
+
v = v.strip()
|
|
243
|
+
if v:
|
|
244
|
+
# migrate forward so it works next time
|
|
245
|
+
settings.setValue("api/astrometry_key", v)
|
|
246
|
+
try:
|
|
247
|
+
settings.remove(k)
|
|
248
|
+
except Exception:
|
|
249
|
+
pass
|
|
250
|
+
try:
|
|
251
|
+
settings.sync()
|
|
252
|
+
except Exception:
|
|
253
|
+
pass
|
|
254
|
+
return v
|
|
255
|
+
|
|
256
|
+
return ""
|
|
238
257
|
|
|
239
|
-
|
|
240
|
-
|
|
258
|
+
|
|
259
|
+
def _set_astrometry_api_key(settings, key: str) -> None:
|
|
260
|
+
if settings is None:
|
|
261
|
+
return
|
|
262
|
+
settings.setValue("api/astrometry_key", (key or "").strip())
|
|
263
|
+
try:
|
|
264
|
+
settings.sync()
|
|
265
|
+
except Exception:
|
|
266
|
+
pass
|
|
241
267
|
|
|
242
268
|
def _wcs_header_from_astrometry_calib(calib: dict, image_shape: tuple[int, ...]) -> Header:
|
|
243
269
|
"""
|
|
@@ -1774,7 +1800,8 @@ def _debug_dump_meta(label: str, meta: dict):
|
|
|
1774
1800
|
print(f" {k}: {type(v).__name__}")
|
|
1775
1801
|
print("================================\n")
|
|
1776
1802
|
|
|
1777
|
-
|
|
1803
|
+
def tr(s: str) -> str:
|
|
1804
|
+
return QCoreApplication.translate("PlateSolver", s)
|
|
1778
1805
|
|
|
1779
1806
|
def plate_solve_doc_inplace(parent, doc, settings) -> Tuple[bool, Header | str]:
|
|
1780
1807
|
img = getattr(doc, "image", None)
|
|
@@ -1831,8 +1858,9 @@ def plate_solve_doc_inplace(parent, doc, settings) -> Tuple[bool, Header | str]:
|
|
|
1831
1858
|
(hasattr(parent, "findChild") and parent.findChild(QLabel, "status_label") is not None)
|
|
1832
1859
|
)
|
|
1833
1860
|
if headless:
|
|
1834
|
-
_status_popup_open(parent,
|
|
1861
|
+
_status_popup_open(parent, tr("Status: Preparing plate solve…"))
|
|
1835
1862
|
|
|
1863
|
+
ok_solve = False
|
|
1836
1864
|
try:
|
|
1837
1865
|
ok, res = _solve_numpy_with_fallback(parent, settings, img, seed_h)
|
|
1838
1866
|
if not ok:
|
|
@@ -1886,11 +1914,18 @@ def plate_solve_doc_inplace(parent, doc, settings) -> Tuple[bool, Header | str]:
|
|
|
1886
1914
|
if hasattr(parent, "currentDocumentChanged"):
|
|
1887
1915
|
QTimer.singleShot(0, lambda: parent.currentDocumentChanged.emit(doc))
|
|
1888
1916
|
|
|
1889
|
-
_set_status_ui(parent,
|
|
1890
|
-
|
|
1917
|
+
_set_status_ui(parent, tr("Status: Plate solve completed."))
|
|
1918
|
+
|
|
1919
|
+
|
|
1920
|
+
ok_solve = True
|
|
1921
|
+
if headless:
|
|
1922
|
+
QTimer.singleShot(1200, _status_popup_close)
|
|
1923
|
+
else:
|
|
1924
|
+
_status_popup_close()
|
|
1891
1925
|
return True, hdr
|
|
1892
1926
|
finally:
|
|
1893
|
-
|
|
1927
|
+
if not ok_solve:
|
|
1928
|
+
_status_popup_close()
|
|
1894
1929
|
|
|
1895
1930
|
|
|
1896
1931
|
|
setiastro/saspro/psf_viewer.py
CHANGED
|
@@ -13,7 +13,7 @@ from PyQt6.QtWidgets import (
|
|
|
13
13
|
)
|
|
14
14
|
from setiastro.saspro.widgets.themed_buttons import themed_toolbtn
|
|
15
15
|
|
|
16
|
-
from PyQt6.QtCore import
|
|
16
|
+
from PyQt6.QtCore import QThread, pyqtSignal, QObject
|
|
17
17
|
from PyQt6.QtWidgets import QWidget
|
|
18
18
|
|
|
19
19
|
class _ProcessingOverlay(QWidget):
|
|
@@ -108,7 +108,10 @@ class PSFViewer(QDialog):
|
|
|
108
108
|
# Accept either a view (with .document) or a doc directly
|
|
109
109
|
doc = getattr(view_or_doc, "document", None)
|
|
110
110
|
self.doc = doc if doc is not None else view_or_doc
|
|
111
|
-
|
|
111
|
+
try:
|
|
112
|
+
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
113
|
+
except Exception:
|
|
114
|
+
pass # older PyQt6 versions
|
|
112
115
|
# Image + state
|
|
113
116
|
self.image = self._grab_image()
|
|
114
117
|
self.zoom_factor = 1.0
|
|
@@ -124,12 +127,19 @@ class PSFViewer(QDialog):
|
|
|
124
127
|
self.threshold_timer.timeout.connect(self._applyThreshold)
|
|
125
128
|
|
|
126
129
|
# Auto-update when the document changes
|
|
130
|
+
|
|
131
|
+
self._psf_thread = None
|
|
132
|
+
self._psf_worker = None
|
|
133
|
+
self._doc_conn = False
|
|
127
134
|
if hasattr(self.doc, "changed"):
|
|
128
135
|
try:
|
|
129
136
|
self.doc.changed.connect(self._on_doc_changed)
|
|
137
|
+
self._doc_conn = True
|
|
130
138
|
except Exception:
|
|
131
|
-
|
|
139
|
+
self._doc_conn = False
|
|
132
140
|
|
|
141
|
+
# cleanup no matter how the dialog is dismissed (accept/reject/done)
|
|
142
|
+
self.finished.connect(self._cleanup)
|
|
133
143
|
self._build_ui()
|
|
134
144
|
# Defer first compute until after the dialog is shown/layouted
|
|
135
145
|
QTimer.singleShot(0, self._applyThreshold)
|
|
@@ -235,7 +245,7 @@ class PSFViewer(QDialog):
|
|
|
235
245
|
|
|
236
246
|
# Close
|
|
237
247
|
close_btn = QPushButton("Close", self)
|
|
238
|
-
close_btn.clicked.connect(self.
|
|
248
|
+
close_btn.clicked.connect(self.close)
|
|
239
249
|
main_layout.addWidget(close_btn)
|
|
240
250
|
|
|
241
251
|
self.setLayout(main_layout)
|
|
@@ -279,7 +289,6 @@ class PSFViewer(QDialog):
|
|
|
279
289
|
self.hist_label.resize(scaled.size())
|
|
280
290
|
|
|
281
291
|
def _applyThreshold(self):
|
|
282
|
-
# kick off worker
|
|
283
292
|
if self.image is None:
|
|
284
293
|
self.star_list = None
|
|
285
294
|
self.status_label.setText("Status: No image.")
|
|
@@ -288,46 +297,72 @@ class PSFViewer(QDialog):
|
|
|
288
297
|
|
|
289
298
|
self._show_processing("Processing… extracting stars / PSFs")
|
|
290
299
|
|
|
291
|
-
#
|
|
292
|
-
|
|
293
|
-
try:
|
|
294
|
-
self._psf_thread.quit()
|
|
295
|
-
self._psf_thread.wait(50)
|
|
296
|
-
except Exception:
|
|
297
|
-
pass
|
|
300
|
+
# stop any previous run cleanly
|
|
301
|
+
self._stop_psf_worker()
|
|
298
302
|
|
|
299
303
|
self._psf_thread = QThread(self)
|
|
300
304
|
self._psf_worker = _PSFWorker(self.image, self.detection_threshold)
|
|
301
305
|
self._psf_worker.moveToThread(self._psf_thread)
|
|
302
306
|
|
|
303
307
|
self._psf_thread.started.connect(self._psf_worker.run)
|
|
308
|
+
self._psf_worker.finished.connect(self._on_psf_done)
|
|
309
|
+
self._psf_worker.failed.connect(self._on_psf_fail)
|
|
304
310
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
self._hide_processing()
|
|
309
|
-
self.drawHistogram()
|
|
310
|
-
self._psf_thread.quit()
|
|
311
|
-
self._psf_thread.wait(100)
|
|
312
|
-
|
|
313
|
-
def _fail(msg):
|
|
314
|
-
self.star_list = None
|
|
315
|
-
self.status_label.setText(f"Status: {msg}")
|
|
316
|
-
self._hide_processing()
|
|
317
|
-
self.drawHistogram()
|
|
318
|
-
self._psf_thread.quit()
|
|
319
|
-
self._psf_thread.wait(100)
|
|
311
|
+
# ensure thread quits once worker reports anything
|
|
312
|
+
self._psf_worker.finished.connect(lambda *_: self._stop_psf_worker(quit_only=False))
|
|
313
|
+
self._psf_worker.failed.connect(lambda *_: self._stop_psf_worker(quit_only=False))
|
|
320
314
|
|
|
321
|
-
self._psf_worker.finished.connect(_done)
|
|
322
|
-
self._psf_worker.failed.connect(_fail)
|
|
323
315
|
|
|
324
316
|
self._psf_thread.start()
|
|
325
317
|
|
|
318
|
+
def _stop_psf_worker(self, quit_only: bool = False):
|
|
319
|
+
thr = getattr(self, "_psf_thread", None)
|
|
320
|
+
wkr = getattr(self, "_psf_worker", None)
|
|
321
|
+
|
|
322
|
+
if thr is None:
|
|
323
|
+
return
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
thr.quit()
|
|
327
|
+
except Exception:
|
|
328
|
+
pass
|
|
329
|
+
try:
|
|
330
|
+
thr.wait(250)
|
|
331
|
+
except Exception:
|
|
332
|
+
pass
|
|
333
|
+
|
|
334
|
+
if not quit_only:
|
|
335
|
+
try:
|
|
336
|
+
if wkr is not None:
|
|
337
|
+
wkr.deleteLater()
|
|
338
|
+
except Exception:
|
|
339
|
+
pass
|
|
340
|
+
try:
|
|
341
|
+
thr.deleteLater()
|
|
342
|
+
except Exception:
|
|
343
|
+
pass
|
|
344
|
+
self._psf_worker = None
|
|
345
|
+
self._psf_thread = None
|
|
346
|
+
|
|
347
|
+
def _on_psf_done(self, tbl, status: str):
|
|
348
|
+
# tbl is an astropy Table or None
|
|
349
|
+
self.star_list = tbl
|
|
350
|
+
self.status_label.setText(status)
|
|
351
|
+
self._hide_processing()
|
|
352
|
+
self.drawHistogram()
|
|
353
|
+
|
|
354
|
+
def _on_psf_fail(self, msg: str):
|
|
355
|
+
self.star_list = None
|
|
356
|
+
self.status_label.setText(f"Status: {msg}")
|
|
357
|
+
self._hide_processing()
|
|
358
|
+
self.drawHistogram()
|
|
359
|
+
|
|
326
360
|
|
|
327
361
|
def updateImage(self, new_image):
|
|
328
362
|
self.image = np.asarray(new_image) if new_image is not None else None
|
|
329
|
-
self.
|
|
330
|
-
|
|
363
|
+
if self.threshold_timer.isActive():
|
|
364
|
+
self.threshold_timer.stop()
|
|
365
|
+
self.threshold_timer.start()
|
|
331
366
|
|
|
332
367
|
def updateZoom(self, _=None):
|
|
333
368
|
self._apply_hist_zoom()
|
|
@@ -538,12 +573,59 @@ class PSFViewer(QDialog):
|
|
|
538
573
|
it.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
539
574
|
self.stats_table.setItem(ri, ci, it)
|
|
540
575
|
|
|
576
|
+
|
|
577
|
+
def _cleanup(self):
|
|
578
|
+
# stop debounce timer
|
|
579
|
+
try:
|
|
580
|
+
if getattr(self, "threshold_timer", None) is not None:
|
|
581
|
+
self.threshold_timer.stop()
|
|
582
|
+
except Exception:
|
|
583
|
+
pass
|
|
584
|
+
|
|
585
|
+
# disconnect doc listener
|
|
586
|
+
try:
|
|
587
|
+
if self._doc_conn and hasattr(self.doc, "changed"):
|
|
588
|
+
self.doc.changed.disconnect(self._on_doc_changed)
|
|
589
|
+
except Exception:
|
|
590
|
+
pass
|
|
591
|
+
self._doc_conn = False
|
|
592
|
+
|
|
593
|
+
# stop worker/thread
|
|
594
|
+
try:
|
|
595
|
+
thr = getattr(self, "_psf_thread", None)
|
|
596
|
+
wkr = getattr(self, "_psf_worker", None)
|
|
597
|
+
|
|
598
|
+
if wkr is not None:
|
|
599
|
+
try:
|
|
600
|
+
wkr.deleteLater()
|
|
601
|
+
except Exception:
|
|
602
|
+
pass
|
|
603
|
+
|
|
604
|
+
if thr is not None:
|
|
605
|
+
try:
|
|
606
|
+
thr.requestInterruption()
|
|
607
|
+
except Exception:
|
|
608
|
+
pass
|
|
609
|
+
try:
|
|
610
|
+
thr.quit()
|
|
611
|
+
except Exception:
|
|
612
|
+
pass
|
|
613
|
+
try:
|
|
614
|
+
thr.wait(250)
|
|
615
|
+
except Exception:
|
|
616
|
+
pass
|
|
617
|
+
try:
|
|
618
|
+
thr.deleteLater()
|
|
619
|
+
except Exception:
|
|
620
|
+
pass
|
|
621
|
+
except Exception:
|
|
622
|
+
pass
|
|
623
|
+
|
|
624
|
+
self._psf_worker = None
|
|
625
|
+
self._psf_thread = None
|
|
626
|
+
|
|
541
627
|
# ---------- lifecycle ----------
|
|
542
628
|
def closeEvent(self, e):
|
|
543
|
-
|
|
544
|
-
if hasattr(self.doc, "changed"):
|
|
545
|
-
try:
|
|
546
|
-
self.doc.changed.disconnect(self._on_doc_changed)
|
|
547
|
-
except Exception:
|
|
548
|
-
pass
|
|
629
|
+
self._cleanup()
|
|
549
630
|
super().closeEvent(e)
|
|
631
|
+
|
setiastro/saspro/resources.py
CHANGED
|
@@ -245,6 +245,39 @@ class Icons:
|
|
|
245
245
|
LIVE_STACKING = property(lambda self: _resource_path('livestacking.png'))
|
|
246
246
|
IMAGE_COMBINE = property(lambda self: _resource_path('imagecombine.png'))
|
|
247
247
|
|
|
248
|
+
# Moon phase (WIMS)
|
|
249
|
+
MOON_NEW = property(lambda self: _resource_path('new_moon.png'))
|
|
250
|
+
MOON_WAXING_CRES_1 = property(lambda self: _resource_path('waxing_crescent_1.png'))
|
|
251
|
+
MOON_WAXING_CRES_2 = property(lambda self: _resource_path('waxing_crescent_2.png'))
|
|
252
|
+
MOON_WAXING_CRES_3 = property(lambda self: _resource_path('waxing_crescent_3.png'))
|
|
253
|
+
MOON_WAXING_CRES_4 = property(lambda self: _resource_path('waxing_crescent_4.png'))
|
|
254
|
+
MOON_WAXING_CRES_5 = property(lambda self: _resource_path('waxing_crescent_5.png'))
|
|
255
|
+
|
|
256
|
+
MOON_FIRST_QUARTER = property(lambda self: _resource_path('first_quarter.png'))
|
|
257
|
+
|
|
258
|
+
MOON_WAXING_GIB_1 = property(lambda self: _resource_path('waxing_gibbous_1.png'))
|
|
259
|
+
MOON_WAXING_GIB_2 = property(lambda self: _resource_path('waxing_gibbous_2.png'))
|
|
260
|
+
MOON_WAXING_GIB_3 = property(lambda self: _resource_path('waxing_gibbous_3.png'))
|
|
261
|
+
MOON_WAXING_GIB_4 = property(lambda self: _resource_path('waxing_gibbous_4.png'))
|
|
262
|
+
MOON_WAXING_GIB_5 = property(lambda self: _resource_path('waxing_gibbous_5.png'))
|
|
263
|
+
|
|
264
|
+
MOON_FULL = property(lambda self: _resource_path('full_moon.png'))
|
|
265
|
+
|
|
266
|
+
MOON_WANING_GIB_1 = property(lambda self: _resource_path('waning_gibbous_1.png'))
|
|
267
|
+
MOON_WANING_GIB_2 = property(lambda self: _resource_path('waning_gibbous_2.png'))
|
|
268
|
+
MOON_WANING_GIB_3 = property(lambda self: _resource_path('waning_gibbous_3.png'))
|
|
269
|
+
MOON_WANING_GIB_4 = property(lambda self: _resource_path('waning_gibbous_4.png'))
|
|
270
|
+
MOON_WANING_GIB_5 = property(lambda self: _resource_path('waning_gibbous_5.png'))
|
|
271
|
+
|
|
272
|
+
MOON_LAST_QUARTER = property(lambda self: _resource_path('last_quarter.png'))
|
|
273
|
+
|
|
274
|
+
MOON_WANING_CRES_1 = property(lambda self: _resource_path('waning_crescent_1.png'))
|
|
275
|
+
MOON_WANING_CRES_2 = property(lambda self: _resource_path('waning_crescent_2.png'))
|
|
276
|
+
MOON_WANING_CRES_3 = property(lambda self: _resource_path('waning_crescent_3.png'))
|
|
277
|
+
MOON_WANING_CRES_4 = property(lambda self: _resource_path('waning_crescent_4.png'))
|
|
278
|
+
MOON_WANING_CRES_5 = property(lambda self: _resource_path('waning_crescent_5.png'))
|
|
279
|
+
|
|
280
|
+
|
|
248
281
|
# Special features
|
|
249
282
|
SUPERNOVA = property(lambda self: _resource_path('supernova.png'))
|
|
250
283
|
PEDESTAL = property(lambda self: _resource_path('pedestal.png'))
|
|
@@ -284,6 +317,7 @@ class Icons:
|
|
|
284
317
|
CSV = property(lambda self: _resource_path('cvs.png'))
|
|
285
318
|
PPP = property(lambda self: _resource_path('ppp.png'))
|
|
286
319
|
SCRIPT = property(lambda self: _resource_path('script.png'))
|
|
320
|
+
ACV = property(lambda self: _resource_path('acv_icon.png'))
|
|
287
321
|
|
|
288
322
|
# Blink & comparison
|
|
289
323
|
BLINK = property(lambda self: _resource_path('blink.png'))
|
|
@@ -393,6 +427,39 @@ def _init_legacy_paths():
|
|
|
393
427
|
'slot7_path': get_icon_path('slot7.png'),
|
|
394
428
|
'slot8_path': get_icon_path('slot8.png'),
|
|
395
429
|
'slot9_path': get_icon_path('slot9.png'),
|
|
430
|
+
'acv_icon_path': get_icon_path('acv_icon.png'),
|
|
431
|
+
|
|
432
|
+
'moon_new_path': get_icon_path('new_moon.png'),
|
|
433
|
+
'moon_waxing_crescent_1_path': get_icon_path('waxing_crescent_1.png'),
|
|
434
|
+
'moon_waxing_crescent_2_path': get_icon_path('waxing_crescent_2.png'),
|
|
435
|
+
'moon_waxing_crescent_3_path': get_icon_path('waxing_crescent_3.png'),
|
|
436
|
+
'moon_waxing_crescent_4_path': get_icon_path('waxing_crescent_4.png'),
|
|
437
|
+
'moon_waxing_crescent_5_path': get_icon_path('waxing_crescent_5.png'),
|
|
438
|
+
|
|
439
|
+
'moon_first_quarter_path': get_icon_path('first_quarter.png'),
|
|
440
|
+
|
|
441
|
+
'moon_waxing_gibbous_1_path': get_icon_path('waxing_gibbous_1.png'),
|
|
442
|
+
'moon_waxing_gibbous_2_path': get_icon_path('waxing_gibbous_2.png'),
|
|
443
|
+
'moon_waxing_gibbous_3_path': get_icon_path('waxing_gibbous_3.png'),
|
|
444
|
+
'moon_waxing_gibbous_4_path': get_icon_path('waxing_gibbous_4.png'),
|
|
445
|
+
'moon_waxing_gibbous_5_path': get_icon_path('waxing_gibbous_5.png'),
|
|
446
|
+
|
|
447
|
+
'moon_full_path': get_icon_path('full_moon.png'),
|
|
448
|
+
|
|
449
|
+
'moon_waning_gibbous_1_path': get_icon_path('waning_gibbous_1.png'),
|
|
450
|
+
'moon_waning_gibbous_2_path': get_icon_path('waning_gibbous_2.png'),
|
|
451
|
+
'moon_waning_gibbous_3_path': get_icon_path('waning_gibbous_3.png'),
|
|
452
|
+
'moon_waning_gibbous_4_path': get_icon_path('waning_gibbous_4.png'),
|
|
453
|
+
'moon_waning_gibbous_5_path': get_icon_path('waning_gibbous_5.png'),
|
|
454
|
+
|
|
455
|
+
'moon_last_quarter_path': get_icon_path('last_quarter.png'),
|
|
456
|
+
|
|
457
|
+
'moon_waning_crescent_1_path': get_icon_path('waning_crescent_1.png'),
|
|
458
|
+
'moon_waning_crescent_2_path': get_icon_path('waning_crescent_2.png'),
|
|
459
|
+
'moon_waning_crescent_3_path': get_icon_path('waning_crescent_3.png'),
|
|
460
|
+
'moon_waning_crescent_4_path': get_icon_path('waning_crescent_4.png'),
|
|
461
|
+
'moon_waning_crescent_5_path': get_icon_path('waning_crescent_5.png'),
|
|
462
|
+
|
|
396
463
|
'rgbcombo_path': get_icon_path('rgbcombo.png'),
|
|
397
464
|
'rgbextract_path': get_icon_path('rgbextract.png'),
|
|
398
465
|
'copyslot_path': get_icon_path('copyslot.png'),
|
setiastro/saspro/rgbalign.py
CHANGED
|
@@ -350,6 +350,10 @@ class RGBAlignDialog(QDialog):
|
|
|
350
350
|
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
351
351
|
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
352
352
|
self.setModal(False)
|
|
353
|
+
try:
|
|
354
|
+
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
355
|
+
except Exception:
|
|
356
|
+
pass # older PyQt6 versions
|
|
353
357
|
self.parent = parent
|
|
354
358
|
# document could be a view; try to unwrap
|
|
355
359
|
self.doc_view = document
|
|
@@ -458,7 +458,10 @@ class SelectiveColorCorrection(QDialog):
|
|
|
458
458
|
self.setWindowTitle(self.tr("Selective Color Correction"))
|
|
459
459
|
if window_icon:
|
|
460
460
|
self.setWindowIcon(window_icon)
|
|
461
|
-
|
|
461
|
+
try:
|
|
462
|
+
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
463
|
+
except Exception:
|
|
464
|
+
pass # older PyQt6 versions
|
|
462
465
|
self.docman = doc_manager
|
|
463
466
|
self.document = document
|
|
464
467
|
if self.document is None or getattr(self.document, "image", None) is None:
|