setiastrosuitepro 1.6.5.post3__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.
Files changed (368) hide show
  1. setiastro/__init__.py +2 -0
  2. setiastro/data/SASP_data.fits +0 -0
  3. setiastro/data/catalogs/List_of_Galaxies_with_Distances_Gly.csv +488 -0
  4. setiastro/data/catalogs/astrobin_filters.csv +890 -0
  5. setiastro/data/catalogs/astrobin_filters_page1_local.csv +51 -0
  6. setiastro/data/catalogs/cali2.csv +63 -0
  7. setiastro/data/catalogs/cali2color.csv +65 -0
  8. setiastro/data/catalogs/celestial_catalog - original.csv +16471 -0
  9. setiastro/data/catalogs/celestial_catalog.csv +24031 -0
  10. setiastro/data/catalogs/detected_stars.csv +24784 -0
  11. setiastro/data/catalogs/fits_header_data.csv +46 -0
  12. setiastro/data/catalogs/test.csv +8 -0
  13. setiastro/data/catalogs/updated_celestial_catalog.csv +16471 -0
  14. setiastro/images/Astro_Spikes.png +0 -0
  15. setiastro/images/Background_startup.jpg +0 -0
  16. setiastro/images/HRDiagram.png +0 -0
  17. setiastro/images/LExtract.png +0 -0
  18. setiastro/images/LInsert.png +0 -0
  19. setiastro/images/Oxygenation-atm-2.svg.png +0 -0
  20. setiastro/images/RGB080604.png +0 -0
  21. setiastro/images/abeicon.png +0 -0
  22. setiastro/images/aberration.png +0 -0
  23. setiastro/images/andromedatry.png +0 -0
  24. setiastro/images/andromedatry_satellited.png +0 -0
  25. setiastro/images/annotated.png +0 -0
  26. setiastro/images/aperture.png +0 -0
  27. setiastro/images/astrosuite.ico +0 -0
  28. setiastro/images/astrosuite.png +0 -0
  29. setiastro/images/astrosuitepro.icns +0 -0
  30. setiastro/images/astrosuitepro.ico +0 -0
  31. setiastro/images/astrosuitepro.png +0 -0
  32. setiastro/images/background.png +0 -0
  33. setiastro/images/background2.png +0 -0
  34. setiastro/images/benchmark.png +0 -0
  35. setiastro/images/big_moon_stabilizer_timeline.png +0 -0
  36. setiastro/images/big_moon_stabilizer_timeline_clean.png +0 -0
  37. setiastro/images/blaster.png +0 -0
  38. setiastro/images/blink.png +0 -0
  39. setiastro/images/clahe.png +0 -0
  40. setiastro/images/collage.png +0 -0
  41. setiastro/images/colorwheel.png +0 -0
  42. setiastro/images/contsub.png +0 -0
  43. setiastro/images/convo.png +0 -0
  44. setiastro/images/copyslot.png +0 -0
  45. setiastro/images/cosmic.png +0 -0
  46. setiastro/images/cosmicsat.png +0 -0
  47. setiastro/images/crop1.png +0 -0
  48. setiastro/images/cropicon.png +0 -0
  49. setiastro/images/curves.png +0 -0
  50. setiastro/images/cvs.png +0 -0
  51. setiastro/images/debayer.png +0 -0
  52. setiastro/images/denoise_cnn_custom.png +0 -0
  53. setiastro/images/denoise_cnn_graph.png +0 -0
  54. setiastro/images/disk.png +0 -0
  55. setiastro/images/dse.png +0 -0
  56. setiastro/images/exoicon.png +0 -0
  57. setiastro/images/eye.png +0 -0
  58. setiastro/images/fliphorizontal.png +0 -0
  59. setiastro/images/flipvertical.png +0 -0
  60. setiastro/images/font.png +0 -0
  61. setiastro/images/freqsep.png +0 -0
  62. setiastro/images/functionbundle.png +0 -0
  63. setiastro/images/graxpert.png +0 -0
  64. setiastro/images/green.png +0 -0
  65. setiastro/images/gridicon.png +0 -0
  66. setiastro/images/halo.png +0 -0
  67. setiastro/images/hdr.png +0 -0
  68. setiastro/images/histogram.png +0 -0
  69. setiastro/images/hubble.png +0 -0
  70. setiastro/images/imagecombine.png +0 -0
  71. setiastro/images/invert.png +0 -0
  72. setiastro/images/isophote.png +0 -0
  73. setiastro/images/isophote_demo_figure.png +0 -0
  74. setiastro/images/isophote_demo_image.png +0 -0
  75. setiastro/images/isophote_demo_model.png +0 -0
  76. setiastro/images/isophote_demo_residual.png +0 -0
  77. setiastro/images/jwstpupil.png +0 -0
  78. setiastro/images/linearfit.png +0 -0
  79. setiastro/images/livestacking.png +0 -0
  80. setiastro/images/mask.png +0 -0
  81. setiastro/images/maskapply.png +0 -0
  82. setiastro/images/maskcreate.png +0 -0
  83. setiastro/images/maskremove.png +0 -0
  84. setiastro/images/morpho.png +0 -0
  85. setiastro/images/mosaic.png +0 -0
  86. setiastro/images/multiscale_decomp.png +0 -0
  87. setiastro/images/nbtorgb.png +0 -0
  88. setiastro/images/neutral.png +0 -0
  89. setiastro/images/nuke.png +0 -0
  90. setiastro/images/openfile.png +0 -0
  91. setiastro/images/pedestal.png +0 -0
  92. setiastro/images/pen.png +0 -0
  93. setiastro/images/pixelmath.png +0 -0
  94. setiastro/images/platesolve.png +0 -0
  95. setiastro/images/ppp.png +0 -0
  96. setiastro/images/pro.png +0 -0
  97. setiastro/images/project.png +0 -0
  98. setiastro/images/psf.png +0 -0
  99. setiastro/images/redo.png +0 -0
  100. setiastro/images/redoicon.png +0 -0
  101. setiastro/images/rescale.png +0 -0
  102. setiastro/images/rgbalign.png +0 -0
  103. setiastro/images/rgbcombo.png +0 -0
  104. setiastro/images/rgbextract.png +0 -0
  105. setiastro/images/rotate180.png +0 -0
  106. setiastro/images/rotatearbitrary.png +0 -0
  107. setiastro/images/rotateclockwise.png +0 -0
  108. setiastro/images/rotatecounterclockwise.png +0 -0
  109. setiastro/images/satellite.png +0 -0
  110. setiastro/images/script.png +0 -0
  111. setiastro/images/selectivecolor.png +0 -0
  112. setiastro/images/simbad.png +0 -0
  113. setiastro/images/slot0.png +0 -0
  114. setiastro/images/slot1.png +0 -0
  115. setiastro/images/slot2.png +0 -0
  116. setiastro/images/slot3.png +0 -0
  117. setiastro/images/slot4.png +0 -0
  118. setiastro/images/slot5.png +0 -0
  119. setiastro/images/slot6.png +0 -0
  120. setiastro/images/slot7.png +0 -0
  121. setiastro/images/slot8.png +0 -0
  122. setiastro/images/slot9.png +0 -0
  123. setiastro/images/spcc.png +0 -0
  124. setiastro/images/spin_precession_vs_lunar_distance.png +0 -0
  125. setiastro/images/spinner.gif +0 -0
  126. setiastro/images/stacking.png +0 -0
  127. setiastro/images/staradd.png +0 -0
  128. setiastro/images/staralign.png +0 -0
  129. setiastro/images/starnet.png +0 -0
  130. setiastro/images/starregistration.png +0 -0
  131. setiastro/images/starspike.png +0 -0
  132. setiastro/images/starstretch.png +0 -0
  133. setiastro/images/statstretch.png +0 -0
  134. setiastro/images/supernova.png +0 -0
  135. setiastro/images/uhs.png +0 -0
  136. setiastro/images/undoicon.png +0 -0
  137. setiastro/images/upscale.png +0 -0
  138. setiastro/images/viewbundle.png +0 -0
  139. setiastro/images/whitebalance.png +0 -0
  140. setiastro/images/wimi_icon_256x256.png +0 -0
  141. setiastro/images/wimilogo.png +0 -0
  142. setiastro/images/wims.png +0 -0
  143. setiastro/images/wrench_icon.png +0 -0
  144. setiastro/images/xisfliberator.png +0 -0
  145. setiastro/qml/ResourceMonitor.qml +126 -0
  146. setiastro/saspro/__init__.py +20 -0
  147. setiastro/saspro/__main__.py +958 -0
  148. setiastro/saspro/_generated/__init__.py +7 -0
  149. setiastro/saspro/_generated/build_info.py +3 -0
  150. setiastro/saspro/abe.py +1346 -0
  151. setiastro/saspro/abe_preset.py +196 -0
  152. setiastro/saspro/aberration_ai.py +698 -0
  153. setiastro/saspro/aberration_ai_preset.py +224 -0
  154. setiastro/saspro/accel_installer.py +218 -0
  155. setiastro/saspro/accel_workers.py +30 -0
  156. setiastro/saspro/add_stars.py +624 -0
  157. setiastro/saspro/astrobin_exporter.py +1010 -0
  158. setiastro/saspro/astrospike.py +153 -0
  159. setiastro/saspro/astrospike_python.py +1841 -0
  160. setiastro/saspro/autostretch.py +198 -0
  161. setiastro/saspro/backgroundneutral.py +611 -0
  162. setiastro/saspro/batch_convert.py +328 -0
  163. setiastro/saspro/batch_renamer.py +522 -0
  164. setiastro/saspro/blemish_blaster.py +491 -0
  165. setiastro/saspro/blink_comparator_pro.py +3149 -0
  166. setiastro/saspro/bundles.py +61 -0
  167. setiastro/saspro/bundles_dock.py +114 -0
  168. setiastro/saspro/cheat_sheet.py +213 -0
  169. setiastro/saspro/clahe.py +368 -0
  170. setiastro/saspro/comet_stacking.py +1442 -0
  171. setiastro/saspro/common_tr.py +107 -0
  172. setiastro/saspro/config.py +38 -0
  173. setiastro/saspro/config_bootstrap.py +40 -0
  174. setiastro/saspro/config_manager.py +316 -0
  175. setiastro/saspro/continuum_subtract.py +1617 -0
  176. setiastro/saspro/convo.py +1400 -0
  177. setiastro/saspro/convo_preset.py +414 -0
  178. setiastro/saspro/copyastro.py +190 -0
  179. setiastro/saspro/cosmicclarity.py +1589 -0
  180. setiastro/saspro/cosmicclarity_preset.py +407 -0
  181. setiastro/saspro/crop_dialog_pro.py +983 -0
  182. setiastro/saspro/crop_preset.py +189 -0
  183. setiastro/saspro/curve_editor_pro.py +2562 -0
  184. setiastro/saspro/curves_preset.py +375 -0
  185. setiastro/saspro/debayer.py +673 -0
  186. setiastro/saspro/debug_utils.py +29 -0
  187. setiastro/saspro/dnd_mime.py +35 -0
  188. setiastro/saspro/doc_manager.py +2664 -0
  189. setiastro/saspro/exoplanet_detector.py +2166 -0
  190. setiastro/saspro/file_utils.py +284 -0
  191. setiastro/saspro/fitsmodifier.py +748 -0
  192. setiastro/saspro/fix_bom.py +32 -0
  193. setiastro/saspro/free_torch_memory.py +48 -0
  194. setiastro/saspro/frequency_separation.py +1349 -0
  195. setiastro/saspro/function_bundle.py +1596 -0
  196. setiastro/saspro/generate_translations.py +3092 -0
  197. setiastro/saspro/ghs_dialog_pro.py +663 -0
  198. setiastro/saspro/ghs_preset.py +284 -0
  199. setiastro/saspro/graxpert.py +637 -0
  200. setiastro/saspro/graxpert_preset.py +287 -0
  201. setiastro/saspro/gui/__init__.py +0 -0
  202. setiastro/saspro/gui/main_window.py +8792 -0
  203. setiastro/saspro/gui/mixins/__init__.py +33 -0
  204. setiastro/saspro/gui/mixins/dock_mixin.py +375 -0
  205. setiastro/saspro/gui/mixins/file_mixin.py +450 -0
  206. setiastro/saspro/gui/mixins/geometry_mixin.py +503 -0
  207. setiastro/saspro/gui/mixins/header_mixin.py +441 -0
  208. setiastro/saspro/gui/mixins/mask_mixin.py +421 -0
  209. setiastro/saspro/gui/mixins/menu_mixin.py +390 -0
  210. setiastro/saspro/gui/mixins/theme_mixin.py +367 -0
  211. setiastro/saspro/gui/mixins/toolbar_mixin.py +1619 -0
  212. setiastro/saspro/gui/mixins/update_mixin.py +323 -0
  213. setiastro/saspro/gui/mixins/view_mixin.py +435 -0
  214. setiastro/saspro/gui/statistics_dialog.py +47 -0
  215. setiastro/saspro/halobgon.py +488 -0
  216. setiastro/saspro/header_viewer.py +448 -0
  217. setiastro/saspro/headless_utils.py +88 -0
  218. setiastro/saspro/histogram.py +756 -0
  219. setiastro/saspro/history_explorer.py +941 -0
  220. setiastro/saspro/i18n.py +168 -0
  221. setiastro/saspro/image_combine.py +417 -0
  222. setiastro/saspro/image_peeker_pro.py +1604 -0
  223. setiastro/saspro/imageops/__init__.py +37 -0
  224. setiastro/saspro/imageops/mdi_snap.py +292 -0
  225. setiastro/saspro/imageops/scnr.py +36 -0
  226. setiastro/saspro/imageops/starbasedwhitebalance.py +210 -0
  227. setiastro/saspro/imageops/stretch.py +236 -0
  228. setiastro/saspro/isophote.py +1182 -0
  229. setiastro/saspro/layers.py +208 -0
  230. setiastro/saspro/layers_dock.py +714 -0
  231. setiastro/saspro/lazy_imports.py +193 -0
  232. setiastro/saspro/legacy/__init__.py +2 -0
  233. setiastro/saspro/legacy/image_manager.py +2360 -0
  234. setiastro/saspro/legacy/numba_utils.py +3676 -0
  235. setiastro/saspro/legacy/xisf.py +1213 -0
  236. setiastro/saspro/linear_fit.py +537 -0
  237. setiastro/saspro/live_stacking.py +1854 -0
  238. setiastro/saspro/log_bus.py +5 -0
  239. setiastro/saspro/logging_config.py +460 -0
  240. setiastro/saspro/luminancerecombine.py +510 -0
  241. setiastro/saspro/main_helpers.py +201 -0
  242. setiastro/saspro/mask_creation.py +1086 -0
  243. setiastro/saspro/masks_core.py +56 -0
  244. setiastro/saspro/mdi_widgets.py +353 -0
  245. setiastro/saspro/memory_utils.py +666 -0
  246. setiastro/saspro/metadata_patcher.py +75 -0
  247. setiastro/saspro/mfdeconv.py +3909 -0
  248. setiastro/saspro/mfdeconv_earlystop.py +71 -0
  249. setiastro/saspro/mfdeconvcudnn.py +3312 -0
  250. setiastro/saspro/mfdeconvsport.py +2459 -0
  251. setiastro/saspro/minorbodycatalog.py +567 -0
  252. setiastro/saspro/morphology.py +407 -0
  253. setiastro/saspro/multiscale_decomp.py +1747 -0
  254. setiastro/saspro/nbtorgb_stars.py +541 -0
  255. setiastro/saspro/numba_utils.py +3145 -0
  256. setiastro/saspro/numba_warmup.py +141 -0
  257. setiastro/saspro/ops/__init__.py +9 -0
  258. setiastro/saspro/ops/command_help_dialog.py +623 -0
  259. setiastro/saspro/ops/command_runner.py +217 -0
  260. setiastro/saspro/ops/commands.py +1594 -0
  261. setiastro/saspro/ops/script_editor.py +1105 -0
  262. setiastro/saspro/ops/scripts.py +1476 -0
  263. setiastro/saspro/ops/settings.py +637 -0
  264. setiastro/saspro/parallel_utils.py +554 -0
  265. setiastro/saspro/pedestal.py +121 -0
  266. setiastro/saspro/perfect_palette_picker.py +1105 -0
  267. setiastro/saspro/pipeline.py +110 -0
  268. setiastro/saspro/pixelmath.py +1604 -0
  269. setiastro/saspro/plate_solver.py +2445 -0
  270. setiastro/saspro/project_io.py +797 -0
  271. setiastro/saspro/psf_utils.py +136 -0
  272. setiastro/saspro/psf_viewer.py +549 -0
  273. setiastro/saspro/pyi_rthook_astroquery.py +95 -0
  274. setiastro/saspro/remove_green.py +331 -0
  275. setiastro/saspro/remove_stars.py +1599 -0
  276. setiastro/saspro/remove_stars_preset.py +446 -0
  277. setiastro/saspro/resources.py +503 -0
  278. setiastro/saspro/rgb_combination.py +208 -0
  279. setiastro/saspro/rgb_extract.py +19 -0
  280. setiastro/saspro/rgbalign.py +723 -0
  281. setiastro/saspro/runtime_imports.py +7 -0
  282. setiastro/saspro/runtime_torch.py +754 -0
  283. setiastro/saspro/save_options.py +73 -0
  284. setiastro/saspro/selective_color.py +1611 -0
  285. setiastro/saspro/sfcc.py +1472 -0
  286. setiastro/saspro/shortcuts.py +3116 -0
  287. setiastro/saspro/signature_insert.py +1102 -0
  288. setiastro/saspro/stacking_suite.py +19066 -0
  289. setiastro/saspro/star_alignment.py +7380 -0
  290. setiastro/saspro/star_alignment_preset.py +329 -0
  291. setiastro/saspro/star_metrics.py +49 -0
  292. setiastro/saspro/star_spikes.py +765 -0
  293. setiastro/saspro/star_stretch.py +507 -0
  294. setiastro/saspro/stat_stretch.py +538 -0
  295. setiastro/saspro/status_log_dock.py +78 -0
  296. setiastro/saspro/subwindow.py +3407 -0
  297. setiastro/saspro/supernovaasteroidhunter.py +1719 -0
  298. setiastro/saspro/swap_manager.py +134 -0
  299. setiastro/saspro/torch_backend.py +89 -0
  300. setiastro/saspro/torch_rejection.py +434 -0
  301. setiastro/saspro/translations/all_source_strings.json +4726 -0
  302. setiastro/saspro/translations/ar_translations.py +4096 -0
  303. setiastro/saspro/translations/de_translations.py +3728 -0
  304. setiastro/saspro/translations/es_translations.py +4169 -0
  305. setiastro/saspro/translations/fr_translations.py +4090 -0
  306. setiastro/saspro/translations/hi_translations.py +3803 -0
  307. setiastro/saspro/translations/integrate_translations.py +271 -0
  308. setiastro/saspro/translations/it_translations.py +4728 -0
  309. setiastro/saspro/translations/ja_translations.py +3834 -0
  310. setiastro/saspro/translations/pt_translations.py +3847 -0
  311. setiastro/saspro/translations/ru_translations.py +3082 -0
  312. setiastro/saspro/translations/saspro_ar.qm +0 -0
  313. setiastro/saspro/translations/saspro_ar.ts +16019 -0
  314. setiastro/saspro/translations/saspro_de.qm +0 -0
  315. setiastro/saspro/translations/saspro_de.ts +14548 -0
  316. setiastro/saspro/translations/saspro_es.qm +0 -0
  317. setiastro/saspro/translations/saspro_es.ts +16202 -0
  318. setiastro/saspro/translations/saspro_fr.qm +0 -0
  319. setiastro/saspro/translations/saspro_fr.ts +15870 -0
  320. setiastro/saspro/translations/saspro_hi.qm +0 -0
  321. setiastro/saspro/translations/saspro_hi.ts +14855 -0
  322. setiastro/saspro/translations/saspro_it.qm +0 -0
  323. setiastro/saspro/translations/saspro_it.ts +19046 -0
  324. setiastro/saspro/translations/saspro_ja.qm +0 -0
  325. setiastro/saspro/translations/saspro_ja.ts +14980 -0
  326. setiastro/saspro/translations/saspro_pt.qm +0 -0
  327. setiastro/saspro/translations/saspro_pt.ts +15024 -0
  328. setiastro/saspro/translations/saspro_ru.qm +0 -0
  329. setiastro/saspro/translations/saspro_ru.ts +11835 -0
  330. setiastro/saspro/translations/saspro_sw.qm +0 -0
  331. setiastro/saspro/translations/saspro_sw.ts +15237 -0
  332. setiastro/saspro/translations/saspro_uk.qm +0 -0
  333. setiastro/saspro/translations/saspro_uk.ts +15248 -0
  334. setiastro/saspro/translations/saspro_zh.qm +0 -0
  335. setiastro/saspro/translations/saspro_zh.ts +15289 -0
  336. setiastro/saspro/translations/sw_translations.py +3897 -0
  337. setiastro/saspro/translations/uk_translations.py +3929 -0
  338. setiastro/saspro/translations/zh_translations.py +3910 -0
  339. setiastro/saspro/versioning.py +77 -0
  340. setiastro/saspro/view_bundle.py +1558 -0
  341. setiastro/saspro/wavescale_hdr.py +645 -0
  342. setiastro/saspro/wavescale_hdr_preset.py +101 -0
  343. setiastro/saspro/wavescalede.py +680 -0
  344. setiastro/saspro/wavescalede_preset.py +230 -0
  345. setiastro/saspro/wcs_update.py +374 -0
  346. setiastro/saspro/whitebalance.py +513 -0
  347. setiastro/saspro/widgets/__init__.py +48 -0
  348. setiastro/saspro/widgets/common_utilities.py +306 -0
  349. setiastro/saspro/widgets/graphics_views.py +122 -0
  350. setiastro/saspro/widgets/image_utils.py +518 -0
  351. setiastro/saspro/widgets/minigame/game.js +991 -0
  352. setiastro/saspro/widgets/minigame/index.html +53 -0
  353. setiastro/saspro/widgets/minigame/style.css +241 -0
  354. setiastro/saspro/widgets/preview_dialogs.py +280 -0
  355. setiastro/saspro/widgets/resource_monitor.py +263 -0
  356. setiastro/saspro/widgets/spinboxes.py +290 -0
  357. setiastro/saspro/widgets/themed_buttons.py +13 -0
  358. setiastro/saspro/widgets/wavelet_utils.py +331 -0
  359. setiastro/saspro/wimi.py +7996 -0
  360. setiastro/saspro/wims.py +578 -0
  361. setiastro/saspro/window_shelf.py +185 -0
  362. setiastro/saspro/xisf.py +1213 -0
  363. setiastrosuitepro-1.6.5.post3.dist-info/METADATA +278 -0
  364. setiastrosuitepro-1.6.5.post3.dist-info/RECORD +368 -0
  365. setiastrosuitepro-1.6.5.post3.dist-info/WHEEL +4 -0
  366. setiastrosuitepro-1.6.5.post3.dist-info/entry_points.txt +6 -0
  367. setiastrosuitepro-1.6.5.post3.dist-info/licenses/LICENSE +674 -0
  368. setiastrosuitepro-1.6.5.post3.dist-info/licenses/license.txt +2580 -0
@@ -0,0 +1,623 @@
1
+ # ops/command_help_dialog.py
2
+ from __future__ import annotations
3
+ from dataclasses import asdict
4
+ from typing import Dict, List, Tuple
5
+
6
+ from PyQt6.QtCore import Qt
7
+ from PyQt6.QtGui import QFont, QTextCursor
8
+ from PyQt6.QtWidgets import (
9
+ QDialog, QVBoxLayout, QHBoxLayout, QListWidget, QListWidgetItem,
10
+ QLineEdit, QTextBrowser, QPushButton, QLabel, QSplitter, QApplication
11
+ )
12
+
13
+ from setiastro.saspro.ops.commands import COMMAND_REGISTRY, CommandSpec, PresetSpec
14
+
15
+ # ---------------------------------------------------------------------
16
+ # Available script libraries (bundled/installed)
17
+ # ---------------------------------------------------------------------
18
+ from dataclasses import dataclass
19
+ from typing import Optional
20
+
21
+ @dataclass(frozen=True)
22
+ class LibSpec:
23
+ import_name: str # what to `import ...`
24
+ pip_name: Optional[str] = None # if different from import
25
+ note: str = "" # short human note
26
+ platforms: str = "all" # "all" / "non-darwin" / "darwin" / etc.
27
+
28
+ AVAILABLE_LIBRARIES: Dict[str, List[LibSpec]] = {
29
+ "numeric & scientific": [
30
+ LibSpec("numpy"),
31
+ LibSpec("scipy"),
32
+ LibSpec("pywt", pip_name="pywavelets", note="wavelets"),
33
+ LibSpec("matplotlib"),
34
+ LibSpec("plotly"),
35
+ LibSpec("exifread"),
36
+ LibSpec("lightkurve"),
37
+ LibSpec("oktopus"),
38
+ ],
39
+ "tables & I/O": [
40
+ LibSpec("pandas"),
41
+ LibSpec("tifffile"),
42
+ LibSpec("PIL", pip_name="Pillow", note="use `from PIL import Image`"),
43
+ ],
44
+ "networking & web": [
45
+ LibSpec("requests"),
46
+ LibSpec("astroquery"),
47
+ ],
48
+ "compression": [
49
+ LibSpec("lz4"),
50
+ LibSpec("zstandard", note="import as `import zstandard as zstd`"),
51
+ ],
52
+ "astronomy": [
53
+ LibSpec("astropy"),
54
+ LibSpec("photutils"),
55
+ LibSpec("astroalign"),
56
+ LibSpec("sep"),
57
+ LibSpec("reproject"),
58
+ LibSpec("tzlocal"),
59
+ ],
60
+ "performance": [
61
+ LibSpec("numba"),
62
+ ],
63
+ "neural net": [
64
+ LibSpec("onnx"),
65
+ LibSpec("onnxruntime"),
66
+ LibSpec(
67
+ "onnxruntime_directml",
68
+ pip_name="onnxruntime-directml",
69
+ note="Windows DirectML backend",
70
+ platforms="non-darwin",
71
+ ),
72
+ ],
73
+ "image formats & camera raw": [
74
+ LibSpec("xisf"),
75
+ LibSpec("rawpy"),
76
+ ],
77
+ "utilities": [
78
+ LibSpec("pytz"),
79
+ ],
80
+ "GUI": [
81
+ LibSpec("PyQt6"),
82
+ LibSpec("pyqtgraph"),
83
+ ],
84
+ "system": [
85
+ LibSpec("psutil"),
86
+ ],
87
+ "computer vision": [
88
+ LibSpec("cv2", pip_name="opencv-python", note="OpenCV"),
89
+ ],
90
+ }
91
+
92
+ def render_available_libs_markdown() -> str:
93
+ lines: List[str] = []
94
+ lines.append("## Available Script Libraries")
95
+ lines.append("These libraries are bundled/installed and importable from SAS scripts.\n")
96
+
97
+ for group, specs in AVAILABLE_LIBRARIES.items():
98
+ lines.append(f"### {group}")
99
+ for s in specs:
100
+ imp = f"`import {s.import_name}`"
101
+ pip = (
102
+ f"(pip: `{s.pip_name}`)"
103
+ if s.pip_name and s.pip_name != s.import_name
104
+ else ""
105
+ )
106
+ plat = "" if s.platforms == "all" else f"*[{s.platforms} only]*"
107
+ note = f" — {s.note}" if s.note else ""
108
+ tail = " ".join(t for t in (pip, plat) if t)
109
+ tail = f" {tail}" if tail else ""
110
+ lines.append(f"- {imp}{tail}{note}")
111
+ lines.append("")
112
+
113
+ return "\n".join(lines)
114
+
115
+
116
+ def _spec_display_title(spec: CommandSpec) -> str:
117
+ # prefer spec.name; fallback to id
118
+ return spec.name or spec.title or spec.id
119
+
120
+
121
+ def _preset_line(ps: PresetSpec) -> str:
122
+ parts = [f"**{ps.key}** ({ps.type})"]
123
+ if ps.default is not None:
124
+ parts.append(f"default={ps.default!r}")
125
+ if ps.min is not None or ps.max is not None:
126
+ parts.append(f"range=[{ps.min},{ps.max}]")
127
+ if ps.enum:
128
+ parts.append(f"enum={ps.enum}")
129
+ if not ps.optional:
130
+ parts.append("**required**")
131
+
132
+ head = " — ".join(parts)
133
+
134
+ # For markdown: use two spaces + newline to force a line-break under the bullet
135
+ # and indent the description a bit. No <br> or weird unicode spaces.
136
+ if ps.desc:
137
+ return f"{head} \n {ps.desc}"
138
+ return head
139
+
140
+
141
+
142
+ def _supports_line(spec: CommandSpec) -> str:
143
+ def yn(b): return "✅" if b else "❌"
144
+ return (
145
+ f"Mono {yn(spec.supports_mono)} · "
146
+ f"RGB {yn(spec.supports_rgb)} · "
147
+ f"Linear {yn(spec.supports_linear)} · "
148
+ f"Nonlinear {yn(spec.supports_nonlinear)}"
149
+ )
150
+
151
+
152
+ def render_spec_markdown(cid: str, spec: CommandSpec) -> str:
153
+ call_style = spec.call_style or "ctx.run_command"
154
+ notes = spec.notes or spec.summary or ""
155
+ name = _spec_display_title(spec)
156
+
157
+ md = [f"## {name} (`{cid}`)"]
158
+ md.append(f"**Group:** {spec.group}")
159
+ md.append(f"**Call style:** `{call_style}('{cid}', preset_dict)`")
160
+ if spec.import_path or spec.callable_name:
161
+ md.append(f"**Headless callable:** `{spec.import_path}.{spec.callable_name}`")
162
+ if spec.headless_method:
163
+ md.append(f"**Headless method:** `main_window.{spec.headless_method}(doc, preset)`")
164
+ if spec.ui_method:
165
+ md.append(f"**UI method:** `main_window.{spec.ui_method}(... )`")
166
+ md.append(f"**Supports:** {_supports_line(spec)}")
167
+
168
+ if spec.aliases:
169
+ md.append(f"**Aliases:** {', '.join(spec.aliases)}")
170
+
171
+ if notes:
172
+ md.append("")
173
+ md.append(notes)
174
+
175
+ if spec.presets:
176
+ md.append("")
177
+ md.append("### Presets")
178
+ for ps in spec.presets:
179
+ md.append(f"- {_preset_line(ps)}")
180
+ else:
181
+ md.append("")
182
+ md.append("### Presets")
183
+ md.append("- *(none)*")
184
+
185
+ if spec.examples:
186
+ md.append("")
187
+ md.append("### Examples")
188
+ for ex in spec.examples:
189
+ md.append("```python")
190
+ md.append(ex)
191
+ md.append("```")
192
+
193
+ return "\n".join(md)
194
+
195
+ # ---------------------------------------------------------------------
196
+ # Script Context (ctx) Help
197
+ # ---------------------------------------------------------------------
198
+
199
+ def render_scripting_quickstart_markdown() -> str:
200
+ return "\n".join([
201
+ "## Scripting Quickstart",
202
+ "User scripts live in your SASpro scripts folder and appear in the Scripts menu.",
203
+ "",
204
+ "### Required entrypoint",
205
+ "Your script must define one of:",
206
+ "- `def run(ctx):` *(preferred)*",
207
+ "- `def main(ctx):` *(fallback)*",
208
+ "",
209
+ "### Minimal example",
210
+ "```python",
211
+ "SCRIPT_NAME = \"My First Script\"",
212
+ "SCRIPT_GROUP = \"User\"",
213
+ "",
214
+ "def run(ctx):",
215
+ " ctx.log(\"Hello world\")",
216
+ "```",
217
+ "",
218
+ "### Typical active-image workflow",
219
+ "This operates on the **current active view** (Undo/ROI-safe):",
220
+ "```python",
221
+ "import numpy as np",
222
+ "",
223
+ "def run(ctx):",
224
+ " img = ctx.get_image()",
225
+ " if img is None:",
226
+ " ctx.log(\"No active image\")",
227
+ " return",
228
+ "",
229
+ " f = img.astype(np.float32)",
230
+ " f = np.clip(f, 0.0, 1.0)",
231
+ "",
232
+ " out = 1.0 - f",
233
+ " ctx.set_image(out, step_name=\"Invert via Script\")",
234
+ "```",
235
+ "",
236
+ "### File-based workflow (no documents opened)",
237
+ "Use this when you want to process images directly on disk without opening subwindows:",
238
+ "```python",
239
+ "import numpy as np",
240
+ "",
241
+ "def run(ctx):",
242
+ " img, hdr, bit, mono = ctx.load_image(r\"D:/data/a.fits\")",
243
+ " out = np.clip(img, 0, 1) ** 0.8",
244
+ " ctx.save_image(out, r\"D:/data/a_gamma.fits\",",
245
+ " original_format=\"fits\",",
246
+ " original_header=hdr,",
247
+ " is_mono=mono)",
248
+ "```",
249
+ "",
250
+ "### Running built-in operations",
251
+ "All headless/scriptable tools go through `ctx.run_command(...)`.",
252
+ "```python",
253
+ "def run(ctx):",
254
+ " ctx.run_command('stat_stretch', {'target_median': 0.25})",
255
+ " ctx.run_command('remove_green', {'amount': 0.7})",
256
+ "```",
257
+ "",
258
+ "### Running Function Bundles from scripts",
259
+ "Function Bundles are saved sequences of commands you manage in the **Function Bundles** dialog.",
260
+ "You can trigger them from scripts using the `function_bundle` command:",
261
+ "",
262
+ "```python",
263
+ "def run(ctx):",
264
+ " cfg = {",
265
+ " 'bundle_name': 'PreProcess', # name as shown in Function Bundles dialog",
266
+ " 'inherit_target': True, # forward the active view / ROI into each step",
267
+ " }",
268
+ " ctx.run_command('function_bundle', cfg)",
269
+ "```",
270
+ "",
271
+ "- `bundle_name` (or `name`) must match an existing Function Bundle.",
272
+ "- `inherit_target=True` makes each step run on the same target (active view or ROI).",
273
+ "- Optional: `targets='all_open'` to apply the bundle to **every** open image,",
274
+ " or `targets=[doc_id1, doc_id2, ...]` to target specific docs (same semantics as drag-and-drop).",
275
+ "- Internally this behaves **exactly like** dropping a Function Bundle chip onto a view.",
276
+ "- The bundled **Run Function Bundle…** script shows a complete picker dialog and then",
277
+ " calls `ctx.run_command('function_bundle', cfg)` under the hood.",
278
+ "",
279
+ "### Tips",
280
+ "- Scripts operate on the **active view** unless you explicitly target others.",
281
+ "- `ctx.set_image(...)` routes through DocManager so undo + ROI previews stay correct.",
282
+ "- File I/O helpers do **not** open documents unless you call `ctx.open_new_document(...)`.",
283
+ "- Use `ctx.log(...)` to write to the SASpro log and Script Editor output.",
284
+ ])
285
+
286
+ def render_ctx_api_markdown() -> str:
287
+ return "\n".join([
288
+ "## Script Context (`ctx`) API",
289
+ "Your script receives a `ScriptContext` instance named `ctx`.",
290
+ "",
291
+ "### Logging",
292
+ "- `ctx.log(msg: str)` — write to the SASpro log/output.",
293
+ "```python",
294
+ "ctx.log(\"Starting my script\")",
295
+ "```",
296
+ "",
297
+ "### Main window access",
298
+ "- `ctx.main_window()` — returns the main SASpro window.",
299
+ "```python",
300
+ "mw = ctx.main_window()",
301
+ "mw.update_status(\"hi from script\")",
302
+ "```",
303
+ "",
304
+ "### Active view / document",
305
+ "- `ctx.active_subwindow()` — current QMdiSubWindow or None.",
306
+ "- `ctx.active_view()` — active view widget or None.",
307
+ "- `ctx.active_document()` — active document or None.",
308
+ "- `ctx.base_document()` — base doc for ROI tabbed views.",
309
+ "",
310
+ "### Image data (active view)",
311
+ "- `ctx.get_image()` — returns `doc.image` (usually float32 [0,1]).",
312
+ "- `ctx.set_image(img, step_name='Script')` — commits new image through DocManager (Undo/ROI safe).",
313
+ "",
314
+ "```python",
315
+ "img = ctx.get_image()",
316
+ "if img is not None:",
317
+ " ctx.set_image(img*0.9, step_name=\"Dim\")",
318
+ "```",
319
+ "",
320
+ "### File I/O (canonical SASpro routes)",
321
+ "These operate on disk **without** opening a document or subwindow.",
322
+ "",
323
+ "- `ctx.load_image(path, return_metadata=False, max_retries=3, wait_seconds=3)`",
324
+ " - Returns `(img, original_header, bit_depth, is_mono)` by default.",
325
+ "- `ctx.save_image(img, path, original_format=None, bit_depth=None, original_header=None, is_mono=False, image_meta=None, file_meta=None)`",
326
+ " - `original_format` inferred from suffix if None (fits/tiff/png/jpg/etc).",
327
+ "",
328
+ "Aliases:",
329
+ "- `ctx.open_image(...)` → `ctx.load_image(...)`",
330
+ "- `ctx.write_image(...)` → `ctx.save_image(...)`",
331
+ "",
332
+ "```python",
333
+ "img, hdr, bit, mono = ctx.load_image(r\"D:/data/a.tiff\")",
334
+ "# ...process...",
335
+ "ctx.save_image(img, r\"D:/data/a_out.tiff\",",
336
+ " original_header=hdr, is_mono=mono)",
337
+ "```",
338
+ "",
339
+ "### Listing and targeting other open views/documents",
340
+ "These helpers let scripts operate on multiple images by name/title/uid.",
341
+ "",
342
+ "- `ctx.list_views()` — list of open views with titles, names, uids, file paths.",
343
+ "- `ctx.list_view_names()` — just the human-visible names.",
344
+ "- `ctx.get_document(name_or_uid)` — fetch an open base document (never ROI wrapper).",
345
+ "- `ctx.get_image_for(name_or_uid)` — ndarray for a specific open view.",
346
+ "- `ctx.set_image_for(name_or_uid, img, step_name='Script')` — update a specific open view.",
347
+ "- `ctx.activate_view(name_or_uid)` — bring a view to front.",
348
+ "",
349
+ "```python",
350
+ "for v in ctx.list_views():",
351
+ " ctx.log(f\"Open view: {v['name']} title={v['title']}\")",
352
+ "",
353
+ "doc = ctx.get_document(\"Andromeda\")",
354
+ "if doc:",
355
+ " ctx.set_image_for(\"Andromeda\", doc.image*1.05, step_name=\"Boost\")",
356
+ "```",
357
+ "",
358
+ "### Creating a new document",
359
+ "- `ctx.open_new_document(img, metadata=None, name=None)` — opens a new view/doc from an array.",
360
+ "```python",
361
+ "out = img ** 0.9",
362
+ "ctx.open_new_document(out, name=\"Gamma\")",
363
+ "```",
364
+ "",
365
+ "### Running commands",
366
+ "- `ctx.run_command(command_id, preset=None, **kwargs)`",
367
+ "",
368
+ "Preferred usage:",
369
+ "```python",
370
+ "ctx.run_command('ghs', {'alpha':1.5, 'beta':1.0})",
371
+ "ctx.run_command('abe', {'degree':2, 'samples':150})",
372
+ "```",
373
+ "",
374
+ "You can also run **Function Bundles**:",
375
+ "```python",
376
+ "cfg = {",
377
+ " 'bundle_name': 'PreProcess', # Function Bundle name",
378
+ " 'inherit_target': True, # forward active view / ROI to each step",
379
+ " # optional: 'targets': 'all_open' ",
380
+ " # optional: 'targets': [doc_id1, doc_id2, ...],",
381
+ "}",
382
+ "ctx.run_command('function_bundle', cfg)",
383
+ "```",
384
+ "",
385
+ "Notes:",
386
+ "- `command_id` can be any registered id or alias (see left panel).",
387
+ "- `preset` is a dict matching that command’s Presets section.",
388
+ "- Most commands support masks automatically if an active mask exists.",
389
+ "- For `function_bundle`, the command delegates to the same internal path as",
390
+ " dragging a Function Bundle chip onto a view, so UI and scripts stay in sync.",
391
+ "",
392
+ "### Environment helpers",
393
+ "- `ctx.is_frozen()` — True if running from packaged app (PyInstaller).",
394
+ "",
395
+ "```python",
396
+ "if ctx.is_frozen():",
397
+ " ctx.log(\"Running from packaged build\")",
398
+ "```",
399
+ "",
400
+ "### Patterns you’ll use a lot",
401
+ "**1) Guard against missing active image**",
402
+ "```python",
403
+ "img = ctx.get_image()",
404
+ "if img is None:",
405
+ " ctx.log(\"No active image\")",
406
+ " return",
407
+ "```",
408
+ "",
409
+ "**2) Apply a tool then tweak pixels**",
410
+ "```python",
411
+ "ctx.run_command('stat_stretch', {'target_median':0.25})",
412
+ "img = ctx.get_image()",
413
+ "ctx.set_image(img**0.9, step_name=\"Gamma tweak\")",
414
+ "```",
415
+ "",
416
+ "**3) Disk batch + optional new doc**",
417
+ "```python",
418
+ "img, hdr, bit, mono = ctx.load_image(r\"D:/data/a.fits\")",
419
+ "out = img * 0.95",
420
+ "ctx.save_image(out, r\"D:/data/a_dim.fits\", original_header=hdr)",
421
+ "# only open a view if you want to:",
422
+ "# ctx.open_new_document(out, name=\"a_dim\")",
423
+ "```",
424
+ ])
425
+
426
+
427
+
428
+ class CommandHelpDialog(QDialog):
429
+ """
430
+ Dialog that documents all registered COMMAND_REGISTRY commands.
431
+ """
432
+ def __init__(self, parent=None, editor=None):
433
+ super().__init__(parent)
434
+ self.setWindowTitle("Command Help (Scriptable / Headless Ops)")
435
+ self.resize(980, 620)
436
+ self.editor = editor # optional CodeEditor to insert examples
437
+
438
+ root = QVBoxLayout(self)
439
+
440
+ # ---- top search bar ----
441
+ top = QHBoxLayout()
442
+ self.search = QLineEdit()
443
+ self.search.setPlaceholderText("Search commands… (id, name, group, alias)")
444
+ top.addWidget(QLabel("Search:"))
445
+ top.addWidget(self.search, 1)
446
+ root.addLayout(top)
447
+
448
+ split = QSplitter(Qt.Orientation.Horizontal)
449
+ root.addWidget(split, 1)
450
+
451
+ # ---- left list ----
452
+ self.listw = QListWidget()
453
+ split.addWidget(self.listw)
454
+
455
+ # ---- right detail ----
456
+ right = QVBoxLayout()
457
+ right_wrap = QDialog()
458
+ right_wrap.setLayout(right)
459
+
460
+ self.detail = QTextBrowser()
461
+ self.detail.setOpenExternalLinks(True)
462
+ #mono = QFont("Consolas")
463
+ #mono.setStyleHint(QFont.StyleHint.Monospace)
464
+ #mono.setPointSize(10)
465
+ #self.detail.setFont(mono)
466
+ right.addWidget(self.detail, 1)
467
+
468
+ btnrow = QHBoxLayout()
469
+ self.btn_copy_md = QPushButton("Copy Markdown")
470
+ self.btn_insert_ex = QPushButton("Insert First Example")
471
+ self.btn_close = QPushButton("Close")
472
+ btnrow.addWidget(self.btn_copy_md)
473
+ btnrow.addWidget(self.btn_insert_ex)
474
+ btnrow.addStretch(1)
475
+ btnrow.addWidget(self.btn_close)
476
+ right.addLayout(btnrow)
477
+
478
+ split.addWidget(right_wrap)
479
+ split.setStretchFactor(1, 2)
480
+
481
+ # ---- wiring ----
482
+ self.btn_close.clicked.connect(self.close)
483
+ self.btn_copy_md.clicked.connect(self.copy_markdown)
484
+ self.btn_insert_ex.clicked.connect(self.insert_first_example)
485
+ self.search.textChanged.connect(self.rebuild_list)
486
+ self.listw.currentItemChanged.connect(self.show_selected)
487
+
488
+ self.rebuild_list()
489
+ if self.listw.count():
490
+ self.listw.setCurrentRow(0)
491
+
492
+ def rebuild_list(self):
493
+ q = (self.search.text() or "").strip().lower()
494
+
495
+ # sort by group then name
496
+ items: List[Tuple[str, CommandSpec]] = sorted(
497
+ COMMAND_REGISTRY.items(),
498
+ key=lambda kv: (kv[1].group or "", _spec_display_title(kv[1]).lower(), kv[0])
499
+ )
500
+
501
+ self.listw.blockSignals(True)
502
+ self.listw.clear()
503
+
504
+ # ---- synthetic doc item: Available Libraries ----
505
+ synth_items = [
506
+ ("Docs · Scripting Quickstart", "__script_quickstart__",
507
+ "quickstart run(ctx) main(ctx) script entrypoint samples"),
508
+
509
+ ("Docs · Script Context (ctx) API", "__ctx_help__",
510
+ "ctx api context get_image set_image run_command log base_document "
511
+ "list_views list_view_names get_document get_image_for set_image_for activate_view "
512
+ "open_new_document "
513
+ "load_image save_image open_image write_image "
514
+ "file io disk batch fits tiff png jpg exr xisf"),
515
+
516
+ ("Docs · Available Libraries", "__available_libs__",
517
+ "available libraries libs imports bundled installed"),
518
+ ]
519
+
520
+ for title, key, searchable in synth_items:
521
+ if (not q) or (q in searchable.lower()):
522
+ it = QListWidgetItem(title)
523
+ it.setData(Qt.ItemDataRole.UserRole, key)
524
+ self.listw.addItem(it)
525
+
526
+ for cid, spec in items:
527
+ searchable = " ".join([
528
+ cid,
529
+ _spec_display_title(spec),
530
+ spec.group or "",
531
+ " ".join(spec.aliases or [])
532
+ ]).lower()
533
+
534
+ if q and q not in searchable:
535
+ continue
536
+
537
+ it = QListWidgetItem(f"{spec.group} · {_spec_display_title(spec)}")
538
+ it.setData(Qt.ItemDataRole.UserRole, cid)
539
+ self.listw.addItem(it)
540
+
541
+ self.listw.blockSignals(False)
542
+ if self.listw.count():
543
+ self.listw.setCurrentRow(0)
544
+ else:
545
+ self.detail.setText("No matches.")
546
+
547
+ def show_selected(self, item: QListWidgetItem | None, _prev=None):
548
+ if item is None:
549
+ self.detail.setText("")
550
+ return
551
+
552
+ cid = item.data(Qt.ItemDataRole.UserRole)
553
+
554
+ # ---- synthetic doc rendering ----
555
+ if cid == "__available_libs__":
556
+ md = render_available_libs_markdown()
557
+ self.detail.setMarkdown(md)
558
+ return
559
+
560
+ if cid == "__script_quickstart__":
561
+ md = render_scripting_quickstart_markdown()
562
+ self.detail.setMarkdown(md)
563
+ return
564
+
565
+ if cid == "__ctx_help__":
566
+ md = render_ctx_api_markdown()
567
+ self.detail.setMarkdown(md)
568
+ return
569
+
570
+ # ---- normal command specs ----
571
+ spec = COMMAND_REGISTRY.get(cid)
572
+ if spec is None:
573
+ self.detail.setText("")
574
+ return
575
+
576
+ md = render_spec_markdown(cid, spec)
577
+ # Real markdown rendering (handles headings, bullets, code fences, etc.)
578
+ self.detail.setMarkdown(md)
579
+
580
+
581
+
582
+ def copy_markdown(self):
583
+ item = self.listw.currentItem()
584
+ if item is None:
585
+ return
586
+
587
+ cid = item.data(Qt.ItemDataRole.UserRole)
588
+
589
+ if cid == "__available_libs__":
590
+ md = render_available_libs_markdown()
591
+ elif cid == "__script_quickstart__":
592
+ md = render_scripting_quickstart_markdown()
593
+ elif cid == "__ctx_help__":
594
+ md = render_ctx_api_markdown()
595
+ else:
596
+ spec = COMMAND_REGISTRY.get(cid)
597
+ if spec is None:
598
+ return
599
+ md = render_spec_markdown(cid, spec)
600
+
601
+ cb = QApplication.clipboard()
602
+ cb.setText(md)
603
+
604
+
605
+
606
+
607
+ def insert_first_example(self):
608
+ if self.editor is None:
609
+ return
610
+ item = self.listw.currentItem()
611
+ if item is None:
612
+ return
613
+ cid = item.data(Qt.ItemDataRole.UserRole)
614
+ spec = COMMAND_REGISTRY.get(cid)
615
+ if spec is None or not spec.examples:
616
+ return
617
+ ex = spec.examples[0]
618
+
619
+ cur = self.editor.textCursor()
620
+ cur.beginEditBlock()
621
+ cur.insertText("\n\n# Example: " + _spec_display_title(spec) + "\n" + ex + "\n")
622
+ cur.endEditBlock()
623
+ self.editor.setTextCursor(cur)