setiastrosuitepro 1.6.4__py3-none-any.whl → 1.6.10__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/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 +19 -0
- 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 +35 -7
- 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 +4 -1
- 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 +67 -4
- 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 +393 -204
- setiastro/saspro/gui/mixins/menu_mixin.py +1 -0
- setiastro/saspro/gui/mixins/theme_mixin.py +160 -14
- setiastro/saspro/gui/mixins/toolbar_mixin.py +356 -12
- 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/stretch.py +531 -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 +43 -0
- 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 -2
- setiastro/saspro/ops/commands.py +18 -18
- setiastro/saspro/ops/script_editor.py +5 -2
- setiastro/saspro/ops/scripts.py +3 -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 +60 -2
- setiastro/saspro/shortcuts.py +142 -23
- setiastro/saspro/signature_insert.py +692 -33
- setiastro/saspro/stacking_suite.py +1017 -400
- setiastro/saspro/star_alignment.py +4 -1
- setiastro/saspro/star_spikes.py +4 -0
- setiastro/saspro/star_stretch.py +38 -3
- setiastro/saspro/stat_stretch.py +702 -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 +60 -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.10.dist-info}/METADATA +2 -1
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.10.dist-info}/RECORD +112 -80
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.10.dist-info}/WHEEL +0 -0
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.10.dist-info}/entry_points.txt +0 -0
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.10.dist-info}/licenses/LICENSE +0 -0
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.10.dist-info}/licenses/license.txt +0 -0
|
@@ -10,11 +10,13 @@ import json
|
|
|
10
10
|
import sys
|
|
11
11
|
import webbrowser
|
|
12
12
|
from typing import TYPE_CHECKING
|
|
13
|
-
|
|
13
|
+
import re
|
|
14
14
|
from PyQt6.QtCore import QUrl
|
|
15
15
|
from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply
|
|
16
16
|
from PyQt6.QtWidgets import QMessageBox, QApplication
|
|
17
17
|
|
|
18
|
+
from PyQt6.QtNetwork import QSslSocket
|
|
19
|
+
|
|
18
20
|
if TYPE_CHECKING:
|
|
19
21
|
pass
|
|
20
22
|
|
|
@@ -44,27 +46,38 @@ class UpdateMixin:
|
|
|
44
46
|
return str(v or "0.0.0")
|
|
45
47
|
|
|
46
48
|
def _ensure_network_manager(self):
|
|
47
|
-
"""Ensure the network access manager exists."""
|
|
48
49
|
from PyQt6.QtNetwork import QNetworkAccessManager
|
|
49
|
-
|
|
50
|
-
if not hasattr(self, "_nam") or self._nam is None:
|
|
50
|
+
if getattr(self, "_nam", None) is None:
|
|
51
51
|
self._nam = QNetworkAccessManager(self)
|
|
52
52
|
self._nam.finished.connect(self._on_update_reply)
|
|
53
53
|
|
|
54
54
|
def _kick_update_check(self, *, interactive: bool):
|
|
55
55
|
"""
|
|
56
56
|
Start an update check request.
|
|
57
|
-
|
|
58
|
-
Args:
|
|
59
|
-
interactive: If True, show UI feedback for the check
|
|
60
57
|
"""
|
|
61
58
|
self._ensure_network_manager()
|
|
62
59
|
url_str = self.settings.value("updates/url", self._updates_url, type=str) or self._updates_url
|
|
60
|
+
|
|
61
|
+
if url_str.lower().startswith("https://"):
|
|
62
|
+
try:
|
|
63
|
+
if not QSslSocket.supportsSsl():
|
|
64
|
+
if self.statusBar():
|
|
65
|
+
self.statusBar().showMessage(self.tr("Update check unavailable (TLS missing)."), 8000)
|
|
66
|
+
if interactive:
|
|
67
|
+
QMessageBox.information(
|
|
68
|
+
self, self.tr("Update Check"),
|
|
69
|
+
self.tr("Update check is unavailable because TLS is not available on this system.")
|
|
70
|
+
)
|
|
71
|
+
else:
|
|
72
|
+
print("[updates] TLS unavailable in Qt; skipping update check.")
|
|
73
|
+
return
|
|
74
|
+
except Exception as e:
|
|
75
|
+
print(f"[updates] TLS probe failed ({e}); skipping update check.")
|
|
76
|
+
return
|
|
77
|
+
|
|
63
78
|
req = QNetworkRequest(QUrl(url_str))
|
|
64
|
-
req.setRawHeader(
|
|
65
|
-
|
|
66
|
-
f"SASPro/{self._current_version_str}".encode("utf-8")
|
|
67
|
-
)
|
|
79
|
+
req.setRawHeader(b"User-Agent", f"SASPro/{self._current_version_str}".encode("utf-8"))
|
|
80
|
+
|
|
68
81
|
reply = self._nam.get(req)
|
|
69
82
|
reply.setProperty("interactive", interactive)
|
|
70
83
|
|
|
@@ -97,20 +110,22 @@ class UpdateMixin:
|
|
|
97
110
|
def _on_update_reply(self, reply: QNetworkReply):
|
|
98
111
|
"""Handle network reply from update check or download."""
|
|
99
112
|
interactive = bool(reply.property("interactive"))
|
|
100
|
-
|
|
113
|
+
|
|
101
114
|
# Was this the second request (the actual installer download)?
|
|
102
115
|
if bool(reply.property("is_update_download")):
|
|
103
116
|
self._on_windows_update_download_finished(reply)
|
|
104
117
|
return
|
|
105
|
-
|
|
118
|
+
|
|
106
119
|
try:
|
|
107
120
|
if reply.error() != QNetworkReply.NetworkError.NoError:
|
|
108
121
|
err = reply.errorString()
|
|
109
122
|
if self.statusBar():
|
|
110
|
-
self.statusBar().showMessage("Update check failed.", 5000)
|
|
123
|
+
self.statusBar().showMessage(self.tr("Update check failed."), 5000)
|
|
111
124
|
if interactive:
|
|
112
|
-
QMessageBox.warning(
|
|
113
|
-
|
|
125
|
+
QMessageBox.warning(
|
|
126
|
+
self, self.tr("Update Check Failed"),
|
|
127
|
+
self.tr("Unable to check for updates.\n\n{0}").format(err)
|
|
128
|
+
)
|
|
114
129
|
else:
|
|
115
130
|
print(f"[updates] check failed: {err}")
|
|
116
131
|
return
|
|
@@ -120,12 +135,14 @@ class UpdateMixin:
|
|
|
120
135
|
data = json.loads(raw.decode("utf-8"))
|
|
121
136
|
except Exception as je:
|
|
122
137
|
if self.statusBar():
|
|
123
|
-
self.statusBar().showMessage("Update check failed (bad JSON).", 5000)
|
|
138
|
+
self.statusBar().showMessage(self.tr("Update check failed (bad JSON)."), 5000)
|
|
124
139
|
if interactive:
|
|
125
|
-
QMessageBox.warning(
|
|
126
|
-
|
|
140
|
+
QMessageBox.warning(
|
|
141
|
+
self, self.tr("Update Check Failed"),
|
|
142
|
+
self.tr("Update JSON is invalid.\n\n{0}").format(str(je))
|
|
143
|
+
)
|
|
127
144
|
else:
|
|
128
|
-
print(f"[updates] bad JSON: {je}")
|
|
145
|
+
print(f"[updates] bad JSON: {je!r}")
|
|
129
146
|
return
|
|
130
147
|
|
|
131
148
|
latest_str = str(data.get("version", "")).strip()
|
|
@@ -134,25 +151,53 @@ class UpdateMixin:
|
|
|
134
151
|
|
|
135
152
|
if not latest_str:
|
|
136
153
|
if self.statusBar():
|
|
137
|
-
self.statusBar().showMessage("Update check failed (no
|
|
154
|
+
self.statusBar().showMessage(self.tr("Update check failed (no version)."), 5000)
|
|
138
155
|
if interactive:
|
|
139
|
-
QMessageBox.warning(
|
|
140
|
-
|
|
156
|
+
QMessageBox.warning(
|
|
157
|
+
self, self.tr("Update Check Failed"),
|
|
158
|
+
self.tr("Update JSON missing the 'version' field.")
|
|
159
|
+
)
|
|
141
160
|
else:
|
|
142
161
|
print("[updates] JSON missing 'version'")
|
|
143
162
|
return
|
|
144
163
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
# ---- PEP 440 version compare ----
|
|
165
|
+
try:
|
|
166
|
+
from packaging.version import Version
|
|
167
|
+
cur_v = Version(str(self._current_version_str).strip())
|
|
168
|
+
latest_v = Version(latest_str)
|
|
169
|
+
except Exception as e:
|
|
170
|
+
if self.statusBar():
|
|
171
|
+
self.statusBar().showMessage(self.tr("Update check failed (version parse)."), 5000)
|
|
172
|
+
if interactive:
|
|
173
|
+
QMessageBox.warning(
|
|
174
|
+
self, self.tr("Update Check Failed"),
|
|
175
|
+
self.tr("Could not compare versions.\n\nCurrent: {0}\nLatest: {1}\n\n{2}")
|
|
176
|
+
.format(self._current_version_str, latest_str, str(e))
|
|
177
|
+
)
|
|
178
|
+
else:
|
|
179
|
+
print(f"[updates] version parse failed: cur={self._current_version_str!r} latest={latest_str!r} err={e!r}")
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
available = latest_v > cur_v
|
|
148
183
|
|
|
149
184
|
if available:
|
|
150
185
|
if self.statusBar():
|
|
151
186
|
self.statusBar().showMessage(self.tr("Update available: {0}").format(latest_str), 5000)
|
|
187
|
+
|
|
152
188
|
msg_box = QMessageBox(self)
|
|
153
189
|
msg_box.setIcon(QMessageBox.Icon.Information)
|
|
154
190
|
msg_box.setWindowTitle(self.tr("Update Available"))
|
|
155
|
-
|
|
191
|
+
installed_norm = str(cur_v)
|
|
192
|
+
reported_norm = str(latest_v)
|
|
193
|
+
|
|
194
|
+
msg_box.setText(
|
|
195
|
+
self.tr(
|
|
196
|
+
"An update is available!\n\n"
|
|
197
|
+
"Installed version: {0}\n"
|
|
198
|
+
"Available version: {1}"
|
|
199
|
+
).format(installed_norm, reported_norm)
|
|
200
|
+
)
|
|
156
201
|
if notes:
|
|
157
202
|
msg_box.setInformativeText(self.tr("Release Notes:\n{0}").format(notes))
|
|
158
203
|
msg_box.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
|
@@ -164,27 +209,48 @@ class UpdateMixin:
|
|
|
164
209
|
|
|
165
210
|
if msg_box.exec() == QMessageBox.StandardButton.Yes:
|
|
166
211
|
plat = sys.platform
|
|
167
|
-
|
|
212
|
+
key = (
|
|
168
213
|
"Windows" if plat.startswith("win") else
|
|
169
|
-
"macOS"
|
|
170
|
-
"Linux"
|
|
214
|
+
"macOS" if plat.startswith("darwin") else
|
|
215
|
+
"Linux" if plat.startswith("linux") else
|
|
216
|
+
""
|
|
171
217
|
)
|
|
218
|
+
link = downloads.get(key, "")
|
|
172
219
|
if not link:
|
|
173
|
-
QMessageBox.warning(self, self.tr("Download"),
|
|
220
|
+
QMessageBox.warning(self, self.tr("Download"),
|
|
221
|
+
self.tr("No download link available for this platform."))
|
|
174
222
|
return
|
|
175
223
|
|
|
176
224
|
if plat.startswith("win"):
|
|
177
|
-
# Use in-app updater for Windows
|
|
178
225
|
self._start_windows_update_download(link)
|
|
179
226
|
else:
|
|
180
|
-
# Open browser for other platforms
|
|
181
227
|
webbrowser.open(link)
|
|
182
228
|
else:
|
|
183
229
|
if self.statusBar():
|
|
184
|
-
self.statusBar().showMessage("You're up to date.", 3000)
|
|
230
|
+
self.statusBar().showMessage(self.tr("You're up to date."), 3000)
|
|
231
|
+
|
|
185
232
|
if interactive:
|
|
186
|
-
|
|
187
|
-
|
|
233
|
+
# Use the same parsed versions you already computed
|
|
234
|
+
installed_str = str(self._current_version_str).strip()
|
|
235
|
+
reported_str = str(latest_str).strip()
|
|
236
|
+
|
|
237
|
+
# If you have cur_v/latest_v (packaging.Version), use their string forms too
|
|
238
|
+
try:
|
|
239
|
+
installed_norm = str(cur_v) # normalized PEP440 (e.g. 1.6.6.post3)
|
|
240
|
+
reported_norm = str(latest_v)
|
|
241
|
+
except Exception:
|
|
242
|
+
installed_norm = installed_str
|
|
243
|
+
reported_norm = reported_str
|
|
244
|
+
|
|
245
|
+
QMessageBox.information(
|
|
246
|
+
self,
|
|
247
|
+
self.tr("Up to Date"),
|
|
248
|
+
self.tr(
|
|
249
|
+
"You're already running the latest version.\n\n"
|
|
250
|
+
"Installed version: {0}\n"
|
|
251
|
+
"Update source reports: {1}"
|
|
252
|
+
).format(installed_norm, reported_norm)
|
|
253
|
+
)
|
|
188
254
|
finally:
|
|
189
255
|
reply.deleteLater()
|
|
190
256
|
|
|
@@ -307,3 +373,39 @@ class UpdateMixin:
|
|
|
307
373
|
|
|
308
374
|
# Close app so the installer can overwrite files
|
|
309
375
|
QApplication.instance().quit()
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _normalize_version_str(self, v: str) -> str:
|
|
379
|
+
v = (v or "").strip()
|
|
380
|
+
# common cases: "v1.2.3", "Version 1.2.3", "1.2.3 (build xyz)"
|
|
381
|
+
v = re.sub(r'^[^\d]*', '', v) # strip leading non-digits
|
|
382
|
+
v = re.split(r'[\s\(\[]', v, 1)[0].strip() # stop at whitespace/( or [
|
|
383
|
+
return v
|
|
384
|
+
|
|
385
|
+
def _parse_version(self, v: str):
|
|
386
|
+
v = self._normalize_version_str(v)
|
|
387
|
+
if not v:
|
|
388
|
+
return None
|
|
389
|
+
# Prefer packaging if present
|
|
390
|
+
try:
|
|
391
|
+
from packaging.version import Version
|
|
392
|
+
return Version(v)
|
|
393
|
+
except Exception:
|
|
394
|
+
# Fallback: compare numeric dot parts only
|
|
395
|
+
parts = re.findall(r'\d+', v)
|
|
396
|
+
if not parts:
|
|
397
|
+
return None
|
|
398
|
+
# normalize length to 3+ (so 1.2 == 1.2.0)
|
|
399
|
+
nums = [int(x) for x in parts[:6]]
|
|
400
|
+
while len(nums) < 3:
|
|
401
|
+
nums.append(0)
|
|
402
|
+
return tuple(nums)
|
|
403
|
+
|
|
404
|
+
def _is_update_available(self, latest_str: str) -> bool:
|
|
405
|
+
cur = self._parse_version(self._current_version_str)
|
|
406
|
+
latest = self._parse_version(latest_str)
|
|
407
|
+
if cur is None or latest is None:
|
|
408
|
+
# If we cannot compare, do NOT claim "up to date".
|
|
409
|
+
# Treat as "unknown" and show a failure message in interactive mode.
|
|
410
|
+
return False
|
|
411
|
+
return latest > cur
|
|
@@ -193,6 +193,7 @@ class ViewMixin:
|
|
|
193
193
|
if hasattr(view, "set_scale") and callable(view.set_scale):
|
|
194
194
|
try:
|
|
195
195
|
view.set_scale(float(scale))
|
|
196
|
+
self._ensure_smooth_resample(view)
|
|
196
197
|
self._center_view(view)
|
|
197
198
|
return
|
|
198
199
|
except Exception:
|
|
@@ -209,6 +210,47 @@ class ViewMixin:
|
|
|
209
210
|
except Exception:
|
|
210
211
|
pass
|
|
211
212
|
|
|
213
|
+
def _ensure_smooth_resample(self, view):
|
|
214
|
+
"""
|
|
215
|
+
Make sure the view is using smooth interpolation for the current scale.
|
|
216
|
+
Different view widgets in SASpro may implement this differently, so we
|
|
217
|
+
try a few known hooks safely.
|
|
218
|
+
"""
|
|
219
|
+
# 1) Best case: explicit API
|
|
220
|
+
for name in ("set_smooth_scaling", "set_interpolation", "set_smooth", "enable_smooth_scaling"):
|
|
221
|
+
fn = getattr(view, name, None)
|
|
222
|
+
if callable(fn):
|
|
223
|
+
try:
|
|
224
|
+
fn(True)
|
|
225
|
+
return
|
|
226
|
+
except Exception:
|
|
227
|
+
pass
|
|
228
|
+
|
|
229
|
+
# 2) Some views store a mode flag
|
|
230
|
+
for attr in ("smooth_scaling", "_smooth_scaling", "_use_smooth_scaling", "use_smooth_scaling"):
|
|
231
|
+
if hasattr(view, attr):
|
|
232
|
+
try:
|
|
233
|
+
setattr(view, attr, True)
|
|
234
|
+
# kick a repaint/update if available
|
|
235
|
+
try:
|
|
236
|
+
view.update()
|
|
237
|
+
except Exception:
|
|
238
|
+
pass
|
|
239
|
+
return
|
|
240
|
+
except Exception:
|
|
241
|
+
pass
|
|
242
|
+
|
|
243
|
+
# 3) QLabel pixmap scaling: if you have a custom "rebuild pixmap" method, call it
|
|
244
|
+
for name in ("_rebuild_pixmap", "_update_pixmap", "_render_scaled", "rebuild_pixmap"):
|
|
245
|
+
fn = getattr(view, name, None)
|
|
246
|
+
if callable(fn):
|
|
247
|
+
try:
|
|
248
|
+
fn()
|
|
249
|
+
return
|
|
250
|
+
except Exception:
|
|
251
|
+
pass
|
|
252
|
+
|
|
253
|
+
|
|
212
254
|
def _infer_image_size(self, view):
|
|
213
255
|
"""Return (img_w, img_h) in device-independent pixels (ints), best-effort."""
|
|
214
256
|
# Preferred: from the label's pixmap
|
setiastro/saspro/halobgon.py
CHANGED
|
@@ -252,6 +252,10 @@ class HaloBGonDialogPro(QDialog):
|
|
|
252
252
|
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
253
253
|
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
254
254
|
self.setModal(False)
|
|
255
|
+
try:
|
|
256
|
+
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
257
|
+
except Exception:
|
|
258
|
+
pass # older PyQt6 versions
|
|
255
259
|
if icon:
|
|
256
260
|
try: self.setWindowIcon(icon)
|
|
257
261
|
except Exception as e:
|
setiastro/saspro/histogram.py
CHANGED
|
@@ -33,13 +33,17 @@ class HistogramDialog(QDialog):
|
|
|
33
33
|
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
34
34
|
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
35
35
|
self.setModal(False)
|
|
36
|
+
try:
|
|
37
|
+
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
38
|
+
except Exception:
|
|
39
|
+
pass # older PyQt6 versions
|
|
36
40
|
self.doc = document
|
|
37
41
|
self.image = _to_float_preserve(document.image)
|
|
38
42
|
|
|
39
43
|
self.zoom_factor = 1.0 # 1.0 = 100%
|
|
40
44
|
self.log_scale = False # log X
|
|
41
45
|
self.log_y = False # log Y
|
|
42
|
-
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
46
|
+
#self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
43
47
|
self._eps_log = 1e-6 # first log bin edge (for labels)
|
|
44
48
|
|
|
45
49
|
# for mapping clicks → normalized x
|
|
@@ -94,6 +94,10 @@ class ImageCombineDialog(QDialog):
|
|
|
94
94
|
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
95
95
|
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
96
96
|
self.setModal(False)
|
|
97
|
+
try:
|
|
98
|
+
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
99
|
+
except Exception:
|
|
100
|
+
pass # older PyQt6 versions
|
|
97
101
|
self.mw = main_window
|
|
98
102
|
self.dm = getattr(main_window, "doc_manager", None) or getattr(main_window, "dm", None)
|
|
99
103
|
self.zoom = 1.0
|
|
@@ -1317,6 +1317,10 @@ class ImagePeekerDialogPro(QDialog):
|
|
|
1317
1317
|
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
1318
1318
|
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
1319
1319
|
self.setModal(False)
|
|
1320
|
+
try:
|
|
1321
|
+
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
1322
|
+
except Exception:
|
|
1323
|
+
pass # older PyQt6 versions
|
|
1320
1324
|
self.document = self._coerce_doc(document) # <- ensure we hold a real doc
|
|
1321
1325
|
self.settings = settings
|
|
1322
1326
|
# status / progress line
|