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
@@ -59,9 +59,10 @@ class PixelImage:
59
59
  a = np.asarray(a, dtype=np.float32)
60
60
  b = np.asarray(b, dtype=np.float32)
61
61
  if a.ndim == 3 and b.ndim == 2:
62
- b = np.repeat(b[..., None], a.shape[2], axis=2)
62
+ # Broadcast b to (H,W,1) virtual view; numpy ufuncs handle (H,W,3) vs (H,W,1) automatically
63
+ b = b[..., None]
63
64
  elif a.ndim == 2 and b.ndim == 3:
64
- a = np.repeat(a[..., None], b.shape[2], axis=2)
65
+ a = a[..., None]
65
66
  return a, b
66
67
 
67
68
  # ---- binary arithmetic helpers ----
@@ -755,7 +756,10 @@ class PixelMathDialogPro(QDialog):
755
756
  """
756
757
  def __init__(self, parent, doc, icon: QIcon | None = None):
757
758
  super().__init__(parent)
758
- self.setWindowTitle("Pixel Math")
759
+ self.setWindowTitle(self.tr("Pixel Math"))
760
+ self.setWindowFlag(Qt.WindowType.Window, True)
761
+ self.setWindowModality(Qt.WindowModality.NonModal)
762
+ self.setModal(False)
759
763
  if icon:
760
764
  try:
761
765
  self.setWindowIcon(icon)
@@ -805,7 +809,7 @@ class PixelMathDialogPro(QDialog):
805
809
  # ──────────────────────────────────────────────────────────────────────────
806
810
  # Variables mapping (raw title → identifier)
807
811
  # ──────────────────────────────────────────────────────────────────────────
808
- vars_grp = QGroupBox("Variables")
812
+ vars_grp = QGroupBox(self.tr("Variables"))
809
813
  vars_layout = QVBoxLayout(vars_grp)
810
814
 
811
815
  self.vars_list = QListWidget()
@@ -832,7 +836,7 @@ class PixelMathDialogPro(QDialog):
832
836
  # First item = active view
833
837
  active_item = QListWidgetItem("img (active)")
834
838
  active_item.setData(Qt.ItemDataRole.UserRole, "img") # ← stash the real name
835
- active_item.setToolTip("img (active)")
839
+ active_item.setToolTip(self.tr("img (active)"))
836
840
  self.vars_list.addItem(active_item)
837
841
 
838
842
  # Other open views
@@ -847,7 +851,7 @@ class PixelMathDialogPro(QDialog):
847
851
  self.vars_list.setMinimumHeight(120)
848
852
  self.vars_list.setMaximumHeight(180)
849
853
 
850
- hint = QLabel("Tip: double-click to insert the identifier at the cursor")
854
+ hint = QLabel(self.tr("Tip: double-click to insert the identifier at the cursor"))
851
855
  hint.setStyleSheet("color: gray; font-size: 11px;")
852
856
 
853
857
  vars_layout.addWidget(self.vars_list)
@@ -873,10 +877,10 @@ class PixelMathDialogPro(QDialog):
873
877
  # ──────────────────────────────────────────────────────────────────────────
874
878
  # Output group
875
879
  # ──────────────────────────────────────────────────────────────────────────
876
- out_grp = QGroupBox("Output")
880
+ out_grp = QGroupBox(self.tr("Output"))
877
881
  out_row = QHBoxLayout(out_grp)
878
- self.rb_out_overwrite = QRadioButton("Overwrite active"); self.rb_out_overwrite.setChecked(True)
879
- self.rb_out_new = QRadioButton("Create new view")
882
+ self.rb_out_overwrite = QRadioButton(self.tr("Overwrite active")); self.rb_out_overwrite.setChecked(True)
883
+ self.rb_out_new = QRadioButton(self.tr("Create new view"))
880
884
  out_row.addWidget(self.rb_out_overwrite)
881
885
  out_row.addWidget(self.rb_out_new)
882
886
  out_row.addStretch(1)
@@ -886,8 +890,8 @@ class PixelMathDialogPro(QDialog):
886
890
  # Mode (single expression vs per-channel)
887
891
  # ──────────────────────────────────────────────────────────────────────────
888
892
  mode_row = QHBoxLayout()
889
- self.rb_single = QRadioButton("Single Expression"); self.rb_single.setChecked(True)
890
- self.rb_sep = QRadioButton("Separate (R / G / B)")
893
+ self.rb_single = QRadioButton(self.tr("Single Expression")); self.rb_single.setChecked(True)
894
+ self.rb_sep = QRadioButton(self.tr("Separate (R / G / B)"))
891
895
  mode_row.addWidget(self.rb_single)
892
896
  mode_row.addWidget(self.rb_sep)
893
897
  mode_row.addStretch(1)
@@ -900,48 +904,48 @@ class PixelMathDialogPro(QDialog):
900
904
 
901
905
  # Editors
902
906
  self.ed_single = QPlainTextEdit()
903
- self.ed_single.setPlaceholderText("e.g. (img + otherView) / 2")
907
+ self.ed_single.setPlaceholderText(self.tr("e.g. (img + otherView) / 2"))
904
908
  left_col.addWidget(self.ed_single)
905
909
 
906
910
  self.tabs = QTabWidget(); self.tabs.setVisible(False)
907
911
  self.ed_r, self.ed_g, self.ed_b = QPlainTextEdit(), QPlainTextEdit(), QPlainTextEdit()
908
- for ed, name in ((self.ed_r, "Red"), (self.ed_g, "Green"), (self.ed_b, "Blue")):
912
+ for ed, name in ((self.ed_r, self.tr("Red")), (self.ed_g, self.tr("Green")), (self.ed_b, self.tr("Blue"))):
909
913
  w = QWidget(); lay = QVBoxLayout(w); lay.addWidget(ed); self.tabs.addTab(w, name)
910
914
  left_col.addWidget(self.tabs)
911
915
 
912
916
  self.rb_single.toggled.connect(lambda on: self._mode(on))
913
917
 
914
- glossary_btn = QPushButton("Glossary…")
918
+ glossary_btn = QPushButton(self.tr("Glossary…"))
915
919
  glossary_btn.clicked.connect(self._open_glossary)
916
920
  left_col.addWidget(glossary_btn)
917
921
 
918
922
  # ──────────────────────────────────────────────────────────────────────────
919
923
  # Preview (right side)
920
924
  # ──────────────────────────────────────────────────────────────────────────
921
- preview_grp = QGroupBox("Preview")
925
+ preview_grp = QGroupBox(self.tr("Preview"))
922
926
  pv_lay = QVBoxLayout(preview_grp)
923
927
 
924
928
  # Toolbar
925
929
  tb = QHBoxLayout()
926
- self.btn_preview = QPushButton("Preview")
927
- self.btn_preview.setToolTip("Compute Pixel Math and show the result here without committing.")
930
+ self.btn_preview = QPushButton(self.tr("Preview"))
931
+ self.btn_preview.setToolTip(self.tr("Compute Pixel Math and show the result here without committing."))
928
932
 
929
933
  # NEW: Auto-stretch toggle with a dropdown menu
930
934
  self.btn_autostretch = QToolButton()
931
- self.btn_autostretch.setText("Auto-stretch")
935
+ self.btn_autostretch.setText(self.tr("Auto-stretch"))
932
936
  self.btn_autostretch.setCheckable(True)
933
937
  self.btn_autostretch.setChecked(self._as_enabled)
934
938
  self.btn_autostretch.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
935
939
 
936
940
  as_menu = QMenu(self)
937
- act_toggle = as_menu.addAction("Enable Auto-stretch")
941
+ act_toggle = as_menu.addAction(self.tr("Enable Auto-stretch"))
938
942
  act_toggle.setCheckable(True); act_toggle.setChecked(self._as_enabled)
939
943
  as_menu.addSeparator()
940
944
 
941
945
  # Target presets
942
- tgt_menu = as_menu.addMenu("Target median")
946
+ tgt_menu = as_menu.addMenu(self.tr("Target median"))
943
947
  self._tgt_group = QActionGroup(self); self._tgt_group.setExclusive(True)
944
- for label, val in (("0.18 (soft)", 0.18), ("0.25 (default)", 0.25), ("0.35 (brighter)", 0.35)):
948
+ for label, val in ((self.tr("0.18 (soft)"), 0.18), (self.tr("0.25 (default)"), 0.25), (self.tr("0.35 (brighter)"), 0.35)):
945
949
  a = tgt_menu.addAction(label)
946
950
  a.setCheckable(True)
947
951
  a.setChecked(abs(self._as_target - val) < 1e-6)
@@ -949,9 +953,9 @@ class PixelMathDialogPro(QDialog):
949
953
  a.triggered.connect(lambda _=False, v=val: self._set_as_target(v))
950
954
 
951
955
  # Sigma presets
952
- sig_menu = as_menu.addMenu("Black-point sigma")
956
+ sig_menu = as_menu.addMenu(self.tr("Black-point sigma"))
953
957
  self._sig_group = QActionGroup(self); self._sig_group.setExclusive(True)
954
- for label, val in (("σ=2.5", 2.5), ("σ=3 (default)", 3.0), ("σ=4 (deeper black)", 4.0)):
958
+ for label, val in ((self.tr("σ=2.5"), 2.5), (self.tr("σ=3 (default)"), 3.0), (self.tr("σ=4 (deeper black)"), 4.0)):
955
959
  a = sig_menu.addAction(label)
956
960
  a.setCheckable(True)
957
961
  a.setChecked(abs(self._as_sigma - val) < 1e-6)
@@ -959,12 +963,12 @@ class PixelMathDialogPro(QDialog):
959
963
  a.triggered.connect(lambda _=False, v=val: self._set_as_sigma(v))
960
964
 
961
965
  # Linked channels
962
- act_linked = as_menu.addAction("Linked channels (use luminance)")
966
+ act_linked = as_menu.addAction(self.tr("Linked channels (use luminance)"))
963
967
  act_linked.setCheckable(True); act_linked.setChecked(self._as_linked)
964
968
  as_menu.addSeparator()
965
969
 
966
970
  # Output precision
967
- act_16 = as_menu.addAction("Use 16-bit stats")
971
+ act_16 = as_menu.addAction(self.tr("Use 16-bit stats"))
968
972
  act_16.setCheckable(True); act_16.setChecked(self._as_16bit)
969
973
 
970
974
  self.btn_autostretch.setMenu(as_menu)
@@ -1021,10 +1025,10 @@ class PixelMathDialogPro(QDialog):
1021
1025
  self._set_as_sigma = _set_sigma
1022
1026
 
1023
1027
  # existing zoom buttons
1024
- self.btn_zoom_in = themed_toolbtn("zoom-in", "Zoom In")
1025
- self.btn_zoom_out = themed_toolbtn("zoom-out", "Zoom Out")
1026
- self.btn_zoom_1_1 = themed_toolbtn("zoom-original", "1:1")
1027
- self.btn_fit = themed_toolbtn("zoom-fit-best", "Fit to Preview")
1028
+ self.btn_zoom_in = themed_toolbtn("zoom-in", self.tr("Zoom In"))
1029
+ self.btn_zoom_out = themed_toolbtn("zoom-out", self.tr("Zoom Out"))
1030
+ self.btn_zoom_1_1 = themed_toolbtn("zoom-original", self.tr("1:1"))
1031
+ self.btn_fit = themed_toolbtn("zoom-fit-best", self.tr("Fit to Preview"))
1028
1032
 
1029
1033
  tb.addWidget(self.btn_preview)
1030
1034
  tb.addWidget(self.btn_autostretch) # ← NEW
@@ -1056,9 +1060,9 @@ class PixelMathDialogPro(QDialog):
1056
1060
  # Examples (insertable templates)
1057
1061
  # ──────────────────────────────────────────────────────────────────────────
1058
1062
  ex_row = QHBoxLayout()
1059
- ex_row.addWidget(QLabel("Examples:"))
1063
+ ex_row.addWidget(QLabel(self.tr("Examples:")))
1060
1064
  self.cb_examples = QComboBox()
1061
- self.cb_examples.addItem("Insert example…")
1065
+ self.cb_examples.addItem(self.tr("Insert example…"))
1062
1066
  for title, kind, payload in self._examples_list():
1063
1067
  self.cb_examples.addItem(title, (kind, payload))
1064
1068
  self.cb_examples.currentIndexChanged.connect(self._apply_example_from_combo)
@@ -1069,12 +1073,12 @@ class PixelMathDialogPro(QDialog):
1069
1073
  # Favorites
1070
1074
  # ──────────────────────────────────────────────────────────────────────────
1071
1075
  fav_row = QHBoxLayout()
1072
- self.cb_fav = QComboBox(); self.cb_fav.addItem("Select a favorite expression")
1076
+ self.cb_fav = QComboBox(); self.cb_fav.addItem(self.tr("Select a favorite expression"))
1073
1077
  self._load_favorites()
1074
1078
  self.cb_fav.currentTextChanged.connect(self._pick_favorite)
1075
1079
 
1076
- b_save = QPushButton("Save as Favorite")
1077
- b_del = QPushButton("Delete Favorite")
1080
+ b_save = QPushButton(self.tr("Save as Favorite"))
1081
+ b_del = QPushButton(self.tr("Delete Favorite"))
1078
1082
 
1079
1083
  b_save.clicked.connect(self._save_favorite)
1080
1084
  b_del.clicked.connect(self._delete_favorite)
@@ -1088,7 +1092,7 @@ class PixelMathDialogPro(QDialog):
1088
1092
  if self.cb_fav.currentIndex() <= 0:
1089
1093
  return
1090
1094
  menu = QMenu(self)
1091
- act_del = menu.addAction("Delete this favorite")
1095
+ act_del = menu.addAction(self.tr("Delete this favorite"))
1092
1096
  act = menu.exec(self.cb_fav.mapToGlobal(point))
1093
1097
  if act == act_del:
1094
1098
  self._delete_favorite()
@@ -1102,7 +1106,7 @@ class PixelMathDialogPro(QDialog):
1102
1106
  btns = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, parent=self)
1103
1107
  btns.accepted.connect(self._apply)
1104
1108
  btns.rejected.connect(self.reject)
1105
- b_help = btns.addButton("Help", QDialogButtonBox.ButtonRole.HelpRole)
1109
+ b_help = btns.addButton(self.tr("Help"), QDialogButtonBox.ButtonRole.HelpRole)
1106
1110
  b_help.clicked.connect(self._help)
1107
1111
  left_col.addWidget(btns)
1108
1112
 
@@ -1184,8 +1188,8 @@ class PixelMathDialogPro(QDialog):
1184
1188
  except Exception as e:
1185
1189
  msg = str(e)
1186
1190
  if "name '" in msg and "' is not defined" in msg:
1187
- msg += "\n\nTip: use the identifier listed in Variables (or the raw title; it’s auto-mapped)."
1188
- QMessageBox.critical(self, "Pixel Math Preview", f"Failed:\n{msg}")
1191
+ msg += self.tr("\n\nTip: use the identifier listed in Variables (or the raw title; it’s auto-mapped).")
1192
+ QMessageBox.critical(self, self.tr("Pixel Math Preview"), self.tr("Failed:\n{0}").format(msg))
1189
1193
  finally:
1190
1194
  QApplication.restoreOverrideCursor()
1191
1195
 
@@ -1256,93 +1260,93 @@ class PixelMathDialogPro(QDialog):
1256
1260
 
1257
1261
  return [
1258
1262
  # --- existing basics ---
1259
- ("Average two views", "single", f"({a} + {b}) / 2"),
1260
- ("Difference (A - B)", "single", f"{a} - {b}"),
1261
- ("Invert active", "single", f"~{a}"),
1262
- ("Subtract median (bias remove)", "single", f"{a} - med({a})"),
1263
- ("Zero-center by mean", "single", f"{a} - mean({a})"),
1264
- ("Min + Max combine", "single", f"min({a}) + max({a})"),
1265
- ("Log transform", "single", f"log({a} + 1e-6)"),
1266
- ("Midtones transform m=0.25", "single", f"mtf({a}, 0.25)"),
1267
- ("If darker than median → 0 else 1", "single", f"iff({a} < med({a}), 0, 1)"),
1268
-
1269
- ("Per-channel: swap R↔B", "rgb", (f"{a}[2]", f"{a}[1]", f"{a}[0]")),
1270
- ("Per-channel: avg A & B", "rgb", (f"({a}[0]+{b}[0])/2", f"({a}[1]+{b}[1])/2", f"({a}[2]+{b}[2])/2")),
1271
- ("Per-channel: build RGB from A,B,C", "rgb", (f"{a}[0]", f"{b}[1]", f"{c}[2]")),
1263
+ (self.tr("Average two views"), "single", f"({a} + {b}) / 2"),
1264
+ (self.tr("Difference (A - B)"), "single", f"{a} - {b}"),
1265
+ (self.tr("Invert active"), "single", f"~{a}"),
1266
+ (self.tr("Subtract median (bias remove)"), "single", f"{a} - med({a})"),
1267
+ (self.tr("Zero-center by mean"), "single", f"{a} - mean({a})"),
1268
+ (self.tr("Min + Max combine"), "single", f"min({a}) + max({a})"),
1269
+ (self.tr("Log transform"), "single", f"log({a} + 1e-6)"),
1270
+ (self.tr("Midtones transform m=0.25"), "single", f"mtf({a}, 0.25)"),
1271
+ (self.tr("If darker than median → 0 else 1"), "single", f"iff({a} < med({a}), 0, 1)"),
1272
+
1273
+ (self.tr("Per-channel: swap R↔B"), "rgb", (f"{a}[2]", f"{a}[1]", f"{a}[0]")),
1274
+ (self.tr("Per-channel: avg A & B"), "rgb", (f"({a}[0]+{b}[0])/2", f"({a}[1]+{b}[1])/2", f"({a}[2]+{b}[2])/2")),
1275
+ (self.tr("Per-channel: build RGB from A,B,C"), "rgb", (f"{a}[0]", f"{b}[1]", f"{c}[2]")),
1272
1276
 
1273
1277
  # --- new, single-expression tone/normalization ---
1274
- ("Normalize to 0–1 (per-channel)", "single", f"normalize01({a})"),
1275
- ("Sigmoid contrast (k=12, mid=0.4)", "single", f"sigmoid({a}, k=12, mid=0.4)"),
1276
- ("Gamma 0.6 (brighten midtones)", "single", f"gamma({a}, 0.6)"),
1277
- ("Percentile stretch 0.5–99.5%", "single",
1278
+ (self.tr("Normalize to 0–1 (per-channel)"), "single", f"normalize01({a})"),
1279
+ (self.tr("Sigmoid contrast (k=12, mid=0.4)"), "single", f"sigmoid({a}, k=12, mid=0.4)"),
1280
+ (self.tr("Gamma 0.6 (brighten midtones)"), "single", f"gamma({a}, 0.6)"),
1281
+ (self.tr("Percentile stretch 0.5–99.5%"), "single",
1278
1282
  f"lo = percentile({a}, 0.5)\nhi = percentile({a}, 99.5)\nclamp(({a} - lo) / (hi - lo), 0, 1)"),
1279
1283
 
1280
1284
  # --- blending & masking ---
1281
- ("Blend A→B by horizontal gradient X", "single", f"t = X\nlerp({a}, {b}, t)"),
1282
- ("Apply active mask to blend A→B", "single", f"m = mask()\napply_mask({a}, {b}, m)"),
1285
+ (self.tr("Blend A→B by horizontal gradient X"), "single", f"t = X\nlerp({a}, {b}, t)"),
1286
+ (self.tr("Apply active mask to blend A→B"), "single", f"m = mask()\napply_mask({a}, {b}, m)"),
1283
1287
 
1284
1288
  # --- sharpening with mask (multiline) ---
1285
- ("Masked unsharp (luma-based)", "single",
1289
+ (self.tr("Masked unsharp (luma-based)"), "single",
1286
1290
  f"base = {a}\nsh = unsharp({a}, sigma=1.2, amount=0.8)\n"
1287
1291
  f"m = smoothstep(0.10, 0.60, luma({a}))\napply_mask(base, sh, m)"),
1288
1292
 
1289
1293
  # --- view matching / calibration ---
1290
- ("Match medians of A to B", "single", f"{a} * (med({b}) / med({a}))"),
1294
+ (self.tr("Match medians of A to B"), "single", f"{a} * (med({b}) / med({a}))"),
1291
1295
 
1292
1296
  # --- small filters ---
1293
- ("Gaussian blur σ=2", "single", f"gauss({a}, sigma=2.0)"),
1294
- ("Median filter k=3", "single", f"median({a}, k=3)"),
1297
+ (self.tr("Gaussian blur σ=2"), "single", f"gauss({a}, sigma=2.0)"),
1298
+ (self.tr("Median filter k=3"), "single", f"median({a}, k=3)"),
1295
1299
 
1296
1300
  # --- per-channel examples using new helpers ---
1297
- ("Per-channel: luma to all channels", "rgb", (f"luma({a})", f"luma({a})", f"luma({a})")),
1298
- ("Per-channel: A’s R, B’s G, C’s B (normed)", "rgb",
1301
+ (self.tr("Per-channel: luma to all channels"), "rgb", (f"luma({a})", f"luma({a})", f"luma({a})")),
1302
+ (self.tr("Per-channel: A’s R, B’s G, C’s B (normed)"), "rgb",
1299
1303
  (f"normalize01({a}[0])", f"normalize01({b}[1])", f"normalize01({c}[2])")),
1300
1304
  ]
1301
1305
 
1302
1306
  def _function_glossary(self):
1303
1307
  # name -> (signature / template, short description)
1304
1308
  return {
1305
- "clamp": ("clamp(x, lo=0, hi=1)", "Limit values to [lo..hi]."),
1306
- "rescale": ("rescale(x, a, b, lo=0, hi=1)", "Map range [a..b] to [lo..hi]."),
1307
- "gamma": ("gamma(x, g)", "Apply gamma curve."),
1308
- "pow_safe": ("pow_safe(x, p)", "Power with EPS floor."),
1309
- "absf": ("absf(x)", "Absolute value."),
1310
- "expf": ("expf(x)", "Exponential."),
1311
- "sqrtf": ("sqrtf(x)", "Square root (clamped to ≥0)."),
1312
- "arcsin": ("arcsin(x)", "Inverse sine (radians), input clipped to [-1,1]."),
1313
- "sigmoid": ("sigmoid(x, k=10, mid=0.5)", "S-shaped tone curve."),
1314
- "smoothstep": ("smoothstep(e0, e1, x)", "Cubic smooth ramp."),
1315
- "lerp/mix": ("lerp(a, b, t)", "Linear blend."),
1316
- "percentile": ("percentile(x, p)", "Per-channel percentile image."),
1317
- "normalize01": ("normalize01(x)", "Per-channel [0..1] normalization."),
1318
- "zscore": ("zscore(x)", "Per-channel (x-mean)/std."),
1319
- "ch": ("ch(x, i)", "Extract channel i (0/1/2) as 2-D."),
1320
- "luma": ("luma(x)", "Rec.709 luminance as 2-D."),
1321
- "compose": ("compose(R, G, B)", "Stack three planes to RGB."),
1322
- "mask": ("m = mask()", "Active mask (2-D, [0..1])."),
1323
- "apply_mask": ("apply_mask(base, out, m)", "Blend by mask."),
1324
- "boxblur": ("boxblur(x, k=3)", "Box blur (cv2 if available)."),
1325
- "gauss": ("gauss(x, sigma=1.0)", "Gaussian blur."),
1326
- "median": ("median(x, k=3)", "Median filter (cv2 if avail)."),
1327
- "unsharp": ("unsharp(x, sigma=1.5, amount=1.0)", "Unsharp mask."),
1328
- "mtf": ("mtf(x, m)", "Midtones transfer (existing)."),
1329
- "iff": ("iff(cond, a, b)", "Conditional (existing)."),
1330
- "X / Y": ("X, Y", "Normalized coordinates in [0..1]."),
1331
- "H/W/C": ("H, W, C, shape", "Image dimensions."),
1309
+ "clamp": ("clamp(x, lo=0, hi=1)", self.tr("Limit values to [lo..hi].")),
1310
+ "rescale": ("rescale(x, a, b, lo=0, hi=1)", self.tr("Map range [a..b] to [lo..hi].")),
1311
+ "gamma": ("gamma(x, g)", self.tr("Apply gamma curve.")),
1312
+ "pow_safe": ("pow_safe(x, p)", self.tr("Power with EPS floor.")),
1313
+ "absf": ("absf(x)", self.tr("Absolute value.")),
1314
+ "expf": ("expf(x)", self.tr("Exponential.")),
1315
+ "sqrtf": ("sqrtf(x)", self.tr("Square root (clamped to ≥0).")),
1316
+ "arcsin": ("arcsin(x)", self.tr("Inverse sine (radians), input clipped to [-1,1].")),
1317
+ "sigmoid": ("sigmoid(x, k=10, mid=0.5)", self.tr("S-shaped tone curve.")),
1318
+ "smoothstep": ("smoothstep(e0, e1, x)", self.tr("Cubic smooth ramp.")),
1319
+ "lerp/mix": ("lerp(a, b, t)", self.tr("Linear blend.")),
1320
+ "percentile": ("percentile(x, p)", self.tr("Per-channel percentile image.")),
1321
+ "normalize01": ("normalize01(x)", self.tr("Per-channel [0..1] normalization.")),
1322
+ "zscore": ("zscore(x)", self.tr("Per-channel (x-mean)/std.")),
1323
+ "ch": ("ch(x, i)", self.tr("Extract channel i (0/1/2) as 2-D.")),
1324
+ "luma": ("luma(x)", self.tr("Rec.709 luminance as 2-D.")),
1325
+ "compose": ("compose(R, G, B)", self.tr("Stack three planes to RGB.")),
1326
+ "mask": ("m = mask()", self.tr("Active mask (2-D, [0..1]).")),
1327
+ "apply_mask": ("apply_mask(base, out, m)", self.tr("Blend by mask.")),
1328
+ "boxblur": ("boxblur(x, k=3)", self.tr("Box blur (cv2 if available).")),
1329
+ "gauss": ("gauss(x, sigma=1.0)", self.tr("Gaussian blur.")),
1330
+ "median": ("median(x, k=3)", self.tr("Median filter (cv2 if avail).")),
1331
+ "unsharp": ("unsharp(x, sigma=1.5, amount=1.0)", self.tr("Unsharp mask.")),
1332
+ "mtf": ("mtf(x, m)", self.tr("Midtones transfer (existing).")),
1333
+ "iff": ("iff(cond, a, b)", self.tr("Conditional (existing).")),
1334
+ "X / Y": ("X, Y", self.tr("Normalized coordinates in [0..1].")),
1335
+ "H/W/C": ("H, W, C, shape", self.tr("Image dimensions.")),
1332
1336
  }
1333
1337
 
1334
1338
  def _open_glossary(self):
1335
1339
  dlg = QDialog(self)
1336
- dlg.setWindowTitle("Pixel Math Glossary")
1340
+ dlg.setWindowTitle(self.tr("Pixel Math Glossary"))
1337
1341
  lay = QVBoxLayout(dlg)
1338
1342
 
1339
- info = QLabel("Double-click to insert a template at the cursor.")
1343
+ info = QLabel(self.tr("Double-click to insert a template at the cursor."))
1340
1344
  info.setStyleSheet("color: gray;")
1341
1345
  lay.addWidget(info)
1342
1346
 
1343
1347
  from PyQt6.QtWidgets import QLineEdit, QListWidget, QListWidgetItem, QHBoxLayout, QPushButton
1344
1348
  search = QLineEdit()
1345
- search.setPlaceholderText("Search…")
1349
+ search.setPlaceholderText(self.tr("Search…"))
1346
1350
  lay.addWidget(search)
1347
1351
 
1348
1352
  lst = QListWidget()
@@ -1372,7 +1376,7 @@ class PixelMathDialogPro(QDialog):
1372
1376
  search.textChanged.connect(lambda *_: _refill())
1373
1377
 
1374
1378
  btns = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
1375
- insert_btn = QPushButton("Insert")
1379
+ insert_btn = QPushButton(self.tr("Insert"))
1376
1380
  btns.addButton(insert_btn, QDialogButtonBox.ButtonRole.ApplyRole)
1377
1381
  insert_btn.clicked.connect(_insert_current)
1378
1382
  btns.rejected.connect(dlg.reject)
@@ -1384,7 +1388,7 @@ class PixelMathDialogPro(QDialog):
1384
1388
 
1385
1389
  def _delete_favorite(self):
1386
1390
  text = self.cb_fav.currentText()
1387
- if text == "Select a favorite expression":
1391
+ if text == self.tr("Select a favorite expression"):
1388
1392
  return
1389
1393
  # Remove from in-memory list
1390
1394
  try:
@@ -1397,7 +1401,7 @@ class PixelMathDialogPro(QDialog):
1397
1401
  # Rebuild combo to keep indices clean
1398
1402
  self.cb_fav.blockSignals(True)
1399
1403
  self.cb_fav.clear()
1400
- self.cb_fav.addItem("Select a favorite expression")
1404
+ self.cb_fav.addItem(self.tr("Select a favorite expression"))
1401
1405
  for f in self._favs:
1402
1406
  self.cb_fav.addItem(f)
1403
1407
  self.cb_fav.setCurrentIndex(0)
@@ -1456,7 +1460,7 @@ class PixelMathDialogPro(QDialog):
1456
1460
  if s: s.setValue("pixelmath_favorites", json.dumps(self._favs))
1457
1461
 
1458
1462
  def _pick_favorite(self, text):
1459
- if text == "Select a favorite expression": return
1463
+ if text == self.tr("Select a favorite expression"): return
1460
1464
  if "[R]" in text or "[G]" in text or "[B]" in text:
1461
1465
  self.rb_sep.setChecked(True); self._mode(False)
1462
1466
  parts = {}
@@ -1502,24 +1506,24 @@ class PixelMathDialogPro(QDialog):
1502
1506
  def _help(self):
1503
1507
  gl = self._function_glossary()
1504
1508
  lines = [
1505
- "Operators: + - * / ^(power) ~(invert)",
1506
- "Comparisons: <, == (use inside iff)",
1509
+ self.tr("Operators: + - * / ^(power) ~(invert)"),
1510
+ self.tr("Comparisons: <, == (use inside iff)"),
1507
1511
  "",
1508
- "Variables:",
1509
- " • img (active) and one per open view (by window title, auto-mapped).",
1510
- " • Coordinates: X, Y in [0..1].",
1511
- " • Sizes: H, W, C, shape.",
1512
+ self.tr("Variables:"),
1513
+ self.tr(" • img (active) and one per open view (by window title, auto-mapped)."),
1514
+ self.tr(" • Coordinates: X, Y in [0..1]."),
1515
+ self.tr(" • Sizes: H, W, C, shape."),
1512
1516
  "",
1513
- "Per-channel indexing: view[0], view[1], view[2].",
1514
- "Multiline: last line is the result.",
1515
- "Output: Overwrite active or Create new view.",
1517
+ self.tr("Per-channel indexing: view[0], view[1], view[2]."),
1518
+ self.tr("Multiline: last line is the result."),
1519
+ self.tr("Output: Overwrite active or Create new view."),
1516
1520
  "",
1517
- "Functions:"
1521
+ self.tr("Functions:")
1518
1522
  ]
1519
1523
  # Pretty column-ish dump
1520
1524
  for name, (sig, desc) in gl.items():
1521
1525
  lines.append(f" {sig}\n {desc}")
1522
- QMessageBox.information(self, "Pixel Math Help", "\n".join(lines))
1526
+ QMessageBox.information(self, self.tr("Pixel Math Help"), "\n".join(lines))
1523
1527
 
1524
1528
  # ---------- Apply ----------------------------------------------------------
1525
1529
  # ---------- Apply ----------------------------------------------------------
@@ -1595,6 +1599,6 @@ class PixelMathDialogPro(QDialog):
1595
1599
  except Exception as e:
1596
1600
  msg = str(e)
1597
1601
  if "name '" in msg and "' is not defined" in msg:
1598
- msg += "\n\nTip: use the identifier shown beside Variables (e.g. 'andromeda_png'), "
1599
- msg += "or just type the raw title; it will be auto-mapped."
1602
+ msg += self.tr("\n\nTip: use the identifier shown beside Variables (e.g. 'andromeda_png'), ")
1603
+ msg += self.tr("or just type the raw title; it will be auto-mapped.")
1600
1604
  QMessageBox.critical(self, "Pixel Math", f"Failed:\n{msg}")