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
@@ -42,8 +42,9 @@ class RGBCombinationDialogPro(QDialog):
42
42
  """
43
43
  def __init__(self, parent, list_open_docs_fn=None, doc_manager=None):
44
44
  super().__init__(parent)
45
- self.setWindowTitle("RGB Combination")
45
+ self.setWindowTitle(self.tr("RGB Combination"))
46
46
  self.setWindowFlag(Qt.WindowType.Window, True)
47
+ self.setWindowModality(Qt.WindowModality.NonModal)
47
48
  self.setModal(False)
48
49
  #self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
49
50
  self._list_open_docs = list_open_docs_fn or (lambda: [])
@@ -55,28 +56,28 @@ class RGBCombinationDialogPro(QDialog):
55
56
  self.b_path = None
56
57
 
57
58
  # ── mode choose
58
- self.load_files_radio = QRadioButton("Load Individual Files")
59
- self.use_views_radio = QRadioButton("Use Open Views")
59
+ self.load_files_radio = QRadioButton(self.tr("Load Individual Files"))
60
+ self.use_views_radio = QRadioButton(self.tr("Use Open Views"))
60
61
  self.use_views_radio.setChecked(True)
61
62
 
62
63
  self.mode_group = QButtonGroup(self)
63
64
  self.mode_group.addButton(self.load_files_radio)
64
65
  self.mode_group.addButton(self.use_views_radio)
65
66
 
66
- mode_box = QGroupBox("Select RGB Combination Mode")
67
+ mode_box = QGroupBox(self.tr("Select RGB Combination Mode"))
67
68
  ml = QVBoxLayout(mode_box)
68
69
  ml.addWidget(self.use_views_radio)
69
70
  ml.addWidget(self.load_files_radio)
70
71
 
71
72
  # ── file mode widgets
72
- self.r_label = QLabel("Red: Not selected")
73
- self.g_label = QLabel("Green: Not selected")
74
- self.b_label = QLabel("Blue: Not selected")
75
- self.btn_load_r = QPushButton("Load Red…"); self.btn_load_r.clicked.connect(lambda: self._pick_file("R"))
76
- self.btn_load_g = QPushButton("Load Green…"); self.btn_load_g.clicked.connect(lambda: self._pick_file("G"))
77
- self.btn_load_b = QPushButton("Load Blue…"); self.btn_load_b.clicked.connect(lambda: self._pick_file("B"))
78
-
79
- file_box = QGroupBox("Files")
73
+ self.r_label = QLabel(self.tr("Red: Not selected"))
74
+ self.g_label = QLabel(self.tr("Green: Not selected"))
75
+ self.b_label = QLabel(self.tr("Blue: Not selected"))
76
+ self.btn_load_r = QPushButton(self.tr("Load Red…")); self.btn_load_r.clicked.connect(lambda: self._pick_file("R"))
77
+ self.btn_load_g = QPushButton(self.tr("Load Green…")); self.btn_load_g.clicked.connect(lambda: self._pick_file("G"))
78
+ self.btn_load_b = QPushButton(self.tr("Load Blue…")); self.btn_load_b.clicked.connect(lambda: self._pick_file("B"))
79
+
80
+ file_box = QGroupBox(self.tr("Files"))
80
81
  fl = QVBoxLayout(file_box)
81
82
  for lab, btn in [(self.r_label, self.btn_load_r),
82
83
  (self.g_label, self.btn_load_g),
@@ -85,16 +86,16 @@ class RGBCombinationDialogPro(QDialog):
85
86
  fl.addLayout(row)
86
87
 
87
88
  # ── open-views widgets
88
- views_box = QGroupBox("Select Open Views for R / G / B")
89
+ views_box = QGroupBox(self.tr("Select Open Views for R / G / B"))
89
90
  vl = QHBoxLayout(views_box)
90
91
  self.cmb_r = QComboBox(); self.cmb_g = QComboBox(); self.cmb_b = QComboBox()
91
- vl.addLayout(self._labeled("Red:", self.cmb_r))
92
- vl.addLayout(self._labeled("Green:", self.cmb_g))
93
- vl.addLayout(self._labeled("Blue:", self.cmb_b))
92
+ vl.addLayout(self._labeled(self.tr("Red:"), self.cmb_r))
93
+ vl.addLayout(self._labeled(self.tr("Green:"), self.cmb_g))
94
+ vl.addLayout(self._labeled(self.tr("Blue:"), self.cmb_b))
94
95
 
95
96
  # ── buttons
96
- self.btn_combine = QPushButton("Combine")
97
- self.btn_cancel = QPushButton("Cancel")
97
+ self.btn_combine = QPushButton(self.tr("Combine"))
98
+ self.btn_cancel = QPushButton(self.tr("Cancel"))
98
99
  self.btn_cancel.clicked.connect(self.reject)
99
100
  self.btn_combine.clicked.connect(self._combine)
100
101
 
@@ -324,15 +324,12 @@ class RGBAlignWorker(QThread):
324
324
  def _warp_channel(self, ch: np.ndarray, kind: str, X, ref_shape):
325
325
  H, W = ref_shape[:2]
326
326
  if kind == "affine":
327
- if cv2 is None:
328
- return ch
327
+ # Just assume cv2 is available (standard dependency) for perf
329
328
  A = np.asarray(X, dtype=np.float32).reshape(2, 3)
330
329
  return cv2.warpAffine(ch, A, (W, H), flags=cv2.INTER_LANCZOS4,
331
330
  borderMode=cv2.BORDER_CONSTANT, borderValue=0)
332
331
 
333
332
  if kind == "homography":
334
- if cv2 is None:
335
- return ch
336
333
  Hm = np.asarray(X, dtype=np.float32).reshape(3, 3)
337
334
  return cv2.warpPerspective(ch, Hm, (W, H), flags=cv2.INTER_LANCZOS4,
338
335
  borderMode=cv2.BORDER_CONSTANT, borderValue=0)
@@ -349,7 +346,10 @@ class RGBAlignWorker(QThread):
349
346
  class RGBAlignDialog(QDialog):
350
347
  def __init__(self, parent=None, document=None):
351
348
  super().__init__(parent)
352
- self.setWindowTitle("RGB Align")
349
+ self.setWindowTitle(self.tr("RGB Align"))
350
+ self.setWindowFlag(Qt.WindowType.Window, True)
351
+ self.setWindowModality(Qt.WindowModality.NonModal)
352
+ self.setModal(False)
353
353
  self.parent = parent
354
354
  # document could be a view; try to unwrap
355
355
  self.doc_view = document
@@ -361,7 +361,7 @@ class RGBAlignDialog(QDialog):
361
361
  "Select model and run."))
362
362
 
363
363
  hl = QHBoxLayout()
364
- hl.addWidget(QLabel("Alignment model:"))
364
+ hl.addWidget(QLabel(self.tr("Alignment model:")))
365
365
  self.model_combo = QComboBox()
366
366
  self.model_combo.addItems([
367
367
  "EDGE", # ← first, new default
@@ -410,7 +410,7 @@ class RGBAlignDialog(QDialog):
410
410
 
411
411
  # ── SEP controls ─────────────────────────
412
412
  sep_row = QHBoxLayout()
413
- sep_row.addWidget(QLabel("SEP sigma:"))
413
+ sep_row.addWidget(QLabel(self.tr("SEP sigma:")))
414
414
 
415
415
  self.sep_spin = QSpinBox()
416
416
  self.sep_spin.setRange(1, 100)
@@ -419,7 +419,7 @@ class RGBAlignDialog(QDialog):
419
419
  "Higher = fewer stars, lower = more stars.")
420
420
  sep_row.addWidget(self.sep_spin)
421
421
 
422
- self.btn_trial_sep = QPushButton("Trial detect stars")
422
+ self.btn_trial_sep = QPushButton(self.tr("Trial detect stars"))
423
423
  self.btn_trial_sep.setToolTip("Run SEP on the green channel with this sigma and report how many "
424
424
  "stars it finds and how many are in the EDGE ring.")
425
425
  self.btn_trial_sep.clicked.connect(self._trial_sep_detect)
@@ -428,7 +428,7 @@ class RGBAlignDialog(QDialog):
428
428
  lay.addLayout(sep_row)
429
429
 
430
430
 
431
- self.chk_new_doc = QCheckBox("Create new document (keep original)")
431
+ self.chk_new_doc = QCheckBox(self.tr("Create new document (keep original)"))
432
432
  self.chk_new_doc.setChecked(True)
433
433
  lay.addWidget(self.chk_new_doc)
434
434
 
@@ -449,8 +449,8 @@ class RGBAlignDialog(QDialog):
449
449
  lay.addWidget(self.summary_box)
450
450
 
451
451
  btns = QHBoxLayout()
452
- self.btn_run = QPushButton("Align")
453
- self.btn_close = QPushButton("Close")
452
+ self.btn_run = QPushButton(self.tr("Align"))
453
+ self.btn_close = QPushButton(self.tr("Close"))
454
454
  btns.addWidget(self.btn_run)
455
455
  btns.addWidget(self.btn_close)
456
456
  lay.addLayout(btns)
@@ -18,8 +18,9 @@ _BIT_DEPTHS = {
18
18
  class SaveOptionsDialog(QDialog):
19
19
  def __init__(self, parent, target_ext: str, current_bit_depth: str | None):
20
20
  super().__init__(parent)
21
- self.setWindowTitle("Save Options")
21
+ self.setWindowTitle(self.tr("Save Options"))
22
22
  self.setWindowFlag(Qt.WindowType.Window, True)
23
+ self.setWindowModality(Qt.WindowModality.NonModal)
23
24
  self.setModal(False)
24
25
  #self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
25
26
 
@@ -48,11 +49,11 @@ class SaveOptionsDialog(QDialog):
48
49
  if current_bit_depth in allowed:
49
50
  self.combo.setCurrentText(current_bit_depth)
50
51
 
51
- lbl = QLabel(f"Choose bit depth for *.{self._ext}* export:")
52
+ lbl = QLabel(self.tr("Choose bit depth for export:"))
52
53
  lbl.setWordWrap(True)
53
54
 
54
- btn_ok = QPushButton("OK")
55
- btn_cancel = QPushButton("Cancel")
55
+ btn_ok = QPushButton(self.tr("OK"))
56
+ btn_cancel = QPushButton(self.tr("Cancel"))
56
57
  btn_ok.clicked.connect(self.accept)
57
58
  btn_cancel.clicked.connect(self.reject)
58
59
 
@@ -455,7 +455,7 @@ class SelectiveColorCorrection(QDialog):
455
455
  """
456
456
  def __init__(self, doc_manager=None, document=None, parent=None, window_icon: QIcon | None = None):
457
457
  super().__init__(parent)
458
- self.setWindowTitle("Selective Color Correction")
458
+ self.setWindowTitle(self.tr("Selective Color Correction"))
459
459
  if window_icon:
460
460
  self.setWindowIcon(window_icon)
461
461
 
@@ -467,7 +467,7 @@ class SelectiveColorCorrection(QDialog):
467
467
 
468
468
  self.img = np.clip(self.document.image.astype(np.float32), 0.0, 1.0)
469
469
  self.preview_img = self.img.copy()
470
-
470
+ self._syncing_hue = False
471
471
  self._imported_mask_full = None # full-res mask (H x W) float32 0..1
472
472
  self._imported_mask_name = None # nice label to show in UI
473
473
  self._use_imported_mask = False # checkbox state mirror
@@ -536,7 +536,7 @@ class SelectiveColorCorrection(QDialog):
536
536
  left.setSpacing(8)
537
537
 
538
538
  # ===== Mask group
539
- gb_mask = QGroupBox("Mask")
539
+ gb_mask = QGroupBox(self.tr("Mask"))
540
540
  gl = QGridLayout(gb_mask)
541
541
  gl.setContentsMargins(8, 8, 8, 8)
542
542
  gl.setHorizontalSpacing(10)
@@ -555,6 +555,36 @@ class SelectiveColorCorrection(QDialog):
555
555
  self.hue_wheel.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum)
556
556
  gl.addWidget(self.hue_wheel, 1, 0, 7, 2)
557
557
 
558
+ # Wheel -> sliders/spins (so dragging wheel updates UI and mask)
559
+ def _wheel_to_sliders(s: int, e: int):
560
+ # If user is dragging the wheel, we’re now custom
561
+ if not self._setting_preset and self.dd_preset.currentText() != "Custom":
562
+ self.dd_preset.blockSignals(True)
563
+ self.dd_preset.setCurrentText("Custom")
564
+ self.dd_preset.blockSignals(False)
565
+
566
+ # Update BOTH sliders and spins, without ping-pong
567
+ self._syncing_hue = True
568
+ try:
569
+ s = int(s) % 360
570
+ e = int(e) % 360
571
+
572
+ for w, val in (
573
+ (self.sl_h1, s), (self.sp_h1, s),
574
+ (self.sl_h2, e), (self.sp_h2, e),
575
+ ):
576
+ w.blockSignals(True)
577
+ w.setValue(val)
578
+ w.blockSignals(False)
579
+ finally:
580
+ self._syncing_hue = False
581
+
582
+ self._schedule_mask()
583
+
584
+ self.hue_wheel.rangeChanged.connect(_wheel_to_sliders)
585
+
586
+
587
+
558
588
  # Helper: integer slider + spin (0..360)
559
589
  def _deg_pair(grid: QGridLayout, label: str, row: int):
560
590
  grid.addWidget(QLabel(label), row, 2)
@@ -574,7 +604,8 @@ class SelectiveColorCorrection(QDialog):
574
604
 
575
605
  # Row 3: chroma + lightness
576
606
  gl.addWidget(QLabel("Min chroma:"), 3, 2)
577
- self.ds_minC = QDoubleSpinBox(); self.ds_minC.setRange(0,1); self.ds_minC.setSingleStep(0.05); self.ds_minC.setValue(0.0)
607
+ self.ds_minC = QDoubleSpinBox(); self.ds_minC.setRange(0,1); self.ds_minC.setSingleStep(0.05); self.ds_minC.setValue(0.05)
608
+
578
609
  self.ds_minC.valueChanged.connect(self._recompute_mask_and_preview)
579
610
  gl.addWidget(self.ds_minC, 3, 3)
580
611
 
@@ -652,7 +683,7 @@ class SelectiveColorCorrection(QDialog):
652
683
 
653
684
  # ===== Adjustments
654
685
  # CMY
655
- gb_cmy = QGroupBox("Complementary colors (CMY)")
686
+ gb_cmy = QGroupBox(self.tr("Complementary colors (CMY)"))
656
687
  glc = QGridLayout(gb_cmy)
657
688
  self.sl_c, self.ds_c = self._slider_pair(glc, "Cyan:", 0)
658
689
  self.sl_m, self.ds_m = self._slider_pair(glc, "Magenta:", 1)
@@ -660,7 +691,7 @@ class SelectiveColorCorrection(QDialog):
660
691
  left.addWidget(gb_cmy)
661
692
 
662
693
  # RGB
663
- gb_rgb = QGroupBox("RGB Colors")
694
+ gb_rgb = QGroupBox(self.tr("RGB Colors"))
664
695
  glr = QGridLayout(gb_rgb)
665
696
  self.sl_r, self.ds_r = self._slider_pair(glr, "Red:", 0)
666
697
  self.sl_g, self.ds_g = self._slider_pair(glr, "Green:", 1)
@@ -668,7 +699,7 @@ class SelectiveColorCorrection(QDialog):
668
699
  left.addWidget(gb_rgb)
669
700
 
670
701
  # LSC
671
- gb_lsc = QGroupBox("Luminance, Chroma/Saturation, Contrast")
702
+ gb_lsc = QGroupBox(self.tr("Luminance, Chroma/Saturation, Contrast"))
672
703
  gll = QGridLayout(gb_lsc)
673
704
  self.sl_l, self.ds_l = self._slider_pair(gll, "Luminance:", 0)
674
705
  self.sl_chroma, self.ds_chroma = self._slider_pair(gll, "Chroma (L-preserving):", 1)
@@ -741,10 +772,12 @@ class SelectiveColorCorrection(QDialog):
741
772
  right.addLayout(zoom_row)
742
773
 
743
774
  self.lbl_help = QLabel(
744
- "🖱️ <b>Click</b>: pick hue &nbsp;•&nbsp; "
775
+ "🖱️ <b>Click</b>: show hue &nbsp;•&nbsp; "
776
+ "<b>Shift + Click</b>: select that color &nbsp;•&nbsp; "
745
777
  "<b>Ctrl + Click & Drag</b>: pan &nbsp;•&nbsp; "
746
778
  "<b>Ctrl + Wheel</b>: zoom"
747
779
  )
780
+
748
781
  self.lbl_help.setWordWrap(True)
749
782
  self.lbl_help.setTextFormat(Qt.TextFormat.RichText)
750
783
  self.lbl_help.setStyleSheet("color: #888; font-size: 11px;")
@@ -797,12 +830,20 @@ class SelectiveColorCorrection(QDialog):
797
830
  w.valueChanged.connect(self._schedule_adjustments)
798
831
 
799
832
  def _sliders_to_wheel(_=None):
833
+ if getattr(self, "_syncing_hue", False):
834
+ return
835
+
800
836
  if not self._setting_preset and self.dd_preset.currentText() != "Custom":
837
+ self.dd_preset.blockSignals(True)
801
838
  self.dd_preset.setCurrentText("Custom")
802
- s = int(self.sp_h1.value()); e = int(self.sp_h2.value())
839
+ self.dd_preset.blockSignals(False)
840
+
841
+ s = int(self.sp_h1.value())
842
+ e = int(self.sp_h2.value())
803
843
  self.hue_wheel.setRange(s, e, notify=False)
804
844
  self._schedule_mask()
805
845
 
846
+
806
847
  self.sp_h1.valueChanged.connect(_sliders_to_wheel)
807
848
  self.sp_h2.valueChanged.connect(_sliders_to_wheel)
808
849
  self.sl_h1.valueChanged.connect(_sliders_to_wheel)
@@ -973,7 +1014,7 @@ class SelectiveColorCorrection(QDialog):
973
1014
  w.setValue(int(val))
974
1015
  w.blockSignals(False)
975
1016
 
976
- setv(self.ds_minC, 0.0)
1017
+ setv(self.ds_minC, 0.05)
977
1018
  setv(self.ds_minL, 0.0)
978
1019
  setv(self.ds_maxL, 1.0)
979
1020
  setv(self.ds_smooth, 10.0)
@@ -1018,23 +1059,27 @@ class SelectiveColorCorrection(QDialog):
1018
1059
  self._recompute_mask_and_preview()
1019
1060
 
1020
1061
 
1021
- def _schedule_adjustments(self, delay_ms: int | None = None):
1062
+ def _schedule_adjustments(self, *_, delay_ms: int | None = None):
1022
1063
  if delay_ms is None:
1023
1064
  delay_ms = getattr(self, "_adj_delay_ms", 200)
1024
- # if called very early, just no-op safely
1065
+
1025
1066
  if not hasattr(self, "_adj_timer"):
1026
1067
  return
1027
- self._adj_timer.stop()
1028
- self._adj_timer.start(int(delay_ms))
1029
1068
 
1069
+ ms = max(1, int(delay_ms)) # never allow 0/negative
1070
+ self._adj_timer.stop()
1071
+ self._adj_timer.start(ms)
1030
1072
 
1031
- def _schedule_mask(self, delay_ms: int | None = None):
1032
- """Debounce mask recomputation for hue changes."""
1073
+ def _schedule_mask(self, *_, delay_ms: int | None = None):
1033
1074
  if delay_ms is None:
1034
- delay_ms = self._mask_delay_ms
1035
- # restart the timer on every change
1075
+ delay_ms = getattr(self, "_mask_delay_ms", 200)
1076
+
1077
+ if not hasattr(self, "_mask_timer"):
1078
+ return
1079
+
1080
+ ms = max(1, int(delay_ms))
1036
1081
  self._mask_timer.stop()
1037
- self._mask_timer.start(int(delay_ms))
1082
+ self._mask_timer.start(ms)
1038
1083
 
1039
1084
 
1040
1085
  def _sample_hue_deg_from_base(self, x: int, y: int) -> float | None:
@@ -1190,16 +1235,30 @@ class SelectiveColorCorrection(QDialog):
1190
1235
  intervals = _PRESETS.get(txt, [])
1191
1236
  if intervals:
1192
1237
  lo, hi = (intervals[0][0], intervals[-1][1]) if len(intervals) > 1 else intervals[0]
1193
- self.hue_wheel.setRange(int(lo), int(hi), notify=False) # update wheel silently
1194
- self.hue_wheel.update() # ensure repaint
1195
- self.sp_h1.blockSignals(True); self.sp_h2.blockSignals(True)
1196
- self.sp_h1.setValue(int(lo)); self.sp_h2.setValue(int(hi))
1197
- self.sp_h1.blockSignals(False); self.sp_h2.blockSignals(False)
1238
+
1239
+ # --- NEW: keep wheel + sliders + spins all in sync ---
1240
+ self._syncing_hue = True
1241
+ try:
1242
+ # update wheel silently
1243
+ self.hue_wheel.setRange(int(lo), int(hi), notify=False)
1244
+
1245
+ # update both sliders and spins (no ping-pong)
1246
+ for w, val in (
1247
+ (self.sl_h1, int(lo)), (self.sp_h1, int(lo)),
1248
+ (self.sl_h2, int(hi)), (self.sp_h2, int(hi)),
1249
+ ):
1250
+ w.blockSignals(True)
1251
+ w.setValue(val)
1252
+ w.blockSignals(False)
1253
+
1254
+ self.hue_wheel.update() # ensure repaint
1255
+ finally:
1256
+ self._syncing_hue = False
1257
+
1198
1258
  self._recompute_mask_and_preview()
1199
1259
  finally:
1200
1260
  self._setting_preset = False
1201
1261
 
1202
-
1203
1262
  def _downsample(self, img, max_dim=1024):
1204
1263
  h, w = img.shape[:2]
1205
1264
  s = max(h, w)