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.
Potentially problematic release.
This version of setiastrosuitepro might be problematic. Click here for more details.
- 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/copyastro.py
CHANGED
|
@@ -15,6 +15,9 @@ class CopyAstrometryDialog(QDialog):
|
|
|
15
15
|
def __init__(self, parent=None, target=None):
|
|
16
16
|
super().__init__(parent)
|
|
17
17
|
self.setWindowTitle("Copy Astrometric Solution")
|
|
18
|
+
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
19
|
+
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
20
|
+
self.setModal(False)
|
|
18
21
|
self.setMinimumWidth(420)
|
|
19
22
|
|
|
20
23
|
self._mw = parent
|
|
@@ -270,7 +270,10 @@ class CosmicClarityDialogPro(QDialog):
|
|
|
270
270
|
logging.debug(f"Exception suppressed: {type(e).__name__}: {e}")
|
|
271
271
|
QTimer.singleShot(0, self.reject)
|
|
272
272
|
return
|
|
273
|
-
self.setWindowTitle("Cosmic Clarity")
|
|
273
|
+
self.setWindowTitle(self.tr("Cosmic Clarity"))
|
|
274
|
+
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
275
|
+
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
276
|
+
self.setModal(False)
|
|
274
277
|
if icon:
|
|
275
278
|
try: self.setWindowIcon(icon)
|
|
276
279
|
except Exception as e:
|
|
@@ -285,19 +288,19 @@ class CosmicClarityDialogPro(QDialog):
|
|
|
285
288
|
v = QVBoxLayout(self)
|
|
286
289
|
|
|
287
290
|
# ---------------- Controls ----------------
|
|
288
|
-
grp = QGroupBox("Parameters")
|
|
291
|
+
grp = QGroupBox(self.tr("Parameters"))
|
|
289
292
|
grid = QGridLayout(grp)
|
|
290
293
|
|
|
291
294
|
# Mode
|
|
292
|
-
grid.addWidget(QLabel("Mode:"), 0, 0)
|
|
295
|
+
grid.addWidget(QLabel(self.tr("Mode:")), 0, 0)
|
|
293
296
|
self.cmb_mode = QComboBox()
|
|
294
297
|
self.cmb_mode.addItems(["Sharpen", "Denoise", "Both", "Super Resolution"])
|
|
295
298
|
self.cmb_mode.currentIndexChanged.connect(self._mode_changed)
|
|
296
299
|
grid.addWidget(self.cmb_mode, 0, 1, 1, 2)
|
|
297
300
|
|
|
298
301
|
# GPU
|
|
299
|
-
grid.addWidget(QLabel("Use GPU:"), 1, 0)
|
|
300
|
-
self.cmb_gpu = QComboBox(); self.cmb_gpu.addItems(["Yes", "No"])
|
|
302
|
+
grid.addWidget(QLabel(self.tr("Use GPU:")), 1, 0)
|
|
303
|
+
self.cmb_gpu = QComboBox(); self.cmb_gpu.addItems([self.tr("Yes"), self.tr("No")])
|
|
301
304
|
grid.addWidget(self.cmb_gpu, 1, 1)
|
|
302
305
|
|
|
303
306
|
# Sharpen block
|
|
@@ -364,8 +367,8 @@ class CosmicClarityDialogPro(QDialog):
|
|
|
364
367
|
|
|
365
368
|
# Buttons
|
|
366
369
|
row = QHBoxLayout()
|
|
367
|
-
b_run = QPushButton("Execute"); b_run.clicked.connect(self._run_main)
|
|
368
|
-
b_close = QPushButton("Close"); b_close.clicked.connect(self.reject)
|
|
370
|
+
b_run = QPushButton(self.tr("Execute")); b_run.clicked.connect(self._run_main)
|
|
371
|
+
b_close = QPushButton(self.tr("Close")); b_close.clicked.connect(self.reject)
|
|
369
372
|
row.addStretch(1); row.addWidget(b_run); row.addWidget(b_close)
|
|
370
373
|
v.addLayout(row)
|
|
371
374
|
|
|
@@ -632,7 +635,53 @@ class CosmicClarityDialogPro(QDialog):
|
|
|
632
635
|
if self._wait: self._wait.close(); self._wait = None
|
|
633
636
|
if self._wait_thread: self._wait_thread.stop(); self._wait_thread = None
|
|
634
637
|
|
|
635
|
-
|
|
638
|
+
has_more = bool(self._op_queue)
|
|
639
|
+
|
|
640
|
+
# --- Optimization: Chained Execution Fast Path ---
|
|
641
|
+
# If we have more steps, skip the expensive load/display/save cycle.
|
|
642
|
+
# Just move the output file to be the input for the next step.
|
|
643
|
+
if has_more:
|
|
644
|
+
if not out_path or not os.path.exists(out_path):
|
|
645
|
+
QMessageBox.critical(self, "Cosmic Clarity", "Output file missing during chain execution.")
|
|
646
|
+
self._op_queue.clear()
|
|
647
|
+
return
|
|
648
|
+
|
|
649
|
+
base = self._base_name()
|
|
650
|
+
next_in = os.path.join(self.cosmic_root, "input", f"{base}.tif")
|
|
651
|
+
prev_in = getattr(self, "_current_input", None)
|
|
652
|
+
|
|
653
|
+
try:
|
|
654
|
+
# Direct move/copy instead of decode+encode
|
|
655
|
+
if os.path.abspath(out_path) != os.path.abspath(next_in):
|
|
656
|
+
# Windows cannot atomic replace if target exists via os.rename usually,
|
|
657
|
+
# but shutil.move is generally robust.
|
|
658
|
+
# We remove target first to be sure.
|
|
659
|
+
if os.path.exists(next_in):
|
|
660
|
+
os.remove(next_in)
|
|
661
|
+
shutil.move(out_path, next_in)
|
|
662
|
+
|
|
663
|
+
# Ensure stability of the *new* input
|
|
664
|
+
if not _wait_stable_file(next_in):
|
|
665
|
+
QMessageBox.critical(self, "Cosmic Clarity", "Staged input for next step is unstable.")
|
|
666
|
+
self._op_queue.clear()
|
|
667
|
+
return
|
|
668
|
+
|
|
669
|
+
self._current_input = next_in
|
|
670
|
+
|
|
671
|
+
# Cleanup previous input if distinct
|
|
672
|
+
if prev_in and prev_in != next_in and os.path.exists(prev_in):
|
|
673
|
+
os.remove(prev_in)
|
|
674
|
+
|
|
675
|
+
except Exception as e:
|
|
676
|
+
QMessageBox.critical(self, "Cosmic Clarity", f"Failed to stage next step:\n{e}")
|
|
677
|
+
self._op_queue.clear()
|
|
678
|
+
return
|
|
679
|
+
|
|
680
|
+
# Trigger next step immediately
|
|
681
|
+
QTimer.singleShot(50, self._run_next)
|
|
682
|
+
return
|
|
683
|
+
|
|
684
|
+
# --- Final Step (or Single Step): Load and Display ---
|
|
636
685
|
try:
|
|
637
686
|
img, hdr, bd, mono = load_image(out_path)
|
|
638
687
|
if img is None:
|
|
@@ -643,61 +692,34 @@ class CosmicClarityDialogPro(QDialog):
|
|
|
643
692
|
|
|
644
693
|
dest = img.astype(np.float32, copy=False)
|
|
645
694
|
|
|
646
|
-
# Apply to document
|
|
695
|
+
# Apply to document
|
|
647
696
|
step_title = f"Cosmic Clarity – {mode.title()}"
|
|
648
697
|
create_new = (self.cmb_target.currentIndex() == 1)
|
|
649
698
|
|
|
650
699
|
if create_new:
|
|
651
700
|
ok = self._spawn_new_doc_from_numpy(dest, step_title)
|
|
652
701
|
if not ok:
|
|
653
|
-
# fall back to overwriting if we couldn’t spawn a new doc
|
|
654
702
|
self._apply_to_active(dest, step_title)
|
|
655
703
|
else:
|
|
656
704
|
self._apply_to_active(dest, step_title)
|
|
657
705
|
|
|
658
|
-
#
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
706
|
+
# Cleanup final output
|
|
707
|
+
if out_path and os.path.exists(out_path):
|
|
708
|
+
try: os.remove(out_path)
|
|
709
|
+
except OSError: pass
|
|
710
|
+
|
|
711
|
+
# Cleanup final input
|
|
712
|
+
prev_in = getattr(self, "_current_input", None)
|
|
713
|
+
if prev_in and os.path.exists(prev_in):
|
|
714
|
+
try: os.remove(prev_in)
|
|
715
|
+
except OSError: pass
|
|
716
|
+
|
|
717
|
+
# Final purge
|
|
664
718
|
try:
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
getattr(self.doc, "is_mono", False))
|
|
670
|
-
_atomic_fsync_replace(_writer, next_in)
|
|
671
|
-
if not _wait_stable_file(next_in):
|
|
672
|
-
QMessageBox.critical(self, "Cosmic Clarity", "Staging for next step failed (not stable).")
|
|
673
|
-
self._op_queue.clear()
|
|
674
|
-
return
|
|
675
|
-
self._current_input = next_in
|
|
676
|
-
|
|
677
|
-
# Now it’s safe to clean up the produced output
|
|
678
|
-
if out_path and os.path.exists(out_path):
|
|
679
|
-
os.remove(out_path)
|
|
680
|
-
|
|
681
|
-
# Remove the previous input file if it’s different from the new one
|
|
682
|
-
if prev_in and prev_in != next_in and os.path.exists(prev_in):
|
|
683
|
-
os.remove(prev_in)
|
|
684
|
-
|
|
685
|
-
except Exception as e:
|
|
686
|
-
QMessageBox.critical(self, "Cosmic Clarity", f"Failed while staging next step:\n{e}")
|
|
687
|
-
self._op_queue.clear()
|
|
688
|
-
return
|
|
689
|
-
|
|
690
|
-
# Continue or finish
|
|
691
|
-
if has_more:
|
|
692
|
-
QTimer.singleShot(100, self._run_next)
|
|
693
|
-
else:
|
|
694
|
-
# Nothing else queued — we're done
|
|
695
|
-
try:
|
|
696
|
-
# 🔸 Final cleanup: clear both input & output
|
|
697
|
-
_purge_cc_io(self.cosmic_root, clear_input=True, clear_output=True)
|
|
698
|
-
except Exception:
|
|
699
|
-
pass
|
|
700
|
-
self.accept()
|
|
719
|
+
_purge_cc_io(self.cosmic_root, clear_input=True, clear_output=True)
|
|
720
|
+
except Exception:
|
|
721
|
+
pass
|
|
722
|
+
self.accept()
|
|
701
723
|
|
|
702
724
|
|
|
703
725
|
def _on_wait_error(self, msg: str):
|
|
@@ -1009,6 +1031,9 @@ class CosmicClaritySatelliteDialogPro(QDialog):
|
|
|
1009
1031
|
def __init__(self, parent, doc=None, icon: QIcon | None = None):
|
|
1010
1032
|
super().__init__(parent)
|
|
1011
1033
|
self.setWindowTitle("Cosmic Clarity – Satellite Removal")
|
|
1034
|
+
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
1035
|
+
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
1036
|
+
self.setModal(False)
|
|
1012
1037
|
if icon:
|
|
1013
1038
|
try: self.setWindowIcon(icon)
|
|
1014
1039
|
except Exception as e:
|
|
@@ -270,8 +270,17 @@ class CropDialogPro(QDialog):
|
|
|
270
270
|
|
|
271
271
|
def __init__(self, parent, document):
|
|
272
272
|
super().__init__(parent)
|
|
273
|
-
self.setWindowTitle("Crop Tool")
|
|
273
|
+
self.setWindowTitle(self.tr("Crop Tool"))
|
|
274
|
+
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
275
|
+
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
276
|
+
self.setModal(False)
|
|
277
|
+
self._main = parent
|
|
274
278
|
self.doc = document
|
|
279
|
+
|
|
280
|
+
# Connect to active document change signal
|
|
281
|
+
if hasattr(self._main, "currentDocumentChanged"):
|
|
282
|
+
self._main.currentDocumentChanged.connect(self._on_active_doc_changed)
|
|
283
|
+
|
|
275
284
|
self._rect_item: Optional[ResizableRotatableRectItem] = None
|
|
276
285
|
self._pix_item: Optional[QGraphicsPixmapItem] = None
|
|
277
286
|
self._drawing = False
|
|
@@ -281,19 +290,29 @@ class CropDialogPro(QDialog):
|
|
|
281
290
|
# ---------- layout ----------
|
|
282
291
|
main = QVBoxLayout(self)
|
|
283
292
|
|
|
284
|
-
info = QLabel(
|
|
293
|
+
info = QLabel(self.tr(
|
|
285
294
|
"• Click–drag to draw a crop\n"
|
|
286
295
|
"• Drag corner handles to resize\n"
|
|
287
296
|
"• Shift + drag on box to rotate"
|
|
288
|
-
); info.setStyleSheet("color: gray; font-style: italic;")
|
|
297
|
+
)); info.setStyleSheet("color: gray; font-style: italic;")
|
|
289
298
|
main.addWidget(info)
|
|
290
299
|
|
|
291
300
|
# aspect row
|
|
292
301
|
row = QHBoxLayout()
|
|
293
302
|
row.addStretch(1)
|
|
294
|
-
row.addWidget(QLabel("Aspect Ratio:"))
|
|
303
|
+
row.addWidget(QLabel(self.tr("Aspect Ratio:")))
|
|
295
304
|
self.cmb_ar = QComboBox()
|
|
296
|
-
self.cmb_ar.addItems([
|
|
305
|
+
self.cmb_ar.addItems([
|
|
306
|
+
self.tr("Free"), self.tr("Original"),
|
|
307
|
+
"1:1",
|
|
308
|
+
"3:2", "2:3",
|
|
309
|
+
"4:3", "3:4",
|
|
310
|
+
"4:5", "5:4",
|
|
311
|
+
"16:9", "9:16",
|
|
312
|
+
"21:9", "9:21",
|
|
313
|
+
"2:1", "1:2",
|
|
314
|
+
"3:5", "5:3",
|
|
315
|
+
])
|
|
297
316
|
row.addWidget(self.cmb_ar)
|
|
298
317
|
row.addStretch(1)
|
|
299
318
|
main.addLayout(row)
|
|
@@ -301,7 +320,7 @@ class CropDialogPro(QDialog):
|
|
|
301
320
|
# typed margins (pixels): Top, Right, Bottom, Left
|
|
302
321
|
margins_row = QHBoxLayout()
|
|
303
322
|
margins_row.addStretch(1)
|
|
304
|
-
margins_row.addWidget(QLabel("Margins (px):"))
|
|
323
|
+
margins_row.addWidget(QLabel(self.tr("Margins (px):")))
|
|
305
324
|
self.sb_top = QSpinBox(); self.sb_top.setSuffix(" px")
|
|
306
325
|
self.sb_right = QSpinBox(); self.sb_right.setSuffix(" px")
|
|
307
326
|
self.sb_bottom = QSpinBox(); self.sb_bottom.setSuffix(" px")
|
|
@@ -312,16 +331,16 @@ class CropDialogPro(QDialog):
|
|
|
312
331
|
sb.setRange(0, 1_000_000)
|
|
313
332
|
|
|
314
333
|
# labels inline for clarity
|
|
315
|
-
margins_row.addWidget(QLabel("Top"))
|
|
334
|
+
margins_row.addWidget(QLabel(self.tr("Top")))
|
|
316
335
|
margins_row.addWidget(self.sb_top)
|
|
317
336
|
margins_row.addSpacing(8)
|
|
318
|
-
margins_row.addWidget(QLabel("Right"))
|
|
337
|
+
margins_row.addWidget(QLabel(self.tr("Right")))
|
|
319
338
|
margins_row.addWidget(self.sb_right)
|
|
320
339
|
margins_row.addSpacing(8)
|
|
321
|
-
margins_row.addWidget(QLabel("Bottom"))
|
|
340
|
+
margins_row.addWidget(QLabel(self.tr("Bottom")))
|
|
322
341
|
margins_row.addWidget(self.sb_bottom)
|
|
323
342
|
margins_row.addSpacing(8)
|
|
324
|
-
margins_row.addWidget(QLabel("Left"))
|
|
343
|
+
margins_row.addWidget(QLabel(self.tr("Left")))
|
|
325
344
|
margins_row.addWidget(self.sb_left)
|
|
326
345
|
margins_row.addStretch(1)
|
|
327
346
|
main.addLayout(margins_row)
|
|
@@ -354,10 +373,10 @@ class CropDialogPro(QDialog):
|
|
|
354
373
|
zoom_row = QHBoxLayout()
|
|
355
374
|
zoom_row.addStretch(1)
|
|
356
375
|
|
|
357
|
-
self.btn_zoom_out = themed_toolbtn("zoom-out", "Zoom Out")
|
|
358
|
-
self.btn_zoom_in = themed_toolbtn("zoom-in", "Zoom In")
|
|
359
|
-
self.btn_zoom_100 = themed_toolbtn("zoom-original", "Zoom 100%")
|
|
360
|
-
self.btn_zoom_fit = themed_toolbtn("zoom-fit-best", "Fit to View")
|
|
376
|
+
self.btn_zoom_out = themed_toolbtn("zoom-out", self.tr("Zoom Out"))
|
|
377
|
+
self.btn_zoom_in = themed_toolbtn("zoom-in", self.tr("Zoom In"))
|
|
378
|
+
self.btn_zoom_100 = themed_toolbtn("zoom-original", self.tr("Zoom 100%"))
|
|
379
|
+
self.btn_zoom_fit = themed_toolbtn("zoom-fit-best", self.tr("Fit to View"))
|
|
361
380
|
|
|
362
381
|
for b in (self.btn_zoom_out, self.btn_zoom_in, self.btn_zoom_100, self.btn_zoom_fit):
|
|
363
382
|
zoom_row.addWidget(b)
|
|
@@ -367,7 +386,7 @@ class CropDialogPro(QDialog):
|
|
|
367
386
|
|
|
368
387
|
dim_row = QHBoxLayout()
|
|
369
388
|
dim_row.addStretch(1)
|
|
370
|
-
self.lbl_dims = QLabel("Selection: —")
|
|
389
|
+
self.lbl_dims = QLabel(self.tr("Selection: —"))
|
|
371
390
|
self.lbl_dims.setStyleSheet("color: gray;")
|
|
372
391
|
dim_row.addWidget(self.lbl_dims)
|
|
373
392
|
dim_row.addStretch(1)
|
|
@@ -381,11 +400,11 @@ class CropDialogPro(QDialog):
|
|
|
381
400
|
|
|
382
401
|
# buttons
|
|
383
402
|
btn_row = QHBoxLayout()
|
|
384
|
-
self.btn_autostretch = QPushButton("Toggle Autostretch")
|
|
385
|
-
self.btn_prev = QPushButton("Load Previous Crop")
|
|
386
|
-
self.btn_apply = QPushButton("Apply")
|
|
387
|
-
self.btn_batch = QPushButton("Batch Crop (all open)")
|
|
388
|
-
self.btn_close = QToolButton(); self.btn_close.setText("Close")
|
|
403
|
+
self.btn_autostretch = QPushButton(self.tr("Toggle Autostretch"))
|
|
404
|
+
self.btn_prev = QPushButton(self.tr("Load Previous Crop"))
|
|
405
|
+
self.btn_apply = QPushButton(self.tr("Apply"))
|
|
406
|
+
self.btn_batch = QPushButton(self.tr("Batch Crop (all open)"))
|
|
407
|
+
self.btn_close = QToolButton(); self.btn_close.setText(self.tr("Close"))
|
|
389
408
|
for b in (self.btn_autostretch, self.btn_prev, self.btn_apply, self.btn_batch, self.btn_close):
|
|
390
409
|
btn_row.addWidget(b)
|
|
391
410
|
main.addLayout(btn_row)
|
|
@@ -463,6 +482,14 @@ class CropDialogPro(QDialog):
|
|
|
463
482
|
arr = arr[..., 0]
|
|
464
483
|
return np.clip(arr, 0.0, 1.0)
|
|
465
484
|
|
|
485
|
+
def _on_active_doc_changed(self, doc):
|
|
486
|
+
"""Called when user clicks a different image window."""
|
|
487
|
+
if doc is None or getattr(doc, "image", None) is None:
|
|
488
|
+
return
|
|
489
|
+
self.doc = doc
|
|
490
|
+
self._rect_item = None
|
|
491
|
+
self._load_from_doc()
|
|
492
|
+
|
|
466
493
|
def _load_from_doc(self):
|
|
467
494
|
self._full01 = self._img01_from_doc()
|
|
468
495
|
self._orig_h, self._orig_w = self._full01.shape[:2]
|
|
@@ -487,7 +514,7 @@ class CropDialogPro(QDialog):
|
|
|
487
514
|
|
|
488
515
|
def _set_dim_label_none(self):
|
|
489
516
|
if hasattr(self, "lbl_dims"):
|
|
490
|
-
self.lbl_dims.setText("Selection: —")
|
|
517
|
+
self.lbl_dims.setText(self.tr("Selection: —"))
|
|
491
518
|
|
|
492
519
|
def _update_dim_label_from_corners(self, corners_scene):
|
|
493
520
|
"""
|
|
@@ -509,7 +536,7 @@ class CropDialogPro(QDialog):
|
|
|
509
536
|
height = float(np.linalg.norm(src[3] - src[0]))
|
|
510
537
|
|
|
511
538
|
self.lbl_dims.setText(
|
|
512
|
-
|
|
539
|
+
self.tr("Selection: {0}×{1} px").format(int(round(height)), int(round(width)))
|
|
513
540
|
)
|
|
514
541
|
|
|
515
542
|
def _update_dim_label_from_rect_item(self):
|
|
@@ -691,17 +718,25 @@ class CropDialogPro(QDialog):
|
|
|
691
718
|
|
|
692
719
|
def _current_ar_value(self) -> Optional[float]:
|
|
693
720
|
txt = self.cmb_ar.currentText()
|
|
694
|
-
if txt == "Free": return None
|
|
695
|
-
if txt == "Original": return self._orig_w / self._orig_h
|
|
721
|
+
if txt == self.tr("Free"): return None
|
|
722
|
+
if txt == self.tr("Original"): return self._orig_w / self._orig_h
|
|
696
723
|
a, b = map(float, txt.split(":")); return a / b
|
|
697
724
|
|
|
698
725
|
def _apply_ar_to_rect(self, r: QRectF, live: bool, scene_pt: QPointF) -> QRectF:
|
|
699
|
-
|
|
700
|
-
if
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
726
|
+
ar = self._current_ar_value()
|
|
727
|
+
if ar is None:
|
|
728
|
+
return r
|
|
729
|
+
|
|
730
|
+
# Calculate height from width using current aspect ratio
|
|
731
|
+
w = r.width()
|
|
732
|
+
h = w / ar
|
|
733
|
+
|
|
734
|
+
# Anchor to the click origin, adjust height based on drag direction
|
|
735
|
+
if scene_pt.y() < self._origin.y():
|
|
736
|
+
r.setTop(r.bottom() - h)
|
|
737
|
+
else:
|
|
738
|
+
r.setBottom(r.top() + h)
|
|
739
|
+
|
|
705
740
|
return r.normalized()
|
|
706
741
|
|
|
707
742
|
def _draw_live_rect(self, r: QRectF):
|
|
@@ -743,7 +778,7 @@ class CropDialogPro(QDialog):
|
|
|
743
778
|
|
|
744
779
|
def _load_previous(self):
|
|
745
780
|
if CropDialogPro._prev_rect is None:
|
|
746
|
-
QMessageBox.information(self, "No Previous", "No previous crop stored.")
|
|
781
|
+
QMessageBox.information(self, self.tr("No Previous"), self.tr("No previous crop stored."))
|
|
747
782
|
return
|
|
748
783
|
if self._rect_item:
|
|
749
784
|
self.scene.removeItem(self._rect_item)
|
|
@@ -770,7 +805,7 @@ class CropDialogPro(QDialog):
|
|
|
770
805
|
|
|
771
806
|
def _apply_one(self):
|
|
772
807
|
if not self._rect_item:
|
|
773
|
-
QMessageBox.warning(self, "No Selection", "Draw & finalize a crop first.")
|
|
808
|
+
QMessageBox.warning(self, self.tr("No Selection"), self.tr("Draw & finalize a crop first."))
|
|
774
809
|
return
|
|
775
810
|
|
|
776
811
|
corners = self._corners_scene()
|
|
@@ -789,7 +824,7 @@ class CropDialogPro(QDialog):
|
|
|
789
824
|
# Pixel-perfect slice
|
|
790
825
|
bounds = self._int_bounds_from_quad(src, W_img, H_img)
|
|
791
826
|
if bounds is None:
|
|
792
|
-
QMessageBox.critical(self, "Apply failed", "Invalid crop bounds.")
|
|
827
|
+
QMessageBox.critical(self, self.tr("Apply failed"), self.tr("Invalid crop bounds."))
|
|
793
828
|
return
|
|
794
829
|
x0, x1, y0, y1 = bounds
|
|
795
830
|
out = self._full01[y0:y1, x0:x1].copy()
|
|
@@ -806,7 +841,7 @@ class CropDialogPro(QDialog):
|
|
|
806
841
|
w_out = int(round(width))
|
|
807
842
|
h_out = int(round(height))
|
|
808
843
|
if w_out <= 0 or h_out <= 0:
|
|
809
|
-
QMessageBox.critical(self, "Apply failed", "Invalid crop size.")
|
|
844
|
+
QMessageBox.critical(self, self.tr("Apply failed"), self.tr("Invalid crop size."))
|
|
810
845
|
return
|
|
811
846
|
|
|
812
847
|
out = cv2.warpPerspective(
|
|
@@ -832,11 +867,11 @@ class CropDialogPro(QDialog):
|
|
|
832
867
|
self.crop_applied.emit(out)
|
|
833
868
|
self.accept()
|
|
834
869
|
except Exception as e:
|
|
835
|
-
QMessageBox.critical(self, "Apply failed", str(e))
|
|
870
|
+
QMessageBox.critical(self, self.tr("Apply failed"), str(e))
|
|
836
871
|
|
|
837
872
|
def _apply_batch(self):
|
|
838
873
|
if not self._rect_item:
|
|
839
|
-
QMessageBox.warning(self, "No Selection", "Draw & finalize a crop first.")
|
|
874
|
+
QMessageBox.warning(self, self.tr("No Selection"), self.tr("Draw & finalize a crop first."))
|
|
840
875
|
return
|
|
841
876
|
|
|
842
877
|
# Normalize the crop polygon to THIS image size
|
|
@@ -855,12 +890,12 @@ class CropDialogPro(QDialog):
|
|
|
855
890
|
docs.append(d)
|
|
856
891
|
|
|
857
892
|
if not docs:
|
|
858
|
-
QMessageBox.information(self, "No Images", "No open images to crop.")
|
|
893
|
+
QMessageBox.information(self, self.tr("No Images"), self.tr("No open images to crop."))
|
|
859
894
|
return
|
|
860
895
|
|
|
861
896
|
ok = QMessageBox.question(
|
|
862
|
-
self, "Confirm Batch",
|
|
863
|
-
|
|
897
|
+
self, self.tr("Confirm Batch"),
|
|
898
|
+
self.tr("Apply this crop to {0} open image(s)?").format(len(docs)),
|
|
864
899
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
|
865
900
|
QMessageBox.StandardButton.No
|
|
866
901
|
)
|
|
@@ -913,7 +948,7 @@ class CropDialogPro(QDialog):
|
|
|
913
948
|
except Exception:
|
|
914
949
|
pass
|
|
915
950
|
|
|
916
|
-
QMessageBox.information(self, "Batch Crop", "Applied crop to all open images. Any Astrometric Solutions has been updated.")
|
|
951
|
+
QMessageBox.information(self, self.tr("Batch Crop"), self.tr("Applied crop to all open images. Any Astrometric Solutions has been updated."))
|
|
917
952
|
if last_cropped is not None:
|
|
918
953
|
self.crop_applied.emit(last_cropped)
|
|
919
954
|
self.accept()
|
|
@@ -936,13 +971,13 @@ class CropDialogPro(QDialog):
|
|
|
936
971
|
size_txt = f"{size[0]}×{size[1]}" if size else "?"
|
|
937
972
|
extra = f"\n{batch_note}" if batch_note else ""
|
|
938
973
|
msg = (
|
|
939
|
-
"Astrometric solution updated ✔️\n\n"
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
974
|
+
self.tr("Astrometric solution updated ✔️\n\n") +
|
|
975
|
+
self.tr("Model: {0} Image: {1}\n").format(sip_txt, size_txt) +
|
|
976
|
+
self.tr("CRVAL: ({0:.6f}, {1:.6f}) → ({2:.6f}, {3:.6f})\n").format(b_ra, b_dec, a_ra, a_dec) +
|
|
977
|
+
self.tr("Fit residuals: RMS {0:.3f}\" (p95 {1:.3f}\")").format(rms, p95) +
|
|
943
978
|
f"{extra}"
|
|
944
979
|
)
|
|
945
|
-
QMessageBox.information(self, "WCS Updated", msg)
|
|
980
|
+
QMessageBox.information(self, self.tr("WCS Updated"), msg)
|
|
946
981
|
except Exception:
|
|
947
982
|
# Be quiet if formatting fails
|
|
948
983
|
pass
|