setiastrosuitepro 1.7.5.post1__py3-none-any.whl → 1.8.0__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.
- setiastro/saspro/_generated/build_info.py +2 -2
- setiastro/saspro/accel_installer.py +21 -8
- setiastro/saspro/accel_workers.py +11 -12
- setiastro/saspro/comet_stacking.py +113 -85
- setiastro/saspro/cosmicclarity.py +604 -826
- setiastro/saspro/cosmicclarity_engines/benchmark_engine.py +715 -0
- setiastro/saspro/cosmicclarity_engines/darkstar_engine.py +576 -0
- setiastro/saspro/cosmicclarity_engines/denoise_engine.py +567 -0
- setiastro/saspro/cosmicclarity_engines/satellite_engine.py +620 -0
- setiastro/saspro/cosmicclarity_engines/sharpen_engine.py +587 -0
- setiastro/saspro/cosmicclarity_engines/superres_engine.py +412 -0
- setiastro/saspro/gui/main_window.py +14 -0
- setiastro/saspro/gui/mixins/menu_mixin.py +2 -0
- setiastro/saspro/model_manager.py +306 -0
- setiastro/saspro/model_workers.py +65 -0
- setiastro/saspro/ops/benchmark.py +320 -0
- setiastro/saspro/ops/settings.py +308 -9
- setiastro/saspro/remove_stars.py +424 -442
- setiastro/saspro/resources.py +73 -10
- setiastro/saspro/runtime_torch.py +107 -22
- setiastro/saspro/signature_insert.py +14 -3
- {setiastrosuitepro-1.7.5.post1.dist-info → setiastrosuitepro-1.8.0.dist-info}/METADATA +2 -1
- {setiastrosuitepro-1.7.5.post1.dist-info → setiastrosuitepro-1.8.0.dist-info}/RECORD +27 -18
- {setiastrosuitepro-1.7.5.post1.dist-info → setiastrosuitepro-1.8.0.dist-info}/WHEEL +0 -0
- {setiastrosuitepro-1.7.5.post1.dist-info → setiastrosuitepro-1.8.0.dist-info}/entry_points.txt +0 -0
- {setiastrosuitepro-1.7.5.post1.dist-info → setiastrosuitepro-1.8.0.dist-info}/licenses/LICENSE +0 -0
- {setiastrosuitepro-1.7.5.post1.dist-info → setiastrosuitepro-1.8.0.dist-info}/licenses/license.txt +0 -0
setiastro/saspro/ops/settings.py
CHANGED
|
@@ -4,10 +4,14 @@ from PyQt6.QtWidgets import (
|
|
|
4
4
|
QHBoxLayout, QVBoxLayout, QWidget, QCheckBox, QComboBox, QSpinBox, QDoubleSpinBox, QLabel, QColorDialog, QFontDialog, QSlider)
|
|
5
5
|
from PyQt6.QtCore import QSettings, Qt
|
|
6
6
|
import pytz # for timezone list
|
|
7
|
-
|
|
7
|
+
from setiastro.saspro.accel_installer import current_backend
|
|
8
|
+
import sys, platform
|
|
9
|
+
from PyQt6.QtWidgets import QToolButton, QProgressDialog
|
|
10
|
+
from PyQt6.QtCore import QThread
|
|
8
11
|
# i18n support
|
|
9
12
|
from setiastro.saspro.i18n import get_available_languages, get_saved_language, save_language
|
|
10
|
-
|
|
13
|
+
import importlib.util
|
|
14
|
+
import importlib.metadata
|
|
11
15
|
|
|
12
16
|
class SettingsDialog(QDialog):
|
|
13
17
|
"""
|
|
@@ -24,7 +28,7 @@ class SettingsDialog(QDialog):
|
|
|
24
28
|
|
|
25
29
|
# ---- Existing fields (paths, checkboxes, etc.) ----
|
|
26
30
|
self.le_graxpert = QLineEdit()
|
|
27
|
-
|
|
31
|
+
|
|
28
32
|
self.le_starnet = QLineEdit()
|
|
29
33
|
self.le_astap = QLineEdit()
|
|
30
34
|
|
|
@@ -75,12 +79,12 @@ class SettingsDialog(QDialog):
|
|
|
75
79
|
self._initial_language = "en" # placeholder, set in refresh_ui
|
|
76
80
|
|
|
77
81
|
btn_grax = QPushButton(self.tr("Browse…")); btn_grax.clicked.connect(lambda: self._browse_into(self.le_graxpert))
|
|
78
|
-
|
|
82
|
+
|
|
79
83
|
btn_star = QPushButton(self.tr("Browse…")); btn_star.clicked.connect(lambda: self._browse_into(self.le_starnet))
|
|
80
84
|
btn_astap = QPushButton(self.tr("Browse…")); btn_astap.clicked.connect(lambda: self._browse_into(self.le_astap))
|
|
81
85
|
|
|
82
86
|
row_grax = QHBoxLayout(); row_grax.addWidget(self.le_graxpert); row_grax.addWidget(btn_grax)
|
|
83
|
-
|
|
87
|
+
|
|
84
88
|
row_star = QHBoxLayout(); row_star.addWidget(self.le_starnet); row_star.addWidget(btn_star)
|
|
85
89
|
row_astap = QHBoxLayout(); row_astap.addWidget(self.le_astap); row_astap.addWidget(btn_astap)
|
|
86
90
|
|
|
@@ -163,7 +167,7 @@ class SettingsDialog(QDialog):
|
|
|
163
167
|
# ---- Left column: Paths & Integrations ----
|
|
164
168
|
left_col.addRow(QLabel(self.tr("<b>Paths & Integrations</b>")))
|
|
165
169
|
w = QWidget(); w.setLayout(row_grax); left_col.addRow(self.tr("GraXpert executable:"), w)
|
|
166
|
-
|
|
170
|
+
|
|
167
171
|
w = QWidget(); w.setLayout(row_star); left_col.addRow(self.tr("StarNet executable:"), w)
|
|
168
172
|
w = QWidget(); w.setLayout(row_astap); left_col.addRow(self.tr("ASTAP executable:"), w)
|
|
169
173
|
left_col.addRow(self.tr("Astrometry.net API key:"), self.le_astrometry)
|
|
@@ -182,6 +186,62 @@ class SettingsDialog(QDialog):
|
|
|
182
186
|
left_col.addRow(self.tr("Background Opacity:"), w_bg_opacity)
|
|
183
187
|
left_col.addRow(self.tr("Background Image:"), w_bg_image)
|
|
184
188
|
|
|
189
|
+
left_col.addRow(QLabel(self.tr("<b>Acceleration</b>")))
|
|
190
|
+
|
|
191
|
+
accel_row = QHBoxLayout()
|
|
192
|
+
self.backend_label = QLabel(self.tr("Backend: {0}").format(current_backend()))
|
|
193
|
+
accel_row.addWidget(self.backend_label)
|
|
194
|
+
# NEW: dependency status label (rich text)
|
|
195
|
+
self.accel_deps_label = QLabel()
|
|
196
|
+
self.accel_deps_label.setTextFormat(Qt.TextFormat.RichText)
|
|
197
|
+
self.accel_deps_label.setStyleSheet("color:#888;") # subtle
|
|
198
|
+
self.accel_deps_label.setToolTip(self.tr("Installed acceleration-related Python packages"))
|
|
199
|
+
accel_row.addSpacing(12)
|
|
200
|
+
accel_row.addWidget(self.accel_deps_label)
|
|
201
|
+
|
|
202
|
+
# NEW: preference combo
|
|
203
|
+
self.cb_accel_pref = QComboBox()
|
|
204
|
+
self.cb_accel_pref.addItems([
|
|
205
|
+
"Auto (recommended)",
|
|
206
|
+
"CUDA (NVIDIA)",
|
|
207
|
+
"Intel XPU (Arc/Xe)",
|
|
208
|
+
"DirectML (Windows AMD/Intel)",
|
|
209
|
+
"CPU only",
|
|
210
|
+
])
|
|
211
|
+
# hide DirectML option on non-Windows if you want:
|
|
212
|
+
if platform.system() != "Windows":
|
|
213
|
+
# remove the DirectML entry (index 3)
|
|
214
|
+
self.cb_accel_pref.removeItem(3)
|
|
215
|
+
|
|
216
|
+
accel_row.addSpacing(8)
|
|
217
|
+
accel_row.addWidget(QLabel(self.tr("Preference:")))
|
|
218
|
+
accel_row.addWidget(self.cb_accel_pref)
|
|
219
|
+
|
|
220
|
+
self.install_accel_btn = QPushButton(self.tr("Install/Update GPU Acceleration…"))
|
|
221
|
+
accel_row.addWidget(self.install_accel_btn)
|
|
222
|
+
|
|
223
|
+
gpu_help_btn = QToolButton()
|
|
224
|
+
gpu_help_btn.setText("?")
|
|
225
|
+
gpu_help_btn.setToolTip(self.tr("If GPU still not being used — click for fix steps"))
|
|
226
|
+
gpu_help_btn.clicked.connect(self._show_gpu_accel_fix_help)
|
|
227
|
+
accel_row.addWidget(gpu_help_btn)
|
|
228
|
+
|
|
229
|
+
accel_row.addStretch(1)
|
|
230
|
+
w_accel = QWidget()
|
|
231
|
+
w_accel.setLayout(accel_row)
|
|
232
|
+
left_col.addRow(w_accel)
|
|
233
|
+
|
|
234
|
+
# ---- Models ----
|
|
235
|
+
right_col.addRow(QLabel(self.tr("<b>AI Models</b>")))
|
|
236
|
+
|
|
237
|
+
self.lbl_models_status = QLabel(self.tr("Status: (unknown)"))
|
|
238
|
+
self.lbl_models_status.setStyleSheet("color:#888;")
|
|
239
|
+
right_col.addRow(self.lbl_models_status)
|
|
240
|
+
|
|
241
|
+
self.btn_models_update = QPushButton(self.tr("Download/Update Models…"))
|
|
242
|
+
self.btn_models_update.clicked.connect(self._models_update_clicked)
|
|
243
|
+
right_col.addRow(self.btn_models_update)
|
|
244
|
+
|
|
185
245
|
# ---- Right column: WIMS + RA/Dec + Updates + Display ----
|
|
186
246
|
right_col.addRow(QLabel(self.tr("<b>What's In My Sky — Defaults</b>")))
|
|
187
247
|
right_col.addRow(self.tr("Latitude (°):"), self.sp_lat)
|
|
@@ -227,13 +287,218 @@ class SettingsDialog(QDialog):
|
|
|
227
287
|
btns = QDialogButtonBox(
|
|
228
288
|
QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, parent=self
|
|
229
289
|
)
|
|
290
|
+
self.cb_accel_pref.currentIndexChanged.connect(self._accel_pref_changed)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
230
294
|
btns.accepted.connect(self._save_and_accept)
|
|
231
295
|
btns.rejected.connect(self.reject)
|
|
232
296
|
root.addWidget(btns)
|
|
233
|
-
|
|
297
|
+
self.install_accel_btn.clicked.connect(self._install_or_update_accel)
|
|
234
298
|
# Initial Load:
|
|
235
299
|
self.refresh_ui()
|
|
236
300
|
|
|
301
|
+
def _models_update_clicked(self):
|
|
302
|
+
from PyQt6.QtWidgets import QMessageBox, QProgressDialog
|
|
303
|
+
from PyQt6.QtCore import Qt, QThread
|
|
304
|
+
|
|
305
|
+
# NOTE: these must be FILE links or file IDs, not folder links.
|
|
306
|
+
# Put your actual *zip file* share links here once you create them.
|
|
307
|
+
PRIMARY = "https://drive.google.com/file/d/1n4p0grtNpfllalMqtgaEmsTYaFhT5u7Y/view?usp=drive_link"
|
|
308
|
+
BACKUP = "https://drive.google.com/file/d/1uRGJCITlfMMN89ZkOO5ICWEKMH24KGit/view?usp=drive_link"
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
self.btn_models_update.setEnabled(False)
|
|
312
|
+
|
|
313
|
+
pd = QProgressDialog(self.tr("Preparing…"), self.tr("Cancel"), 0, 0, self)
|
|
314
|
+
pd.setWindowTitle(self.tr("Updating Models"))
|
|
315
|
+
pd.setWindowModality(Qt.WindowModality.ApplicationModal)
|
|
316
|
+
pd.setAutoClose(True)
|
|
317
|
+
pd.setMinimumDuration(0)
|
|
318
|
+
pd.show()
|
|
319
|
+
|
|
320
|
+
from setiastro.saspro.model_workers import ModelsDownloadWorker
|
|
321
|
+
|
|
322
|
+
self._models_thread = QThread(self)
|
|
323
|
+
self._models_worker = ModelsDownloadWorker(PRIMARY, BACKUP, expected_sha256=None)
|
|
324
|
+
self._models_worker.moveToThread(self._models_thread)
|
|
325
|
+
|
|
326
|
+
self._models_thread.started.connect(self._models_worker.run, Qt.ConnectionType.QueuedConnection)
|
|
327
|
+
self._models_worker.progress.connect(pd.setLabelText, Qt.ConnectionType.QueuedConnection)
|
|
328
|
+
def should_cancel():
|
|
329
|
+
return self._models_thread.isInterruptionRequested()
|
|
330
|
+
def _cancel():
|
|
331
|
+
if self._models_thread.isRunning():
|
|
332
|
+
self._models_thread.requestInterruption()
|
|
333
|
+
pd.canceled.connect(_cancel, Qt.ConnectionType.QueuedConnection)
|
|
334
|
+
|
|
335
|
+
def _done(ok: bool, msg: str):
|
|
336
|
+
pd.reset()
|
|
337
|
+
pd.deleteLater()
|
|
338
|
+
|
|
339
|
+
self._models_thread.quit()
|
|
340
|
+
self._models_thread.wait()
|
|
341
|
+
|
|
342
|
+
self.btn_models_update.setEnabled(True)
|
|
343
|
+
self._refresh_models_status()
|
|
344
|
+
|
|
345
|
+
if ok:
|
|
346
|
+
QMessageBox.information(self, self.tr("Models"), self.tr("✅ {0}").format(msg))
|
|
347
|
+
else:
|
|
348
|
+
QMessageBox.warning(self, self.tr("Models"), self.tr("❌ {0}").format(msg))
|
|
349
|
+
|
|
350
|
+
self._models_worker.finished.connect(_done, Qt.ConnectionType.QueuedConnection)
|
|
351
|
+
self._models_thread.finished.connect(self._models_worker.deleteLater, Qt.ConnectionType.QueuedConnection)
|
|
352
|
+
self._models_thread.finished.connect(self._models_thread.deleteLater, Qt.ConnectionType.QueuedConnection)
|
|
353
|
+
|
|
354
|
+
self._models_thread.start()
|
|
355
|
+
|
|
356
|
+
def _refresh_models_status(self):
|
|
357
|
+
from setiastro.saspro.model_manager import read_installed_manifest, models_root
|
|
358
|
+
m = read_installed_manifest()
|
|
359
|
+
if not m:
|
|
360
|
+
self.lbl_models_status.setText(self.tr("Status: not installed"))
|
|
361
|
+
return
|
|
362
|
+
fid = m.get("file_id", "")
|
|
363
|
+
self.lbl_models_status.setText(
|
|
364
|
+
self.tr("Status: installed\nLocation: {0}").format(models_root())
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _accel_pref_changed(self, idx: int):
|
|
369
|
+
inv = {0:"auto", 1:"cuda", 2:"xpu", 3:"directml", 4:"cpu"}
|
|
370
|
+
val = inv.get(idx, "auto")
|
|
371
|
+
self.settings.setValue("accel/preferred_backend", val)
|
|
372
|
+
self.settings.sync()
|
|
373
|
+
|
|
374
|
+
def _show_gpu_accel_fix_help(self):
|
|
375
|
+
from PyQt6.QtWidgets import QMessageBox
|
|
376
|
+
QMessageBox.information(
|
|
377
|
+
self, self.tr("GPU Acceleration Help"),
|
|
378
|
+
self.tr(
|
|
379
|
+
"If GPU is not being used:\n"
|
|
380
|
+
" • Click Install/Update GPU Acceleration…\n"
|
|
381
|
+
" • Restart SAS Pro\n"
|
|
382
|
+
" • On NVIDIA systems, verify drivers and that 'nvidia-smi' works.\n"
|
|
383
|
+
" • On Windows non-NVIDIA, DirectML may be used.\n"
|
|
384
|
+
)
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
def _pkg_status(self, dist_name: str, import_name: str | None = None) -> tuple[bool, str]:
|
|
388
|
+
"""
|
|
389
|
+
Returns (installed?, display_text). Does NOT import the module.
|
|
390
|
+
dist_name = pip distribution name used for version lookup (e.g., 'torchvision')
|
|
391
|
+
import_name = python import name used for find_spec (e.g., 'torch_directml')
|
|
392
|
+
"""
|
|
393
|
+
mod = import_name or dist_name.replace("-", "_")
|
|
394
|
+
present = importlib.util.find_spec(mod) is not None
|
|
395
|
+
|
|
396
|
+
ver = ""
|
|
397
|
+
if present:
|
|
398
|
+
try:
|
|
399
|
+
ver = importlib.metadata.version(dist_name)
|
|
400
|
+
except importlib.metadata.PackageNotFoundError:
|
|
401
|
+
# Sometimes dist name differs; fall back to "installed" without version
|
|
402
|
+
ver = ""
|
|
403
|
+
except Exception:
|
|
404
|
+
ver = ""
|
|
405
|
+
|
|
406
|
+
if present:
|
|
407
|
+
return True, (f"✅ {ver}" if ver else "✅ installed")
|
|
408
|
+
return False, "— not installed"
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def _format_accel_deps_text(self) -> str:
|
|
412
|
+
# Torch + friends
|
|
413
|
+
torch_ok, torch_txt = self._pkg_status("torch", "torch")
|
|
414
|
+
dml_ok, dml_txt = self._pkg_status("torch-directml", "torch_directml")
|
|
415
|
+
ta_ok, ta_txt = self._pkg_status("torchaudio", "torchaudio")
|
|
416
|
+
tv_ok, tv_txt = self._pkg_status("torchvision", "torchvision")
|
|
417
|
+
|
|
418
|
+
# Pretty, compact, and stable in a QLabel
|
|
419
|
+
return (
|
|
420
|
+
f"Torch: <b>{torch_txt}</b><br>"
|
|
421
|
+
f"Torch-DirectML: <b>{dml_txt}</b><br>"
|
|
422
|
+
f"TorchAudio: <b>{ta_txt}</b><br>"
|
|
423
|
+
f"TorchVision: <b>{tv_txt}</b>"
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def _install_or_update_accel(self):
|
|
428
|
+
import sys, platform
|
|
429
|
+
from PyQt6.QtWidgets import QMessageBox, QProgressDialog
|
|
430
|
+
from PyQt6.QtCore import Qt, QThread
|
|
431
|
+
from setiastro.saspro.accel_installer import current_backend
|
|
432
|
+
from setiastro.saspro.accel_workers import AccelInstallWorker # wherever yours lives
|
|
433
|
+
|
|
434
|
+
v = sys.version_info
|
|
435
|
+
if not (v.major == 3 and v.minor in (10, 11, 12)):
|
|
436
|
+
why = self.tr("This app is running on Python {0}.{1}. GPU acceleration requires Python 3.10, 3.11, or 3.12.").format(v.major, v.minor)
|
|
437
|
+
tip = ""
|
|
438
|
+
sysname = platform.system()
|
|
439
|
+
if sysname == "Darwin":
|
|
440
|
+
tip = self.tr("\n\nmacOS tip (Apple Silicon):\n • Install Python 3.12: brew install python@3.12\n • Relaunch the app.")
|
|
441
|
+
elif sysname == "Windows":
|
|
442
|
+
tip = self.tr("\n\nWindows tip:\n • Install Python 3.12/3.11/3.10 (x64) from python.org\n • Relaunch the app.")
|
|
443
|
+
else:
|
|
444
|
+
tip = self.tr("\n\nLinux tip:\n • Install python3.12 or 3.11 via your package manager\n • Relaunch the app.")
|
|
445
|
+
|
|
446
|
+
QMessageBox.warning(self, self.tr("Unsupported Python Version"), why + tip)
|
|
447
|
+
self.backend_label.setText(self.tr("Backend: CPU (Python version not supported for GPU install)"))
|
|
448
|
+
return
|
|
449
|
+
|
|
450
|
+
self.install_accel_btn.setEnabled(False)
|
|
451
|
+
self.backend_label.setText(self.tr("Backend: installing…"))
|
|
452
|
+
|
|
453
|
+
pref = (self.settings.value("accel/preferred_backend", "auto", type=str) or "auto").lower()
|
|
454
|
+
|
|
455
|
+
self._accel_pd = QProgressDialog(self.tr("Preparing runtime…"), self.tr("Cancel"), 0, 0, self)
|
|
456
|
+
self._accel_pd.setWindowTitle(self.tr("Installing GPU Acceleration"))
|
|
457
|
+
self._accel_pd.setWindowModality(Qt.WindowModality.ApplicationModal)
|
|
458
|
+
self._accel_pd.setAutoClose(True)
|
|
459
|
+
self._accel_pd.setMinimumDuration(0)
|
|
460
|
+
self._accel_pd.show()
|
|
461
|
+
|
|
462
|
+
self._accel_thread = QThread(self)
|
|
463
|
+
self._accel_worker = AccelInstallWorker(prefer_gpu=True, preferred_backend=pref)
|
|
464
|
+
self._accel_worker.moveToThread(self._accel_thread)
|
|
465
|
+
|
|
466
|
+
self._accel_thread.started.connect(self._accel_worker.run, Qt.ConnectionType.QueuedConnection)
|
|
467
|
+
self._accel_worker.progress.connect(self._accel_pd.setLabelText, Qt.ConnectionType.QueuedConnection)
|
|
468
|
+
|
|
469
|
+
def _cancel():
|
|
470
|
+
if self._accel_thread.isRunning():
|
|
471
|
+
self._accel_thread.requestInterruption()
|
|
472
|
+
self._accel_pd.canceled.connect(_cancel, Qt.ConnectionType.QueuedConnection)
|
|
473
|
+
|
|
474
|
+
def _done(ok: bool, msg: str):
|
|
475
|
+
if getattr(self, "_accel_pd", None):
|
|
476
|
+
self._accel_pd.reset()
|
|
477
|
+
self._accel_pd.deleteLater()
|
|
478
|
+
self._accel_pd = None
|
|
479
|
+
|
|
480
|
+
self._accel_thread.quit()
|
|
481
|
+
self._accel_thread.wait()
|
|
482
|
+
|
|
483
|
+
self.install_accel_btn.setEnabled(True)
|
|
484
|
+
self.backend_label.setText(self.tr("Backend: {0}").format(current_backend()))
|
|
485
|
+
try:
|
|
486
|
+
self.accel_deps_label.setText(self._format_accel_deps_text())
|
|
487
|
+
except Exception:
|
|
488
|
+
pass
|
|
489
|
+
|
|
490
|
+
if ok:
|
|
491
|
+
QMessageBox.information(self, self.tr("Acceleration"), self.tr("✅ {0}").format(msg))
|
|
492
|
+
else:
|
|
493
|
+
QMessageBox.warning(self, self.tr("Acceleration"), self.tr("❌ {0}").format(msg))
|
|
494
|
+
|
|
495
|
+
self._accel_worker.finished.connect(_done, Qt.ConnectionType.QueuedConnection)
|
|
496
|
+
self._accel_thread.finished.connect(self._accel_worker.deleteLater, Qt.ConnectionType.QueuedConnection)
|
|
497
|
+
self._accel_thread.finished.connect(self._accel_thread.deleteLater, Qt.ConnectionType.QueuedConnection)
|
|
498
|
+
|
|
499
|
+
self._accel_thread.start()
|
|
500
|
+
|
|
501
|
+
|
|
237
502
|
def refresh_ui(self):
|
|
238
503
|
"""
|
|
239
504
|
Reloads all settings from self.settings and updates the UI widgets.
|
|
@@ -279,7 +544,7 @@ class SettingsDialog(QDialog):
|
|
|
279
544
|
|
|
280
545
|
# Path fields
|
|
281
546
|
self.le_graxpert.setText(self.settings.value("paths/graxpert", "", type=str))
|
|
282
|
-
|
|
547
|
+
|
|
283
548
|
self.le_starnet.setText(self.settings.value("paths/starnet", "", type=str))
|
|
284
549
|
self.le_astap.setText(self.settings.value("paths/astap", "", type=str))
|
|
285
550
|
self.le_astrometry.setText(self.settings.value("api/astrometry_key", "", type=str))
|
|
@@ -330,6 +595,29 @@ class SettingsDialog(QDialog):
|
|
|
330
595
|
self.sp_wcs_step.setEnabled(self.cb_wcs_mode.currentIndex() == 1)
|
|
331
596
|
self.sp_wcs_step.setSuffix(" °" if self.cb_wcs_unit.currentIndex() == 0 else " arcmin")
|
|
332
597
|
|
|
598
|
+
pref = (self.settings.value("accel/preferred_backend", "auto", type=str) or "auto").lower()
|
|
599
|
+
|
|
600
|
+
# map stored -> combobox index (adjust if you removed DirectML on non-Windows)
|
|
601
|
+
idx_map = {"auto": 0, "cuda": 1, "xpu": 2, "directml": 3, "cpu": 4}
|
|
602
|
+
|
|
603
|
+
idx = idx_map.get(pref, 0)
|
|
604
|
+
# if non-Windows and directml was saved earlier, clamp to Auto
|
|
605
|
+
if platform.system() != "Windows" and pref == "directml":
|
|
606
|
+
idx = 0
|
|
607
|
+
|
|
608
|
+
self.cb_accel_pref.setCurrentIndex(idx)
|
|
609
|
+
|
|
610
|
+
from setiastro.saspro.accel_installer import current_backend
|
|
611
|
+
self.backend_label.setText(self.tr("Backend: {0}").format(current_backend()))
|
|
612
|
+
try:
|
|
613
|
+
self.accel_deps_label.setText(self._format_accel_deps_text())
|
|
614
|
+
except Exception:
|
|
615
|
+
self.accel_deps_label.setText(self.tr("Torch: — unknown"))
|
|
616
|
+
try:
|
|
617
|
+
self._refresh_models_status()
|
|
618
|
+
except Exception:
|
|
619
|
+
pass
|
|
620
|
+
|
|
333
621
|
def reject(self):
|
|
334
622
|
"""User cancelled: restore the original background opacity (revert live changes)."""
|
|
335
623
|
try:
|
|
@@ -439,7 +727,7 @@ class SettingsDialog(QDialog):
|
|
|
439
727
|
def _save_and_accept(self):
|
|
440
728
|
# Paths / Integrations
|
|
441
729
|
self.settings.setValue("paths/graxpert", self.le_graxpert.text().strip())
|
|
442
|
-
|
|
730
|
+
|
|
443
731
|
self.settings.setValue("paths/starnet", self.le_starnet.text().strip())
|
|
444
732
|
self.settings.setValue("paths/astap", self.le_astap.text().strip())
|
|
445
733
|
self.settings.setValue("shortcuts/save_on_exit", self.chk_save_shortcuts.isChecked())
|
|
@@ -466,6 +754,12 @@ class SettingsDialog(QDialog):
|
|
|
466
754
|
self.settings.setValue("updates/url", self.le_updates_url.text().strip())
|
|
467
755
|
self.settings.setValue("display/autostretch_24bit", self.chk_autostretch_24bit.isChecked())
|
|
468
756
|
|
|
757
|
+
# accel preference
|
|
758
|
+
pref_idx = self.cb_accel_pref.currentIndex()
|
|
759
|
+
# map index -> stored string (again, adjust if DirectML removed on non-Windows)
|
|
760
|
+
inv = {0:"auto", 1:"cuda", 2:"xpu", 3:"directml", 4:"cpu"}
|
|
761
|
+
self.settings.setValue("accel/preferred_backend", inv.get(pref_idx, "auto"))
|
|
762
|
+
|
|
469
763
|
# Custom background: persist the chosen path (empty -> remove)
|
|
470
764
|
bg_path = (self.le_bg_path.text() or "").strip()
|
|
471
765
|
if bg_path:
|
|
@@ -520,6 +814,11 @@ class SettingsDialog(QDialog):
|
|
|
520
814
|
if hasattr(p, "mdi") and hasattr(p.mdi, "viewport"):
|
|
521
815
|
p.mdi.viewport().update()
|
|
522
816
|
|
|
817
|
+
try:
|
|
818
|
+
self.settings.remove("paths/cosmic_clarity")
|
|
819
|
+
except Exception:
|
|
820
|
+
pass
|
|
821
|
+
|
|
523
822
|
self.accept()
|
|
524
823
|
|
|
525
824
|
from PyQt6.QtGui import QColor, QFont
|