setiastrosuitepro 1.6.2.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 (367) 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/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/__init__.py +20 -0
  146. setiastro/saspro/__main__.py +945 -0
  147. setiastro/saspro/_generated/__init__.py +7 -0
  148. setiastro/saspro/_generated/build_info.py +3 -0
  149. setiastro/saspro/abe.py +1346 -0
  150. setiastro/saspro/abe_preset.py +196 -0
  151. setiastro/saspro/aberration_ai.py +694 -0
  152. setiastro/saspro/aberration_ai_preset.py +224 -0
  153. setiastro/saspro/accel_installer.py +218 -0
  154. setiastro/saspro/accel_workers.py +30 -0
  155. setiastro/saspro/add_stars.py +624 -0
  156. setiastro/saspro/astrobin_exporter.py +1010 -0
  157. setiastro/saspro/astrospike.py +153 -0
  158. setiastro/saspro/astrospike_python.py +1841 -0
  159. setiastro/saspro/autostretch.py +198 -0
  160. setiastro/saspro/backgroundneutral.py +602 -0
  161. setiastro/saspro/batch_convert.py +328 -0
  162. setiastro/saspro/batch_renamer.py +522 -0
  163. setiastro/saspro/blemish_blaster.py +491 -0
  164. setiastro/saspro/blink_comparator_pro.py +2926 -0
  165. setiastro/saspro/bundles.py +61 -0
  166. setiastro/saspro/bundles_dock.py +114 -0
  167. setiastro/saspro/cheat_sheet.py +213 -0
  168. setiastro/saspro/clahe.py +368 -0
  169. setiastro/saspro/comet_stacking.py +1442 -0
  170. setiastro/saspro/common_tr.py +107 -0
  171. setiastro/saspro/config.py +38 -0
  172. setiastro/saspro/config_bootstrap.py +40 -0
  173. setiastro/saspro/config_manager.py +316 -0
  174. setiastro/saspro/continuum_subtract.py +1617 -0
  175. setiastro/saspro/convo.py +1400 -0
  176. setiastro/saspro/convo_preset.py +414 -0
  177. setiastro/saspro/copyastro.py +190 -0
  178. setiastro/saspro/cosmicclarity.py +1589 -0
  179. setiastro/saspro/cosmicclarity_preset.py +407 -0
  180. setiastro/saspro/crop_dialog_pro.py +973 -0
  181. setiastro/saspro/crop_preset.py +189 -0
  182. setiastro/saspro/curve_editor_pro.py +2562 -0
  183. setiastro/saspro/curves_preset.py +375 -0
  184. setiastro/saspro/debayer.py +673 -0
  185. setiastro/saspro/debug_utils.py +29 -0
  186. setiastro/saspro/dnd_mime.py +35 -0
  187. setiastro/saspro/doc_manager.py +2664 -0
  188. setiastro/saspro/exoplanet_detector.py +2166 -0
  189. setiastro/saspro/file_utils.py +284 -0
  190. setiastro/saspro/fitsmodifier.py +748 -0
  191. setiastro/saspro/fix_bom.py +32 -0
  192. setiastro/saspro/free_torch_memory.py +48 -0
  193. setiastro/saspro/frequency_separation.py +1349 -0
  194. setiastro/saspro/function_bundle.py +1596 -0
  195. setiastro/saspro/generate_translations.py +3092 -0
  196. setiastro/saspro/ghs_dialog_pro.py +663 -0
  197. setiastro/saspro/ghs_preset.py +284 -0
  198. setiastro/saspro/graxpert.py +637 -0
  199. setiastro/saspro/graxpert_preset.py +287 -0
  200. setiastro/saspro/gui/__init__.py +0 -0
  201. setiastro/saspro/gui/main_window.py +8810 -0
  202. setiastro/saspro/gui/mixins/__init__.py +33 -0
  203. setiastro/saspro/gui/mixins/dock_mixin.py +362 -0
  204. setiastro/saspro/gui/mixins/file_mixin.py +450 -0
  205. setiastro/saspro/gui/mixins/geometry_mixin.py +403 -0
  206. setiastro/saspro/gui/mixins/header_mixin.py +441 -0
  207. setiastro/saspro/gui/mixins/mask_mixin.py +421 -0
  208. setiastro/saspro/gui/mixins/menu_mixin.py +389 -0
  209. setiastro/saspro/gui/mixins/theme_mixin.py +367 -0
  210. setiastro/saspro/gui/mixins/toolbar_mixin.py +1457 -0
  211. setiastro/saspro/gui/mixins/update_mixin.py +309 -0
  212. setiastro/saspro/gui/mixins/view_mixin.py +435 -0
  213. setiastro/saspro/gui/statistics_dialog.py +47 -0
  214. setiastro/saspro/halobgon.py +488 -0
  215. setiastro/saspro/header_viewer.py +448 -0
  216. setiastro/saspro/headless_utils.py +88 -0
  217. setiastro/saspro/histogram.py +756 -0
  218. setiastro/saspro/history_explorer.py +941 -0
  219. setiastro/saspro/i18n.py +168 -0
  220. setiastro/saspro/image_combine.py +417 -0
  221. setiastro/saspro/image_peeker_pro.py +1604 -0
  222. setiastro/saspro/imageops/__init__.py +37 -0
  223. setiastro/saspro/imageops/mdi_snap.py +292 -0
  224. setiastro/saspro/imageops/scnr.py +36 -0
  225. setiastro/saspro/imageops/starbasedwhitebalance.py +210 -0
  226. setiastro/saspro/imageops/stretch.py +236 -0
  227. setiastro/saspro/isophote.py +1182 -0
  228. setiastro/saspro/layers.py +208 -0
  229. setiastro/saspro/layers_dock.py +714 -0
  230. setiastro/saspro/lazy_imports.py +193 -0
  231. setiastro/saspro/legacy/__init__.py +2 -0
  232. setiastro/saspro/legacy/image_manager.py +2226 -0
  233. setiastro/saspro/legacy/numba_utils.py +3676 -0
  234. setiastro/saspro/legacy/xisf.py +1071 -0
  235. setiastro/saspro/linear_fit.py +537 -0
  236. setiastro/saspro/live_stacking.py +1841 -0
  237. setiastro/saspro/log_bus.py +5 -0
  238. setiastro/saspro/logging_config.py +460 -0
  239. setiastro/saspro/luminancerecombine.py +309 -0
  240. setiastro/saspro/main_helpers.py +201 -0
  241. setiastro/saspro/mask_creation.py +931 -0
  242. setiastro/saspro/masks_core.py +56 -0
  243. setiastro/saspro/mdi_widgets.py +353 -0
  244. setiastro/saspro/memory_utils.py +666 -0
  245. setiastro/saspro/metadata_patcher.py +75 -0
  246. setiastro/saspro/mfdeconv.py +3831 -0
  247. setiastro/saspro/mfdeconv_earlystop.py +71 -0
  248. setiastro/saspro/mfdeconvcudnn.py +3263 -0
  249. setiastro/saspro/mfdeconvsport.py +2382 -0
  250. setiastro/saspro/minorbodycatalog.py +567 -0
  251. setiastro/saspro/morphology.py +407 -0
  252. setiastro/saspro/multiscale_decomp.py +1293 -0
  253. setiastro/saspro/nbtorgb_stars.py +541 -0
  254. setiastro/saspro/numba_utils.py +3145 -0
  255. setiastro/saspro/numba_warmup.py +141 -0
  256. setiastro/saspro/ops/__init__.py +9 -0
  257. setiastro/saspro/ops/command_help_dialog.py +623 -0
  258. setiastro/saspro/ops/command_runner.py +217 -0
  259. setiastro/saspro/ops/commands.py +1594 -0
  260. setiastro/saspro/ops/script_editor.py +1102 -0
  261. setiastro/saspro/ops/scripts.py +1473 -0
  262. setiastro/saspro/ops/settings.py +637 -0
  263. setiastro/saspro/parallel_utils.py +554 -0
  264. setiastro/saspro/pedestal.py +121 -0
  265. setiastro/saspro/perfect_palette_picker.py +1071 -0
  266. setiastro/saspro/pipeline.py +110 -0
  267. setiastro/saspro/pixelmath.py +1604 -0
  268. setiastro/saspro/plate_solver.py +2445 -0
  269. setiastro/saspro/project_io.py +797 -0
  270. setiastro/saspro/psf_utils.py +136 -0
  271. setiastro/saspro/psf_viewer.py +549 -0
  272. setiastro/saspro/pyi_rthook_astroquery.py +95 -0
  273. setiastro/saspro/remove_green.py +331 -0
  274. setiastro/saspro/remove_stars.py +1599 -0
  275. setiastro/saspro/remove_stars_preset.py +404 -0
  276. setiastro/saspro/resources.py +501 -0
  277. setiastro/saspro/rgb_combination.py +208 -0
  278. setiastro/saspro/rgb_extract.py +19 -0
  279. setiastro/saspro/rgbalign.py +723 -0
  280. setiastro/saspro/runtime_imports.py +7 -0
  281. setiastro/saspro/runtime_torch.py +754 -0
  282. setiastro/saspro/save_options.py +73 -0
  283. setiastro/saspro/selective_color.py +1552 -0
  284. setiastro/saspro/sfcc.py +1472 -0
  285. setiastro/saspro/shortcuts.py +3043 -0
  286. setiastro/saspro/signature_insert.py +1102 -0
  287. setiastro/saspro/stacking_suite.py +18470 -0
  288. setiastro/saspro/star_alignment.py +7435 -0
  289. setiastro/saspro/star_alignment_preset.py +329 -0
  290. setiastro/saspro/star_metrics.py +49 -0
  291. setiastro/saspro/star_spikes.py +765 -0
  292. setiastro/saspro/star_stretch.py +507 -0
  293. setiastro/saspro/stat_stretch.py +538 -0
  294. setiastro/saspro/status_log_dock.py +78 -0
  295. setiastro/saspro/subwindow.py +3328 -0
  296. setiastro/saspro/supernovaasteroidhunter.py +1719 -0
  297. setiastro/saspro/swap_manager.py +99 -0
  298. setiastro/saspro/torch_backend.py +89 -0
  299. setiastro/saspro/torch_rejection.py +434 -0
  300. setiastro/saspro/translations/all_source_strings.json +3654 -0
  301. setiastro/saspro/translations/ar_translations.py +3865 -0
  302. setiastro/saspro/translations/de_translations.py +3749 -0
  303. setiastro/saspro/translations/es_translations.py +3939 -0
  304. setiastro/saspro/translations/fr_translations.py +3858 -0
  305. setiastro/saspro/translations/hi_translations.py +3571 -0
  306. setiastro/saspro/translations/integrate_translations.py +270 -0
  307. setiastro/saspro/translations/it_translations.py +3678 -0
  308. setiastro/saspro/translations/ja_translations.py +3601 -0
  309. setiastro/saspro/translations/pt_translations.py +3869 -0
  310. setiastro/saspro/translations/ru_translations.py +2848 -0
  311. setiastro/saspro/translations/saspro_ar.qm +0 -0
  312. setiastro/saspro/translations/saspro_ar.ts +255 -0
  313. setiastro/saspro/translations/saspro_de.qm +0 -0
  314. setiastro/saspro/translations/saspro_de.ts +253 -0
  315. setiastro/saspro/translations/saspro_es.qm +0 -0
  316. setiastro/saspro/translations/saspro_es.ts +12520 -0
  317. setiastro/saspro/translations/saspro_fr.qm +0 -0
  318. setiastro/saspro/translations/saspro_fr.ts +12514 -0
  319. setiastro/saspro/translations/saspro_hi.qm +0 -0
  320. setiastro/saspro/translations/saspro_hi.ts +257 -0
  321. setiastro/saspro/translations/saspro_it.qm +0 -0
  322. setiastro/saspro/translations/saspro_it.ts +12520 -0
  323. setiastro/saspro/translations/saspro_ja.qm +0 -0
  324. setiastro/saspro/translations/saspro_ja.ts +257 -0
  325. setiastro/saspro/translations/saspro_pt.qm +0 -0
  326. setiastro/saspro/translations/saspro_pt.ts +257 -0
  327. setiastro/saspro/translations/saspro_ru.qm +0 -0
  328. setiastro/saspro/translations/saspro_ru.ts +237 -0
  329. setiastro/saspro/translations/saspro_sw.qm +0 -0
  330. setiastro/saspro/translations/saspro_sw.ts +257 -0
  331. setiastro/saspro/translations/saspro_uk.qm +0 -0
  332. setiastro/saspro/translations/saspro_uk.ts +10771 -0
  333. setiastro/saspro/translations/saspro_zh.qm +0 -0
  334. setiastro/saspro/translations/saspro_zh.ts +12520 -0
  335. setiastro/saspro/translations/sw_translations.py +3671 -0
  336. setiastro/saspro/translations/uk_translations.py +3700 -0
  337. setiastro/saspro/translations/zh_translations.py +3675 -0
  338. setiastro/saspro/versioning.py +77 -0
  339. setiastro/saspro/view_bundle.py +1558 -0
  340. setiastro/saspro/wavescale_hdr.py +645 -0
  341. setiastro/saspro/wavescale_hdr_preset.py +101 -0
  342. setiastro/saspro/wavescalede.py +680 -0
  343. setiastro/saspro/wavescalede_preset.py +230 -0
  344. setiastro/saspro/wcs_update.py +374 -0
  345. setiastro/saspro/whitebalance.py +492 -0
  346. setiastro/saspro/widgets/__init__.py +48 -0
  347. setiastro/saspro/widgets/common_utilities.py +306 -0
  348. setiastro/saspro/widgets/graphics_views.py +122 -0
  349. setiastro/saspro/widgets/image_utils.py +518 -0
  350. setiastro/saspro/widgets/minigame/game.js +986 -0
  351. setiastro/saspro/widgets/minigame/index.html +53 -0
  352. setiastro/saspro/widgets/minigame/style.css +241 -0
  353. setiastro/saspro/widgets/preview_dialogs.py +280 -0
  354. setiastro/saspro/widgets/resource_monitor.py +237 -0
  355. setiastro/saspro/widgets/spinboxes.py +275 -0
  356. setiastro/saspro/widgets/themed_buttons.py +13 -0
  357. setiastro/saspro/widgets/wavelet_utils.py +331 -0
  358. setiastro/saspro/wimi.py +7996 -0
  359. setiastro/saspro/wims.py +578 -0
  360. setiastro/saspro/window_shelf.py +185 -0
  361. setiastro/saspro/xisf.py +1123 -0
  362. setiastrosuitepro-1.6.2.post1.dist-info/METADATA +278 -0
  363. setiastrosuitepro-1.6.2.post1.dist-info/RECORD +367 -0
  364. setiastrosuitepro-1.6.2.post1.dist-info/WHEEL +4 -0
  365. setiastrosuitepro-1.6.2.post1.dist-info/entry_points.txt +6 -0
  366. setiastrosuitepro-1.6.2.post1.dist-info/licenses/LICENSE +674 -0
  367. setiastrosuitepro-1.6.2.post1.dist-info/licenses/license.txt +2580 -0
@@ -0,0 +1,61 @@
1
+ # pro/bundles.py
2
+ from __future__ import annotations
3
+ from dataclasses import dataclass, field
4
+ from typing import List, Dict, Optional
5
+ from PyQt6.QtCore import QObject, pyqtSignal
6
+
7
+ @dataclass
8
+ class ViewBundle:
9
+ id: str
10
+ name: str
11
+ doc_uids: List[str] = field(default_factory=list) # store stable doc UIDs
12
+
13
+ class BundleManager(QObject):
14
+ changed = pyqtSignal()
15
+ def __init__(self, dm):
16
+ super().__init__()
17
+ self.dm = dm
18
+ self._by_id: Dict[str, ViewBundle] = {}
19
+
20
+ def add(self, b: ViewBundle):
21
+ self._by_id[b.id] = b
22
+ self.changed.emit()
23
+
24
+ def remove(self, bid: str):
25
+ self._by_id.pop(bid, None)
26
+ self.changed.emit()
27
+
28
+ def get(self, bid: str) -> Optional[ViewBundle]:
29
+ return self._by_id.get(bid)
30
+
31
+ def all(self) -> list[ViewBundle]:
32
+ return list(self._by_id.values())
33
+
34
+ def add_doc(self, bid: str, doc_uid: str):
35
+ b = self._by_id.get(bid)
36
+ if not b: return
37
+ if doc_uid not in b.doc_uids:
38
+ b.doc_uids.append(doc_uid)
39
+ self.changed.emit()
40
+
41
+ def remove_doc(self, bid: str, doc_uid: str):
42
+ b = self._by_id.get(bid)
43
+ if not b: return
44
+ if doc_uid in b.doc_uids:
45
+ b.doc_uids.remove(doc_uid)
46
+ self.changed.emit()
47
+
48
+ def docs(self, bid: str):
49
+ b = self._by_id.get(bid)
50
+ if not b: return []
51
+ out = []
52
+ for uid in b.doc_uids:
53
+ d = getattr(self.dm, "find_document_by_uid", None)
54
+ if callable(d):
55
+ doc = d(uid)
56
+ else:
57
+ # fallback: linear scan over open docs if you don't have an index
58
+ doc = next((x for x in getattr(self.dm, "documents", []) if getattr(x, "uid", None) == uid), None)
59
+ if doc:
60
+ out.append(doc)
61
+ return out
@@ -0,0 +1,114 @@
1
+ # pro/bundles_dock.py
2
+ from __future__ import annotations
3
+ from PyQt6.QtWidgets import QDockWidget, QListWidget, QListWidgetItem, QMenu, QInputDialog
4
+ from PyQt6.QtCore import Qt, QMimeData, QByteArray
5
+ import json
6
+
7
+ from setiastro.saspro.dnd_mime import MIME_CMD # you already use this
8
+
9
+ class BundlesDock(QDockWidget):
10
+ def __init__(self, mw, bm, pipelines):
11
+ super().__init__("Bundles", mw)
12
+ self.mw = mw
13
+ self.bm = bm
14
+ self.pipelines = pipelines
15
+
16
+ self.list = QListWidget(self)
17
+ self.list.setSelectionMode(self.list.SelectionMode.SingleSelection)
18
+ self.list.setAcceptDrops(True)
19
+ self.list.setDragEnabled(False)
20
+ self.setWidget(self.list)
21
+
22
+ self.setAllowedAreas(Qt.DockWidgetArea.LeftDockWidgetArea | Qt.DockWidgetArea.RightDockWidgetArea)
23
+ self._refresh()
24
+ bm.changed.connect(self._refresh)
25
+
26
+ self.list.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
27
+ self.list.customContextMenuRequested.connect(self._ctx)
28
+
29
+ def _refresh(self):
30
+ self.list.clear()
31
+ for b in self.bm.all():
32
+ it = QListWidgetItem(f"{b.name} ({len(b.doc_uids)} views)")
33
+ it.setData(Qt.ItemDataRole.UserRole, b.id)
34
+ self.list.addItem(it)
35
+
36
+ def _ctx(self, pos):
37
+ it = self.list.itemAt(pos)
38
+ m = QMenu(self)
39
+ m.addAction("New Bundle…", self._new_bundle)
40
+ if it:
41
+ bid = it.data(Qt.ItemDataRole.UserRole)
42
+ m.addSeparator()
43
+ m.addAction("Rename…", lambda: self._rename(bid))
44
+ m.addAction("Delete", lambda: self._delete(bid))
45
+ m.addSeparator()
46
+ m.addAction("Run Pipeline…", lambda: self._pick_and_run(bid))
47
+ m.exec(self.list.mapToGlobal(pos))
48
+
49
+ def _new_bundle(self):
50
+ name, ok = QInputDialog.getText(self, "New Bundle", "Name:")
51
+ if not ok or not name.strip(): return
52
+ import uuid
53
+ from setiastro.saspro.bundles import ViewBundle
54
+ self.bm.add(ViewBundle(id=uuid.uuid4().hex, name=name.strip()))
55
+
56
+ def _rename(self, bid):
57
+ b = self.bm.get(bid);
58
+ if not b: return
59
+ name, ok = QInputDialog.getText(self, "Rename Bundle", "Name:", text=b.name)
60
+ if ok and name.strip():
61
+ b.name = name.strip()
62
+ self.bm.changed.emit()
63
+
64
+ def _delete(self, bid):
65
+ self.bm.remove(bid)
66
+
67
+ # --- DnD: add docs or run commands ---
68
+ def dragEnterEvent(self, e):
69
+ if e.mimeData().hasFormat(MIME_CMD):
70
+ e.acceptProposedAction(); return
71
+ # Let your explorer/subwindow provide a doc UID mime if you have one; example below:
72
+ if e.mimeData().hasFormat("application/x-saspro-doc-uid"):
73
+ e.acceptProposedAction(); return
74
+ e.ignore()
75
+
76
+ def dropEvent(self, e):
77
+ it = self.list.itemAt(e.position().toPoint())
78
+ if not it:
79
+ e.ignore(); return
80
+ bid = it.data(Qt.ItemDataRole.UserRole)
81
+
82
+ md: QMimeData = e.mimeData()
83
+ # 1) drop a command/pipeline onto a bundle -> run across all docs in the bundle
84
+ if md.hasFormat(MIME_CMD):
85
+ try:
86
+ payload = json.loads(bytes(md.data(MIME_CMD)).decode("utf-8"))
87
+ except Exception:
88
+ e.ignore(); return
89
+ docs = self.bm.docs(bid)
90
+ if not docs:
91
+ e.ignore(); return
92
+ # Pipeline support via "pipeline:<id>" (see MW patch below)
93
+ self.mw._run_payload_on_docs(payload, docs)
94
+ e.acceptProposedAction(); return
95
+
96
+ # 2) drop doc(s) onto a bundle -> add
97
+ if md.hasFormat("application/x-saspro-doc-uid"):
98
+ uid = bytes(md.data("application/x-saspro-doc-uid")).decode("utf-8")
99
+ self.bm.add_doc(bid, uid)
100
+ e.acceptProposedAction(); return
101
+
102
+ e.ignore()
103
+
104
+ # quick picker to run a pipeline without DnD
105
+ def _pick_and_run(self, bid):
106
+ plist = self.pipelines.all()
107
+ if not plist:
108
+ return
109
+ names = [p.name for p in plist]
110
+ i, ok = QInputDialog.getItem(self, "Run Pipeline", "Pick:", names, 0, False)
111
+ if not ok: return
112
+ p = plist[names.index(i)]
113
+ payload = {"command_id": f"pipeline:{p.id}", "preset": {}}
114
+ self.mw._run_payload_on_docs(payload, self.bm.docs(bid))
@@ -0,0 +1,213 @@
1
+ # pro/cheat_sheet.py
2
+ """
3
+ Keyboard shortcut cheat sheet dialog.
4
+
5
+ Displays all keyboard shortcuts and mouse gestures in a tabbed dialog.
6
+ """
7
+
8
+ from PyQt6.QtWidgets import (
9
+ QDialog, QVBoxLayout, QHBoxLayout, QTabWidget, QWidget,
10
+ QTableWidget, QTableWidgetItem, QHeaderView, QPushButton,
11
+ QApplication, QMessageBox
12
+ )
13
+ from PyQt6.QtGui import QAction, QShortcut, QKeySequence
14
+ from PyQt6.QtCore import Qt, QCoreApplication
15
+
16
+
17
+ def _qs_to_str(seq: QKeySequence) -> str:
18
+ """Convert a QKeySequence to a human-readable string."""
19
+ return seq.toString(QKeySequence.SequenceFormat.NativeText).strip()
20
+
21
+
22
+ def _clean_text(text: str) -> str:
23
+ """Remove common Unicode decoration from text."""
24
+ if not text:
25
+ return ""
26
+ # Remove common ellipsis, arrows, etc.
27
+ return text.replace("…", "").replace("→", "->").replace("←", "<-").strip()
28
+
29
+
30
+ def _uniq_keep_order(items):
31
+ """Return unique items preserving order."""
32
+ seen = set()
33
+ out = []
34
+ for x in items:
35
+ if x in seen:
36
+ continue
37
+ seen.add(x)
38
+ out.append(x)
39
+ return out
40
+
41
+
42
+ def _seqs_for_action(act: QAction):
43
+ """Get non-empty key sequences for an action."""
44
+ seqs = [s for s in act.shortcuts() or []] or ([act.shortcut()] if act.shortcut() else [])
45
+ return [s for s in seqs if not s.isEmpty()]
46
+
47
+
48
+ def _where_for_action(act: QAction) -> str:
49
+ """Determine where an action is available (Menus/Toolbar or Window)."""
50
+ try:
51
+ parent = act.parent()
52
+ if parent is not None:
53
+ pn = parent.__class__.__name__
54
+ if pn.startswith("QMenu") or pn.startswith("QToolBar"):
55
+ return QCoreApplication.translate("CheatSheet", "Menus/Toolbar")
56
+ except Exception:
57
+ pass
58
+ return QCoreApplication.translate("CheatSheet", "Window")
59
+
60
+
61
+ def _describe_action(act: QAction) -> str:
62
+ """Get a human-readable description for an action."""
63
+ try:
64
+ desc = _clean_text(
65
+ act.statusTip() or act.toolTip() or act.text() or act.objectName() or ""
66
+ )
67
+ except Exception:
68
+ desc = ""
69
+
70
+ if not desc:
71
+ desc = QCoreApplication.translate("CheatSheet", "Action")
72
+ return desc
73
+
74
+
75
+ def _describe_shortcut(sc: QShortcut) -> str:
76
+ """Get a human-readable description for a shortcut."""
77
+ try:
78
+ hint = sc.property("hint")
79
+ desc = _clean_text(hint or sc.whatsThis() or sc.objectName() or "")
80
+ except Exception:
81
+ desc = ""
82
+
83
+ if not desc:
84
+ desc = QCoreApplication.translate("CheatSheet", "Shortcut")
85
+ return desc
86
+
87
+ def add_extra_shortcuts(rows):
88
+ """
89
+ Add app-level shortcuts that aren't represented by QActions/QShortcuts.
90
+ rows: list of (shortcut_str, action_str, where_str)
91
+ """
92
+ rows.append((
93
+ _clean_text("Ctrl+K"),
94
+ _clean_text("Toggle Show/Hide Mask"),
95
+ QCoreApplication.translate("CheatSheet", "Window"),
96
+ ))
97
+ rows.append((
98
+ _clean_text("Ctrl+Alt+M"),
99
+ _clean_text("SASpro Neon Invaders (Easter Egg)"),
100
+ QCoreApplication.translate("CheatSheet", "Window"),
101
+ ))
102
+
103
+ def _where_for_shortcut(sc: QShortcut) -> str:
104
+ """Determine where a shortcut is available."""
105
+ try:
106
+ par = sc.parent()
107
+ if par is not None:
108
+ return par.__class__.__name__
109
+ except Exception:
110
+ pass
111
+ return QCoreApplication.translate("CheatSheet", "Window")
112
+
113
+
114
+ class CheatSheetDialog(QDialog):
115
+ """
116
+ Dialog showing all keyboard shortcuts and mouse gestures.
117
+
118
+ Displays two tabs:
119
+ - Keyboard shortcuts (from QActions)
120
+ - Mouse/drag gestures
121
+ """
122
+
123
+ def __init__(self, parent, keyboard_rows, gesture_rows):
124
+ super().__init__(parent)
125
+ self.setWindowTitle(self.tr("Keyboard Shortcut Cheat Sheet"))
126
+ self.setWindowFlag(Qt.WindowType.Window, True)
127
+ self.setWindowModality(Qt.WindowModality.NonModal)
128
+ self.setModal(False)
129
+ self.resize(780, 520)
130
+
131
+ self._keyboard_rows = keyboard_rows
132
+ self._gesture_rows = gesture_rows
133
+
134
+ tabs = QTabWidget(self)
135
+
136
+ # --- Keyboard tab ---
137
+ pg_keys = QWidget(tabs)
138
+ v1 = QVBoxLayout(pg_keys)
139
+ tbl_keys = QTableWidget(0, 3, pg_keys)
140
+ tbl_keys.setHorizontalHeaderLabels([self.tr("Shortcut"), self.tr("Action"), self.tr("Where")])
141
+ tbl_keys.verticalHeader().setVisible(False)
142
+ tbl_keys.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
143
+ tbl_keys.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
144
+ tbl_keys.setSelectionMode(QTableWidget.SelectionMode.SingleSelection)
145
+ tbl_keys.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
146
+ tbl_keys.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
147
+ tbl_keys.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)
148
+ v1.addWidget(tbl_keys)
149
+
150
+ # Populate keyboard shortcuts
151
+ for s, action, where in keyboard_rows:
152
+ r = tbl_keys.rowCount()
153
+ tbl_keys.insertRow(r)
154
+ tbl_keys.setItem(r, 0, QTableWidgetItem(s))
155
+ tbl_keys.setItem(r, 1, QTableWidgetItem(action))
156
+ tbl_keys.setItem(r, 2, QTableWidgetItem(where))
157
+
158
+ # --- Mouse/Drag tab ---
159
+ pg_mouse = QWidget(tabs)
160
+ v2 = QVBoxLayout(pg_mouse)
161
+ tbl_mouse = QTableWidget(0, 3, pg_mouse)
162
+ tbl_mouse.setHorizontalHeaderLabels([self.tr("Gesture"), self.tr("Context"), self.tr("Effect")])
163
+ tbl_mouse.verticalHeader().setVisible(False)
164
+ tbl_mouse.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
165
+ tbl_mouse.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
166
+ tbl_mouse.setSelectionMode(QTableWidget.SelectionMode.SingleSelection)
167
+ tbl_mouse.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
168
+ tbl_mouse.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents)
169
+ tbl_mouse.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch)
170
+ v2.addWidget(tbl_mouse)
171
+
172
+ # Populate mouse gestures
173
+ for gesture, context, effect in gesture_rows:
174
+ r = tbl_mouse.rowCount()
175
+ tbl_mouse.insertRow(r)
176
+ tbl_mouse.setItem(r, 0, QTableWidgetItem(gesture))
177
+ tbl_mouse.setItem(r, 1, QTableWidgetItem(context))
178
+ tbl_mouse.setItem(r, 2, QTableWidgetItem(effect))
179
+
180
+ tabs.addTab(pg_keys, self.tr("Base Keyboard"))
181
+ tabs.addTab(pg_mouse, self.tr("Additional & Mouse & Drag"))
182
+
183
+ # Buttons
184
+ btns = QHBoxLayout()
185
+ btns.addStretch(1)
186
+
187
+ b_copy = QPushButton(self.tr("Copy"))
188
+ b_copy.clicked.connect(self._copy_all)
189
+ b_close = QPushButton(self.tr("Close"))
190
+ b_close.clicked.connect(self.accept)
191
+ btns.addWidget(b_copy)
192
+ btns.addWidget(b_close)
193
+
194
+ top = QVBoxLayout(self)
195
+ top.addWidget(tabs)
196
+ top.addLayout(btns)
197
+
198
+ def _copy_all(self):
199
+ """Copy all shortcuts to clipboard as plain text."""
200
+ lines = []
201
+ lines.append("== Keyboard ==")
202
+ for s, a, w in self._keyboard_rows:
203
+ lines.append(f"{s:20} {a} [{w}]")
204
+ lines.append("")
205
+ lines.append("== Mouse & Drag ==")
206
+ for g, c, e in self._gesture_rows:
207
+ lines.append(f"{g:24} {c:18} {e}")
208
+ QApplication.clipboard().setText("\n".join(lines))
209
+ QMessageBox.information(self, self.tr("Copied"), self.tr("Cheat sheet copied to clipboard."))
210
+
211
+
212
+ # Legacy alias for backward compatibility
213
+ _CheatSheetDialog = CheatSheetDialog