setiastrosuitepro 1.6.0__py3-none-any.whl → 1.6.4.post1__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/data/SASP_data.fits +0 -0
- setiastro/data/catalogs/List_of_Galaxies_with_Distances_Gly.csv +488 -0
- setiastro/data/catalogs/astrobin_filters.csv +890 -0
- setiastro/data/catalogs/astrobin_filters_page1_local.csv +51 -0
- setiastro/data/catalogs/cali2.csv +63 -0
- setiastro/data/catalogs/cali2color.csv +65 -0
- setiastro/data/catalogs/celestial_catalog - original.csv +16471 -0
- setiastro/data/catalogs/celestial_catalog.csv +24031 -0
- setiastro/data/catalogs/detected_stars.csv +24784 -0
- setiastro/data/catalogs/fits_header_data.csv +46 -0
- setiastro/data/catalogs/test.csv +8 -0
- setiastro/data/catalogs/updated_celestial_catalog.csv +16471 -0
- setiastro/images/Astro_Spikes.png +0 -0
- setiastro/images/Background_startup.jpg +0 -0
- setiastro/images/HRDiagram.png +0 -0
- setiastro/images/LExtract.png +0 -0
- setiastro/images/LInsert.png +0 -0
- setiastro/images/Oxygenation-atm-2.svg.png +0 -0
- setiastro/images/RGB080604.png +0 -0
- setiastro/images/abeicon.png +0 -0
- setiastro/images/aberration.png +0 -0
- setiastro/images/andromedatry.png +0 -0
- setiastro/images/andromedatry_satellited.png +0 -0
- setiastro/images/annotated.png +0 -0
- setiastro/images/aperture.png +0 -0
- setiastro/images/astrosuite.ico +0 -0
- setiastro/images/astrosuite.png +0 -0
- setiastro/images/astrosuitepro.icns +0 -0
- setiastro/images/astrosuitepro.ico +0 -0
- setiastro/images/astrosuitepro.png +0 -0
- setiastro/images/background.png +0 -0
- setiastro/images/background2.png +0 -0
- setiastro/images/benchmark.png +0 -0
- setiastro/images/big_moon_stabilizer_timeline.png +0 -0
- setiastro/images/big_moon_stabilizer_timeline_clean.png +0 -0
- setiastro/images/blaster.png +0 -0
- setiastro/images/blink.png +0 -0
- setiastro/images/clahe.png +0 -0
- setiastro/images/collage.png +0 -0
- setiastro/images/colorwheel.png +0 -0
- setiastro/images/contsub.png +0 -0
- setiastro/images/convo.png +0 -0
- setiastro/images/copyslot.png +0 -0
- setiastro/images/cosmic.png +0 -0
- setiastro/images/cosmicsat.png +0 -0
- setiastro/images/crop1.png +0 -0
- setiastro/images/cropicon.png +0 -0
- setiastro/images/curves.png +0 -0
- setiastro/images/cvs.png +0 -0
- setiastro/images/debayer.png +0 -0
- setiastro/images/denoise_cnn_custom.png +0 -0
- setiastro/images/denoise_cnn_graph.png +0 -0
- setiastro/images/disk.png +0 -0
- setiastro/images/dse.png +0 -0
- setiastro/images/exoicon.png +0 -0
- setiastro/images/eye.png +0 -0
- setiastro/images/fliphorizontal.png +0 -0
- setiastro/images/flipvertical.png +0 -0
- setiastro/images/font.png +0 -0
- setiastro/images/freqsep.png +0 -0
- setiastro/images/functionbundle.png +0 -0
- setiastro/images/graxpert.png +0 -0
- setiastro/images/green.png +0 -0
- setiastro/images/gridicon.png +0 -0
- setiastro/images/halo.png +0 -0
- setiastro/images/hdr.png +0 -0
- setiastro/images/histogram.png +0 -0
- setiastro/images/hubble.png +0 -0
- setiastro/images/imagecombine.png +0 -0
- setiastro/images/invert.png +0 -0
- setiastro/images/isophote.png +0 -0
- setiastro/images/isophote_demo_figure.png +0 -0
- setiastro/images/isophote_demo_image.png +0 -0
- setiastro/images/isophote_demo_model.png +0 -0
- setiastro/images/isophote_demo_residual.png +0 -0
- setiastro/images/jwstpupil.png +0 -0
- setiastro/images/linearfit.png +0 -0
- setiastro/images/livestacking.png +0 -0
- setiastro/images/mask.png +0 -0
- setiastro/images/maskapply.png +0 -0
- setiastro/images/maskcreate.png +0 -0
- setiastro/images/maskremove.png +0 -0
- setiastro/images/morpho.png +0 -0
- setiastro/images/mosaic.png +0 -0
- setiastro/images/multiscale_decomp.png +0 -0
- setiastro/images/nbtorgb.png +0 -0
- setiastro/images/neutral.png +0 -0
- setiastro/images/nuke.png +0 -0
- setiastro/images/openfile.png +0 -0
- setiastro/images/pedestal.png +0 -0
- setiastro/images/pen.png +0 -0
- setiastro/images/pixelmath.png +0 -0
- setiastro/images/platesolve.png +0 -0
- setiastro/images/ppp.png +0 -0
- setiastro/images/pro.png +0 -0
- setiastro/images/project.png +0 -0
- setiastro/images/psf.png +0 -0
- setiastro/images/redo.png +0 -0
- setiastro/images/redoicon.png +0 -0
- setiastro/images/rescale.png +0 -0
- setiastro/images/rgbalign.png +0 -0
- setiastro/images/rgbcombo.png +0 -0
- setiastro/images/rgbextract.png +0 -0
- setiastro/images/rotate180.png +0 -0
- setiastro/images/rotatearbitrary.png +0 -0
- setiastro/images/rotateclockwise.png +0 -0
- setiastro/images/rotatecounterclockwise.png +0 -0
- setiastro/images/satellite.png +0 -0
- setiastro/images/script.png +0 -0
- setiastro/images/selectivecolor.png +0 -0
- setiastro/images/simbad.png +0 -0
- setiastro/images/slot0.png +0 -0
- setiastro/images/slot1.png +0 -0
- setiastro/images/slot2.png +0 -0
- setiastro/images/slot3.png +0 -0
- setiastro/images/slot4.png +0 -0
- setiastro/images/slot5.png +0 -0
- setiastro/images/slot6.png +0 -0
- setiastro/images/slot7.png +0 -0
- setiastro/images/slot8.png +0 -0
- setiastro/images/slot9.png +0 -0
- setiastro/images/spcc.png +0 -0
- setiastro/images/spin_precession_vs_lunar_distance.png +0 -0
- setiastro/images/spinner.gif +0 -0
- setiastro/images/stacking.png +0 -0
- setiastro/images/staradd.png +0 -0
- setiastro/images/staralign.png +0 -0
- setiastro/images/starnet.png +0 -0
- setiastro/images/starregistration.png +0 -0
- setiastro/images/starspike.png +0 -0
- setiastro/images/starstretch.png +0 -0
- setiastro/images/statstretch.png +0 -0
- setiastro/images/supernova.png +0 -0
- setiastro/images/uhs.png +0 -0
- setiastro/images/undoicon.png +0 -0
- setiastro/images/upscale.png +0 -0
- setiastro/images/viewbundle.png +0 -0
- setiastro/images/whitebalance.png +0 -0
- setiastro/images/wimi_icon_256x256.png +0 -0
- setiastro/images/wimilogo.png +0 -0
- setiastro/images/wims.png +0 -0
- setiastro/images/wrench_icon.png +0 -0
- setiastro/images/xisfliberator.png +0 -0
- setiastro/qml/ResourceMonitor.qml +126 -0
- setiastro/saspro/__main__.py +228 -67
- setiastro/saspro/_generated/build_info.py +2 -1
- setiastro/saspro/abe.py +76 -25
- setiastro/saspro/aberration_ai.py +14 -14
- setiastro/saspro/add_stars.py +15 -12
- setiastro/saspro/astrobin_exporter.py +61 -58
- setiastro/saspro/astrospike_python.py +3 -1
- setiastro/saspro/autostretch.py +4 -2
- setiastro/saspro/backgroundneutral.py +65 -14
- setiastro/saspro/batch_convert.py +8 -5
- setiastro/saspro/batch_renamer.py +39 -36
- setiastro/saspro/blemish_blaster.py +15 -12
- setiastro/saspro/blink_comparator_pro.py +605 -379
- setiastro/saspro/cheat_sheet.py +62 -17
- setiastro/saspro/clahe.py +34 -8
- setiastro/saspro/comet_stacking.py +103 -38
- setiastro/saspro/common_tr.py +107 -0
- setiastro/saspro/continuum_subtract.py +7 -7
- setiastro/saspro/convo.py +12 -9
- setiastro/saspro/copyastro.py +3 -0
- setiastro/saspro/cosmicclarity.py +77 -52
- setiastro/saspro/crop_dialog_pro.py +80 -45
- setiastro/saspro/curve_editor_pro.py +51 -33
- setiastro/saspro/debayer.py +6 -3
- setiastro/saspro/doc_manager.py +49 -19
- setiastro/saspro/exoplanet_detector.py +11 -11
- setiastro/saspro/fitsmodifier.py +48 -44
- setiastro/saspro/fix_bom.py +32 -0
- setiastro/saspro/frequency_separation.py +18 -12
- setiastro/saspro/function_bundle.py +18 -16
- setiastro/saspro/generate_translations.py +3092 -0
- setiastro/saspro/ghs_dialog_pro.py +19 -16
- setiastro/saspro/graxpert.py +3 -0
- setiastro/saspro/gui/main_window.py +471 -126
- setiastro/saspro/gui/mixins/dock_mixin.py +123 -11
- setiastro/saspro/gui/mixins/file_mixin.py +25 -20
- setiastro/saspro/gui/mixins/geometry_mixin.py +115 -15
- setiastro/saspro/gui/mixins/header_mixin.py +6 -6
- setiastro/saspro/gui/mixins/mask_mixin.py +8 -8
- setiastro/saspro/gui/mixins/menu_mixin.py +62 -33
- setiastro/saspro/gui/mixins/toolbar_mixin.py +382 -226
- setiastro/saspro/gui/mixins/update_mixin.py +26 -26
- setiastro/saspro/gui/statistics_dialog.py +47 -0
- setiastro/saspro/halobgon.py +29 -3
- setiastro/saspro/header_viewer.py +21 -18
- setiastro/saspro/histogram.py +29 -26
- setiastro/saspro/history_explorer.py +2 -0
- setiastro/saspro/i18n.py +168 -0
- setiastro/saspro/image_combine.py +3 -0
- setiastro/saspro/image_peeker_pro.py +52 -44
- setiastro/saspro/imageops/stretch.py +5 -13
- setiastro/saspro/isophote.py +3 -0
- setiastro/saspro/legacy/numba_utils.py +64 -47
- setiastro/saspro/linear_fit.py +3 -0
- setiastro/saspro/live_stacking.py +13 -2
- setiastro/saspro/mask_creation.py +180 -22
- setiastro/saspro/mfdeconv.py +5 -0
- setiastro/saspro/morphology.py +38 -13
- setiastro/saspro/multiscale_decomp.py +713 -256
- setiastro/saspro/nbtorgb_stars.py +12 -2
- setiastro/saspro/numba_utils.py +149 -48
- setiastro/saspro/ops/scripts.py +77 -17
- setiastro/saspro/ops/settings.py +177 -100
- setiastro/saspro/perfect_palette_picker.py +25 -7
- setiastro/saspro/pixelmath.py +114 -110
- setiastro/saspro/plate_solver.py +118 -108
- setiastro/saspro/remove_green.py +24 -7
- setiastro/saspro/remove_stars.py +136 -162
- setiastro/saspro/remove_stars_preset.py +55 -13
- setiastro/saspro/resources.py +46 -15
- setiastro/saspro/rgb_combination.py +19 -18
- setiastro/saspro/rgbalign.py +11 -11
- setiastro/saspro/save_options.py +5 -4
- setiastro/saspro/selective_color.py +84 -25
- setiastro/saspro/sfcc.py +119 -72
- setiastro/saspro/shortcuts.py +345 -36
- setiastro/saspro/signature_insert.py +4 -1
- setiastro/saspro/stacking_suite.py +2066 -1119
- setiastro/saspro/star_alignment.py +291 -331
- setiastro/saspro/star_spikes.py +137 -53
- setiastro/saspro/star_stretch.py +47 -10
- setiastro/saspro/stat_stretch.py +52 -16
- setiastro/saspro/status_log_dock.py +1 -1
- setiastro/saspro/subwindow.py +97 -36
- setiastro/saspro/supernovaasteroidhunter.py +68 -61
- setiastro/saspro/swap_manager.py +77 -42
- setiastro/saspro/translations/all_source_strings.json +4726 -0
- setiastro/saspro/translations/ar_translations.py +4096 -0
- setiastro/saspro/translations/de_translations.py +3728 -0
- setiastro/saspro/translations/es_translations.py +4169 -0
- setiastro/saspro/translations/fr_translations.py +4090 -0
- setiastro/saspro/translations/hi_translations.py +3803 -0
- setiastro/saspro/translations/integrate_translations.py +271 -0
- setiastro/saspro/translations/it_translations.py +4728 -0
- setiastro/saspro/translations/ja_translations.py +3834 -0
- setiastro/saspro/translations/pt_translations.py +3847 -0
- setiastro/saspro/translations/ru_translations.py +3082 -0
- setiastro/saspro/translations/saspro_ar.qm +0 -0
- setiastro/saspro/translations/saspro_ar.ts +16019 -0
- setiastro/saspro/translations/saspro_de.qm +0 -0
- setiastro/saspro/translations/saspro_de.ts +14548 -0
- setiastro/saspro/translations/saspro_es.qm +0 -0
- setiastro/saspro/translations/saspro_es.ts +16202 -0
- setiastro/saspro/translations/saspro_fr.qm +0 -0
- setiastro/saspro/translations/saspro_fr.ts +15870 -0
- setiastro/saspro/translations/saspro_hi.qm +0 -0
- setiastro/saspro/translations/saspro_hi.ts +14855 -0
- setiastro/saspro/translations/saspro_it.qm +0 -0
- setiastro/saspro/translations/saspro_it.ts +19046 -0
- setiastro/saspro/translations/saspro_ja.qm +0 -0
- setiastro/saspro/translations/saspro_ja.ts +14980 -0
- setiastro/saspro/translations/saspro_pt.qm +0 -0
- setiastro/saspro/translations/saspro_pt.ts +15024 -0
- setiastro/saspro/translations/saspro_ru.qm +0 -0
- setiastro/saspro/translations/saspro_ru.ts +11835 -0
- setiastro/saspro/translations/saspro_sw.qm +0 -0
- setiastro/saspro/translations/saspro_sw.ts +15237 -0
- setiastro/saspro/translations/saspro_uk.qm +0 -0
- setiastro/saspro/translations/saspro_uk.ts +15248 -0
- setiastro/saspro/translations/saspro_zh.qm +0 -0
- setiastro/saspro/translations/saspro_zh.ts +15289 -0
- setiastro/saspro/translations/sw_translations.py +3897 -0
- setiastro/saspro/translations/uk_translations.py +3929 -0
- setiastro/saspro/translations/zh_translations.py +3910 -0
- setiastro/saspro/versioning.py +77 -0
- setiastro/saspro/view_bundle.py +20 -17
- setiastro/saspro/wavescale_hdr.py +54 -33
- setiastro/saspro/wavescale_hdr_preset.py +6 -5
- setiastro/saspro/wavescalede.py +54 -31
- setiastro/saspro/wavescalede_preset.py +9 -7
- setiastro/saspro/whitebalance.py +58 -22
- setiastro/saspro/widgets/common_utilities.py +12 -11
- setiastro/saspro/widgets/minigame/game.js +991 -0
- setiastro/saspro/widgets/minigame/index.html +53 -0
- setiastro/saspro/widgets/minigame/style.css +241 -0
- setiastro/saspro/widgets/preview_dialogs.py +8 -8
- setiastro/saspro/widgets/resource_monitor.py +263 -0
- setiastro/saspro/widgets/spinboxes.py +18 -0
- setiastro/saspro/widgets/wavelet_utils.py +52 -20
- setiastro/saspro/wimi.py +7996 -0
- setiastro/saspro/wims.py +578 -0
- setiastro/saspro/window_shelf.py +2 -2
- {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/METADATA +15 -3
- setiastrosuitepro-1.6.4.post1.dist-info/RECORD +368 -0
- setiastrosuitepro-1.6.0.dist-info/RECORD +0 -174
- {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/WHEEL +0 -0
- {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/entry_points.txt +0 -0
- {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/licenses/LICENSE +0 -0
- {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/licenses/license.txt +0 -0
setiastro/saspro/plate_solver.py
CHANGED
|
@@ -16,7 +16,7 @@ from astropy.io import fits
|
|
|
16
16
|
from astropy.io.fits import Header
|
|
17
17
|
from astropy.wcs import WCS
|
|
18
18
|
|
|
19
|
-
from PyQt6.QtCore import QProcess, QTimer, QEventLoop, Qt
|
|
19
|
+
from PyQt6.QtCore import QProcess, QTimer, QEventLoop, Qt, QCoreApplication
|
|
20
20
|
from PyQt6.QtGui import QIcon
|
|
21
21
|
from PyQt6.QtWidgets import (
|
|
22
22
|
QDialog, QLabel, QPushButton, QVBoxLayout, QHBoxLayout,
|
|
@@ -68,7 +68,7 @@ class _SolveStatusPopup(QDialog):
|
|
|
68
68
|
def __init__(self, parent=None):
|
|
69
69
|
super().__init__(parent, Qt.WindowType.Tool)
|
|
70
70
|
self.setObjectName("plate_solve_status_popup")
|
|
71
|
-
self.setWindowTitle("Plate Solving")
|
|
71
|
+
self.setWindowTitle(self.tr("Plate Solving"))
|
|
72
72
|
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
73
73
|
self.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint, True)
|
|
74
74
|
self.setMinimumWidth(420)
|
|
@@ -77,7 +77,7 @@ class _SolveStatusPopup(QDialog):
|
|
|
77
77
|
lay.setContentsMargins(12, 12, 12, 12)
|
|
78
78
|
lay.setSpacing(10)
|
|
79
79
|
|
|
80
|
-
self.label = QLabel("Starting…", self)
|
|
80
|
+
self.label = QLabel(self.tr("Starting…"), self)
|
|
81
81
|
self.label.setWordWrap(True)
|
|
82
82
|
lay.addWidget(self.label)
|
|
83
83
|
|
|
@@ -87,7 +87,7 @@ class _SolveStatusPopup(QDialog):
|
|
|
87
87
|
|
|
88
88
|
row = QHBoxLayout()
|
|
89
89
|
row.addStretch(1)
|
|
90
|
-
hide_btn = QPushButton("Hide", self)
|
|
90
|
+
hide_btn = QPushButton(self.tr("Hide"), self)
|
|
91
91
|
hide_btn.clicked.connect(self.hide)
|
|
92
92
|
row.addWidget(hide_btn)
|
|
93
93
|
lay.addLayout(row)
|
|
@@ -160,7 +160,8 @@ def _set_status_ui(parent, text: str):
|
|
|
160
160
|
# Batch log?
|
|
161
161
|
logw = getattr(parent, "log", None)
|
|
162
162
|
if logw and hasattr(logw, "append"):
|
|
163
|
-
|
|
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("❌")):
|
|
164
165
|
logw.append(text)
|
|
165
166
|
updated_any = True
|
|
166
167
|
|
|
@@ -206,11 +207,11 @@ def _wait_process(proc: QProcess, timeout_ms: int, parent=None) -> bool:
|
|
|
206
207
|
proc.waitForFinished(2000)
|
|
207
208
|
except Exception:
|
|
208
209
|
pass
|
|
209
|
-
_set_status_ui(parent, "Status: process timed out.")
|
|
210
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: process timed out."))
|
|
210
211
|
return False
|
|
211
212
|
|
|
212
213
|
if proc.exitStatus() != QProcess.ExitStatus.NormalExit:
|
|
213
|
-
_set_status_ui(parent, "Status: process did not exit normally.")
|
|
214
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: process did not exit normally."))
|
|
214
215
|
return False
|
|
215
216
|
|
|
216
217
|
return True
|
|
@@ -385,7 +386,8 @@ def _astrometry_api_request(method: str, url: str, *, data=None, files=None,
|
|
|
385
386
|
if attempt >= max_retries:
|
|
386
387
|
break
|
|
387
388
|
delay = min(8.0, 0.5 * (2 ** (attempt - 1))) + random.random() * 0.2
|
|
388
|
-
|
|
389
|
+
msg = QCoreApplication.translate("PlateSolver", "Status: {0} retry {1}/{2}…").format(stage or 'request', attempt, max_retries)
|
|
390
|
+
_set_status_ui(parent, msg)
|
|
389
391
|
_sleep_ui(int(delay * 1000))
|
|
390
392
|
return None
|
|
391
393
|
|
|
@@ -621,7 +623,7 @@ def _astrometry_download_wcs_file(settings, job_id: int, parent=None) -> Header
|
|
|
621
623
|
base_site = _get_astrometry_api_url(settings).split("/api/")[0].rstrip("/") + "/"
|
|
622
624
|
url = base_site + f"wcs_file/{int(job_id)}"
|
|
623
625
|
|
|
624
|
-
_set_status_ui(parent, "Status: Downloading WCS file (with SIP) from Astrometry.net…")
|
|
626
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Downloading WCS file (with SIP) from Astrometry.net…"))
|
|
625
627
|
try:
|
|
626
628
|
r = requests.get(url, timeout=(10, 60))
|
|
627
629
|
if r.status_code != 200 or len(r.content) < 2000:
|
|
@@ -903,13 +905,13 @@ def _build_astap_seed(h: Header) -> Tuple[list[str], str]:
|
|
|
903
905
|
|
|
904
906
|
|
|
905
907
|
def _astrometry_login(settings, parent=None) -> str | None:
|
|
906
|
-
_set_status_ui(parent, "Status: Logging in to Astrometry.net…")
|
|
908
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Logging in to Astrometry.net…"))
|
|
907
909
|
api_key = _get_astrometry_api_key(settings)
|
|
908
910
|
if not api_key:
|
|
909
911
|
from PyQt6.QtWidgets import QInputDialog
|
|
910
|
-
key, ok = QInputDialog.getText(None, "Astrometry.net API Key", "Enter your Astrometry.net API key:")
|
|
912
|
+
key, ok = QInputDialog.getText(None, QCoreApplication.translate("PlateSolver", "Astrometry.net API Key"), QCoreApplication.translate("PlateSolver", "Enter your Astrometry.net API key:"))
|
|
911
913
|
if not ok or not key:
|
|
912
|
-
_set_status_ui(parent, "Status: Login canceled (no API key).")
|
|
914
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Login canceled (no API key)."))
|
|
913
915
|
return None
|
|
914
916
|
_set_astrometry_api_key(settings, key)
|
|
915
917
|
api_key = key
|
|
@@ -921,20 +923,20 @@ def _astrometry_login(settings, parent=None) -> str | None:
|
|
|
921
923
|
parent=parent, stage="login"
|
|
922
924
|
)
|
|
923
925
|
if resp and resp.get("status") == "success":
|
|
924
|
-
_set_status_ui(parent, "Status: Login successful.")
|
|
926
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Login successful."))
|
|
925
927
|
return resp.get("session")
|
|
926
|
-
_set_status_ui(parent, "Status: Login failed.")
|
|
928
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Login failed."))
|
|
927
929
|
return None
|
|
928
930
|
|
|
929
931
|
def _astrometry_upload(settings, session: str, image_path: str, parent=None) -> int | None:
|
|
930
|
-
_set_status_ui(parent, "Status: Uploading image to Astrometry.net…")
|
|
932
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Uploading image to Astrometry.net…"))
|
|
931
933
|
base = _get_astrometry_api_url(settings)
|
|
932
934
|
|
|
933
935
|
try:
|
|
934
936
|
sz = os.path.getsize(image_path)
|
|
935
937
|
if sz < 1024: # fits headers alone are ~2880 bytes
|
|
936
938
|
print(f"[Astrometry] temp FITS too small ({sz} bytes): {image_path}")
|
|
937
|
-
_set_status_ui(parent, "Status: Upload failed (temp FITS empty).")
|
|
939
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Upload failed (temp FITS empty)."))
|
|
938
940
|
return None
|
|
939
941
|
except Exception:
|
|
940
942
|
pass
|
|
@@ -955,12 +957,12 @@ def _astrometry_upload(settings, session: str, image_path: str, parent=None) ->
|
|
|
955
957
|
parent=parent, stage="upload"
|
|
956
958
|
)
|
|
957
959
|
if resp and resp.get("status") == "success":
|
|
958
|
-
_set_status_ui(parent, "Status: Upload complete.")
|
|
960
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Upload complete."))
|
|
959
961
|
return int(resp["subid"])
|
|
960
962
|
except Exception as e:
|
|
961
963
|
print("Upload error:", e)
|
|
962
964
|
|
|
963
|
-
_set_status_ui(parent, "Status: Upload failed.")
|
|
965
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Upload failed."))
|
|
964
966
|
return None
|
|
965
967
|
|
|
966
968
|
|
|
@@ -968,7 +970,7 @@ def _astrometry_upload(settings, session: str, image_path: str, parent=None) ->
|
|
|
968
970
|
def _solve_with_local_solvefield(parent, settings, tmp_fit_path: str) -> tuple[bool, Header | str]:
|
|
969
971
|
solvefield = _get_solvefield_exe(settings)
|
|
970
972
|
if not solvefield or not os.path.exists(solvefield):
|
|
971
|
-
return False, "solve-field not configured."
|
|
973
|
+
return False, QCoreApplication.translate("PlateSolver", "solve-field not configured.")
|
|
972
974
|
|
|
973
975
|
args = [
|
|
974
976
|
"--overwrite",
|
|
@@ -978,22 +980,22 @@ def _solve_with_local_solvefield(parent, settings, tmp_fit_path: str) -> tuple[b
|
|
|
978
980
|
"--write-wcs", "wcs",
|
|
979
981
|
tmp_fit_path
|
|
980
982
|
]
|
|
981
|
-
_set_status_ui(parent, "Status: Running local solve-field…")
|
|
983
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Running local solve-field…"))
|
|
982
984
|
print("Running solve-field:", solvefield, " ".join(args))
|
|
983
985
|
p = QProcess(parent)
|
|
984
986
|
p.start(solvefield, args)
|
|
985
987
|
if not p.waitForStarted(5000):
|
|
986
|
-
_set_status_ui(parent, "Status: solve-field failed to start.")
|
|
988
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: solve-field failed to start."))
|
|
987
989
|
return False, f"Failed to start solve-field: {p.errorString()}"
|
|
988
990
|
|
|
989
991
|
if not _wait_process(p, 300000, parent=parent):
|
|
990
|
-
_set_status_ui(parent, "Status: solve-field timed out.")
|
|
992
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: solve-field timed out."))
|
|
991
993
|
return False, "solve-field timed out."
|
|
992
994
|
|
|
993
995
|
if p.exitCode() != 0:
|
|
994
996
|
out = bytes(p.readAllStandardOutput()).decode(errors="ignore")
|
|
995
997
|
err = bytes(p.readAllStandardError()).decode(errors="ignore")
|
|
996
|
-
_set_status_ui(parent, "Status: solve-field failed.")
|
|
998
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: solve-field failed."))
|
|
997
999
|
print("solve-field failed.\nSTDOUT:\n", out, "\nSTDERR:\n", err)
|
|
998
1000
|
return False, "solve-field returned non-zero exit."
|
|
999
1001
|
|
|
@@ -1021,7 +1023,7 @@ def _solve_with_local_solvefield(parent, settings, tmp_fit_path: str) -> tuple[b
|
|
|
1021
1023
|
|
|
1022
1024
|
|
|
1023
1025
|
def _astrometry_poll_job(settings, subid: int, *, max_wait_s=900, parent=None) -> int | None:
|
|
1024
|
-
_set_status_ui(parent, "Status: Waiting for job assignment…")
|
|
1026
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Waiting for job assignment…"))
|
|
1025
1027
|
base = _get_astrometry_api_url(settings)
|
|
1026
1028
|
t0 = time.time()
|
|
1027
1029
|
while time.time() - t0 < max_wait_s:
|
|
@@ -1030,7 +1032,7 @@ def _astrometry_poll_job(settings, subid: int, *, max_wait_s=900, parent=None) -
|
|
|
1030
1032
|
if resp:
|
|
1031
1033
|
jobs = resp.get("jobs", [])
|
|
1032
1034
|
if jobs and jobs[0] is not None:
|
|
1033
|
-
_set_status_ui(parent,
|
|
1035
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Job assigned (ID {0}).").format(jobs[0]))
|
|
1034
1036
|
try: return int(jobs[0])
|
|
1035
1037
|
except Exception as e:
|
|
1036
1038
|
import logging
|
|
@@ -1039,14 +1041,14 @@ def _astrometry_poll_job(settings, subid: int, *, max_wait_s=900, parent=None) -
|
|
|
1039
1041
|
return None
|
|
1040
1042
|
|
|
1041
1043
|
def _astrometry_poll_calib(settings, job_id: int, *, max_wait_s=900, parent=None) -> dict | None:
|
|
1042
|
-
_set_status_ui(parent, "Status: Waiting for solution…")
|
|
1044
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Waiting for solution…"))
|
|
1043
1045
|
base = _get_astrometry_api_url(settings)
|
|
1044
1046
|
t0 = time.time()
|
|
1045
1047
|
while time.time() - t0 < max_wait_s:
|
|
1046
1048
|
resp = _astrometry_api_request("GET", base + f"jobs/{job_id}/calibration/",
|
|
1047
1049
|
parent=parent, stage="poll calib")
|
|
1048
1050
|
if resp and all(k in resp for k in ("ra","dec","pixscale")):
|
|
1049
|
-
_set_status_ui(parent, "Status: Solution received.")
|
|
1051
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Solution received."))
|
|
1050
1052
|
return resp
|
|
1051
1053
|
_sleep_ui(1500)
|
|
1052
1054
|
return None
|
|
@@ -1403,13 +1405,13 @@ def _solve_numpy_with_astrometry(
|
|
|
1403
1405
|
import logging
|
|
1404
1406
|
logging.debug(f"Exception suppressed: {type(e).__name__}: {e}")
|
|
1405
1407
|
return True, hh
|
|
1406
|
-
return False, "solve-field returned no header."
|
|
1408
|
+
return False, QCoreApplication.translate("PlateSolver", "solve-field returned no header.")
|
|
1407
1409
|
|
|
1408
1410
|
# 2) web API fallback (full-res, 16-bit upload)
|
|
1409
1411
|
if requests is None:
|
|
1410
|
-
return False, "requests not available for astrometry.net API."
|
|
1412
|
+
return False, QCoreApplication.translate("PlateSolver", "requests not available for astrometry.net API.")
|
|
1411
1413
|
|
|
1412
|
-
_set_status_ui(parent, "Status: Preparing full-res 16-bit FITS for web solve…")
|
|
1414
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Preparing full-res 16-bit FITS for web solve…"))
|
|
1413
1415
|
|
|
1414
1416
|
tmp_fit_web = _write_temp_fit_web_16bit(gray_full)
|
|
1415
1417
|
|
|
@@ -1417,21 +1419,21 @@ def _solve_numpy_with_astrometry(
|
|
|
1417
1419
|
try:
|
|
1418
1420
|
sz = os.path.getsize(tmp_fit_web)
|
|
1419
1421
|
if sz < 3000:
|
|
1420
|
-
return False,
|
|
1422
|
+
return False, QCoreApplication.translate("PlateSolver", "Temp FITS for web upload is empty/tiny ({0} bytes).").format(sz)
|
|
1421
1423
|
except Exception:
|
|
1422
1424
|
pass
|
|
1423
1425
|
|
|
1424
1426
|
session = _astrometry_login(settings, parent=parent)
|
|
1425
1427
|
if not session:
|
|
1426
|
-
return False, "Astrometry.net login failed."
|
|
1428
|
+
return False, QCoreApplication.translate("PlateSolver", "Astrometry.net login failed.")
|
|
1427
1429
|
|
|
1428
1430
|
subid = _astrometry_upload(settings, session, tmp_fit_web, parent=parent)
|
|
1429
1431
|
if not subid:
|
|
1430
|
-
return False, "Astrometry.net upload failed."
|
|
1432
|
+
return False, QCoreApplication.translate("PlateSolver", "Astrometry.net upload failed.")
|
|
1431
1433
|
|
|
1432
1434
|
job_id = _astrometry_poll_job(settings, subid, parent=parent)
|
|
1433
1435
|
if not job_id:
|
|
1434
|
-
return False, "Astrometry.net job ID not received in time."
|
|
1436
|
+
return False, QCoreApplication.translate("PlateSolver", "Astrometry.net job ID not received in time.")
|
|
1435
1437
|
|
|
1436
1438
|
# Prefer full WCS file (includes SIP)
|
|
1437
1439
|
hdr_wcs = _astrometry_download_wcs_file(settings, job_id, parent=parent)
|
|
@@ -1440,9 +1442,9 @@ def _solve_numpy_with_astrometry(
|
|
|
1440
1442
|
# fallback to calibration (no SIP)
|
|
1441
1443
|
calib = _astrometry_poll_calib(settings, job_id, parent=parent)
|
|
1442
1444
|
if not calib:
|
|
1443
|
-
return False, "Astrometry.net calibration not received in time."
|
|
1445
|
+
return False, QCoreApplication.translate("PlateSolver", "Astrometry.net calibration not received in time.")
|
|
1444
1446
|
|
|
1445
|
-
_set_status_ui(parent, "Status: Building WCS header from calibration…")
|
|
1447
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Building WCS header from calibration…"))
|
|
1446
1448
|
hdr_wcs = _wcs_header_from_astrometry_calib(calib, (Hfull, Wfull))
|
|
1447
1449
|
|
|
1448
1450
|
# Coerce & ensure TAN-SIP if SIP terms exist
|
|
@@ -1487,24 +1489,24 @@ def _solve_numpy_with_astrometry(
|
|
|
1487
1489
|
|
|
1488
1490
|
def _solve_numpy_with_fallback(parent, settings, image: np.ndarray, seed_header: Header | None) -> tuple[bool, Header | str]:
|
|
1489
1491
|
# Try ASTAP first
|
|
1490
|
-
_set_status_ui(parent, "Status: Solving with ASTAP…")
|
|
1492
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Solving with ASTAP…"))
|
|
1491
1493
|
ok, res = _solve_numpy_with_astap(parent, settings, image, seed_header)
|
|
1492
1494
|
if ok:
|
|
1493
|
-
_set_status_ui(parent, "Status: Solved with ASTAP.")
|
|
1495
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Solved with ASTAP."))
|
|
1494
1496
|
return True, res
|
|
1495
1497
|
|
|
1496
1498
|
# ASTAP failed → tell the user and fall back
|
|
1497
1499
|
err_msg = str(res) if res is not None else "unknown error"
|
|
1498
1500
|
print("ASTAP failed:", err_msg)
|
|
1499
|
-
_set_status_ui(parent,
|
|
1501
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: ASTAP failed ({0}). Falling back to Astrometry.net…").format(err_msg))
|
|
1500
1502
|
QApplication.processEvents()
|
|
1501
1503
|
|
|
1502
1504
|
# Fallback: astrometry.net (local solve-field first, then web API inside)
|
|
1503
1505
|
ok2, res2 = _solve_numpy_with_astrometry(parent, settings, image, seed_header)
|
|
1504
1506
|
if ok2:
|
|
1505
|
-
_set_status_ui(parent, "Status: Solved via Astrometry.net.")
|
|
1507
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Solved via Astrometry.net."))
|
|
1506
1508
|
else:
|
|
1507
|
-
_set_status_ui(parent,
|
|
1509
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Astrometry.net failed ({0}).").format(res2))
|
|
1508
1510
|
|
|
1509
1511
|
return ok2, res2
|
|
1510
1512
|
|
|
@@ -1626,7 +1628,7 @@ def _solve_numpy_with_astap(parent, settings, image: np.ndarray, seed_header: He
|
|
|
1626
1628
|
"""
|
|
1627
1629
|
astap_exe = _get_astap_exe(settings)
|
|
1628
1630
|
if not astap_exe or not os.path.exists(astap_exe):
|
|
1629
|
-
return False, "ASTAP path is not set (see Preferences) or file not found."
|
|
1631
|
+
return False, QCoreApplication.translate("PlateSolver", "ASTAP path is not set (see Preferences) or file not found.")
|
|
1630
1632
|
|
|
1631
1633
|
# normalize and force 2-D luminance in [0,1]
|
|
1632
1634
|
norm = _normalize_for_astap(image)
|
|
@@ -1696,13 +1698,13 @@ def _solve_numpy_with_astap(parent, settings, image: np.ndarray, seed_header: He
|
|
|
1696
1698
|
proc = QProcess(parent)
|
|
1697
1699
|
proc.start(astap_exe, args)
|
|
1698
1700
|
if not proc.waitForStarted(5000):
|
|
1699
|
-
_set_status_ui(parent, "Status: ASTAP failed to start.")
|
|
1700
|
-
return False,
|
|
1701
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: ASTAP failed to start."))
|
|
1702
|
+
return False, QCoreApplication.translate("PlateSolver", "Failed to start ASTAP: {0}").format(proc.errorString())
|
|
1701
1703
|
|
|
1702
|
-
_set_status_ui(parent, "Status: ASTAP solving…")
|
|
1704
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: ASTAP solving…"))
|
|
1703
1705
|
if not _wait_process(proc, 300000, parent=parent):
|
|
1704
|
-
_set_status_ui(parent, "Status: ASTAP timed out.")
|
|
1705
|
-
return False, "ASTAP timed out."
|
|
1706
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: ASTAP timed out."))
|
|
1707
|
+
return False, QCoreApplication.translate("PlateSolver", "ASTAP timed out.")
|
|
1706
1708
|
|
|
1707
1709
|
if proc.exitCode() != 0:
|
|
1708
1710
|
out = bytes(proc.readAllStandardOutput()).decode(errors="ignore")
|
|
@@ -1717,7 +1719,7 @@ def _solve_numpy_with_astap(parent, settings, image: np.ndarray, seed_header: He
|
|
|
1717
1719
|
except Exception as e:
|
|
1718
1720
|
import logging
|
|
1719
1721
|
logging.debug(f"Exception suppressed: {type(e).__name__}: {e}")
|
|
1720
|
-
return False, "ASTAP returned a non-zero exit code."
|
|
1722
|
+
return False, QCoreApplication.translate("PlateSolver", "ASTAP returned a non-zero exit code.")
|
|
1721
1723
|
|
|
1722
1724
|
# >>> THIS is the key change: read the header **directly** from the FITS ASTAP wrote
|
|
1723
1725
|
try:
|
|
@@ -1777,7 +1779,7 @@ def _debug_dump_meta(label: str, meta: dict):
|
|
|
1777
1779
|
def plate_solve_doc_inplace(parent, doc, settings) -> Tuple[bool, Header | str]:
|
|
1778
1780
|
img = getattr(doc, "image", None)
|
|
1779
1781
|
if img is None:
|
|
1780
|
-
return False, "Active document has no image data."
|
|
1782
|
+
return False, QCoreApplication.translate("PlateSolver", "Active document has no image data.")
|
|
1781
1783
|
|
|
1782
1784
|
# Make sure metadata is a dict we can mutate
|
|
1783
1785
|
meta = getattr(doc, "metadata", {}) or {}
|
|
@@ -1829,7 +1831,7 @@ def plate_solve_doc_inplace(parent, doc, settings) -> Tuple[bool, Header | str]:
|
|
|
1829
1831
|
(hasattr(parent, "findChild") and parent.findChild(QLabel, "status_label") is not None)
|
|
1830
1832
|
)
|
|
1831
1833
|
if headless:
|
|
1832
|
-
_status_popup_open(parent, "Status: Preparing plate solve…")
|
|
1834
|
+
_status_popup_open(parent, QCoreApplication.translate("PlateSolver", "Status: Preparing plate solve…"))
|
|
1833
1835
|
|
|
1834
1836
|
try:
|
|
1835
1837
|
ok, res = _solve_numpy_with_fallback(parent, settings, img, seed_h)
|
|
@@ -1884,7 +1886,7 @@ def plate_solve_doc_inplace(parent, doc, settings) -> Tuple[bool, Header | str]:
|
|
|
1884
1886
|
if hasattr(parent, "currentDocumentChanged"):
|
|
1885
1887
|
QTimer.singleShot(0, lambda: parent.currentDocumentChanged.emit(doc))
|
|
1886
1888
|
|
|
1887
|
-
_set_status_ui(parent, "Status: Plate solve completed.")
|
|
1889
|
+
_set_status_ui(parent, QCoreApplication.translate("PlateSolver", "Status: Plate solve completed."))
|
|
1888
1890
|
_status_popup_close()
|
|
1889
1891
|
return True, hdr
|
|
1890
1892
|
finally:
|
|
@@ -2035,7 +2037,7 @@ def plate_solve_active_document(parent, settings) -> tuple[bool, Header | str]:
|
|
|
2035
2037
|
"""
|
|
2036
2038
|
doc = _active_doc_from_parent(parent)
|
|
2037
2039
|
if doc is None:
|
|
2038
|
-
return False, "No active document to plate-solve."
|
|
2040
|
+
return False, QCoreApplication.translate("PlateSolver", "No active document to plate-solve.")
|
|
2039
2041
|
|
|
2040
2042
|
return plate_solve_doc_inplace(parent, doc, settings)
|
|
2041
2043
|
|
|
@@ -2054,9 +2056,10 @@ class PlateSolverDialog(QDialog):
|
|
|
2054
2056
|
def __init__(self, settings, parent=None, icon: QIcon | None = None):
|
|
2055
2057
|
super().__init__(parent)
|
|
2056
2058
|
self.settings = settings
|
|
2057
|
-
self.setWindowTitle("Plate Solver")
|
|
2059
|
+
self.setWindowTitle(self.tr("Plate Solver"))
|
|
2058
2060
|
self.setMinimumWidth(560)
|
|
2059
2061
|
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
2062
|
+
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
2060
2063
|
self.setModal(False)
|
|
2061
2064
|
#self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
2062
2065
|
|
|
@@ -2067,16 +2070,18 @@ class PlateSolverDialog(QDialog):
|
|
|
2067
2070
|
|
|
2068
2071
|
# ---- Top row: Mode selector ----
|
|
2069
2072
|
top = QHBoxLayout()
|
|
2070
|
-
top.addWidget(QLabel("Mode:", self))
|
|
2073
|
+
top.addWidget(QLabel(self.tr("Mode:"), self))
|
|
2071
2074
|
self.mode_combo = QComboBox(self)
|
|
2072
|
-
self.mode_combo.
|
|
2075
|
+
self.mode_combo.addItem(self.tr("Active View"), "Active View")
|
|
2076
|
+
self.mode_combo.addItem(self.tr("File"), "File")
|
|
2077
|
+
self.mode_combo.addItem(self.tr("Batch"), "Batch")
|
|
2073
2078
|
top.addWidget(self.mode_combo, 1)
|
|
2074
2079
|
top.addStretch(1)
|
|
2075
2080
|
main.addLayout(top)
|
|
2076
2081
|
|
|
2077
2082
|
# ---- Seeding group (shared) ----
|
|
2078
2083
|
from PyQt6.QtWidgets import QGroupBox, QFormLayout
|
|
2079
|
-
seed_box = QGroupBox("Seeding & Constraints", self)
|
|
2084
|
+
seed_box = QGroupBox(self.tr("Seeding & Constraints"), self)
|
|
2080
2085
|
seed_form = QFormLayout(seed_box)
|
|
2081
2086
|
seed_form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow)
|
|
2082
2087
|
seed_form.setHorizontalSpacing(8)
|
|
@@ -2084,46 +2089,51 @@ class PlateSolverDialog(QDialog):
|
|
|
2084
2089
|
|
|
2085
2090
|
# Seed mode
|
|
2086
2091
|
self.cb_seed_mode = QComboBox(seed_box)
|
|
2087
|
-
self.cb_seed_mode.
|
|
2088
|
-
|
|
2092
|
+
self.cb_seed_mode.addItem(self.tr("Auto (from header)"), "Auto (from header)")
|
|
2093
|
+
self.cb_seed_mode.addItem(self.tr("Manual"), "Manual")
|
|
2094
|
+
self.cb_seed_mode.addItem(self.tr("None (blind)"), "None (blind)")
|
|
2095
|
+
seed_form.addRow(self.tr("Seed mode:"), self.cb_seed_mode)
|
|
2089
2096
|
|
|
2090
2097
|
# Manual RA/Dec/Scale row
|
|
2091
2098
|
manual_row = QHBoxLayout()
|
|
2092
|
-
self.le_ra = QLineEdit(seed_box); self.le_ra.setPlaceholderText("RA (e.g. 22:32:14 or 338.1385)")
|
|
2093
|
-
self.le_dec = QLineEdit(seed_box); self.le_dec.setPlaceholderText("Dec (e.g. +40:42:43 or 40.7123)")
|
|
2094
|
-
self.le_scale = QLineEdit(seed_box); self.le_scale.setPlaceholderText('Scale [" / px] (e.g. 1.46)')
|
|
2099
|
+
self.le_ra = QLineEdit(seed_box); self.le_ra.setPlaceholderText(self.tr("RA (e.g. 22:32:14 or 338.1385)"))
|
|
2100
|
+
self.le_dec = QLineEdit(seed_box); self.le_dec.setPlaceholderText(self.tr("Dec (e.g. +40:42:43 or 40.7123)"))
|
|
2101
|
+
self.le_scale = QLineEdit(seed_box); self.le_scale.setPlaceholderText(self.tr('Scale [" / px] (e.g. 1.46)'))
|
|
2095
2102
|
manual_row.addWidget(self.le_ra, 1)
|
|
2096
2103
|
manual_row.addWidget(self.le_dec, 1)
|
|
2097
2104
|
manual_row.addWidget(self.le_scale, 1)
|
|
2098
|
-
seed_form.addRow("Manual RA/Dec/Scale:", manual_row)
|
|
2105
|
+
seed_form.addRow(self.tr("Manual RA/Dec/Scale:"), manual_row)
|
|
2099
2106
|
|
|
2100
2107
|
# Search radius (-r)
|
|
2101
2108
|
rad_row = QHBoxLayout()
|
|
2102
2109
|
self.cb_radius_mode = QComboBox(seed_box)
|
|
2103
|
-
self.cb_radius_mode.
|
|
2104
|
-
self.
|
|
2110
|
+
self.cb_radius_mode.addItem(self.tr("Auto (-r 0)"), "Auto (-r 0)")
|
|
2111
|
+
self.cb_radius_mode.addItem(self.tr("Value (deg)"), "Value (deg)")
|
|
2112
|
+
self.le_radius_val = QLineEdit(seed_box); self.le_radius_val.setPlaceholderText(self.tr("e.g. 5.0"))
|
|
2105
2113
|
self.le_radius_val.setFixedWidth(120)
|
|
2106
2114
|
rad_row.addWidget(self.cb_radius_mode)
|
|
2107
2115
|
rad_row.addWidget(self.le_radius_val)
|
|
2108
2116
|
rad_row.addStretch(1)
|
|
2109
|
-
seed_form.addRow("Search radius:", rad_row)
|
|
2117
|
+
seed_form.addRow(self.tr("Search radius:"), rad_row)
|
|
2110
2118
|
|
|
2111
2119
|
# FOV (-fov)
|
|
2112
2120
|
fov_row = QHBoxLayout()
|
|
2113
2121
|
self.cb_fov_mode = QComboBox(seed_box)
|
|
2114
|
-
self.cb_fov_mode.
|
|
2115
|
-
self.
|
|
2122
|
+
self.cb_fov_mode.addItem(self.tr("Compute from scale"), "Compute from scale")
|
|
2123
|
+
self.cb_fov_mode.addItem(self.tr("Auto (-fov 0)"), "Auto (-fov 0)")
|
|
2124
|
+
self.cb_fov_mode.addItem(self.tr("Value (deg)"), "Value (deg)")
|
|
2125
|
+
self.le_fov_val = QLineEdit(seed_box); self.le_fov_val.setPlaceholderText(self.tr("e.g. 1.80"))
|
|
2116
2126
|
self.le_fov_val.setFixedWidth(120)
|
|
2117
2127
|
fov_row.addWidget(self.cb_fov_mode)
|
|
2118
2128
|
fov_row.addWidget(self.le_fov_val)
|
|
2119
2129
|
fov_row.addStretch(1)
|
|
2120
|
-
seed_form.addRow("FOV:", fov_row)
|
|
2130
|
+
seed_form.addRow(self.tr("FOV:"), fov_row)
|
|
2121
2131
|
|
|
2122
2132
|
# Tooltips
|
|
2123
|
-
self.cb_seed_mode.setToolTip("Use FITS header, your manual RA/Dec/scale, or blind solve.")
|
|
2124
|
-
self.le_scale.setToolTip('Pixel scale in arcseconds/pixel (e.g., 1.46).')
|
|
2125
|
-
self.cb_radius_mode.setToolTip("ASTAP -r. Auto lets ASTAP choose; Value forces a cone radius.")
|
|
2126
|
-
self.cb_fov_mode.setToolTip("ASTAP -fov. Compute uses image height × scale; Auto lets ASTAP infer.")
|
|
2133
|
+
self.cb_seed_mode.setToolTip(self.tr("Use FITS header, your manual RA/Dec/scale, or blind solve."))
|
|
2134
|
+
self.le_scale.setToolTip(self.tr('Pixel scale in arcseconds/pixel (e.g., 1.46).'))
|
|
2135
|
+
self.cb_radius_mode.setToolTip(self.tr("ASTAP -r. Auto lets ASTAP choose; Value forces a cone radius."))
|
|
2136
|
+
self.cb_fov_mode.setToolTip(self.tr("ASTAP -fov. Compute uses image height × scale; Auto lets ASTAP infer."))
|
|
2127
2137
|
|
|
2128
2138
|
main.addWidget(seed_box)
|
|
2129
2139
|
|
|
@@ -2133,15 +2143,15 @@ class PlateSolverDialog(QDialog):
|
|
|
2133
2143
|
|
|
2134
2144
|
# Page 0: Active View
|
|
2135
2145
|
p0 = QWidget(self); l0 = QVBoxLayout(p0)
|
|
2136
|
-
l0.addWidget(QLabel("Solve the currently active image view.", p0))
|
|
2146
|
+
l0.addWidget(QLabel(self.tr("Solve the currently active image view."), p0))
|
|
2137
2147
|
l0.addStretch(1)
|
|
2138
2148
|
self.stack.addWidget(p0)
|
|
2139
2149
|
|
|
2140
2150
|
# Page 1: File picker
|
|
2141
2151
|
p1 = QWidget(self); l1 = QVBoxLayout(p1)
|
|
2142
2152
|
file_row = QHBoxLayout()
|
|
2143
|
-
self.le_path = QLineEdit(p1); self.le_path.setPlaceholderText("Choose an image…")
|
|
2144
|
-
btn_browse = QPushButton("Browse…", p1)
|
|
2153
|
+
self.le_path = QLineEdit(p1); self.le_path.setPlaceholderText(self.tr("Choose an image…"))
|
|
2154
|
+
btn_browse = QPushButton(self.tr("Browse…"), p1)
|
|
2145
2155
|
file_row.addWidget(self.le_path, 1); file_row.addWidget(btn_browse)
|
|
2146
2156
|
l1.addLayout(file_row); l1.addStretch(1)
|
|
2147
2157
|
self.stack.addWidget(p1)
|
|
@@ -2149,14 +2159,14 @@ class PlateSolverDialog(QDialog):
|
|
|
2149
2159
|
# Page 2: Batch
|
|
2150
2160
|
p2 = QWidget(self); l2 = QVBoxLayout(p2)
|
|
2151
2161
|
in_row = QHBoxLayout(); out_row = QHBoxLayout()
|
|
2152
|
-
self.le_in = QLineEdit(p2); self.le_in.setPlaceholderText("Input directory")
|
|
2153
|
-
self.le_out = QLineEdit(p2); self.le_out.setPlaceholderText("Output directory")
|
|
2154
|
-
b_in = QPushButton("Browse Input…", p2)
|
|
2155
|
-
b_out = QPushButton("Browse Output…", p2)
|
|
2162
|
+
self.le_in = QLineEdit(p2); self.le_in.setPlaceholderText(self.tr("Input directory"))
|
|
2163
|
+
self.le_out = QLineEdit(p2); self.le_out.setPlaceholderText(self.tr("Output directory"))
|
|
2164
|
+
b_in = QPushButton(self.tr("Browse Input…"), p2)
|
|
2165
|
+
b_out = QPushButton(self.tr("Browse Output…"), p2)
|
|
2156
2166
|
in_row.addWidget(self.le_in, 1); in_row.addWidget(b_in)
|
|
2157
2167
|
out_row.addWidget(self.le_out, 1); out_row.addWidget(b_out)
|
|
2158
2168
|
self.log = QTextEdit(p2); self.log.setReadOnly(True); self.log.setMinimumHeight(160)
|
|
2159
|
-
l2.addLayout(in_row); l2.addLayout(out_row); l2.addWidget(QLabel("Status:", p2)); l2.addWidget(self.log, 1)
|
|
2169
|
+
l2.addLayout(in_row); l2.addLayout(out_row); l2.addWidget(QLabel(self.tr("Status:"), p2)); l2.addWidget(self.log, 1)
|
|
2160
2170
|
self.stack.addWidget(p2)
|
|
2161
2171
|
|
|
2162
2172
|
# ---------------- Status + buttons ----------------
|
|
@@ -2166,8 +2176,8 @@ class PlateSolverDialog(QDialog):
|
|
|
2166
2176
|
|
|
2167
2177
|
btn_row = QHBoxLayout()
|
|
2168
2178
|
btn_row.addStretch(1)
|
|
2169
|
-
self.btn_go = QPushButton("Start", self)
|
|
2170
|
-
self.btn_close = QPushButton("Close", self)
|
|
2179
|
+
self.btn_go = QPushButton(self.tr("Start"), self)
|
|
2180
|
+
self.btn_close = QPushButton(self.tr("Close"), self)
|
|
2171
2181
|
btn_row.addWidget(self.btn_go)
|
|
2172
2182
|
btn_row.addWidget(self.btn_close)
|
|
2173
2183
|
main.addLayout(btn_row)
|
|
@@ -2218,26 +2228,26 @@ class PlateSolverDialog(QDialog):
|
|
|
2218
2228
|
# ---------- file/batch pickers ----------
|
|
2219
2229
|
def _browse_file(self):
|
|
2220
2230
|
f, _ = QFileDialog.getOpenFileName(
|
|
2221
|
-
self, "Choose Image",
|
|
2222
|
-
"", "Images (*.fits *.fit *.xisf *.tif *.tiff *.png *.jpg *.jpeg);;All files (*)"
|
|
2231
|
+
self, self.tr("Choose Image"),
|
|
2232
|
+
"", self.tr("Images (*.fits *.fit *.xisf *.tif *.tiff *.png *.jpg *.jpeg);;All files (*)")
|
|
2223
2233
|
)
|
|
2224
2234
|
if f:
|
|
2225
2235
|
self.le_path.setText(f)
|
|
2226
2236
|
|
|
2227
2237
|
def _browse_in(self):
|
|
2228
|
-
d = QFileDialog.getExistingDirectory(self, "Choose input directory")
|
|
2238
|
+
d = QFileDialog.getExistingDirectory(self, self.tr("Choose input directory"))
|
|
2229
2239
|
if d: self.le_in.setText(d)
|
|
2230
2240
|
|
|
2231
2241
|
def _browse_out(self):
|
|
2232
|
-
d = QFileDialog.getExistingDirectory(self, "Choose output directory")
|
|
2242
|
+
d = QFileDialog.getExistingDirectory(self, self.tr("Choose output directory"))
|
|
2233
2243
|
if d: self.le_out.setText(d)
|
|
2234
2244
|
|
|
2235
2245
|
# ---------- actions ----------
|
|
2236
2246
|
def _run(self):
|
|
2237
2247
|
astap_exe = _get_astap_exe(self.settings)
|
|
2238
2248
|
if not astap_exe or not os.path.exists(astap_exe):
|
|
2239
|
-
self.status.setText("ASTAP path missing. Set Preferences → ASTAP executable.")
|
|
2240
|
-
QMessageBox.warning(self, "Plate Solver", "ASTAP path missing.\nSet it in Preferences → ASTAP executable.")
|
|
2249
|
+
self.status.setText(self.tr("ASTAP path missing. Set Preferences → ASTAP executable."))
|
|
2250
|
+
QMessageBox.warning(self, self.tr("Plate Solver"), self.tr("ASTAP path missing.\nSet it in Preferences → ASTAP executable."))
|
|
2241
2251
|
return
|
|
2242
2252
|
|
|
2243
2253
|
idx = self.cb_seed_mode.currentIndex()
|
|
@@ -2267,11 +2277,11 @@ class PlateSolverDialog(QDialog):
|
|
|
2267
2277
|
# Active view
|
|
2268
2278
|
doc = _active_doc_from_parent(self.parent())
|
|
2269
2279
|
if not doc:
|
|
2270
|
-
QMessageBox.information(self, "Plate Solver", "No active image view.")
|
|
2280
|
+
QMessageBox.information(self, self.tr("Plate Solver"), self.tr("No active image view."))
|
|
2271
2281
|
return
|
|
2272
2282
|
ok, res = plate_solve_doc_inplace(self, doc, self.settings)
|
|
2273
2283
|
if ok:
|
|
2274
|
-
self.status.setText("Solved with ASTAP (WCS + SIP applied to active doc).")
|
|
2284
|
+
self.status.setText(self.tr("Solved with ASTAP (WCS + SIP applied to active doc)."))
|
|
2275
2285
|
QTimer.singleShot(0, self.accept) # close when done
|
|
2276
2286
|
else:
|
|
2277
2287
|
self.status.setText(str(res))
|
|
@@ -2279,10 +2289,10 @@ class PlateSolverDialog(QDialog):
|
|
|
2279
2289
|
# Single file
|
|
2280
2290
|
path = self.le_path.text().strip()
|
|
2281
2291
|
if not path:
|
|
2282
|
-
QMessageBox.information(self, "Plate Solver", "Choose a file to solve.")
|
|
2292
|
+
QMessageBox.information(self, self.tr("Plate Solver"), self.tr("Choose a file to solve."))
|
|
2283
2293
|
return
|
|
2284
2294
|
if not os.path.exists(path):
|
|
2285
|
-
QMessageBox.warning(self, "Plate Solver", "Selected file does not exist.")
|
|
2295
|
+
QMessageBox.warning(self, self.tr("Plate Solver"), self.tr("Selected file does not exist."))
|
|
2286
2296
|
return
|
|
2287
2297
|
self._solve_file(path)
|
|
2288
2298
|
else:
|
|
@@ -2293,10 +2303,10 @@ class PlateSolverDialog(QDialog):
|
|
|
2293
2303
|
try:
|
|
2294
2304
|
image_data, original_header, bit_depth, is_mono = load_image(path)
|
|
2295
2305
|
except Exception as e:
|
|
2296
|
-
QMessageBox.warning(self, "Plate Solver",
|
|
2306
|
+
QMessageBox.warning(self, self.tr("Plate Solver"), self.tr("Cannot read image:\n{0}").format(e))
|
|
2297
2307
|
return
|
|
2298
2308
|
if image_data is None:
|
|
2299
|
-
QMessageBox.warning(self, "Plate Solver", "Unsupported or unreadable image.")
|
|
2309
|
+
QMessageBox.warning(self, self.tr("Plate Solver"), self.tr("Unsupported or unreadable image."))
|
|
2300
2310
|
return
|
|
2301
2311
|
|
|
2302
2312
|
# Seed header from original_header
|
|
@@ -2323,9 +2333,9 @@ class PlateSolverDialog(QDialog):
|
|
|
2323
2333
|
# Save-as using legacy.save_image() with ORIGINAL pixels (not normalized)
|
|
2324
2334
|
save_path, _ = QFileDialog.getSaveFileName(
|
|
2325
2335
|
self,
|
|
2326
|
-
"Save Plate-Solved FITS",
|
|
2336
|
+
self.tr("Save Plate-Solved FITS"),
|
|
2327
2337
|
"",
|
|
2328
|
-
"FITS files (*.fits *.fit)"
|
|
2338
|
+
self.tr("FITS files (*.fits *.fit)")
|
|
2329
2339
|
)
|
|
2330
2340
|
if save_path:
|
|
2331
2341
|
try:
|
|
@@ -2343,22 +2353,22 @@ class PlateSolverDialog(QDialog):
|
|
|
2343
2353
|
original_header=h2,
|
|
2344
2354
|
is_mono=is_mono
|
|
2345
2355
|
)
|
|
2346
|
-
self.status.setText(
|
|
2356
|
+
self.status.setText(self.tr("Solved FITS saved:\n{0}").format(save_path))
|
|
2347
2357
|
QTimer.singleShot(0, self.accept)
|
|
2348
2358
|
except Exception as e:
|
|
2349
|
-
QMessageBox.critical(self, "Save Error",
|
|
2359
|
+
QMessageBox.critical(self, self.tr("Save Error"), self.tr("Failed to save: {0}").format(e))
|
|
2350
2360
|
else:
|
|
2351
|
-
self.status.setText("Solved (not saved).")
|
|
2361
|
+
self.status.setText(self.tr("Solved (not saved)."))
|
|
2352
2362
|
|
|
2353
2363
|
|
|
2354
2364
|
def _run_batch(self):
|
|
2355
2365
|
in_dir = self.le_in.text().strip()
|
|
2356
2366
|
out_dir = self.le_out.text().strip()
|
|
2357
2367
|
if not in_dir or not os.path.isdir(in_dir):
|
|
2358
|
-
QMessageBox.warning(self, "Batch", "Please choose a valid input directory.")
|
|
2368
|
+
QMessageBox.warning(self, self.tr("Batch"), self.tr("Please choose a valid input directory."))
|
|
2359
2369
|
return
|
|
2360
2370
|
if not out_dir or not os.path.isdir(out_dir):
|
|
2361
|
-
QMessageBox.warning(self, "Batch", "Please choose a valid output directory.")
|
|
2371
|
+
QMessageBox.warning(self, self.tr("Batch"), self.tr("Please choose a valid output directory."))
|
|
2362
2372
|
return
|
|
2363
2373
|
|
|
2364
2374
|
exts = {".xisf", ".fits", ".fit", ".tif", ".tiff", ".png", ".jpg", ".jpeg"}
|
|
@@ -2368,24 +2378,24 @@ class PlateSolverDialog(QDialog):
|
|
|
2368
2378
|
if os.path.splitext(f)[1].lower() in exts
|
|
2369
2379
|
]
|
|
2370
2380
|
if not files:
|
|
2371
|
-
QMessageBox.information(self, "Batch", "No acceptable image files found.")
|
|
2381
|
+
QMessageBox.information(self, self.tr("Batch"), self.tr("No acceptable image files found."))
|
|
2372
2382
|
return
|
|
2373
2383
|
|
|
2374
2384
|
self.log.clear()
|
|
2375
|
-
self.log.append(
|
|
2385
|
+
self.log.append(self.tr("Found {0} files. Starting batch…").format(len(files)))
|
|
2376
2386
|
QApplication.processEvents()
|
|
2377
2387
|
|
|
2378
2388
|
for path in files:
|
|
2379
2389
|
base = os.path.splitext(os.path.basename(path))[0]
|
|
2380
2390
|
out = os.path.join(out_dir, base + "_plate_solved.fits")
|
|
2381
|
-
self.log.append(f"▶ {path}")
|
|
2391
|
+
self.log.append(f"▶ {path}") # Symbol, no need to translate
|
|
2382
2392
|
QApplication.processEvents()
|
|
2383
2393
|
|
|
2384
2394
|
try:
|
|
2385
2395
|
# Load using legacy.load_image()
|
|
2386
2396
|
image_data, original_header, bit_depth, is_mono = load_image(path)
|
|
2387
2397
|
if image_data is None:
|
|
2388
|
-
self.log.append(" ❌ Failed to load")
|
|
2398
|
+
self.log.append(self.tr(" ❌ Failed to load"))
|
|
2389
2399
|
continue
|
|
2390
2400
|
|
|
2391
2401
|
# Seed header from original_header
|
|
@@ -2424,12 +2434,12 @@ class PlateSolverDialog(QDialog):
|
|
|
2424
2434
|
original_header=h2,
|
|
2425
2435
|
is_mono=is_mono
|
|
2426
2436
|
)
|
|
2427
|
-
self.log.append(" ✔ saved: " + out)
|
|
2437
|
+
self.log.append(self.tr(" ✔ saved: ") + out)
|
|
2428
2438
|
|
|
2429
2439
|
except Exception as e:
|
|
2430
|
-
self.log.append(" ❌ error: " + str(e))
|
|
2440
|
+
self.log.append(self.tr(" ❌ error: ") + str(e))
|
|
2431
2441
|
|
|
2432
2442
|
QApplication.processEvents()
|
|
2433
2443
|
|
|
2434
|
-
self.log.append("Batch plate solving completed.")
|
|
2444
|
+
self.log.append(self.tr("Batch plate solving completed."))
|
|
2435
2445
|
|