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.

Files changed (293) hide show
  1. setiastro/data/SASP_data.fits +0 -0
  2. setiastro/data/catalogs/List_of_Galaxies_with_Distances_Gly.csv +488 -0
  3. setiastro/data/catalogs/astrobin_filters.csv +890 -0
  4. setiastro/data/catalogs/astrobin_filters_page1_local.csv +51 -0
  5. setiastro/data/catalogs/cali2.csv +63 -0
  6. setiastro/data/catalogs/cali2color.csv +65 -0
  7. setiastro/data/catalogs/celestial_catalog - original.csv +16471 -0
  8. setiastro/data/catalogs/celestial_catalog.csv +24031 -0
  9. setiastro/data/catalogs/detected_stars.csv +24784 -0
  10. setiastro/data/catalogs/fits_header_data.csv +46 -0
  11. setiastro/data/catalogs/test.csv +8 -0
  12. setiastro/data/catalogs/updated_celestial_catalog.csv +16471 -0
  13. setiastro/images/Astro_Spikes.png +0 -0
  14. setiastro/images/Background_startup.jpg +0 -0
  15. setiastro/images/HRDiagram.png +0 -0
  16. setiastro/images/LExtract.png +0 -0
  17. setiastro/images/LInsert.png +0 -0
  18. setiastro/images/Oxygenation-atm-2.svg.png +0 -0
  19. setiastro/images/RGB080604.png +0 -0
  20. setiastro/images/abeicon.png +0 -0
  21. setiastro/images/aberration.png +0 -0
  22. setiastro/images/andromedatry.png +0 -0
  23. setiastro/images/andromedatry_satellited.png +0 -0
  24. setiastro/images/annotated.png +0 -0
  25. setiastro/images/aperture.png +0 -0
  26. setiastro/images/astrosuite.ico +0 -0
  27. setiastro/images/astrosuite.png +0 -0
  28. setiastro/images/astrosuitepro.icns +0 -0
  29. setiastro/images/astrosuitepro.ico +0 -0
  30. setiastro/images/astrosuitepro.png +0 -0
  31. setiastro/images/background.png +0 -0
  32. setiastro/images/background2.png +0 -0
  33. setiastro/images/benchmark.png +0 -0
  34. setiastro/images/big_moon_stabilizer_timeline.png +0 -0
  35. setiastro/images/big_moon_stabilizer_timeline_clean.png +0 -0
  36. setiastro/images/blaster.png +0 -0
  37. setiastro/images/blink.png +0 -0
  38. setiastro/images/clahe.png +0 -0
  39. setiastro/images/collage.png +0 -0
  40. setiastro/images/colorwheel.png +0 -0
  41. setiastro/images/contsub.png +0 -0
  42. setiastro/images/convo.png +0 -0
  43. setiastro/images/copyslot.png +0 -0
  44. setiastro/images/cosmic.png +0 -0
  45. setiastro/images/cosmicsat.png +0 -0
  46. setiastro/images/crop1.png +0 -0
  47. setiastro/images/cropicon.png +0 -0
  48. setiastro/images/curves.png +0 -0
  49. setiastro/images/cvs.png +0 -0
  50. setiastro/images/debayer.png +0 -0
  51. setiastro/images/denoise_cnn_custom.png +0 -0
  52. setiastro/images/denoise_cnn_graph.png +0 -0
  53. setiastro/images/disk.png +0 -0
  54. setiastro/images/dse.png +0 -0
  55. setiastro/images/exoicon.png +0 -0
  56. setiastro/images/eye.png +0 -0
  57. setiastro/images/fliphorizontal.png +0 -0
  58. setiastro/images/flipvertical.png +0 -0
  59. setiastro/images/font.png +0 -0
  60. setiastro/images/freqsep.png +0 -0
  61. setiastro/images/functionbundle.png +0 -0
  62. setiastro/images/graxpert.png +0 -0
  63. setiastro/images/green.png +0 -0
  64. setiastro/images/gridicon.png +0 -0
  65. setiastro/images/halo.png +0 -0
  66. setiastro/images/hdr.png +0 -0
  67. setiastro/images/histogram.png +0 -0
  68. setiastro/images/hubble.png +0 -0
  69. setiastro/images/imagecombine.png +0 -0
  70. setiastro/images/invert.png +0 -0
  71. setiastro/images/isophote.png +0 -0
  72. setiastro/images/isophote_demo_figure.png +0 -0
  73. setiastro/images/isophote_demo_image.png +0 -0
  74. setiastro/images/isophote_demo_model.png +0 -0
  75. setiastro/images/isophote_demo_residual.png +0 -0
  76. setiastro/images/jwstpupil.png +0 -0
  77. setiastro/images/linearfit.png +0 -0
  78. setiastro/images/livestacking.png +0 -0
  79. setiastro/images/mask.png +0 -0
  80. setiastro/images/maskapply.png +0 -0
  81. setiastro/images/maskcreate.png +0 -0
  82. setiastro/images/maskremove.png +0 -0
  83. setiastro/images/morpho.png +0 -0
  84. setiastro/images/mosaic.png +0 -0
  85. setiastro/images/multiscale_decomp.png +0 -0
  86. setiastro/images/nbtorgb.png +0 -0
  87. setiastro/images/neutral.png +0 -0
  88. setiastro/images/nuke.png +0 -0
  89. setiastro/images/openfile.png +0 -0
  90. setiastro/images/pedestal.png +0 -0
  91. setiastro/images/pen.png +0 -0
  92. setiastro/images/pixelmath.png +0 -0
  93. setiastro/images/platesolve.png +0 -0
  94. setiastro/images/ppp.png +0 -0
  95. setiastro/images/pro.png +0 -0
  96. setiastro/images/project.png +0 -0
  97. setiastro/images/psf.png +0 -0
  98. setiastro/images/redo.png +0 -0
  99. setiastro/images/redoicon.png +0 -0
  100. setiastro/images/rescale.png +0 -0
  101. setiastro/images/rgbalign.png +0 -0
  102. setiastro/images/rgbcombo.png +0 -0
  103. setiastro/images/rgbextract.png +0 -0
  104. setiastro/images/rotate180.png +0 -0
  105. setiastro/images/rotatearbitrary.png +0 -0
  106. setiastro/images/rotateclockwise.png +0 -0
  107. setiastro/images/rotatecounterclockwise.png +0 -0
  108. setiastro/images/satellite.png +0 -0
  109. setiastro/images/script.png +0 -0
  110. setiastro/images/selectivecolor.png +0 -0
  111. setiastro/images/simbad.png +0 -0
  112. setiastro/images/slot0.png +0 -0
  113. setiastro/images/slot1.png +0 -0
  114. setiastro/images/slot2.png +0 -0
  115. setiastro/images/slot3.png +0 -0
  116. setiastro/images/slot4.png +0 -0
  117. setiastro/images/slot5.png +0 -0
  118. setiastro/images/slot6.png +0 -0
  119. setiastro/images/slot7.png +0 -0
  120. setiastro/images/slot8.png +0 -0
  121. setiastro/images/slot9.png +0 -0
  122. setiastro/images/spcc.png +0 -0
  123. setiastro/images/spin_precession_vs_lunar_distance.png +0 -0
  124. setiastro/images/spinner.gif +0 -0
  125. setiastro/images/stacking.png +0 -0
  126. setiastro/images/staradd.png +0 -0
  127. setiastro/images/staralign.png +0 -0
  128. setiastro/images/starnet.png +0 -0
  129. setiastro/images/starregistration.png +0 -0
  130. setiastro/images/starspike.png +0 -0
  131. setiastro/images/starstretch.png +0 -0
  132. setiastro/images/statstretch.png +0 -0
  133. setiastro/images/supernova.png +0 -0
  134. setiastro/images/uhs.png +0 -0
  135. setiastro/images/undoicon.png +0 -0
  136. setiastro/images/upscale.png +0 -0
  137. setiastro/images/viewbundle.png +0 -0
  138. setiastro/images/whitebalance.png +0 -0
  139. setiastro/images/wimi_icon_256x256.png +0 -0
  140. setiastro/images/wimilogo.png +0 -0
  141. setiastro/images/wims.png +0 -0
  142. setiastro/images/wrench_icon.png +0 -0
  143. setiastro/images/xisfliberator.png +0 -0
  144. setiastro/qml/ResourceMonitor.qml +126 -0
  145. setiastro/saspro/__main__.py +228 -67
  146. setiastro/saspro/_generated/build_info.py +2 -1
  147. setiastro/saspro/abe.py +76 -25
  148. setiastro/saspro/aberration_ai.py +14 -14
  149. setiastro/saspro/add_stars.py +15 -12
  150. setiastro/saspro/astrobin_exporter.py +61 -58
  151. setiastro/saspro/astrospike_python.py +3 -1
  152. setiastro/saspro/autostretch.py +4 -2
  153. setiastro/saspro/backgroundneutral.py +65 -14
  154. setiastro/saspro/batch_convert.py +8 -5
  155. setiastro/saspro/batch_renamer.py +39 -36
  156. setiastro/saspro/blemish_blaster.py +15 -12
  157. setiastro/saspro/blink_comparator_pro.py +605 -379
  158. setiastro/saspro/cheat_sheet.py +62 -17
  159. setiastro/saspro/clahe.py +34 -8
  160. setiastro/saspro/comet_stacking.py +103 -38
  161. setiastro/saspro/common_tr.py +107 -0
  162. setiastro/saspro/continuum_subtract.py +7 -7
  163. setiastro/saspro/convo.py +12 -9
  164. setiastro/saspro/copyastro.py +3 -0
  165. setiastro/saspro/cosmicclarity.py +77 -52
  166. setiastro/saspro/crop_dialog_pro.py +80 -45
  167. setiastro/saspro/curve_editor_pro.py +51 -33
  168. setiastro/saspro/debayer.py +6 -3
  169. setiastro/saspro/doc_manager.py +49 -19
  170. setiastro/saspro/exoplanet_detector.py +11 -11
  171. setiastro/saspro/fitsmodifier.py +48 -44
  172. setiastro/saspro/fix_bom.py +32 -0
  173. setiastro/saspro/frequency_separation.py +18 -12
  174. setiastro/saspro/function_bundle.py +18 -16
  175. setiastro/saspro/generate_translations.py +3092 -0
  176. setiastro/saspro/ghs_dialog_pro.py +19 -16
  177. setiastro/saspro/graxpert.py +3 -0
  178. setiastro/saspro/gui/main_window.py +471 -126
  179. setiastro/saspro/gui/mixins/dock_mixin.py +123 -11
  180. setiastro/saspro/gui/mixins/file_mixin.py +25 -20
  181. setiastro/saspro/gui/mixins/geometry_mixin.py +115 -15
  182. setiastro/saspro/gui/mixins/header_mixin.py +6 -6
  183. setiastro/saspro/gui/mixins/mask_mixin.py +8 -8
  184. setiastro/saspro/gui/mixins/menu_mixin.py +62 -33
  185. setiastro/saspro/gui/mixins/toolbar_mixin.py +382 -226
  186. setiastro/saspro/gui/mixins/update_mixin.py +26 -26
  187. setiastro/saspro/gui/statistics_dialog.py +47 -0
  188. setiastro/saspro/halobgon.py +29 -3
  189. setiastro/saspro/header_viewer.py +21 -18
  190. setiastro/saspro/histogram.py +29 -26
  191. setiastro/saspro/history_explorer.py +2 -0
  192. setiastro/saspro/i18n.py +168 -0
  193. setiastro/saspro/image_combine.py +3 -0
  194. setiastro/saspro/image_peeker_pro.py +52 -44
  195. setiastro/saspro/imageops/stretch.py +5 -13
  196. setiastro/saspro/isophote.py +3 -0
  197. setiastro/saspro/legacy/numba_utils.py +64 -47
  198. setiastro/saspro/linear_fit.py +3 -0
  199. setiastro/saspro/live_stacking.py +13 -2
  200. setiastro/saspro/mask_creation.py +180 -22
  201. setiastro/saspro/mfdeconv.py +5 -0
  202. setiastro/saspro/morphology.py +38 -13
  203. setiastro/saspro/multiscale_decomp.py +713 -256
  204. setiastro/saspro/nbtorgb_stars.py +12 -2
  205. setiastro/saspro/numba_utils.py +149 -48
  206. setiastro/saspro/ops/scripts.py +77 -17
  207. setiastro/saspro/ops/settings.py +177 -100
  208. setiastro/saspro/perfect_palette_picker.py +25 -7
  209. setiastro/saspro/pixelmath.py +114 -110
  210. setiastro/saspro/plate_solver.py +118 -108
  211. setiastro/saspro/remove_green.py +24 -7
  212. setiastro/saspro/remove_stars.py +136 -162
  213. setiastro/saspro/remove_stars_preset.py +55 -13
  214. setiastro/saspro/resources.py +46 -15
  215. setiastro/saspro/rgb_combination.py +19 -18
  216. setiastro/saspro/rgbalign.py +11 -11
  217. setiastro/saspro/save_options.py +5 -4
  218. setiastro/saspro/selective_color.py +84 -25
  219. setiastro/saspro/sfcc.py +119 -72
  220. setiastro/saspro/shortcuts.py +345 -36
  221. setiastro/saspro/signature_insert.py +4 -1
  222. setiastro/saspro/stacking_suite.py +2066 -1119
  223. setiastro/saspro/star_alignment.py +291 -331
  224. setiastro/saspro/star_spikes.py +137 -53
  225. setiastro/saspro/star_stretch.py +47 -10
  226. setiastro/saspro/stat_stretch.py +52 -16
  227. setiastro/saspro/status_log_dock.py +1 -1
  228. setiastro/saspro/subwindow.py +97 -36
  229. setiastro/saspro/supernovaasteroidhunter.py +68 -61
  230. setiastro/saspro/swap_manager.py +77 -42
  231. setiastro/saspro/translations/all_source_strings.json +4726 -0
  232. setiastro/saspro/translations/ar_translations.py +4096 -0
  233. setiastro/saspro/translations/de_translations.py +3728 -0
  234. setiastro/saspro/translations/es_translations.py +4169 -0
  235. setiastro/saspro/translations/fr_translations.py +4090 -0
  236. setiastro/saspro/translations/hi_translations.py +3803 -0
  237. setiastro/saspro/translations/integrate_translations.py +271 -0
  238. setiastro/saspro/translations/it_translations.py +4728 -0
  239. setiastro/saspro/translations/ja_translations.py +3834 -0
  240. setiastro/saspro/translations/pt_translations.py +3847 -0
  241. setiastro/saspro/translations/ru_translations.py +3082 -0
  242. setiastro/saspro/translations/saspro_ar.qm +0 -0
  243. setiastro/saspro/translations/saspro_ar.ts +16019 -0
  244. setiastro/saspro/translations/saspro_de.qm +0 -0
  245. setiastro/saspro/translations/saspro_de.ts +14548 -0
  246. setiastro/saspro/translations/saspro_es.qm +0 -0
  247. setiastro/saspro/translations/saspro_es.ts +16202 -0
  248. setiastro/saspro/translations/saspro_fr.qm +0 -0
  249. setiastro/saspro/translations/saspro_fr.ts +15870 -0
  250. setiastro/saspro/translations/saspro_hi.qm +0 -0
  251. setiastro/saspro/translations/saspro_hi.ts +14855 -0
  252. setiastro/saspro/translations/saspro_it.qm +0 -0
  253. setiastro/saspro/translations/saspro_it.ts +19046 -0
  254. setiastro/saspro/translations/saspro_ja.qm +0 -0
  255. setiastro/saspro/translations/saspro_ja.ts +14980 -0
  256. setiastro/saspro/translations/saspro_pt.qm +0 -0
  257. setiastro/saspro/translations/saspro_pt.ts +15024 -0
  258. setiastro/saspro/translations/saspro_ru.qm +0 -0
  259. setiastro/saspro/translations/saspro_ru.ts +11835 -0
  260. setiastro/saspro/translations/saspro_sw.qm +0 -0
  261. setiastro/saspro/translations/saspro_sw.ts +15237 -0
  262. setiastro/saspro/translations/saspro_uk.qm +0 -0
  263. setiastro/saspro/translations/saspro_uk.ts +15248 -0
  264. setiastro/saspro/translations/saspro_zh.qm +0 -0
  265. setiastro/saspro/translations/saspro_zh.ts +15289 -0
  266. setiastro/saspro/translations/sw_translations.py +3897 -0
  267. setiastro/saspro/translations/uk_translations.py +3929 -0
  268. setiastro/saspro/translations/zh_translations.py +3910 -0
  269. setiastro/saspro/versioning.py +77 -0
  270. setiastro/saspro/view_bundle.py +20 -17
  271. setiastro/saspro/wavescale_hdr.py +54 -33
  272. setiastro/saspro/wavescale_hdr_preset.py +6 -5
  273. setiastro/saspro/wavescalede.py +54 -31
  274. setiastro/saspro/wavescalede_preset.py +9 -7
  275. setiastro/saspro/whitebalance.py +58 -22
  276. setiastro/saspro/widgets/common_utilities.py +12 -11
  277. setiastro/saspro/widgets/minigame/game.js +991 -0
  278. setiastro/saspro/widgets/minigame/index.html +53 -0
  279. setiastro/saspro/widgets/minigame/style.css +241 -0
  280. setiastro/saspro/widgets/preview_dialogs.py +8 -8
  281. setiastro/saspro/widgets/resource_monitor.py +263 -0
  282. setiastro/saspro/widgets/spinboxes.py +18 -0
  283. setiastro/saspro/widgets/wavelet_utils.py +52 -20
  284. setiastro/saspro/wimi.py +7996 -0
  285. setiastro/saspro/wims.py +578 -0
  286. setiastro/saspro/window_shelf.py +2 -2
  287. {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/METADATA +15 -3
  288. setiastrosuitepro-1.6.4.post1.dist-info/RECORD +368 -0
  289. setiastrosuitepro-1.6.0.dist-info/RECORD +0 -174
  290. {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/WHEEL +0 -0
  291. {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/entry_points.txt +0 -0
  292. {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/licenses/LICENSE +0 -0
  293. {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/licenses/license.txt +0 -0
setiastro/saspro/abe.py CHANGED
@@ -75,15 +75,28 @@ def _fit_poly_on_small(small: np.ndarray, points: np.ndarray, degree: int, patch
75
75
 
76
76
  if small.ndim == 3 and small.shape[2] == 3:
77
77
  bg_small = np.zeros_like(small, dtype=np.float32)
78
+
79
+ # Batch collect samples: (num_samples, 3)
80
+ # We need N samples. z will be list of (3,) arrays
81
+
82
+ # Pre-allocate Z: (N, 3)
83
+ Z = np.zeros((len(xs), 3), dtype=np.float32)
84
+
85
+ for k, (x, y) in enumerate(zip(xs, ys)):
86
+ x0, x1 = max(0, x - half), min(W, x + half + 1)
87
+ y0, y1 = max(0, y - half), min(H, y + half + 1)
88
+ # Efficiently compute median for all channels in this patch
89
+ patch = small[y0:y1, x0:x1, :]
90
+ Z[k] = np.median(patch, axis=(0, 1))
91
+
92
+ # Solve once: A is (N, terms), Z is (N, 3) -> coeffs is (terms, 3)
93
+ coeffs_all, *_ = np.linalg.lstsq(A, Z, rcond=None)
94
+
95
+ # Evaluate per channel
78
96
  for c in range(3):
79
- z = []
80
- for x, y in zip(xs, ys):
81
- x0, x1 = max(0, x - half), min(W, x + half + 1)
82
- y0, y1 = max(0, y - half), min(H, y + half + 1)
83
- z.append(np.median(small[y0:y1, x0:x1, c]))
84
- z = np.asarray(z, dtype=np.float32)
85
- coeffs, *_ = np.linalg.lstsq(A, z, rcond=None)
86
- bg_small[..., c] = evaluate_polynomial(H, W, coeffs.astype(np.float32), degree)
97
+ # coeffs_all[:, c] gives the terms for channel c
98
+ bg_small[..., c] = evaluate_polynomial(H, W, coeffs_all[:, c].astype(np.float32), degree)
99
+
87
100
  return bg_small
88
101
  else:
89
102
  z = []
@@ -471,17 +484,22 @@ class ABEDialog(QDialog):
471
484
  """
472
485
  def __init__(self, parent, document: ImageDocument):
473
486
  super().__init__(parent)
474
- self.setWindowTitle("Automatic Background Extraction (ABE)")
487
+ self.setWindowTitle(self.tr("Automatic Background Extraction (ABE)"))
475
488
 
476
489
  # IMPORTANT: avoid “attached modal sheet” behavior on some Linux WMs
477
490
  self.setWindowFlag(Qt.WindowType.Window, True)
478
- # keep it blocking if you want, but as a top-level window
479
- self.setWindowModality(Qt.WindowModality.ApplicationModal)
491
+ # Non-modal: allow user to switch between images while dialog is open
492
+ self.setWindowModality(Qt.WindowModality.NonModal)
480
493
  self.setModal(False)
481
494
  #self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
482
495
 
496
+ self._main = parent
483
497
  self.doc = document
484
498
 
499
+ # Connect to active document change signal
500
+ if hasattr(self._main, "currentDocumentChanged"):
501
+ self._main.currentDocumentChanged.connect(self._on_active_doc_changed)
502
+
485
503
  self._preview_scale = 1.0
486
504
  self._preview_qimg = None
487
505
  self._last_preview = None # backing ndarray for QImage lifetime
@@ -500,10 +518,10 @@ class ABEDialog(QDialog):
500
518
  self.sp_samples = QSpinBox(); self.sp_samples.setRange(20, 10000); self.sp_samples.setSingleStep(20); self.sp_samples.setValue(120)
501
519
  self.sp_down = QSpinBox(); self.sp_down.setRange(1, 32); self.sp_down.setValue(4)
502
520
  self.sp_patch = QSpinBox(); self.sp_patch.setRange(5, 151); self.sp_patch.setSingleStep(2); self.sp_patch.setValue(15)
503
- self.chk_use_rbf = QCheckBox("Enable RBF refinement (after polynomial)"); self.chk_use_rbf.setChecked(True)
521
+ self.chk_use_rbf = QCheckBox(self.tr("Enable RBF refinement (after polynomial)")); self.chk_use_rbf.setChecked(True)
504
522
  self.sp_rbf = QSpinBox(); self.sp_rbf.setRange(0, 1000); self.sp_rbf.setValue(100) # shown as ×0.01 below
505
- self.chk_make_bg_doc = QCheckBox("Create background document"); self.chk_make_bg_doc.setChecked(False)
506
- self.chk_preview_bg = QCheckBox("Preview background instead of corrected"); self.chk_preview_bg.setChecked(False)
523
+ self.chk_make_bg_doc = QCheckBox(self.tr("Create background document")); self.chk_make_bg_doc.setChecked(False)
524
+ self.chk_preview_bg = QCheckBox(self.tr("Preview background instead of corrected")); self.chk_preview_bg.setChecked(False)
507
525
 
508
526
  # Preview area
509
527
  self.preview_label = QLabel(alignment=Qt.AlignmentFlag.AlignCenter)
@@ -516,10 +534,10 @@ class ABEDialog(QDialog):
516
534
  self.preview_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
517
535
 
518
536
  # Buttons
519
- self.btn_preview = QPushButton("Preview")
520
- self.btn_apply = QPushButton("Apply")
521
- self.btn_close = QPushButton("Close")
522
- self.btn_clear = QPushButton("Clear Exclusions")
537
+ self.btn_preview = QPushButton(self.tr("Preview"))
538
+ self.btn_apply = QPushButton(self.tr("Apply"))
539
+ self.btn_close = QPushButton(self.tr("Close"))
540
+ self.btn_clear = QPushButton(self.tr("Clear Exclusions"))
523
541
  self.btn_preview.clicked.connect(self._do_preview)
524
542
  self.btn_apply.clicked.connect(self._do_apply)
525
543
  self.btn_close.clicked.connect(self.close)
@@ -527,15 +545,15 @@ class ABEDialog(QDialog):
527
545
 
528
546
  # Layout
529
547
  params = QFormLayout()
530
- params.addRow("Polynomial degree:", self.sp_degree)
531
- params.addRow("# sample points:", self.sp_samples)
532
- params.addRow("Downsample factor:", self.sp_down)
533
- params.addRow("Patch size (px):", self.sp_patch)
548
+ params.addRow(self.tr("Polynomial degree:"), self.sp_degree)
549
+ params.addRow(self.tr("# sample points:"), self.sp_samples)
550
+ params.addRow(self.tr("Downsample factor:"), self.sp_down)
551
+ params.addRow(self.tr("Patch size (px):"), self.sp_patch)
534
552
 
535
- rbf_box = QGroupBox("RBF Refinement")
553
+ rbf_box = QGroupBox(self.tr("RBF Refinement"))
536
554
  rbf_form = QFormLayout()
537
555
  rbf_form.addRow(self.chk_use_rbf)
538
- rbf_form.addRow("Smooth (×0.01):", self.sp_rbf)
556
+ rbf_form.addRow(self.tr("Smooth (x0.01):"), self.sp_rbf)
539
557
  rbf_box.setLayout(rbf_form)
540
558
 
541
559
  opts = QVBoxLayout()
@@ -612,6 +630,17 @@ class ABEDialog(QDialog):
612
630
  bar.addWidget(self.btn_autostr)
613
631
  return bar
614
632
 
633
+ # ----- active document change -----
634
+ def _on_active_doc_changed(self, doc):
635
+ """Called when user clicks a different image window."""
636
+ if doc is None or getattr(doc, "image", None) is None:
637
+ return
638
+ self.doc = doc
639
+ self._polygons.clear()
640
+ self._drawing_poly = None
641
+ self._preview_source_f01 = None
642
+ self._populate_initial_preview()
643
+
615
644
  # ----- data helpers -----
616
645
  def _get_source_float(self) -> np.ndarray | None:
617
646
  src = np.asarray(self.doc.image)
@@ -813,12 +842,34 @@ class ABEDialog(QDialog):
813
842
  pass
814
843
 
815
844
  self._set_status("Done")
816
- self.accept()
845
+ # Dialog stays open so user can apply to other images
846
+ # Refresh to use the now-active document for next operation
847
+ self._refresh_document_from_active()
817
848
 
818
849
  except Exception as e:
819
850
  self._set_status("Error")
820
851
  QMessageBox.critical(self, "Apply failed", str(e))
821
852
 
853
+ def _refresh_document_from_active(self):
854
+ """
855
+ Refresh the dialog's document reference to the currently active document.
856
+ This allows reusing the same dialog on different images.
857
+ """
858
+ try:
859
+ main = self.parent()
860
+ if main and hasattr(main, "_active_doc"):
861
+ new_doc = main._active_doc()
862
+ if new_doc is not None and new_doc is not self.doc:
863
+ self.doc = new_doc
864
+ # Reset preview state for new document
865
+ self._preview_source_f01 = None
866
+ self._last_preview = None
867
+ self._preview_qimg = None
868
+ # Clear polygons since they were for old image
869
+ self._clear_polys()
870
+ except Exception:
871
+ pass
872
+
822
873
 
823
874
  # ----- exclusion polygons & mask -----
824
875
  def _clear_polys(self):
@@ -292,14 +292,14 @@ class _ONNXWorker(QThread):
292
292
  class AberrationAIDialog(QDialog):
293
293
  def __init__(self, parent, docman, get_active_doc_callable, icon: QIcon | None = None):
294
294
  super().__init__(parent)
295
- self.setWindowTitle("R.A.'s Aberration Correction (AI)")
295
+ self.setWindowTitle(self.tr("R.A.'s Aberration Correction (AI)"))
296
296
  if icon is not None:
297
297
  self.setWindowIcon(icon)
298
298
 
299
299
  # Normalize window behavior across platforms
300
300
  self.setWindowFlag(Qt.WindowType.Window, True)
301
- # This is a “big operation” tool; app-modal is usually fine here
302
- self.setWindowModality(Qt.WindowModality.ApplicationModal)
301
+ # Non-modal: allow user to switch between images while dialog is open
302
+ self.setWindowModality(Qt.WindowModality.NonModal)
303
303
  self.setModal(False)
304
304
  #self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
305
305
 
@@ -312,40 +312,40 @@ class AberrationAIDialog(QDialog):
312
312
 
313
313
  # Model row
314
314
  row = QHBoxLayout()
315
- row.addWidget(QLabel("Model:"))
315
+ row.addWidget(QLabel(self.tr("Model:")))
316
316
  self.model_label = QLabel("—")
317
317
  self.model_label.setToolTip("")
318
- btn_browse = QPushButton("Browse…"); btn_browse.clicked.connect(self._browse_model)
318
+ btn_browse = QPushButton(self.tr("Browse…")); btn_browse.clicked.connect(self._browse_model)
319
319
  row.addWidget(self.model_label, 1)
320
320
  row.addWidget(btn_browse)
321
321
  v.addLayout(row)
322
322
 
323
323
  # Providers row
324
324
  row2 = QHBoxLayout()
325
- self.chk_auto = QCheckBox("Auto GPU (if available)")
325
+ self.chk_auto = QCheckBox(self.tr("Auto GPU (if available)"))
326
326
  self.chk_auto.setChecked(True)
327
327
  row2.addWidget(self.chk_auto)
328
328
  self.cmb_provider = QComboBox()
329
- row2.addWidget(QLabel("Provider:"))
329
+ row2.addWidget(QLabel(self.tr("Provider:")))
330
330
  row2.addWidget(self.cmb_provider, 1)
331
331
  v.addLayout(row2)
332
332
 
333
333
  # Params row
334
334
  row3 = QHBoxLayout()
335
- row3.addWidget(QLabel("Patch"))
335
+ row3.addWidget(QLabel(self.tr("Patch")))
336
336
  self.spin_patch = QSpinBox(minimum=128, maximum=2048); self.spin_patch.setValue(512)
337
337
  row3.addWidget(self.spin_patch)
338
- row3.addWidget(QLabel("Overlap"))
338
+ row3.addWidget(QLabel(self.tr("Overlap")))
339
339
  self.spin_overlap = QSpinBox(minimum=16, maximum=512); self.spin_overlap.setValue(64)
340
340
  row3.addWidget(self.spin_overlap)
341
341
  v.addLayout(row3)
342
342
 
343
343
  # Download / Open folder
344
344
  row4 = QHBoxLayout()
345
- btn_latest = QPushButton("Download latest model…")
345
+ btn_latest = QPushButton(self.tr("Download latest model…"))
346
346
  btn_latest.clicked.connect(self._download_latest_model)
347
347
  row4.addWidget(btn_latest)
348
- btn_openfolder = QPushButton("Open model folder")
348
+ btn_openfolder = QPushButton(self.tr("Open model folder"))
349
349
  btn_openfolder.clicked.connect(self._open_model_folder)
350
350
  row4.addWidget(btn_openfolder)
351
351
  row4.addStretch(1)
@@ -354,8 +354,8 @@ class AberrationAIDialog(QDialog):
354
354
  # Progress + actions
355
355
  self.progress = QProgressBar(); self.progress.setRange(0, 100); v.addWidget(self.progress)
356
356
  row5 = QHBoxLayout()
357
- self.btn_run = QPushButton("Run"); self.btn_run.clicked.connect(self._run)
358
- btn_close = QPushButton("Close"); btn_close.clicked.connect(self.reject)
357
+ self.btn_run = QPushButton(self.tr("Run")); self.btn_run.clicked.connect(self._run)
358
+ btn_close = QPushButton(self.tr("Close")); btn_close.clicked.connect(self.reject)
359
359
  row5.addStretch(1); row5.addWidget(self.btn_run); row5.addWidget(btn_close)
360
360
  v.addLayout(row5)
361
361
 
@@ -680,7 +680,7 @@ class AberrationAIDialog(QDialog):
680
680
  )
681
681
 
682
682
  self.progress.setValue(100)
683
- self.accept()
683
+ # Dialog stays open so user can apply to other images
684
684
 
685
685
  def _on_worker_finished(self):
686
686
  # If dialog is already gone, this method is never called because the receiver (self)
@@ -224,10 +224,11 @@ class AddStarsDialog(QDialog):
224
224
  stars_added = pyqtSignal(object, object)
225
225
  def __init__(self, main, parent=None):
226
226
  super().__init__(parent)
227
- self.setWindowTitle("Add Stars to Image")
227
+ self.setWindowTitle(self.tr("Add Stars to Image"))
228
228
 
229
229
  self.setWindowFlag(Qt.WindowType.Window, True)
230
- self.setWindowModality(Qt.WindowModality.ApplicationModal)
230
+ # Non-modal: allow user to switch between images while dialog is open
231
+ self.setWindowModality(Qt.WindowModality.NonModal)
231
232
  self.setModal(False)
232
233
  #self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
233
234
 
@@ -277,27 +278,27 @@ class AddStarsDialog(QDialog):
277
278
  grid = QGridLayout()
278
279
 
279
280
  # Blend type
280
- grid.addWidget(QLabel("Blend Type:"), 0, 0)
281
+ grid.addWidget(QLabel(self.tr("Blend Type:")), 0, 0)
281
282
  self.cmb_blend = QComboBox(); self.cmb_blend.addItems(["Screen", "Add"])
282
283
  self.cmb_blend.currentIndexChanged.connect(self.update_preview)
283
284
  grid.addWidget(self.cmb_blend, 0, 1)
284
285
 
285
286
  # Starless source
286
- grid.addWidget(QLabel("Starless View:"), 1, 0)
287
+ grid.addWidget(QLabel(self.tr("Starless View:")), 1, 0)
287
288
  self.cmb_starless = QComboBox(); grid.addWidget(self.cmb_starless, 1, 1)
288
- btn_sless_file = QPushButton("Load from File"); btn_sless_file.clicked.connect(lambda: self._load_from_file('starless'))
289
+ btn_sless_file = QPushButton(self.tr("Load from File")); btn_sless_file.clicked.connect(lambda: self._load_from_file('starless'))
289
290
  grid.addWidget(btn_sless_file, 1, 2)
290
291
 
291
292
  # Stars-only source
292
- grid.addWidget(QLabel("Stars-Only View:"), 2, 0)
293
+ grid.addWidget(QLabel(self.tr("Stars-Only View:")), 2, 0)
293
294
  self.cmb_stars = QComboBox(); grid.addWidget(self.cmb_stars, 2, 1)
294
- btn_stars_file = QPushButton("Load from File"); btn_stars_file.clicked.connect(lambda: self._load_from_file('stars'))
295
+ btn_stars_file = QPushButton(self.tr("Load from File")); btn_stars_file.clicked.connect(lambda: self._load_from_file('stars'))
295
296
  grid.addWidget(btn_stars_file, 2, 2)
296
297
 
297
298
  layout.addLayout(grid)
298
299
 
299
300
  refresh_row = QHBoxLayout()
300
- btn_refresh = QPushButton("Refresh Views")
301
+ btn_refresh = QPushButton(self.tr("Refresh Views"))
301
302
  btn_refresh.clicked.connect(self._populate_doc_combos)
302
303
  refresh_row.addStretch(1)
303
304
  refresh_row.addWidget(btn_refresh)
@@ -305,7 +306,7 @@ class AddStarsDialog(QDialog):
305
306
 
306
307
  # Ratio slider
307
308
  row = QHBoxLayout()
308
- row.addWidget(QLabel("Blend Ratio (Screen/Add Intensity):"))
309
+ row.addWidget(QLabel(self.tr("Blend Ratio (Screen/Add Intensity):")))
309
310
  self.slider_ratio = QSlider(Qt.Orientation.Horizontal)
310
311
  self.slider_ratio.setRange(0, 100); self.slider_ratio.setValue(100)
311
312
  self.slider_ratio.setTickInterval(10); self.slider_ratio.setTickPosition(QSlider.TickPosition.TicksBelow)
@@ -315,8 +316,8 @@ class AddStarsDialog(QDialog):
315
316
 
316
317
  # Buttons
317
318
  brow = QHBoxLayout(); brow.addStretch(1)
318
- btn_apply = QPushButton("Apply"); btn_apply.clicked.connect(self._apply)
319
- btn_cancel= QPushButton("Cancel"); btn_cancel.clicked.connect(self.reject)
319
+ btn_apply = QPushButton(self.tr("Apply")); btn_apply.clicked.connect(self._apply)
320
+ btn_cancel= QPushButton(self.tr("Cancel")); btn_cancel.clicked.connect(self.reject)
320
321
  brow.addWidget(btn_apply); brow.addWidget(btn_cancel)
321
322
  layout.addLayout(brow)
322
323
 
@@ -574,7 +575,9 @@ class AddStarsDialog(QDialog):
574
575
 
575
576
  # Emit (target_doc, blended_image)
576
577
  self.stars_added.emit(target_doc, self.blended_image.astype(np.float32, copy=False))
577
- self.accept()
578
+ # Dialog stays open so user can apply to other images
579
+ # Refresh combo boxes for next operation
580
+ self._populate_doc_combos()
578
581
 
579
582
 
580
583
  # Ensure initial fit once shown