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
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# src/setiastro/saspro/versioning.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
def _read_pyproject_version(start: Path) -> str | None:
|
|
8
|
+
"""
|
|
9
|
+
Walk upward from 'start' looking for pyproject.toml,
|
|
10
|
+
return [tool.poetry].version if found.
|
|
11
|
+
"""
|
|
12
|
+
# Python 3.11+: tomllib; Python 3.10: tomli
|
|
13
|
+
try:
|
|
14
|
+
import tomllib as _toml # type: ignore
|
|
15
|
+
except Exception:
|
|
16
|
+
try:
|
|
17
|
+
import tomli as _toml # type: ignore
|
|
18
|
+
except Exception:
|
|
19
|
+
_toml = None
|
|
20
|
+
|
|
21
|
+
if _toml is None:
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
cur = start
|
|
25
|
+
for _ in range(8): # don't walk forever
|
|
26
|
+
pp = cur / "pyproject.toml"
|
|
27
|
+
if pp.exists():
|
|
28
|
+
try:
|
|
29
|
+
data = _toml.loads(pp.read_text(encoding="utf-8"))
|
|
30
|
+
v = (
|
|
31
|
+
data.get("tool", {})
|
|
32
|
+
.get("poetry", {})
|
|
33
|
+
.get("version", None)
|
|
34
|
+
)
|
|
35
|
+
if isinstance(v, str) and v.strip():
|
|
36
|
+
return v.strip()
|
|
37
|
+
except Exception:
|
|
38
|
+
return None
|
|
39
|
+
if cur.parent == cur:
|
|
40
|
+
break
|
|
41
|
+
cur = cur.parent
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
def get_app_version(dist_name: str = "setiastrosuitepro") -> str:
|
|
45
|
+
"""
|
|
46
|
+
Single source of truth for SASpro version.
|
|
47
|
+
|
|
48
|
+
Order:
|
|
49
|
+
0) build_info.py (best for PyInstaller frozen builds)
|
|
50
|
+
1) installed distribution metadata (best for pip installs)
|
|
51
|
+
2) pyproject.toml (best for running from source checkout)
|
|
52
|
+
3) safe fallback
|
|
53
|
+
"""
|
|
54
|
+
# 0) build_info (PyInstaller-friendly)
|
|
55
|
+
try:
|
|
56
|
+
from ._generated.build_info import APP_VERSION
|
|
57
|
+
if isinstance(APP_VERSION, str) and APP_VERSION.strip() and APP_VERSION.strip() != "0.0.0":
|
|
58
|
+
return APP_VERSION.strip()
|
|
59
|
+
except Exception:
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
# 1) Installed package metadata
|
|
63
|
+
try:
|
|
64
|
+
from importlib.metadata import version as _dist_version
|
|
65
|
+
v = _dist_version(dist_name)
|
|
66
|
+
if v and v != "0.1.0":
|
|
67
|
+
return v
|
|
68
|
+
except Exception:
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
# 2) Source tree pyproject.toml
|
|
72
|
+
here = Path(__file__).resolve()
|
|
73
|
+
v2 = _read_pyproject_version(here.parent)
|
|
74
|
+
if v2:
|
|
75
|
+
return v2
|
|
76
|
+
|
|
77
|
+
return "0.0.0"
|
setiastro/saspro/view_bundle.py
CHANGED
|
@@ -174,7 +174,7 @@ class BundleChip(QWidget):
|
|
|
174
174
|
v.setSpacing(0)
|
|
175
175
|
self._title = QLabel(self._name)
|
|
176
176
|
self._title.setObjectName("chipTitle")
|
|
177
|
-
self._hint = QLabel("Drag to move · Ctrl+drag to apply · Drop views/shortcuts here")
|
|
177
|
+
self._hint = QLabel(self._panel.tr("Drag to move · Ctrl+drag to apply · Drop views/shortcuts here"))
|
|
178
178
|
self._hint.setObjectName("chipHint")
|
|
179
179
|
v.addWidget(self._title, 0, Qt.AlignmentFlag.AlignCenter)
|
|
180
180
|
v.addWidget(self._hint, 0, Qt.AlignmentFlag.AlignCenter)
|
|
@@ -265,7 +265,7 @@ class BundleChip(QWidget):
|
|
|
265
265
|
|
|
266
266
|
def contextMenuEvent(self, ev):
|
|
267
267
|
m = QMenu(self)
|
|
268
|
-
act_del = m.addAction("Delete Chip")
|
|
268
|
+
act_del = m.addAction(self._panel.tr("Delete Chip"))
|
|
269
269
|
act = m.exec(ev.globalPos())
|
|
270
270
|
if act is act_del:
|
|
271
271
|
try:
|
|
@@ -378,16 +378,17 @@ class SelectViewsDialog(QDialog):
|
|
|
378
378
|
super().__init__(parent)
|
|
379
379
|
self.setWindowTitle("Add Views to Bundle")
|
|
380
380
|
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
381
|
+
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
381
382
|
self.setModal(False)
|
|
382
383
|
#self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
383
384
|
self._boxes: list[QCheckBox] = []
|
|
384
385
|
|
|
385
386
|
v = QVBoxLayout(self)
|
|
386
|
-
v.addWidget(QLabel("Choose views to add:"))
|
|
387
|
+
v.addWidget(QLabel(self.tr("Choose views to add:")))
|
|
387
388
|
v.setSpacing(6)
|
|
388
389
|
|
|
389
390
|
# NEW: "Select all" checkbox
|
|
390
|
-
self._select_all = QCheckBox("Select all open views")
|
|
391
|
+
self._select_all = QCheckBox(self.tr("Select all open views"))
|
|
391
392
|
self._select_all.toggled.connect(self._on_select_all_toggled)
|
|
392
393
|
v.addWidget(self._select_all)
|
|
393
394
|
|
|
@@ -509,7 +510,9 @@ class ViewBundleDialog(QDialog):
|
|
|
509
510
|
def __init__(self, parent: QWidget | None = None):
|
|
510
511
|
super().__init__(parent)
|
|
511
512
|
_pin_on_top_mac(self)
|
|
512
|
-
self.setWindowTitle("View Bundles")
|
|
513
|
+
self.setWindowTitle(self.tr("View Bundles"))
|
|
514
|
+
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
515
|
+
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
513
516
|
self.setModal(False)
|
|
514
517
|
self.resize(900, 540)
|
|
515
518
|
self.setAcceptDrops(True)
|
|
@@ -535,28 +538,28 @@ class ViewBundleDialog(QDialog):
|
|
|
535
538
|
self.docs.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
|
536
539
|
self.docs.customContextMenuRequested.connect(self._docs_context_menu)
|
|
537
540
|
self.docs.itemDoubleClicked.connect(self._docs_item_activated)
|
|
538
|
-
self.btn_new = QPushButton("New Bundle")
|
|
539
|
-
self.btn_dup = QPushButton("Duplicate")
|
|
540
|
-
self.btn_del = QPushButton("Delete")
|
|
541
|
-
self.btn_clear = QPushButton("Clear Views")
|
|
542
|
-
self.btn_remove_sel = QPushButton("Remove Selected")
|
|
543
|
-
self.btn_add_from_open = QPushButton("Add from Open…")
|
|
544
|
-
self.btn_add_files = QPushButton("Add Files…")
|
|
545
|
-
self.btn_add_dir = QPushButton("Add Directory (Recursive)…")
|
|
546
|
-
self.btn_compress = QPushButton("Compress to Chip")
|
|
547
|
-
self.drop_hint = QLabel("Drop views here to add • Drop shortcuts here to apply to THIS bundle")
|
|
541
|
+
self.btn_new = QPushButton(self.tr("New Bundle"))
|
|
542
|
+
self.btn_dup = QPushButton(self.tr("Duplicate"))
|
|
543
|
+
self.btn_del = QPushButton(self.tr("Delete"))
|
|
544
|
+
self.btn_clear = QPushButton(self.tr("Clear Views"))
|
|
545
|
+
self.btn_remove_sel = QPushButton(self.tr("Remove Selected"))
|
|
546
|
+
self.btn_add_from_open = QPushButton(self.tr("Add from Open…"))
|
|
547
|
+
self.btn_add_files = QPushButton(self.tr("Add Files…"))
|
|
548
|
+
self.btn_add_dir = QPushButton(self.tr("Add Directory (Recursive)…"))
|
|
549
|
+
self.btn_compress = QPushButton(self.tr("Compress to Chip"))
|
|
550
|
+
self.drop_hint = QLabel(self.tr("Drop views here to add • Drop shortcuts here to apply to THIS bundle"))
|
|
548
551
|
self.drop_hint.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
549
552
|
self.drop_hint.setStyleSheet("color:#aaa; padding:6px; border:1px dashed #666; border-radius:6px;")
|
|
550
553
|
|
|
551
554
|
left = QVBoxLayout()
|
|
552
|
-
left.addWidget(QLabel("Bundles"))
|
|
555
|
+
left.addWidget(QLabel(self.tr("Bundles")))
|
|
553
556
|
left.addWidget(self.list, 1)
|
|
554
557
|
row = QHBoxLayout()
|
|
555
558
|
row.addWidget(self.btn_new); row.addWidget(self.btn_dup); row.addWidget(self.btn_del)
|
|
556
559
|
left.addLayout(row)
|
|
557
560
|
|
|
558
561
|
right = QVBoxLayout()
|
|
559
|
-
right.addWidget(QLabel("Views in Selected Bundle"))
|
|
562
|
+
right.addWidget(QLabel(self.tr("Views in Selected Bundle")))
|
|
560
563
|
right.addWidget(self.docs, 1)
|
|
561
564
|
|
|
562
565
|
rrow = QHBoxLayout()
|
|
@@ -132,14 +132,14 @@ class HDRWorker(QObject):
|
|
|
132
132
|
|
|
133
133
|
def run(self):
|
|
134
134
|
try:
|
|
135
|
-
self.progress_update.emit("Converting to Lab color space…", 10)
|
|
135
|
+
self.progress_update.emit(self.tr("Converting to Lab color space…"), 10)
|
|
136
136
|
# progress checkpoints inline here are cosmetic
|
|
137
|
-
self.progress_update.emit("Decomposing luminance with starlet…", 20)
|
|
137
|
+
self.progress_update.emit(self.tr("Decomposing luminance with starlet…"), 20)
|
|
138
138
|
# full compute
|
|
139
139
|
transformed, mask = compute_wavescale_hdr(
|
|
140
140
|
self.rgb_image, self.n_scales, self.compression_factor, self.mask_gamma, self.base_kernel
|
|
141
141
|
)
|
|
142
|
-
self.progress_update.emit("Finalizing…", 95)
|
|
142
|
+
self.progress_update.emit(self.tr("Finalizing…"), 95)
|
|
143
143
|
self.finished.emit(transformed, mask)
|
|
144
144
|
except Exception as e:
|
|
145
145
|
print("WaveScale HDR error:", e)
|
|
@@ -152,7 +152,7 @@ class HDRWorker(QObject):
|
|
|
152
152
|
class MaskDisplayWindow(QDialog):
|
|
153
153
|
def __init__(self, parent=None):
|
|
154
154
|
super().__init__(parent)
|
|
155
|
-
self.setWindowTitle("HDR Mask (L-based)")
|
|
155
|
+
self.setWindowTitle(self.tr("HDR Mask (L-based)"))
|
|
156
156
|
self.lbl = QLabel(alignment=Qt.AlignmentFlag.AlignCenter)
|
|
157
157
|
self.lbl.setFixedSize(400, 400) # keep it small
|
|
158
158
|
lay = QVBoxLayout(self)
|
|
@@ -184,7 +184,7 @@ class WaveScaleHDRDialogPro(QDialog):
|
|
|
184
184
|
|
|
185
185
|
def __init__(self, parent, doc, icon_path: str | None = None, *, headless: bool=False, bypass_guard: bool=False):
|
|
186
186
|
super().__init__(parent)
|
|
187
|
-
self.setWindowTitle("WaveScale HDR")
|
|
187
|
+
self.setWindowTitle(self.tr("WaveScale HDR"))
|
|
188
188
|
self._headless = bool(headless)
|
|
189
189
|
self._bypass_guard = bool(bypass_guard)
|
|
190
190
|
if self._headless:
|
|
@@ -199,6 +199,9 @@ class WaveScaleHDRDialogPro(QDialog):
|
|
|
199
199
|
import logging
|
|
200
200
|
logging.debug(f"Exception suppressed: {type(e).__name__}: {e}")
|
|
201
201
|
self.resize(980, 700)
|
|
202
|
+
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
203
|
+
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
204
|
+
self.setModal(False)
|
|
202
205
|
|
|
203
206
|
self._doc = doc
|
|
204
207
|
base = getattr(doc, "image", None)
|
|
@@ -241,45 +244,45 @@ class WaveScaleHDRDialogPro(QDialog):
|
|
|
241
244
|
self.scroll.setWidget(self.view)
|
|
242
245
|
|
|
243
246
|
# controls (add zoom row)
|
|
244
|
-
self.grp = QGroupBox("HDR Controls")
|
|
247
|
+
self.grp = QGroupBox(self.tr("HDR Controls"))
|
|
245
248
|
form = QFormLayout(self.grp)
|
|
246
249
|
|
|
247
250
|
self.s_scales = QSlider(Qt.Orientation.Horizontal); self.s_scales.setRange(2, 10); self.s_scales.setValue(5)
|
|
248
251
|
self.s_comp = QSlider(Qt.Orientation.Horizontal); self.s_comp.setRange(10, 500); self.s_comp.setValue(150)
|
|
249
252
|
self.s_gamma = QSlider(Qt.Orientation.Horizontal); self.s_gamma.setRange(10, 1000); self.s_gamma.setValue(500)
|
|
250
253
|
|
|
251
|
-
form.addRow("Number of Scales:", self.s_scales)
|
|
252
|
-
form.addRow("Coarse Compression:", self.s_comp)
|
|
253
|
-
form.addRow("Mask Gamma:", self.s_gamma)
|
|
254
|
+
form.addRow(self.tr("Number of Scales:"), self.s_scales)
|
|
255
|
+
form.addRow(self.tr("Coarse Compression:"), self.s_comp)
|
|
256
|
+
form.addRow(self.tr("Mask Gamma:"), self.s_gamma)
|
|
254
257
|
|
|
255
258
|
row = QHBoxLayout()
|
|
256
|
-
self.btn_preview = QPushButton("Preview")
|
|
257
|
-
self.btn_toggle = QPushButton("Show Original"); self.btn_toggle.setCheckable(True)
|
|
259
|
+
self.btn_preview = QPushButton(self.tr("Preview"))
|
|
260
|
+
self.btn_toggle = QPushButton(self.tr("Show Original")); self.btn_toggle.setCheckable(True)
|
|
258
261
|
row.addWidget(self.btn_preview); row.addWidget(self.btn_toggle)
|
|
259
262
|
form.addRow(row)
|
|
260
263
|
|
|
261
264
|
# ↓ NEW: zoom controls
|
|
262
265
|
zoom_row = QHBoxLayout()
|
|
263
|
-
self.btn_zoom_in = QPushButton("Zoom In")
|
|
264
|
-
self.btn_zoom_out = QPushButton("Zoom Out")
|
|
265
|
-
self.btn_fit = QPushButton("Fit to Preview")
|
|
266
|
+
self.btn_zoom_in = QPushButton(self.tr("Zoom In"))
|
|
267
|
+
self.btn_zoom_out = QPushButton(self.tr("Zoom Out"))
|
|
268
|
+
self.btn_fit = QPushButton(self.tr("Fit to Preview"))
|
|
266
269
|
zoom_row.addWidget(self.btn_zoom_in)
|
|
267
270
|
zoom_row.addWidget(self.btn_zoom_out)
|
|
268
271
|
zoom_row.addWidget(self.btn_fit)
|
|
269
272
|
form.addRow(zoom_row)
|
|
270
273
|
|
|
271
274
|
# progress group (unchanged)
|
|
272
|
-
self.prog_grp = QGroupBox("Processing Progress")
|
|
275
|
+
self.prog_grp = QGroupBox(self.tr("Processing Progress"))
|
|
273
276
|
vprog = QVBoxLayout(self.prog_grp)
|
|
274
|
-
self.lbl_step = QLabel("Idle")
|
|
277
|
+
self.lbl_step = QLabel(self.tr("Idle"))
|
|
275
278
|
self.bar = QProgressBar(); self.bar.setRange(0, 100); self.bar.setValue(0)
|
|
276
279
|
vprog.addWidget(self.lbl_step); vprog.addWidget(self.bar)
|
|
277
280
|
|
|
278
281
|
# bottom buttons (unchanged)
|
|
279
282
|
bot = QHBoxLayout()
|
|
280
|
-
self.btn_apply = QPushButton("Apply to Document"); self.btn_apply.setEnabled(False)
|
|
281
|
-
self.btn_reset = QPushButton("Reset")
|
|
282
|
-
self.btn_close = QPushButton("Close")
|
|
283
|
+
self.btn_apply = QPushButton(self.tr("Apply to Document")); self.btn_apply.setEnabled(False)
|
|
284
|
+
self.btn_reset = QPushButton(self.tr("Reset"))
|
|
285
|
+
self.btn_close = QPushButton(self.tr("Close"))
|
|
283
286
|
bot.addStretch(1); bot.addWidget(self.btn_apply); bot.addWidget(self.btn_reset); bot.addWidget(self.btn_close)
|
|
284
287
|
|
|
285
288
|
# layout (unchanged)
|
|
@@ -381,9 +384,9 @@ class WaveScaleHDRDialogPro(QDialog):
|
|
|
381
384
|
# Soft warning instead of rejecting the dialog
|
|
382
385
|
try:
|
|
383
386
|
QMessageBox.information(
|
|
384
|
-
self, "WaveScale HDR",
|
|
385
|
-
"A headless HDR run appears to be in progress. "
|
|
386
|
-
|
|
387
|
+
self, self.tr("WaveScale HDR"),
|
|
388
|
+
self.tr("A headless HDR run appears to be in progress. "
|
|
389
|
+
"This window will remain open; you can still preview safely.")
|
|
387
390
|
)
|
|
388
391
|
except Exception:
|
|
389
392
|
pass
|
|
@@ -480,10 +483,10 @@ class WaveScaleHDRDialogPro(QDialog):
|
|
|
480
483
|
|
|
481
484
|
def _toggle(self):
|
|
482
485
|
if self.btn_toggle.isChecked():
|
|
483
|
-
self.btn_toggle.setText("Show Preview")
|
|
486
|
+
self.btn_toggle.setText(self.tr("Show Preview"))
|
|
484
487
|
self._set_pix(self.original_rgb)
|
|
485
488
|
else:
|
|
486
|
-
self.btn_toggle.setText("Show Original")
|
|
489
|
+
self.btn_toggle.setText(self.tr("Show Original"))
|
|
487
490
|
self._set_pix(self.preview_rgb)
|
|
488
491
|
|
|
489
492
|
def _reset(self):
|
|
@@ -492,9 +495,9 @@ class WaveScaleHDRDialogPro(QDialog):
|
|
|
492
495
|
self.s_gamma.setValue(500)
|
|
493
496
|
self.preview_rgb = self.original_rgb.copy()
|
|
494
497
|
self._set_pix(self.preview_rgb)
|
|
495
|
-
self.lbl_step.setText("Idle"); self.bar.setValue(0)
|
|
498
|
+
self.lbl_step.setText(self.tr("Idle")); self.bar.setValue(0)
|
|
496
499
|
self.btn_apply.setEnabled(False)
|
|
497
|
-
self.btn_toggle.setChecked(False); self.btn_toggle.setText("Show Original")
|
|
500
|
+
self.btn_toggle.setChecked(False); self.btn_toggle.setText(self.tr("Show Original"))
|
|
498
501
|
|
|
499
502
|
def _start_preview(self):
|
|
500
503
|
self.btn_preview.setEnabled(False); self.btn_apply.setEnabled(False)
|
|
@@ -519,7 +522,7 @@ class WaveScaleHDRDialogPro(QDialog):
|
|
|
519
522
|
def _on_finished(self, transformed_rgb: np.ndarray, mask: np.ndarray):
|
|
520
523
|
self.btn_preview.setEnabled(True)
|
|
521
524
|
if transformed_rgb is None:
|
|
522
|
-
QMessageBox.critical(self, "WaveScale HDR", "Processing failed.")
|
|
525
|
+
QMessageBox.critical(self, self.tr("WaveScale HDR"), self.tr("Processing failed."))
|
|
523
526
|
return
|
|
524
527
|
|
|
525
528
|
# ← NEW: combine HDR's luminance mask with the doc's active mask (if present)
|
|
@@ -532,13 +535,13 @@ class WaveScaleHDRDialogPro(QDialog):
|
|
|
532
535
|
|
|
533
536
|
# show the *combined* mask in the little window
|
|
534
537
|
self.mask_win.setWindowTitle(
|
|
535
|
-
"HDR Mask (L × Active Mask)" if self._get_doc_active_mask_2d() is not None else "HDR Mask (L-based)"
|
|
538
|
+
self.tr("HDR Mask (L × Active Mask)") if self._get_doc_active_mask_2d() is not None else self.tr("HDR Mask (L-based)")
|
|
536
539
|
)
|
|
537
540
|
self.mask_win.update_mask(mask_comb)
|
|
538
541
|
|
|
539
542
|
self.btn_apply.setEnabled(True)
|
|
540
|
-
self.btn_toggle.setChecked(False); self.btn_toggle.setText("Show Original")
|
|
541
|
-
self.lbl_step.setText("Preview ready"); self.bar.setValue(100)
|
|
543
|
+
self.btn_toggle.setChecked(False); self.btn_toggle.setText(self.tr("Show Original"))
|
|
544
|
+
self.lbl_step.setText(self.tr("Preview ready")); self.bar.setValue(100)
|
|
542
545
|
# Headless: apply immediately (exactly like clicking "Apply to Document")
|
|
543
546
|
if self._headless:
|
|
544
547
|
QTimer.singleShot(0, self._apply_to_doc)
|
|
@@ -561,7 +564,7 @@ class WaveScaleHDRDialogPro(QDialog):
|
|
|
561
564
|
else:
|
|
562
565
|
self._doc.image = out
|
|
563
566
|
except Exception as e:
|
|
564
|
-
QMessageBox.critical(self, "WaveScale HDR",
|
|
567
|
+
QMessageBox.critical(self, self.tr("WaveScale HDR"), self.tr("Failed to write to document:\n{0}").format(e))
|
|
565
568
|
return
|
|
566
569
|
|
|
567
570
|
# ── Build preset from current sliders ─────────────────────────
|
|
@@ -606,8 +609,26 @@ class WaveScaleHDRDialogPro(QDialog):
|
|
|
606
609
|
except Exception:
|
|
607
610
|
pass
|
|
608
611
|
|
|
609
|
-
|
|
612
|
+
# Dialog stays open so user can apply to other images
|
|
613
|
+
# Refresh document reference for next operation
|
|
614
|
+
self._refresh_document_from_active()
|
|
610
615
|
|
|
616
|
+
def _refresh_document_from_active(self):
|
|
617
|
+
"""
|
|
618
|
+
Refresh the dialog's document reference to the currently active document.
|
|
619
|
+
This allows reusing the same dialog on different images.
|
|
620
|
+
"""
|
|
621
|
+
try:
|
|
622
|
+
main = self.parent()
|
|
623
|
+
if main and hasattr(main, "_active_doc"):
|
|
624
|
+
new_doc = main._active_doc()
|
|
625
|
+
if new_doc is not None and new_doc is not self._doc:
|
|
626
|
+
self._doc = new_doc
|
|
627
|
+
# Reset L channel and refresh preview for new document
|
|
628
|
+
self._L_original = None
|
|
629
|
+
self._last_preview = None
|
|
630
|
+
except Exception:
|
|
631
|
+
pass
|
|
611
632
|
|
|
612
633
|
|
|
613
634
|
def _schedule_mask_refresh(self, _value):
|
|
@@ -619,6 +640,6 @@ class WaveScaleHDRDialogPro(QDialog):
|
|
|
619
640
|
hdr_mask = _mask_from_L(self._L_original, gamma=gamma)
|
|
620
641
|
mask_comb = self._combine_with_doc_mask(hdr_mask)
|
|
621
642
|
self.mask_win.setWindowTitle(
|
|
622
|
-
"HDR Mask (L × Active Mask)" if self._get_doc_active_mask_2d() is not None else "HDR Mask (L-based)"
|
|
643
|
+
self.tr("HDR Mask (L × Active Mask)") if self._get_doc_active_mask_2d() is not None else self.tr("HDR Mask (L-based)")
|
|
623
644
|
)
|
|
624
645
|
self.mask_win.update_mask(mask_comb)
|
|
@@ -6,7 +6,7 @@ from .wavescale_hdr import WaveScaleHDRDialogPro
|
|
|
6
6
|
class WaveScaleHDRPresetDialog(QDialog):
|
|
7
7
|
def __init__(self, parent=None, initial: dict | None = None):
|
|
8
8
|
super().__init__(parent)
|
|
9
|
-
self.setWindowTitle("WaveScale HDR — Preset")
|
|
9
|
+
self.setWindowTitle(self.tr("WaveScale HDR — Preset"))
|
|
10
10
|
p = dict(initial or {})
|
|
11
11
|
f = QFormLayout(self)
|
|
12
12
|
|
|
@@ -26,9 +26,9 @@ class WaveScaleHDRPresetDialog(QDialog):
|
|
|
26
26
|
self.gamma.setDecimals(2)
|
|
27
27
|
self.gamma.setValue(float(p.get("mask_gamma", 5.0)))
|
|
28
28
|
|
|
29
|
-
f.addRow("Number of Scales:", self.n_scales)
|
|
30
|
-
f.addRow("Coarse Compression:", self.comp)
|
|
31
|
-
f.addRow("Mask Gamma:", self.gamma)
|
|
29
|
+
f.addRow(self.tr("Number of Scales:"), self.n_scales)
|
|
30
|
+
f.addRow(self.tr("Coarse Compression:"), self.comp)
|
|
31
|
+
f.addRow(self.tr("Mask Gamma:"), self.gamma)
|
|
32
32
|
|
|
33
33
|
btns = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, parent=self)
|
|
34
34
|
btns.accepted.connect(self.accept); btns.rejected.connect(self.reject)
|
|
@@ -73,7 +73,8 @@ def run_wavescale_hdr_via_preset(main, preset: dict | None = None, target_doc=No
|
|
|
73
73
|
|
|
74
74
|
main, doc, _dm = normalize_headless_main(main, target_doc)
|
|
75
75
|
if doc is None or getattr(doc, "image", None) is None:
|
|
76
|
-
|
|
76
|
+
from PyQt6.QtCore import QCoreApplication
|
|
77
|
+
QMessageBox.warning(main or None, "...", QCoreApplication.translate("WaveScaleHDRPresetDialog", "Load an image first."))
|
|
77
78
|
return
|
|
78
79
|
|
|
79
80
|
# Build headless dialog and apply preset
|
setiastro/saspro/wavescalede.py
CHANGED
|
@@ -149,13 +149,13 @@ class DSEWorker(QObject):
|
|
|
149
149
|
|
|
150
150
|
def run(self):
|
|
151
151
|
try:
|
|
152
|
-
self.progress_update.emit("Analyzing dark structure…", 20)
|
|
152
|
+
self.progress_update.emit(self.tr("Analyzing dark structure…"), 20)
|
|
153
153
|
out, mask = compute_wavescale_dse(
|
|
154
154
|
self.image, self.n_scales, self.boost, self.gamma,
|
|
155
155
|
self.iterations, self.base_kernel,
|
|
156
156
|
external_mask=self.external_mask # ← NEW
|
|
157
157
|
)
|
|
158
|
-
self.progress_update.emit("Finalizing…", 95)
|
|
158
|
+
self.progress_update.emit(self.tr("Finalizing…"), 95)
|
|
159
159
|
self.finished.emit(out, mask)
|
|
160
160
|
except Exception as e:
|
|
161
161
|
print("WaveScale DSE error:", e)
|
|
@@ -167,7 +167,7 @@ class DSEWorker(QObject):
|
|
|
167
167
|
class _MaskWindow(QDialog):
|
|
168
168
|
def __init__(self, parent=None):
|
|
169
169
|
super().__init__(parent)
|
|
170
|
-
self.setWindowTitle("Dark Mask")
|
|
170
|
+
self.setWindowTitle(self.tr("Dark Mask"))
|
|
171
171
|
self.setMinimumSize(300, 300)
|
|
172
172
|
self.resize(400, 400)
|
|
173
173
|
v = QVBoxLayout(self)
|
|
@@ -194,13 +194,16 @@ class _MaskWindow(QDialog):
|
|
|
194
194
|
class WaveScaleDarkEnhancerDialogPro(QDialog):
|
|
195
195
|
def __init__(self, parent, doc, icon_path: str | None = None):
|
|
196
196
|
super().__init__(parent)
|
|
197
|
-
self.setWindowTitle("WaveScale Dark Enhancer")
|
|
197
|
+
self.setWindowTitle(self.tr("WaveScale Dark Enhancer"))
|
|
198
198
|
if icon_path:
|
|
199
199
|
try: self.setWindowIcon(QIcon(icon_path))
|
|
200
200
|
except Exception as e:
|
|
201
201
|
import logging
|
|
202
202
|
logging.debug(f"Exception suppressed: {type(e).__name__}: {e}")
|
|
203
203
|
self.resize(980, 700)
|
|
204
|
+
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
205
|
+
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
206
|
+
self.setModal(False)
|
|
204
207
|
|
|
205
208
|
self._doc = doc
|
|
206
209
|
base = getattr(doc, "image", None)
|
|
@@ -243,7 +246,7 @@ class WaveScaleDarkEnhancerDialogPro(QDialog):
|
|
|
243
246
|
self.zoom_max = 5.0
|
|
244
247
|
|
|
245
248
|
# controls
|
|
246
|
-
self.grp = QGroupBox("Dark Enhancer Controls")
|
|
249
|
+
self.grp = QGroupBox(self.tr("Dark Enhancer Controls"))
|
|
247
250
|
form = QFormLayout(self.grp)
|
|
248
251
|
|
|
249
252
|
self.s_scales = QSlider(Qt.Orientation.Horizontal); self.s_scales.setRange(2, 10); self.s_scales.setValue(6)
|
|
@@ -251,40 +254,40 @@ class WaveScaleDarkEnhancerDialogPro(QDialog):
|
|
|
251
254
|
self.s_gamma = QSlider(Qt.Orientation.Horizontal); self.s_gamma.setRange(10, 1000); self.s_gamma.setValue(100) # 0.10..10.00
|
|
252
255
|
self.s_iters = QSlider(Qt.Orientation.Horizontal); self.s_iters.setRange(1, 10); self.s_iters.setValue(2)
|
|
253
256
|
|
|
254
|
-
form.addRow("Number of Scales:", self.s_scales)
|
|
255
|
-
form.addRow("Boost Factor:", self.s_boost)
|
|
256
|
-
form.addRow("Mask Gamma:", self.s_gamma)
|
|
257
|
-
form.addRow("Iterations:", self.s_iters)
|
|
257
|
+
form.addRow(self.tr("Number of Scales:"), self.s_scales)
|
|
258
|
+
form.addRow(self.tr("Boost Factor:"), self.s_boost)
|
|
259
|
+
form.addRow(self.tr("Mask Gamma:"), self.s_gamma)
|
|
260
|
+
form.addRow(self.tr("Iterations:"), self.s_iters)
|
|
258
261
|
|
|
259
262
|
row = QHBoxLayout()
|
|
260
|
-
self.btn_preview = QPushButton("Preview")
|
|
261
|
-
self.btn_toggle = QPushButton("Show Original"); self.btn_toggle.setCheckable(True)
|
|
263
|
+
self.btn_preview = QPushButton(self.tr("Preview"))
|
|
264
|
+
self.btn_toggle = QPushButton(self.tr("Show Original")); self.btn_toggle.setCheckable(True)
|
|
262
265
|
row.addWidget(self.btn_preview); row.addWidget(self.btn_toggle)
|
|
263
266
|
form.addRow(row)
|
|
264
267
|
|
|
265
268
|
# progress
|
|
266
|
-
self.prog_grp = QGroupBox("Progress")
|
|
269
|
+
self.prog_grp = QGroupBox(self.tr("Progress"))
|
|
267
270
|
vprog = QVBoxLayout(self.prog_grp)
|
|
268
|
-
self.lbl_step = QLabel("Idle")
|
|
271
|
+
self.lbl_step = QLabel(self.tr("Idle"))
|
|
269
272
|
self.bar = QProgressBar(); self.bar.setRange(0, 100); self.bar.setValue(0)
|
|
270
273
|
vprog.addWidget(self.lbl_step); vprog.addWidget(self.bar)
|
|
271
274
|
|
|
272
275
|
# bottom
|
|
273
276
|
bot = QHBoxLayout()
|
|
274
|
-
self.btn_apply = QPushButton("Apply to Document"); self.btn_apply.setEnabled(False)
|
|
275
|
-
self.btn_reset = QPushButton("Reset")
|
|
276
|
-
self.btn_close = QPushButton("Close")
|
|
277
|
+
self.btn_apply = QPushButton(self.tr("Apply to Document")); self.btn_apply.setEnabled(False)
|
|
278
|
+
self.btn_reset = QPushButton(self.tr("Reset"))
|
|
279
|
+
self.btn_close = QPushButton(self.tr("Close"))
|
|
277
280
|
bot.addStretch(1); bot.addWidget(self.btn_apply); bot.addWidget(self.btn_reset); bot.addWidget(self.btn_close)
|
|
278
281
|
|
|
279
282
|
# layout
|
|
280
283
|
main = QVBoxLayout(self)
|
|
281
284
|
main.addWidget(self.scroll)
|
|
282
285
|
|
|
283
|
-
zoom_box = QGroupBox("Zoom Controls")
|
|
286
|
+
zoom_box = QGroupBox(self.tr("Zoom Controls"))
|
|
284
287
|
zr = QHBoxLayout(zoom_box)
|
|
285
|
-
self.btn_zin = QPushButton("Zoom In")
|
|
286
|
-
self.btn_zout = QPushButton("Zoom Out")
|
|
287
|
-
self.btn_fit = QPushButton("Fit to Preview")
|
|
288
|
+
self.btn_zin = QPushButton(self.tr("Zoom In"))
|
|
289
|
+
self.btn_zout = QPushButton(self.tr("Zoom Out"))
|
|
290
|
+
self.btn_fit = QPushButton(self.tr("Fit to Preview"))
|
|
288
291
|
zr.addWidget(self.btn_zin); zr.addWidget(self.btn_zout); zr.addWidget(self.btn_fit)
|
|
289
292
|
main.addWidget(zoom_box)
|
|
290
293
|
|
|
@@ -408,10 +411,10 @@ class WaveScaleDarkEnhancerDialogPro(QDialog):
|
|
|
408
411
|
# --- toggle ---
|
|
409
412
|
def _toggle(self):
|
|
410
413
|
if self.btn_toggle.isChecked():
|
|
411
|
-
self.btn_toggle.setText("Show Preview")
|
|
414
|
+
self.btn_toggle.setText(self.tr("Show Preview"))
|
|
412
415
|
self._set_pix(self.original)
|
|
413
416
|
else:
|
|
414
|
-
self.btn_toggle.setText("Show Original")
|
|
417
|
+
self.btn_toggle.setText(self.tr("Show Original"))
|
|
415
418
|
self._set_pix(self.preview)
|
|
416
419
|
|
|
417
420
|
# --- reset ---
|
|
@@ -422,9 +425,9 @@ class WaveScaleDarkEnhancerDialogPro(QDialog):
|
|
|
422
425
|
self.s_iters.setValue(2)
|
|
423
426
|
self.preview = self.original.copy()
|
|
424
427
|
self._set_pix(self.preview)
|
|
425
|
-
self.lbl_step.setText("Idle"); self.bar.setValue(0)
|
|
428
|
+
self.lbl_step.setText(self.tr("Idle")); self.bar.setValue(0)
|
|
426
429
|
self.btn_apply.setEnabled(False)
|
|
427
|
-
self.btn_toggle.setChecked(False); self.btn_toggle.setText("Show Original")
|
|
430
|
+
self.btn_toggle.setChecked(False); self.btn_toggle.setText(self.tr("Show Original"))
|
|
428
431
|
self._update_mask_only()
|
|
429
432
|
|
|
430
433
|
# --- zoom + Ctrl+Wheel ---
|
|
@@ -467,7 +470,7 @@ class WaveScaleDarkEnhancerDialogPro(QDialog):
|
|
|
467
470
|
self.base_kernel, mgamma)
|
|
468
471
|
mask_comb = self._combine_with_doc_mask(algo_mask)
|
|
469
472
|
self.mask_win.setWindowTitle(
|
|
470
|
-
"Dark Mask (Algo × Active Mask)" if self._get_doc_active_mask_2d() is not None else "Dark Mask"
|
|
473
|
+
self.tr("Dark Mask (Algo × Active Mask)") if self._get_doc_active_mask_2d() is not None else self.tr("Dark Mask")
|
|
471
474
|
)
|
|
472
475
|
self.mask_win.set_mask(mask_comb)
|
|
473
476
|
# --- threaded preview ---
|
|
@@ -498,7 +501,7 @@ class WaveScaleDarkEnhancerDialogPro(QDialog):
|
|
|
498
501
|
def _on_finished(self, out: np.ndarray, mask: np.ndarray):
|
|
499
502
|
self.btn_preview.setEnabled(True)
|
|
500
503
|
if out is None:
|
|
501
|
-
QMessageBox.critical(self, "WaveScale Dark Enhancer", "Processing failed.")
|
|
504
|
+
QMessageBox.critical(self, self.tr("WaveScale Dark Enhancer"), self.tr("Processing failed."))
|
|
502
505
|
return
|
|
503
506
|
|
|
504
507
|
# Respect the document mask
|
|
@@ -520,8 +523,8 @@ class WaveScaleDarkEnhancerDialogPro(QDialog):
|
|
|
520
523
|
self._set_pix(self.preview)
|
|
521
524
|
self.mask_win.set_mask(mask)
|
|
522
525
|
self.btn_apply.setEnabled(True)
|
|
523
|
-
self.btn_toggle.setChecked(False); self.btn_toggle.setText("Show Original")
|
|
524
|
-
self.lbl_step.setText("Preview ready"); self.bar.setValue(100)
|
|
526
|
+
self.btn_toggle.setChecked(False); self.btn_toggle.setText(self.tr("Show Original"))
|
|
527
|
+
self.lbl_step.setText(self.tr("Preview ready")); self.bar.setValue(100)
|
|
525
528
|
|
|
526
529
|
# --- apply back to doc ---
|
|
527
530
|
# --- apply back to doc ---
|
|
@@ -541,7 +544,7 @@ class WaveScaleDarkEnhancerDialogPro(QDialog):
|
|
|
541
544
|
else:
|
|
542
545
|
self._doc.image = out
|
|
543
546
|
except Exception as e:
|
|
544
|
-
QMessageBox.critical(self, "WaveScale Dark Enhancer",
|
|
547
|
+
QMessageBox.critical(self, self.tr("WaveScale Dark Enhancer"), self.tr("Failed to write to document:\n{0}").format(e))
|
|
545
548
|
return
|
|
546
549
|
|
|
547
550
|
# ── Build preset from current sliders ─────────────────────────
|
|
@@ -581,7 +584,26 @@ class WaveScaleDarkEnhancerDialogPro(QDialog):
|
|
|
581
584
|
# Never let replay wiring break the apply
|
|
582
585
|
pass
|
|
583
586
|
|
|
584
|
-
|
|
587
|
+
# Dialog stays open so user can apply to other images
|
|
588
|
+
# Refresh document reference for next operation
|
|
589
|
+
self._refresh_document_from_active()
|
|
590
|
+
|
|
591
|
+
def _refresh_document_from_active(self):
|
|
592
|
+
"""
|
|
593
|
+
Refresh the dialog's document reference to the currently active document.
|
|
594
|
+
This allows reusing the same dialog on different images.
|
|
595
|
+
"""
|
|
596
|
+
try:
|
|
597
|
+
main = self.parent()
|
|
598
|
+
if main and hasattr(main, "_active_doc"):
|
|
599
|
+
new_doc = main._active_doc()
|
|
600
|
+
if new_doc is not None and new_doc is not self._doc:
|
|
601
|
+
self._doc = new_doc
|
|
602
|
+
# Reset state and refresh for new document
|
|
603
|
+
self._L_original = None
|
|
604
|
+
self._last_preview = None
|
|
605
|
+
except Exception:
|
|
606
|
+
pass
|
|
585
607
|
|
|
586
608
|
|
|
587
609
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -615,7 +637,8 @@ def install_wavescale_dark_enhancer(main_window: QMainWindow,
|
|
|
615
637
|
if docman and hasattr(docman, "current_document"):
|
|
616
638
|
doc = docman.current_document()
|
|
617
639
|
if doc is None or getattr(doc, "image", None) is None:
|
|
618
|
-
|
|
640
|
+
from PyQt6.QtCore import QCoreApplication
|
|
641
|
+
QMessageBox.warning(main_window, QCoreApplication.translate("WaveScaleDarkEnhancerDialogPro", "WaveScale Dark Enhancer"), QCoreApplication.translate("WaveScaleDarkEnhancerDialogPro", "No active image."))
|
|
619
642
|
return
|
|
620
643
|
dlg = WaveScaleDarkEnhancerDialogPro(main_window, doc, icon_path=dse_icon_path)
|
|
621
644
|
dlg.exec()
|
|
@@ -9,7 +9,7 @@ from .wavescalede import compute_wavescale_dse
|
|
|
9
9
|
class WaveScaleDSEPresetDialog(QDialog):
|
|
10
10
|
def __init__(self, parent=None, initial: dict | None = None):
|
|
11
11
|
super().__init__(parent)
|
|
12
|
-
self.setWindowTitle("WaveScale Dark Enhancer — Preset")
|
|
12
|
+
self.setWindowTitle(self.tr("WaveScale Dark Enhancer — Preset"))
|
|
13
13
|
p = dict(initial or {})
|
|
14
14
|
f = QFormLayout(self)
|
|
15
15
|
|
|
@@ -33,10 +33,10 @@ class WaveScaleDSEPresetDialog(QDialog):
|
|
|
33
33
|
self.iters.setRange(1, 10)
|
|
34
34
|
self.iters.setValue(int(p.get("iterations", 2)))
|
|
35
35
|
|
|
36
|
-
f.addRow("Number of Scales:", self.n_scales)
|
|
37
|
-
f.addRow("Boost Factor:", self.boost)
|
|
38
|
-
f.addRow("Mask Gamma:", self.gamma)
|
|
39
|
-
f.addRow("Iterations:", self.iters)
|
|
36
|
+
f.addRow(self.tr("Number of Scales:"), self.n_scales)
|
|
37
|
+
f.addRow(self.tr("Boost Factor:"), self.boost)
|
|
38
|
+
f.addRow(self.tr("Mask Gamma:"), self.gamma)
|
|
39
|
+
f.addRow(self.tr("Iterations:"), self.iters)
|
|
40
40
|
|
|
41
41
|
btns = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, parent=self)
|
|
42
42
|
btns.accepted.connect(self.accept); btns.rejected.connect(self.reject)
|
|
@@ -101,7 +101,8 @@ def run_wavescalede_via_preset(main, preset: dict | None = None, target_doc=None
|
|
|
101
101
|
|
|
102
102
|
main, doc, _dm = normalize_headless_main(main, target_doc)
|
|
103
103
|
if doc is None or getattr(doc, "image", None) is None:
|
|
104
|
-
|
|
104
|
+
from PyQt6.QtCore import QCoreApplication
|
|
105
|
+
QMessageBox.warning(main or None, QCoreApplication.translate("WaveScaleDSEPresetDialog", "WaveScale Dark Enhancer"), QCoreApplication.translate("WaveScaleDSEPresetDialog", "Load an image first."))
|
|
105
106
|
return
|
|
106
107
|
|
|
107
108
|
# pull & normalize image like the dialog
|
|
@@ -174,7 +175,8 @@ def run_wavescalede_via_preset(main, preset: dict | None = None, target_doc=None
|
|
|
174
175
|
else:
|
|
175
176
|
doc.image = result
|
|
176
177
|
except Exception as e:
|
|
177
|
-
|
|
178
|
+
from PyQt6.QtCore import QCoreApplication
|
|
179
|
+
QMessageBox.critical(main, QCoreApplication.translate("WaveScaleDSEPresetDialog", "WaveScale Dark Enhancer"), QCoreApplication.translate("WaveScaleDSEPresetDialog", "Failed to write to document:\n{0}").format(e))
|
|
178
180
|
return
|
|
179
181
|
|
|
180
182
|
|