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
@@ -83,12 +83,12 @@ class _DragTab(QLabel):
83
83
  self.owner = owner
84
84
  self._press_pos = None
85
85
  self.setText("⧉")
86
- self.setToolTip(
86
+ self.setToolTip(self.tr(
87
87
  "Drag to duplicate/copy view.\n"
88
88
  "Hold Alt while dragging to LINK this view with another (live pan/zoom sync).\n"
89
89
  "Hold Shift while dragging to drop this image as a mask onto another view.\n"
90
90
  "Hold Ctrl while dragging to copy the astrometric solution (WCS) to another view."
91
- )
91
+ ))
92
92
 
93
93
  self.setFixedSize(22, 18)
94
94
  self.setAlignment(Qt.AlignmentFlag.AlignCenter)
@@ -371,7 +371,8 @@ class ImageSubWindow(QWidget):
371
371
  # pixel readout live-probe state
372
372
  self._space_down = False
373
373
  self._readout_dragging = False
374
- self._last_readout = None
374
+ # Pinch gesture state (macOS trackpad)
375
+ self._gesture_zoom_start = None
375
376
  self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
376
377
 
377
378
  # Title (doc/view) sync
@@ -414,31 +415,31 @@ class ImageSubWindow(QWidget):
414
415
 
415
416
  self._preview_btn = QToolButton(self)
416
417
  self._preview_btn.setText("⟂") # crosshair glyph
417
- self._preview_btn.setToolTip("Create Preview: click, then drag on the image to define a preview rectangle.")
418
+ self._preview_btn.setToolTip(self.tr("Create Preview: click, then drag on the image to define a preview rectangle."))
418
419
  self._preview_btn.setCheckable(True)
419
420
  self._preview_btn.clicked.connect(self._toggle_preview_select_mode)
420
421
  row.addWidget(self._preview_btn, 0, Qt.AlignmentFlag.AlignLeft)
421
422
  # — Undo / Redo just for this subwindow —
422
423
  self._btn_undo = QToolButton(self)
423
424
  self._btn_undo.setText("↶") # or use an icon
424
- self._btn_undo.setToolTip("Undo (this view)")
425
+ self._btn_undo.setToolTip(self.tr("Undo (this view)"))
425
426
  self._btn_undo.setEnabled(False)
426
427
  self._btn_undo.clicked.connect(self._on_local_undo)
427
428
  row.addWidget(self._btn_undo, 0, Qt.AlignmentFlag.AlignLeft)
428
429
 
429
430
  self._btn_redo = QToolButton(self)
430
431
  self._btn_redo.setText("↷")
431
- self._btn_redo.setToolTip("Redo (this view)")
432
+ self._btn_redo.setToolTip(self.tr("Redo (this view)"))
432
433
  self._btn_redo.setEnabled(False)
433
434
  self._btn_redo.clicked.connect(self._on_local_redo)
434
435
  row.addWidget(self._btn_redo, 0, Qt.AlignmentFlag.AlignLeft)
435
436
 
436
437
  self._btn_replay_main = QToolButton(self)
437
438
  self._btn_replay_main.setText("⟳") # pick any glyph you like
438
- self._btn_replay_main.setToolTip(
439
+ self._btn_replay_main.setToolTip(self.tr(
439
440
  "Click: replay the last action on the base image.\n"
440
441
  "Arrow: pick a specific past action to replay on the base image."
441
- )
442
+ ))
442
443
  self._btn_replay_main.setEnabled(False) # enabled only when preview + history
443
444
 
444
445
  # Left-click = your existing 'replay last on base'
@@ -457,7 +458,7 @@ class ImageSubWindow(QWidget):
457
458
  # ── NEW: WCS grid toggle ─────────────────────────────────────────
458
459
  self._btn_wcs = QToolButton(self)
459
460
  self._btn_wcs.setText("⌗")
460
- self._btn_wcs.setToolTip("Toggle WCS grid overlay (if WCS exists)")
461
+ self._btn_wcs.setToolTip(self.tr("Toggle WCS grid overlay (if WCS exists)"))
461
462
  self._btn_wcs.setCheckable(True)
462
463
 
463
464
  # Start OFF on every new view, regardless of WCS presence or past sessions
@@ -484,6 +485,7 @@ class ImageSubWindow(QWidget):
484
485
 
485
486
  self.scroll = QScrollArea(full_host)
486
487
  self.scroll.setWidgetResizable(False)
488
+ self.scroll.setAlignment(Qt.AlignmentFlag.AlignCenter)
487
489
  self.label = QLabel(alignment=Qt.AlignmentFlag.AlignCenter)
488
490
  self.scroll.setWidget(self.label)
489
491
  self.scroll.viewport().setMouseTracking(True)
@@ -498,7 +500,7 @@ class ImageSubWindow(QWidget):
498
500
  bar.actionTriggered.connect(self._on_scroll_changed)
499
501
 
500
502
  # IMPORTANT: add the tab BEFORE connecting signals so currentChanged can't fire early
501
- self._full_tab_idx = self._tabs.addTab(full_host, "Full")
503
+ self._full_tab_idx = self._tabs.addTab(full_host, self.tr("Full"))
502
504
  self._full_host = full_host
503
505
  self._tabs.tabBar().setVisible(False) # hidden until a preview exists
504
506
  lyt.addWidget(self._tabs)
@@ -1029,7 +1031,7 @@ class ImageSubWindow(QWidget):
1029
1031
  self.label = QLabel(alignment=Qt.AlignmentFlag.AlignCenter)
1030
1032
  self.scroll.setWidget(self.label)
1031
1033
  v.addWidget(self.scroll)
1032
- self._full_tab_idx = self._tabs.addTab(full_host, "Full")
1034
+ self._full_tab_idx = self._tabs.addTab(full_host, self.tr("Full"))
1033
1035
  self._full_host = full_host
1034
1036
  self._tabs.tabBar().setVisible(False) # hidden until a first preview exists
1035
1037
 
@@ -1090,7 +1092,7 @@ class ImageSubWindow(QWidget):
1090
1092
  if self._preview_select_mode:
1091
1093
  mw = self._find_main_window()
1092
1094
  if mw and hasattr(mw, "statusBar"):
1093
- mw.statusBar().showMessage("Preview mode: drag a rectangle on the image to create a preview.", 6000)
1095
+ mw.statusBar().showMessage(self.tr("Preview mode: drag a rectangle on the image to create a preview."), 6000)
1094
1096
  else:
1095
1097
  self._cancel_rubber()
1096
1098
 
@@ -1165,7 +1167,7 @@ class ImageSubWindow(QWidget):
1165
1167
  if mw and hasattr(mw, "statusBar"):
1166
1168
  sb = mw.statusBar()
1167
1169
  if sb:
1168
- sb.showMessage("Press Space + Click/Drag to probe pixels (WCS shown if available)", 8000)
1170
+ sb.showMessage(self.tr("Press Space + Click/Drag to probe pixels (WCS shown if available)"), 8000)
1169
1171
 
1170
1172
 
1171
1173
 
@@ -1344,7 +1346,7 @@ class ImageSubWindow(QWidget):
1344
1346
  sub = self._mdi_subwindow()
1345
1347
  if not sub: return
1346
1348
  if base is None:
1347
- base = self._effective_title() or "Untitled"
1349
+ base = self._effective_title() or self.tr("Untitled")
1348
1350
 
1349
1351
  # ✅ strip any carried-over glyphs (🔗, ■, “Active View: ”) from overrides/doc names
1350
1352
  core, _ = self._strip_decorations(base)
@@ -1420,7 +1422,7 @@ class ImageSubWindow(QWidget):
1420
1422
 
1421
1423
  def base_doc_title(self) -> str:
1422
1424
  """The clean, base title (document display name), no prefixes/suffixes."""
1423
- return self.document.display_name() or "Untitled"
1425
+ return self.document.display_name() or self.tr("Untitled")
1424
1426
 
1425
1427
  def _active_mask_array(self):
1426
1428
  """Return the active mask ndarray (H,W) or None."""
@@ -1526,17 +1528,17 @@ class ImageSubWindow(QWidget):
1526
1528
 
1527
1529
  def _show_ctx_menu(self, pos):
1528
1530
  menu = QMenu(self)
1529
- a_view = menu.addAction("Rename View… (F2)")
1530
- a_doc = menu.addAction("Rename Document…")
1531
+ a_view = menu.addAction(self.tr("Rename View… (F2)"))
1532
+ a_doc = menu.addAction(self.tr("Rename Document…"))
1531
1533
  menu.addSeparator()
1532
- a_min = menu.addAction("Send to Shelf")
1533
- a_clear = menu.addAction("Clear View Name (use doc name)")
1534
+ a_min = menu.addAction(self.tr("Send to Shelf"))
1535
+ a_clear = menu.addAction(self.tr("Clear View Name (use doc name)"))
1534
1536
  menu.addSeparator()
1535
- a_unlink = menu.addAction("Unlink from Linked Views") # ← NEW
1537
+ a_unlink = menu.addAction(self.tr("Unlink from Linked Views")) # ← NEW
1536
1538
  menu.addSeparator()
1537
- a_help = menu.addAction("Show pixel/WCS readout hint")
1539
+ a_help = menu.addAction(self.tr("Show pixel/WCS readout hint"))
1538
1540
  menu.addSeparator()
1539
- a_prev = menu.addAction("Create Preview (drag rectangle)")
1541
+ a_prev = menu.addAction(self.tr("Create Preview (drag rectangle)"))
1540
1542
 
1541
1543
  act = menu.exec(self.mapToGlobal(pos))
1542
1544
 
@@ -1569,7 +1571,7 @@ class ImageSubWindow(QWidget):
1569
1571
 
1570
1572
  def _rename_view(self):
1571
1573
  current = self._view_title_override or self.document.display_name()
1572
- new, ok = QInputDialog.getText(self, "Rename View", "New view name:", text=current)
1574
+ new, ok = QInputDialog.getText(self, self.tr("Rename View"), self.tr("New view name:"), text=current)
1573
1575
  if ok and new.strip():
1574
1576
  self._view_title_override = new.strip()
1575
1577
  self._sync_host_title() # calls _rebuild_title → emits viewTitleChanged
@@ -1584,7 +1586,7 @@ class ImageSubWindow(QWidget):
1584
1586
 
1585
1587
  def _rename_document(self):
1586
1588
  current = self.document.display_name()
1587
- new, ok = QInputDialog.getText(self, "Rename Document", "New document name:", text=current)
1589
+ new, ok = QInputDialog.getText(self, self.tr("Rename Document"), self.tr("New document name:"), text=current)
1588
1590
  if ok and new.strip():
1589
1591
  # store on the doc so Explorer + other views update too
1590
1592
  self.document.metadata["display_name"] = new.strip()
@@ -1775,9 +1777,9 @@ class ImageSubWindow(QWidget):
1775
1777
  # ---- DnD 'view tab' -------------------------------------------------
1776
1778
  def _install_view_tab(self):
1777
1779
  self._view_tab = QToolButton(self)
1778
- self._view_tab.setText("View")
1779
- self._view_tab.setToolTip("Drag onto another window to copy zoom/pan.\n"
1780
- "Double-click to duplicate this view.")
1780
+ self._view_tab.setText(self.tr("View"))
1781
+ self._view_tab.setToolTip(self.tr("Drag onto another window to copy zoom/pan.\n"
1782
+ "Double-click to duplicate this view."))
1781
1783
  self._view_tab.setCursor(Qt.CursorShape.OpenHandCursor)
1782
1784
  self._view_tab.setAutoRaise(True)
1783
1785
  self._view_tab.move(8, 8) # pinned near top-left of the subwindow
@@ -2526,6 +2528,43 @@ class ImageSubWindow(QWidget):
2526
2528
  p = p.parent()
2527
2529
  return p
2528
2530
 
2531
+ def event(self, e):
2532
+ """Override event() to handle native macOS gestures (pinch zoom)."""
2533
+ # Handle native gestures (macOS trackpad pinch zoom)
2534
+ if e.type() == QEvent.Type.NativeGesture:
2535
+ gesture_type = e.gestureType()
2536
+
2537
+ if gesture_type == Qt.NativeGestureType.BeginNativeGesture:
2538
+ # Start of pinch gesture - store initial scale
2539
+ self._gesture_zoom_start = self.scale
2540
+ e.accept()
2541
+ return True
2542
+
2543
+ elif gesture_type == Qt.NativeGestureType.ZoomNativeGesture:
2544
+ # Ongoing pinch zoom - value() is cumulative scale factor
2545
+ # Typical values: -0.5 to +0.5 for moderate pinches
2546
+ zoom_delta = e.value()
2547
+
2548
+ # Convert delta to zoom factor
2549
+ # Use smaller multiplier for smoother feel (0.5x damping)
2550
+ factor = 1.0 + (zoom_delta * 0.5)
2551
+
2552
+ # Apply incremental zoom
2553
+ self._zoom_at_anchor(factor)
2554
+ e.accept()
2555
+ return True
2556
+
2557
+ elif gesture_type == Qt.NativeGestureType.EndNativeGesture:
2558
+ # End of pinch gesture - cleanup
2559
+ self._gesture_zoom_start = None
2560
+ e.accept()
2561
+ return True
2562
+
2563
+ # Let parent handle all other events
2564
+ return super().event(e)
2565
+
2566
+
2567
+
2529
2568
  def eventFilter(self, obj, ev):
2530
2569
  is_on_view = (obj is self.label) or (obj is self.scroll.viewport())
2531
2570
 
@@ -2557,7 +2596,29 @@ class ImageSubWindow(QWidget):
2557
2596
  # 1) Ctrl + wheel → zoom
2558
2597
  if ev.type() == QEvent.Type.Wheel:
2559
2598
  if ev.modifiers() & Qt.KeyboardModifier.ControlModifier:
2560
- factor = 1.25 if ev.angleDelta().y() > 0 else 1/1.25
2599
+ # Try pixelDelta first (macOS trackpad gives smooth values)
2600
+ dy = ev.pixelDelta().y()
2601
+
2602
+ if dy != 0:
2603
+ # Smooth trackpad scrolling: use smaller base factor
2604
+ # Scale proportionally to delta magnitude for natural feel
2605
+ # Typical trackpad deltas are 1-10 pixels per event
2606
+ abs_dy = abs(dy)
2607
+ if abs_dy <= 3:
2608
+ base_factor = 1.01 # Very gentle for tiny movements
2609
+ elif abs_dy <= 10:
2610
+ base_factor = 1.02 # Gentle for small movements
2611
+ else:
2612
+ base_factor = 1.03 # Moderate for larger gestures
2613
+
2614
+ factor = base_factor if dy > 0 else 1/base_factor
2615
+ else:
2616
+ # Traditional mouse wheel: use angleDelta with moderate factor
2617
+ dy = ev.angleDelta().y()
2618
+ if dy == 0:
2619
+ return True
2620
+ # Use 1.15 for mouse wheel (gentler than original 1.25)
2621
+ factor = 1.15 if dy > 0 else 1/1.15
2561
2622
  self._zoom_at_anchor(factor)
2562
2623
  return True
2563
2624
  return False
@@ -2642,7 +2703,7 @@ class ImageSubWindow(QWidget):
2642
2703
 
2643
2704
  pid = self._next_preview_id
2644
2705
  self._next_preview_id += 1
2645
- name = f"Preview {pid} ({w}×{h})"
2706
+ name = self.tr("Preview {0} ({1}×{2})").format(pid, w, h)
2646
2707
 
2647
2708
  self._previews.append({"id": pid, "name": name, "roi": (x, y, w, h), "arr": crop})
2648
2709
 
@@ -3062,8 +3123,8 @@ class ImageSubWindow(QWidget):
3062
3123
 
3063
3124
  if should_warn:
3064
3125
  r = QMessageBox.question(
3065
- self, "Close Image?",
3066
- "This image has edits that aren’t applied/saved.\nClose anyway?",
3126
+ self, self.tr("Close Image?"),
3127
+ self.tr("This image has edits that aren’t applied/saved.\nClose anyway?"),
3067
3128
  QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
3068
3129
  QMessageBox.StandardButton.No
3069
3130
  )
@@ -3180,7 +3241,7 @@ class TableSubWindow(QWidget):
3180
3241
  title_row.addWidget(self.title_lbl)
3181
3242
  title_row.addStretch(1)
3182
3243
 
3183
- self.export_btn = QPushButton("Export CSV…")
3244
+ self.export_btn = QPushButton(self.tr("Export CSV…"))
3184
3245
  self.export_btn.clicked.connect(self._export_csv)
3185
3246
  title_row.addWidget(self.export_btn)
3186
3247
  lyt.addLayout(title_row)
@@ -3238,17 +3299,17 @@ class TableSubWindow(QWidget):
3238
3299
  existing = self.document.metadata.get("table_csv")
3239
3300
  if existing and os.path.exists(existing):
3240
3301
  # Offer to open/save-as that CSV
3241
- dst, ok = QFileDialog.getSaveFileName(self, "Save CSV As…", os.path.basename(existing), "CSV Files (*.csv)")
3302
+ dst, ok = QFileDialog.getSaveFileName(self, self.tr("Save CSV As…"), os.path.basename(existing), self.tr("CSV Files (*.csv)"))
3242
3303
  if ok and dst:
3243
3304
  try:
3244
3305
  import shutil
3245
3306
  shutil.copyfile(existing, dst)
3246
3307
  except Exception as e:
3247
- QMessageBox.warning(self, "Export CSV", f"Failed to copy CSV:\n{e}")
3308
+ QMessageBox.warning(self, self.tr("Export CSV"), self.tr("Failed to copy CSV:\n{0}").format(e))
3248
3309
  return
3249
3310
 
3250
3311
  # No pre-export → write one from the model
3251
- dst, ok = QFileDialog.getSaveFileName(self, "Export CSV…", "table.csv", "CSV Files (*.csv)")
3312
+ dst, ok = QFileDialog.getSaveFileName(self, self.tr("Export CSV…"), "table.csv", self.tr("CSV Files (*.csv)"))
3252
3313
  if not ok or not dst:
3253
3314
  return
3254
3315
  try:
@@ -3264,4 +3325,4 @@ class TableSubWindow(QWidget):
3264
3325
  for r in range(rows):
3265
3326
  w.writerow([self._model.data(self._model.index(r, c), Qt.ItemDataRole.DisplayRole) for c in range(cols)])
3266
3327
  except Exception as e:
3267
- QMessageBox.warning(self, "Export CSV", f"Failed to export CSV:\n{e}")
3328
+ QMessageBox.warning(self, self.tr("Export CSV"), self.tr("Failed to export CSV:\n{0}").format(e))