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.
- setiastro/__init__.py +2 -0
- setiastro/data/SASP_data.fits +0 -0
- setiastro/data/catalogs/List_of_Galaxies_with_Distances_Gly.csv +488 -0
- setiastro/data/catalogs/astrobin_filters.csv +890 -0
- setiastro/data/catalogs/astrobin_filters_page1_local.csv +51 -0
- setiastro/data/catalogs/cali2.csv +63 -0
- setiastro/data/catalogs/cali2color.csv +65 -0
- setiastro/data/catalogs/celestial_catalog - original.csv +16471 -0
- setiastro/data/catalogs/celestial_catalog.csv +24031 -0
- setiastro/data/catalogs/detected_stars.csv +24784 -0
- setiastro/data/catalogs/fits_header_data.csv +46 -0
- setiastro/data/catalogs/test.csv +8 -0
- setiastro/data/catalogs/updated_celestial_catalog.csv +16471 -0
- setiastro/images/Astro_Spikes.png +0 -0
- setiastro/images/Background_startup.jpg +0 -0
- setiastro/images/HRDiagram.png +0 -0
- setiastro/images/LExtract.png +0 -0
- setiastro/images/LInsert.png +0 -0
- setiastro/images/Oxygenation-atm-2.svg.png +0 -0
- setiastro/images/RGB080604.png +0 -0
- setiastro/images/abeicon.png +0 -0
- setiastro/images/aberration.png +0 -0
- setiastro/images/andromedatry.png +0 -0
- setiastro/images/andromedatry_satellited.png +0 -0
- setiastro/images/annotated.png +0 -0
- setiastro/images/aperture.png +0 -0
- setiastro/images/astrosuite.ico +0 -0
- setiastro/images/astrosuite.png +0 -0
- setiastro/images/astrosuitepro.icns +0 -0
- setiastro/images/astrosuitepro.ico +0 -0
- setiastro/images/astrosuitepro.png +0 -0
- setiastro/images/background.png +0 -0
- setiastro/images/background2.png +0 -0
- setiastro/images/benchmark.png +0 -0
- setiastro/images/big_moon_stabilizer_timeline.png +0 -0
- setiastro/images/big_moon_stabilizer_timeline_clean.png +0 -0
- setiastro/images/blaster.png +0 -0
- setiastro/images/blink.png +0 -0
- setiastro/images/clahe.png +0 -0
- setiastro/images/collage.png +0 -0
- setiastro/images/colorwheel.png +0 -0
- setiastro/images/contsub.png +0 -0
- setiastro/images/convo.png +0 -0
- setiastro/images/copyslot.png +0 -0
- setiastro/images/cosmic.png +0 -0
- setiastro/images/cosmicsat.png +0 -0
- setiastro/images/crop1.png +0 -0
- setiastro/images/cropicon.png +0 -0
- setiastro/images/curves.png +0 -0
- setiastro/images/cvs.png +0 -0
- setiastro/images/debayer.png +0 -0
- setiastro/images/denoise_cnn_custom.png +0 -0
- setiastro/images/denoise_cnn_graph.png +0 -0
- setiastro/images/disk.png +0 -0
- setiastro/images/dse.png +0 -0
- setiastro/images/exoicon.png +0 -0
- setiastro/images/eye.png +0 -0
- setiastro/images/fliphorizontal.png +0 -0
- setiastro/images/flipvertical.png +0 -0
- setiastro/images/font.png +0 -0
- setiastro/images/freqsep.png +0 -0
- setiastro/images/functionbundle.png +0 -0
- setiastro/images/graxpert.png +0 -0
- setiastro/images/green.png +0 -0
- setiastro/images/gridicon.png +0 -0
- setiastro/images/halo.png +0 -0
- setiastro/images/hdr.png +0 -0
- setiastro/images/histogram.png +0 -0
- setiastro/images/hubble.png +0 -0
- setiastro/images/imagecombine.png +0 -0
- setiastro/images/invert.png +0 -0
- setiastro/images/isophote.png +0 -0
- setiastro/images/isophote_demo_figure.png +0 -0
- setiastro/images/isophote_demo_image.png +0 -0
- setiastro/images/isophote_demo_model.png +0 -0
- setiastro/images/isophote_demo_residual.png +0 -0
- setiastro/images/jwstpupil.png +0 -0
- setiastro/images/linearfit.png +0 -0
- setiastro/images/livestacking.png +0 -0
- setiastro/images/mask.png +0 -0
- setiastro/images/maskapply.png +0 -0
- setiastro/images/maskcreate.png +0 -0
- setiastro/images/maskremove.png +0 -0
- setiastro/images/morpho.png +0 -0
- setiastro/images/mosaic.png +0 -0
- setiastro/images/multiscale_decomp.png +0 -0
- setiastro/images/nbtorgb.png +0 -0
- setiastro/images/neutral.png +0 -0
- setiastro/images/nuke.png +0 -0
- setiastro/images/openfile.png +0 -0
- setiastro/images/pedestal.png +0 -0
- setiastro/images/pen.png +0 -0
- setiastro/images/pixelmath.png +0 -0
- setiastro/images/platesolve.png +0 -0
- setiastro/images/ppp.png +0 -0
- setiastro/images/pro.png +0 -0
- setiastro/images/project.png +0 -0
- setiastro/images/psf.png +0 -0
- setiastro/images/redo.png +0 -0
- setiastro/images/redoicon.png +0 -0
- setiastro/images/rescale.png +0 -0
- setiastro/images/rgbalign.png +0 -0
- setiastro/images/rgbcombo.png +0 -0
- setiastro/images/rgbextract.png +0 -0
- setiastro/images/rotate180.png +0 -0
- setiastro/images/rotateclockwise.png +0 -0
- setiastro/images/rotatecounterclockwise.png +0 -0
- setiastro/images/satellite.png +0 -0
- setiastro/images/script.png +0 -0
- setiastro/images/selectivecolor.png +0 -0
- setiastro/images/simbad.png +0 -0
- setiastro/images/slot0.png +0 -0
- setiastro/images/slot1.png +0 -0
- setiastro/images/slot2.png +0 -0
- setiastro/images/slot3.png +0 -0
- setiastro/images/slot4.png +0 -0
- setiastro/images/slot5.png +0 -0
- setiastro/images/slot6.png +0 -0
- setiastro/images/slot7.png +0 -0
- setiastro/images/slot8.png +0 -0
- setiastro/images/slot9.png +0 -0
- setiastro/images/spcc.png +0 -0
- setiastro/images/spin_precession_vs_lunar_distance.png +0 -0
- setiastro/images/spinner.gif +0 -0
- setiastro/images/stacking.png +0 -0
- setiastro/images/staradd.png +0 -0
- setiastro/images/staralign.png +0 -0
- setiastro/images/starnet.png +0 -0
- setiastro/images/starregistration.png +0 -0
- setiastro/images/starspike.png +0 -0
- setiastro/images/starstretch.png +0 -0
- setiastro/images/statstretch.png +0 -0
- setiastro/images/supernova.png +0 -0
- setiastro/images/uhs.png +0 -0
- setiastro/images/undoicon.png +0 -0
- setiastro/images/upscale.png +0 -0
- setiastro/images/viewbundle.png +0 -0
- setiastro/images/whitebalance.png +0 -0
- setiastro/images/wimi_icon_256x256.png +0 -0
- setiastro/images/wimilogo.png +0 -0
- setiastro/images/wims.png +0 -0
- setiastro/images/wrench_icon.png +0 -0
- setiastro/images/xisfliberator.png +0 -0
- setiastro/qml/ResourceMonitor.qml +126 -0
- setiastro/saspro/__init__.py +20 -0
- setiastro/saspro/__main__.py +945 -0
- setiastro/saspro/_generated/__init__.py +7 -0
- setiastro/saspro/_generated/build_info.py +3 -0
- setiastro/saspro/abe.py +1346 -0
- setiastro/saspro/abe_preset.py +196 -0
- setiastro/saspro/aberration_ai.py +694 -0
- setiastro/saspro/aberration_ai_preset.py +224 -0
- setiastro/saspro/accel_installer.py +218 -0
- setiastro/saspro/accel_workers.py +30 -0
- setiastro/saspro/add_stars.py +624 -0
- setiastro/saspro/astrobin_exporter.py +1010 -0
- setiastro/saspro/astrospike.py +153 -0
- setiastro/saspro/astrospike_python.py +1841 -0
- setiastro/saspro/autostretch.py +198 -0
- setiastro/saspro/backgroundneutral.py +602 -0
- setiastro/saspro/batch_convert.py +328 -0
- setiastro/saspro/batch_renamer.py +522 -0
- setiastro/saspro/blemish_blaster.py +491 -0
- setiastro/saspro/blink_comparator_pro.py +2926 -0
- setiastro/saspro/bundles.py +61 -0
- setiastro/saspro/bundles_dock.py +114 -0
- setiastro/saspro/cheat_sheet.py +213 -0
- setiastro/saspro/clahe.py +368 -0
- setiastro/saspro/comet_stacking.py +1442 -0
- setiastro/saspro/common_tr.py +107 -0
- setiastro/saspro/config.py +38 -0
- setiastro/saspro/config_bootstrap.py +40 -0
- setiastro/saspro/config_manager.py +316 -0
- setiastro/saspro/continuum_subtract.py +1617 -0
- setiastro/saspro/convo.py +1400 -0
- setiastro/saspro/convo_preset.py +414 -0
- setiastro/saspro/copyastro.py +190 -0
- setiastro/saspro/cosmicclarity.py +1589 -0
- setiastro/saspro/cosmicclarity_preset.py +407 -0
- setiastro/saspro/crop_dialog_pro.py +973 -0
- setiastro/saspro/crop_preset.py +189 -0
- setiastro/saspro/curve_editor_pro.py +2562 -0
- setiastro/saspro/curves_preset.py +375 -0
- setiastro/saspro/debayer.py +673 -0
- setiastro/saspro/debug_utils.py +29 -0
- setiastro/saspro/dnd_mime.py +35 -0
- setiastro/saspro/doc_manager.py +2664 -0
- setiastro/saspro/exoplanet_detector.py +2166 -0
- setiastro/saspro/file_utils.py +284 -0
- setiastro/saspro/fitsmodifier.py +748 -0
- setiastro/saspro/fix_bom.py +32 -0
- setiastro/saspro/free_torch_memory.py +48 -0
- setiastro/saspro/frequency_separation.py +1349 -0
- setiastro/saspro/function_bundle.py +1596 -0
- setiastro/saspro/generate_translations.py +3092 -0
- setiastro/saspro/ghs_dialog_pro.py +663 -0
- setiastro/saspro/ghs_preset.py +284 -0
- setiastro/saspro/graxpert.py +637 -0
- setiastro/saspro/graxpert_preset.py +287 -0
- setiastro/saspro/gui/__init__.py +0 -0
- setiastro/saspro/gui/main_window.py +8810 -0
- setiastro/saspro/gui/mixins/__init__.py +33 -0
- setiastro/saspro/gui/mixins/dock_mixin.py +362 -0
- setiastro/saspro/gui/mixins/file_mixin.py +450 -0
- setiastro/saspro/gui/mixins/geometry_mixin.py +403 -0
- setiastro/saspro/gui/mixins/header_mixin.py +441 -0
- setiastro/saspro/gui/mixins/mask_mixin.py +421 -0
- setiastro/saspro/gui/mixins/menu_mixin.py +389 -0
- setiastro/saspro/gui/mixins/theme_mixin.py +367 -0
- setiastro/saspro/gui/mixins/toolbar_mixin.py +1457 -0
- setiastro/saspro/gui/mixins/update_mixin.py +309 -0
- setiastro/saspro/gui/mixins/view_mixin.py +435 -0
- setiastro/saspro/gui/statistics_dialog.py +47 -0
- setiastro/saspro/halobgon.py +488 -0
- setiastro/saspro/header_viewer.py +448 -0
- setiastro/saspro/headless_utils.py +88 -0
- setiastro/saspro/histogram.py +756 -0
- setiastro/saspro/history_explorer.py +941 -0
- setiastro/saspro/i18n.py +168 -0
- setiastro/saspro/image_combine.py +417 -0
- setiastro/saspro/image_peeker_pro.py +1604 -0
- setiastro/saspro/imageops/__init__.py +37 -0
- setiastro/saspro/imageops/mdi_snap.py +292 -0
- setiastro/saspro/imageops/scnr.py +36 -0
- setiastro/saspro/imageops/starbasedwhitebalance.py +210 -0
- setiastro/saspro/imageops/stretch.py +236 -0
- setiastro/saspro/isophote.py +1182 -0
- setiastro/saspro/layers.py +208 -0
- setiastro/saspro/layers_dock.py +714 -0
- setiastro/saspro/lazy_imports.py +193 -0
- setiastro/saspro/legacy/__init__.py +2 -0
- setiastro/saspro/legacy/image_manager.py +2226 -0
- setiastro/saspro/legacy/numba_utils.py +3676 -0
- setiastro/saspro/legacy/xisf.py +1071 -0
- setiastro/saspro/linear_fit.py +537 -0
- setiastro/saspro/live_stacking.py +1841 -0
- setiastro/saspro/log_bus.py +5 -0
- setiastro/saspro/logging_config.py +460 -0
- setiastro/saspro/luminancerecombine.py +309 -0
- setiastro/saspro/main_helpers.py +201 -0
- setiastro/saspro/mask_creation.py +931 -0
- setiastro/saspro/masks_core.py +56 -0
- setiastro/saspro/mdi_widgets.py +353 -0
- setiastro/saspro/memory_utils.py +666 -0
- setiastro/saspro/metadata_patcher.py +75 -0
- setiastro/saspro/mfdeconv.py +3831 -0
- setiastro/saspro/mfdeconv_earlystop.py +71 -0
- setiastro/saspro/mfdeconvcudnn.py +3263 -0
- setiastro/saspro/mfdeconvsport.py +2382 -0
- setiastro/saspro/minorbodycatalog.py +567 -0
- setiastro/saspro/morphology.py +407 -0
- setiastro/saspro/multiscale_decomp.py +1293 -0
- setiastro/saspro/nbtorgb_stars.py +541 -0
- setiastro/saspro/numba_utils.py +3145 -0
- setiastro/saspro/numba_warmup.py +141 -0
- setiastro/saspro/ops/__init__.py +9 -0
- setiastro/saspro/ops/command_help_dialog.py +623 -0
- setiastro/saspro/ops/command_runner.py +217 -0
- setiastro/saspro/ops/commands.py +1594 -0
- setiastro/saspro/ops/script_editor.py +1102 -0
- setiastro/saspro/ops/scripts.py +1473 -0
- setiastro/saspro/ops/settings.py +637 -0
- setiastro/saspro/parallel_utils.py +554 -0
- setiastro/saspro/pedestal.py +121 -0
- setiastro/saspro/perfect_palette_picker.py +1071 -0
- setiastro/saspro/pipeline.py +110 -0
- setiastro/saspro/pixelmath.py +1604 -0
- setiastro/saspro/plate_solver.py +2445 -0
- setiastro/saspro/project_io.py +797 -0
- setiastro/saspro/psf_utils.py +136 -0
- setiastro/saspro/psf_viewer.py +549 -0
- setiastro/saspro/pyi_rthook_astroquery.py +95 -0
- setiastro/saspro/remove_green.py +331 -0
- setiastro/saspro/remove_stars.py +1599 -0
- setiastro/saspro/remove_stars_preset.py +404 -0
- setiastro/saspro/resources.py +501 -0
- setiastro/saspro/rgb_combination.py +208 -0
- setiastro/saspro/rgb_extract.py +19 -0
- setiastro/saspro/rgbalign.py +723 -0
- setiastro/saspro/runtime_imports.py +7 -0
- setiastro/saspro/runtime_torch.py +754 -0
- setiastro/saspro/save_options.py +73 -0
- setiastro/saspro/selective_color.py +1552 -0
- setiastro/saspro/sfcc.py +1472 -0
- setiastro/saspro/shortcuts.py +3043 -0
- setiastro/saspro/signature_insert.py +1102 -0
- setiastro/saspro/stacking_suite.py +18470 -0
- setiastro/saspro/star_alignment.py +7435 -0
- setiastro/saspro/star_alignment_preset.py +329 -0
- setiastro/saspro/star_metrics.py +49 -0
- setiastro/saspro/star_spikes.py +765 -0
- setiastro/saspro/star_stretch.py +507 -0
- setiastro/saspro/stat_stretch.py +538 -0
- setiastro/saspro/status_log_dock.py +78 -0
- setiastro/saspro/subwindow.py +3328 -0
- setiastro/saspro/supernovaasteroidhunter.py +1719 -0
- setiastro/saspro/swap_manager.py +99 -0
- setiastro/saspro/torch_backend.py +89 -0
- setiastro/saspro/torch_rejection.py +434 -0
- setiastro/saspro/translations/all_source_strings.json +3654 -0
- setiastro/saspro/translations/ar_translations.py +3865 -0
- setiastro/saspro/translations/de_translations.py +3749 -0
- setiastro/saspro/translations/es_translations.py +3939 -0
- setiastro/saspro/translations/fr_translations.py +3858 -0
- setiastro/saspro/translations/hi_translations.py +3571 -0
- setiastro/saspro/translations/integrate_translations.py +270 -0
- setiastro/saspro/translations/it_translations.py +3678 -0
- setiastro/saspro/translations/ja_translations.py +3601 -0
- setiastro/saspro/translations/pt_translations.py +3869 -0
- setiastro/saspro/translations/ru_translations.py +2848 -0
- setiastro/saspro/translations/saspro_ar.qm +0 -0
- setiastro/saspro/translations/saspro_ar.ts +255 -0
- setiastro/saspro/translations/saspro_de.qm +0 -0
- setiastro/saspro/translations/saspro_de.ts +253 -0
- setiastro/saspro/translations/saspro_es.qm +0 -0
- setiastro/saspro/translations/saspro_es.ts +12520 -0
- setiastro/saspro/translations/saspro_fr.qm +0 -0
- setiastro/saspro/translations/saspro_fr.ts +12514 -0
- setiastro/saspro/translations/saspro_hi.qm +0 -0
- setiastro/saspro/translations/saspro_hi.ts +257 -0
- setiastro/saspro/translations/saspro_it.qm +0 -0
- setiastro/saspro/translations/saspro_it.ts +12520 -0
- setiastro/saspro/translations/saspro_ja.qm +0 -0
- setiastro/saspro/translations/saspro_ja.ts +257 -0
- setiastro/saspro/translations/saspro_pt.qm +0 -0
- setiastro/saspro/translations/saspro_pt.ts +257 -0
- setiastro/saspro/translations/saspro_ru.qm +0 -0
- setiastro/saspro/translations/saspro_ru.ts +237 -0
- setiastro/saspro/translations/saspro_sw.qm +0 -0
- setiastro/saspro/translations/saspro_sw.ts +257 -0
- setiastro/saspro/translations/saspro_uk.qm +0 -0
- setiastro/saspro/translations/saspro_uk.ts +10771 -0
- setiastro/saspro/translations/saspro_zh.qm +0 -0
- setiastro/saspro/translations/saspro_zh.ts +12520 -0
- setiastro/saspro/translations/sw_translations.py +3671 -0
- setiastro/saspro/translations/uk_translations.py +3700 -0
- setiastro/saspro/translations/zh_translations.py +3675 -0
- setiastro/saspro/versioning.py +77 -0
- setiastro/saspro/view_bundle.py +1558 -0
- setiastro/saspro/wavescale_hdr.py +645 -0
- setiastro/saspro/wavescale_hdr_preset.py +101 -0
- setiastro/saspro/wavescalede.py +680 -0
- setiastro/saspro/wavescalede_preset.py +230 -0
- setiastro/saspro/wcs_update.py +374 -0
- setiastro/saspro/whitebalance.py +492 -0
- setiastro/saspro/widgets/__init__.py +48 -0
- setiastro/saspro/widgets/common_utilities.py +306 -0
- setiastro/saspro/widgets/graphics_views.py +122 -0
- setiastro/saspro/widgets/image_utils.py +518 -0
- setiastro/saspro/widgets/minigame/game.js +986 -0
- setiastro/saspro/widgets/minigame/index.html +53 -0
- setiastro/saspro/widgets/minigame/style.css +241 -0
- setiastro/saspro/widgets/preview_dialogs.py +280 -0
- setiastro/saspro/widgets/resource_monitor.py +237 -0
- setiastro/saspro/widgets/spinboxes.py +275 -0
- setiastro/saspro/widgets/themed_buttons.py +13 -0
- setiastro/saspro/widgets/wavelet_utils.py +331 -0
- setiastro/saspro/wimi.py +7996 -0
- setiastro/saspro/wims.py +578 -0
- setiastro/saspro/window_shelf.py +185 -0
- setiastro/saspro/xisf.py +1123 -0
- setiastrosuitepro-1.6.2.post1.dist-info/METADATA +278 -0
- setiastrosuitepro-1.6.2.post1.dist-info/RECORD +367 -0
- setiastrosuitepro-1.6.2.post1.dist-info/WHEEL +4 -0
- setiastrosuitepro-1.6.2.post1.dist-info/entry_points.txt +6 -0
- setiastrosuitepro-1.6.2.post1.dist-info/licenses/LICENSE +674 -0
- setiastrosuitepro-1.6.2.post1.dist-info/licenses/license.txt +2580 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Common UI utilities and shared components.
|
|
3
|
+
|
|
4
|
+
This module provides centralized implementations of commonly used
|
|
5
|
+
UI components and utilities to avoid code duplication across the codebase.
|
|
6
|
+
|
|
7
|
+
Included:
|
|
8
|
+
- AboutDialog: Standard about dialog
|
|
9
|
+
- ProjectSaveWorker: Background thread for saving projects
|
|
10
|
+
- install_crash_handlers: Global exception/crash handling setup
|
|
11
|
+
- UI decoration helpers: DECOR_GLYPHS, strip_ui_decorations
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import sys
|
|
17
|
+
import os
|
|
18
|
+
import threading
|
|
19
|
+
import traceback
|
|
20
|
+
import logging
|
|
21
|
+
import atexit
|
|
22
|
+
from typing import TYPE_CHECKING, Optional, List, Any
|
|
23
|
+
|
|
24
|
+
from PyQt6.QtCore import QThread, pyqtSignal, QTimer, Qt, QCoreApplication
|
|
25
|
+
from PyQt6.QtWidgets import QDialog, QVBoxLayout, QLabel, QMessageBox
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from PyQt6.QtWidgets import QApplication, QMdiArea
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
# Version and Build Info (should be imported from main module)
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
def get_version() -> str:
|
|
36
|
+
"""Get application version from main module."""
|
|
37
|
+
try:
|
|
38
|
+
from setiastrosuitepro import VERSION
|
|
39
|
+
return VERSION
|
|
40
|
+
except ImportError:
|
|
41
|
+
return "Unknown"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_build_timestamp() -> str:
|
|
45
|
+
"""Get human-friendly build timestamp from main module."""
|
|
46
|
+
try:
|
|
47
|
+
from setiastrosuitepro import BUILD_TIMESTAMP
|
|
48
|
+
except ImportError:
|
|
49
|
+
return "Unknown"
|
|
50
|
+
|
|
51
|
+
if BUILD_TIMESTAMP == "dev":
|
|
52
|
+
# No generated build_info → running from local source checkout
|
|
53
|
+
return "Running locally from source code"
|
|
54
|
+
return BUILD_TIMESTAMP
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
# About Dialog
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
class AboutDialog(QDialog):
|
|
62
|
+
"""
|
|
63
|
+
Standard About dialog for Seti Astro Suite.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(self, parent: Optional[Any] = None, version: str = "", build_timestamp: str = ""):
|
|
67
|
+
super().__init__(parent)
|
|
68
|
+
self.setWindowTitle(self.tr("About Seti Astro Suite"))
|
|
69
|
+
|
|
70
|
+
# Get version info if not provided
|
|
71
|
+
if not version:
|
|
72
|
+
version = get_version()
|
|
73
|
+
|
|
74
|
+
# Normalize build_timestamp
|
|
75
|
+
if not build_timestamp:
|
|
76
|
+
build_timestamp = get_build_timestamp()
|
|
77
|
+
else:
|
|
78
|
+
# If someone passed the raw sentinel from the main module
|
|
79
|
+
if build_timestamp == "dev":
|
|
80
|
+
build_timestamp = "Running locally from source code"
|
|
81
|
+
|
|
82
|
+
layout = QVBoxLayout()
|
|
83
|
+
|
|
84
|
+
# Build about text with optional build timestamp
|
|
85
|
+
about_lines = [
|
|
86
|
+
f"<h2>Seti Astro's Suite Pro {version}</h2>",
|
|
87
|
+
f"<p>{self.tr('Written by Franklin Marek')}</p>",
|
|
88
|
+
f"<p>{self.tr('Collaborators: Fabio Tempera')}</p>",
|
|
89
|
+
f"<p>{self.tr('Copyright © 2025 Seti Astro')}</p>",
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
if build_timestamp and build_timestamp != "Unknown":
|
|
93
|
+
about_lines.append(f"<p><b>{self.tr('Build:')}</b> {build_timestamp}</p>")
|
|
94
|
+
|
|
95
|
+
about_lines.extend([
|
|
96
|
+
f"<p>{self.tr('Website:')} <a href='http://www.setiastro.com'>www.setiastro.com</a></p>",
|
|
97
|
+
f"<p>{self.tr('Donations:')} <a href='https://www.setiastro.com/checkout/donate?donatePageId=65ae7e7bac20370d8c04c1ab'>{self.tr('Click here to donate')}</a></p>",
|
|
98
|
+
])
|
|
99
|
+
|
|
100
|
+
about_text = "".join(about_lines)
|
|
101
|
+
|
|
102
|
+
label = QLabel(about_text)
|
|
103
|
+
label.setTextFormat(Qt.TextFormat.RichText)
|
|
104
|
+
label.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
|
|
105
|
+
label.setOpenExternalLinks(True)
|
|
106
|
+
|
|
107
|
+
layout.addWidget(label)
|
|
108
|
+
self.setLayout(layout)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# ---------------------------------------------------------------------------
|
|
113
|
+
# Project Save Worker
|
|
114
|
+
# ---------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
class ProjectSaveWorker(QThread):
|
|
117
|
+
"""
|
|
118
|
+
Background thread for saving projects.
|
|
119
|
+
|
|
120
|
+
Emits 'ok' signal on success, 'error' signal with message on failure.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
path: Path to save the project to
|
|
124
|
+
docs: List of documents to save
|
|
125
|
+
shortcuts: Shortcuts configuration
|
|
126
|
+
mdi: MDI area reference
|
|
127
|
+
compress: Whether to compress the project
|
|
128
|
+
window_shelf: Optional window shelf reference
|
|
129
|
+
parent: Parent QObject
|
|
130
|
+
|
|
131
|
+
Example:
|
|
132
|
+
worker = ProjectSaveWorker(path, docs, shortcuts, mdi, compress=True)
|
|
133
|
+
worker.ok.connect(on_save_complete)
|
|
134
|
+
worker.error.connect(on_save_error)
|
|
135
|
+
worker.start()
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
ok = pyqtSignal()
|
|
139
|
+
error = pyqtSignal(str)
|
|
140
|
+
|
|
141
|
+
def __init__(
|
|
142
|
+
self,
|
|
143
|
+
path: str,
|
|
144
|
+
docs: List[Any],
|
|
145
|
+
shortcuts: Any,
|
|
146
|
+
mdi: 'QMdiArea',
|
|
147
|
+
compress: bool,
|
|
148
|
+
window_shelf: Optional[Any] = None,
|
|
149
|
+
parent: Optional[Any] = None
|
|
150
|
+
):
|
|
151
|
+
super().__init__(parent)
|
|
152
|
+
self.path = path
|
|
153
|
+
self.docs = docs
|
|
154
|
+
self.shortcuts = shortcuts
|
|
155
|
+
self.mdi = mdi
|
|
156
|
+
self.compress = compress
|
|
157
|
+
self.window_shelf = window_shelf
|
|
158
|
+
|
|
159
|
+
def run(self) -> None:
|
|
160
|
+
"""Execute the save operation in background thread."""
|
|
161
|
+
try:
|
|
162
|
+
from setiastro.saspro.project_io import ProjectWriter
|
|
163
|
+
ProjectWriter.write(
|
|
164
|
+
self.path,
|
|
165
|
+
docs=self.docs,
|
|
166
|
+
shortcuts=self.shortcuts,
|
|
167
|
+
mdi=self.mdi,
|
|
168
|
+
compress=self.compress,
|
|
169
|
+
shelf=self.window_shelf,
|
|
170
|
+
)
|
|
171
|
+
self.ok.emit()
|
|
172
|
+
except Exception as e:
|
|
173
|
+
self.error.emit(str(e))
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# ---------------------------------------------------------------------------
|
|
177
|
+
# UI Decoration Helpers
|
|
178
|
+
# ---------------------------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
DECOR_GLYPHS = "■●◆▲▪▫•◼◻◾◽"
|
|
181
|
+
|
|
182
|
+
def strip_ui_decorations(s: str) -> str:
|
|
183
|
+
"""
|
|
184
|
+
Remove UI decoration glyphs and prefixes from a string.
|
|
185
|
+
|
|
186
|
+
Strips:
|
|
187
|
+
- Leading glyph characters followed by space
|
|
188
|
+
- "Active View: " prefix
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
s: String to clean
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Cleaned string without decorations
|
|
195
|
+
|
|
196
|
+
Example:
|
|
197
|
+
>>> strip_ui_decorations("● Active View: My Image.fits")
|
|
198
|
+
'My Image.fits'
|
|
199
|
+
"""
|
|
200
|
+
s = s or ""
|
|
201
|
+
|
|
202
|
+
# Strip any number of leading glyph+space
|
|
203
|
+
while len(s) >= 2 and s[1] == " " and s[0] in DECOR_GLYPHS:
|
|
204
|
+
s = s[2:]
|
|
205
|
+
|
|
206
|
+
# Strip leading Active prefix if present
|
|
207
|
+
ACTIVE = "Active View: "
|
|
208
|
+
if s.startswith(ACTIVE):
|
|
209
|
+
s = s[len(ACTIVE):]
|
|
210
|
+
|
|
211
|
+
return s
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# Alias for backward compatibility
|
|
215
|
+
_strip_ui_decorations = strip_ui_decorations
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
# ---------------------------------------------------------------------------
|
|
219
|
+
# Crash/Exception Handlers
|
|
220
|
+
# ---------------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
def install_crash_handlers(app: 'QApplication') -> None:
|
|
223
|
+
"""
|
|
224
|
+
Install global crash and exception handlers for the application.
|
|
225
|
+
|
|
226
|
+
Sets up:
|
|
227
|
+
1. faulthandler for hard crashes (segfaults) → saspro_crash.log
|
|
228
|
+
2. sys.excepthook for uncaught main thread exceptions
|
|
229
|
+
3. threading.excepthook for uncaught background thread exceptions
|
|
230
|
+
|
|
231
|
+
All exceptions are logged and displayed in a dialog to the user.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
app: The QApplication instance
|
|
235
|
+
|
|
236
|
+
Example:
|
|
237
|
+
app = QApplication(sys.argv)
|
|
238
|
+
install_crash_handlers(app)
|
|
239
|
+
"""
|
|
240
|
+
import faulthandler
|
|
241
|
+
|
|
242
|
+
# 1) Hard crashes (segfaults, access violations) → saspro_crash.log
|
|
243
|
+
try:
|
|
244
|
+
_crash_log = open("saspro_crash.log", "w", encoding="utf-8", errors="replace")
|
|
245
|
+
faulthandler.enable(file=_crash_log, all_threads=True)
|
|
246
|
+
atexit.register(_crash_log.close)
|
|
247
|
+
except Exception:
|
|
248
|
+
logging.exception("Failed to enable faulthandler")
|
|
249
|
+
|
|
250
|
+
def _show_dialog(title: str, head: str, details: str) -> None:
|
|
251
|
+
"""Show error dialog marshaled to main thread."""
|
|
252
|
+
def _ui():
|
|
253
|
+
m = QMessageBox(app.activeWindow())
|
|
254
|
+
m.setIcon(QMessageBox.Icon.Critical)
|
|
255
|
+
m.setWindowTitle(title)
|
|
256
|
+
m.setText(head)
|
|
257
|
+
from PyQt6.QtCore import QCoreApplication
|
|
258
|
+
m.setInformativeText(QCoreApplication.translate("CrashHandler", "Details are available below and in saspro.log."))
|
|
259
|
+
if details:
|
|
260
|
+
m.setDetailedText(details)
|
|
261
|
+
m.setStandardButtons(QMessageBox.StandardButton.Ok)
|
|
262
|
+
m.exec()
|
|
263
|
+
QTimer.singleShot(0, _ui)
|
|
264
|
+
|
|
265
|
+
# 2) Any uncaught exception on the main thread
|
|
266
|
+
def _excepthook(exc_type, exc_value, exc_tb):
|
|
267
|
+
tb = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
|
|
268
|
+
logging.error("Uncaught exception:\n%s", tb)
|
|
269
|
+
_show_dialog(
|
|
270
|
+
QCoreApplication.translate("CrashHandler", "Unhandled Exception"),
|
|
271
|
+
f"{exc_type.__name__}: {exc_value}",
|
|
272
|
+
tb
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
sys.excepthook = _excepthook
|
|
276
|
+
|
|
277
|
+
# 3) Any uncaught exception in background threads (Py3.8+)
|
|
278
|
+
def _threadhook(args: threading.ExceptHookArgs):
|
|
279
|
+
tb = "".join(traceback.format_exception(args.exc_type, args.exc_value, args.exc_traceback))
|
|
280
|
+
logging.error("Uncaught thread exception (%s):\n%s", args.thread.name, tb)
|
|
281
|
+
_show_dialog(
|
|
282
|
+
QCoreApplication.translate("CrashHandler", "Unhandled Thread Exception"),
|
|
283
|
+
f"{args.exc_type.__name__}: {args.exc_value}",
|
|
284
|
+
tb
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
try:
|
|
288
|
+
threading.excepthook = _threadhook # type: ignore[attr-defined]
|
|
289
|
+
except Exception:
|
|
290
|
+
pass
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# ---------------------------------------------------------------------------
|
|
294
|
+
# Module exports
|
|
295
|
+
# ---------------------------------------------------------------------------
|
|
296
|
+
|
|
297
|
+
__all__ = [
|
|
298
|
+
'AboutDialog',
|
|
299
|
+
'ProjectSaveWorker',
|
|
300
|
+
'DECOR_GLYPHS',
|
|
301
|
+
'strip_ui_decorations',
|
|
302
|
+
'_strip_ui_decorations', # Backward compatibility
|
|
303
|
+
'install_crash_handlers',
|
|
304
|
+
'get_version',
|
|
305
|
+
'get_build_timestamp',
|
|
306
|
+
]
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# pro/widgets/graphics_views.py
|
|
2
|
+
"""
|
|
3
|
+
Centralized graphics view widgets for Seti Astro Suite Pro.
|
|
4
|
+
|
|
5
|
+
Provides reusable zoomable and interactive QGraphicsView widgets.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from PyQt6.QtCore import Qt
|
|
10
|
+
from PyQt6.QtGui import QWheelEvent, QPainter
|
|
11
|
+
from PyQt6.QtWidgets import QGraphicsView
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ZoomableGraphicsView(QGraphicsView):
|
|
15
|
+
"""
|
|
16
|
+
A QGraphicsView with mouse wheel zoom (Ctrl+wheel) and drag support.
|
|
17
|
+
|
|
18
|
+
Features:
|
|
19
|
+
- Ctrl+wheel to zoom in/out
|
|
20
|
+
- Scroll hand drag mode
|
|
21
|
+
- Smooth pixmap transform
|
|
22
|
+
- Configurable zoom limits and step
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
view = ZoomableGraphicsView()
|
|
26
|
+
view.setScene(scene)
|
|
27
|
+
view.zoom_in()
|
|
28
|
+
view.zoom_out()
|
|
29
|
+
view.fit_to_item(pixmap_item)
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, scene=None, parent=None, *,
|
|
33
|
+
zoom_min: float = 0.05,
|
|
34
|
+
zoom_max: float = 12.0,
|
|
35
|
+
zoom_step: float = 1.25):
|
|
36
|
+
super().__init__(parent)
|
|
37
|
+
if scene is not None:
|
|
38
|
+
self.setScene(scene)
|
|
39
|
+
|
|
40
|
+
self._zoom = 1.0
|
|
41
|
+
self._zoom_min = zoom_min
|
|
42
|
+
self._zoom_max = zoom_max
|
|
43
|
+
self._zoom_step = zoom_step
|
|
44
|
+
|
|
45
|
+
# Default configuration
|
|
46
|
+
self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
|
|
47
|
+
self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
|
|
48
|
+
self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorViewCenter)
|
|
49
|
+
self.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform, True)
|
|
50
|
+
self.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
|
51
|
+
|
|
52
|
+
def wheelEvent(self, event: QWheelEvent):
|
|
53
|
+
"""Handle wheel events - zoom with Ctrl modifier."""
|
|
54
|
+
if event.modifiers() & Qt.KeyboardModifier.ControlModifier:
|
|
55
|
+
delta = event.angleDelta().y()
|
|
56
|
+
if delta == 0:
|
|
57
|
+
event.accept()
|
|
58
|
+
return
|
|
59
|
+
self._apply_zoom(
|
|
60
|
+
up=(delta > 0),
|
|
61
|
+
anchor=QGraphicsView.ViewportAnchor.AnchorUnderMouse
|
|
62
|
+
)
|
|
63
|
+
event.accept()
|
|
64
|
+
else:
|
|
65
|
+
super().wheelEvent(event)
|
|
66
|
+
|
|
67
|
+
def _apply_zoom(self, up: bool, anchor: QGraphicsView.ViewportAnchor | None = None):
|
|
68
|
+
"""Apply zoom in/out with optional anchor point."""
|
|
69
|
+
old_anchor = self.transformationAnchor()
|
|
70
|
+
if anchor is not None:
|
|
71
|
+
self.setTransformationAnchor(anchor)
|
|
72
|
+
|
|
73
|
+
step = self._zoom_step if up else (1.0 / self._zoom_step)
|
|
74
|
+
new_zoom = max(self._zoom_min, min(self._zoom_max, self._zoom * step))
|
|
75
|
+
factor = new_zoom / self._zoom
|
|
76
|
+
|
|
77
|
+
if factor != 1.0:
|
|
78
|
+
self.scale(factor, factor)
|
|
79
|
+
self._zoom = new_zoom
|
|
80
|
+
|
|
81
|
+
if anchor is not None:
|
|
82
|
+
self.setTransformationAnchor(old_anchor)
|
|
83
|
+
|
|
84
|
+
def zoom_in(self):
|
|
85
|
+
"""Zoom in centered on the view."""
|
|
86
|
+
self._apply_zoom(True, anchor=QGraphicsView.ViewportAnchor.AnchorViewCenter)
|
|
87
|
+
|
|
88
|
+
def zoom_out(self):
|
|
89
|
+
"""Zoom out centered on the view."""
|
|
90
|
+
self._apply_zoom(False, anchor=QGraphicsView.ViewportAnchor.AnchorViewCenter)
|
|
91
|
+
|
|
92
|
+
def fit_to_item(self, item):
|
|
93
|
+
"""Fit the view to show the entire item."""
|
|
94
|
+
if item is None:
|
|
95
|
+
return
|
|
96
|
+
# Handle both pixmap items and generic items
|
|
97
|
+
if hasattr(item, 'pixmap') and item.pixmap().isNull():
|
|
98
|
+
return
|
|
99
|
+
self._zoom = 1.0
|
|
100
|
+
self.resetTransform()
|
|
101
|
+
self.fitInView(item, Qt.AspectRatioMode.KeepAspectRatio)
|
|
102
|
+
|
|
103
|
+
# Alias for compatibility
|
|
104
|
+
fit_item = fit_to_item
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def zoom_level(self) -> float:
|
|
108
|
+
"""Get current zoom level."""
|
|
109
|
+
return self._zoom
|
|
110
|
+
|
|
111
|
+
def set_zoom(self, level: float):
|
|
112
|
+
"""Set zoom to a specific level."""
|
|
113
|
+
level = max(self._zoom_min, min(self._zoom_max, level))
|
|
114
|
+
factor = level / self._zoom
|
|
115
|
+
if factor != 1.0:
|
|
116
|
+
self.scale(factor, factor)
|
|
117
|
+
self._zoom = level
|
|
118
|
+
|
|
119
|
+
def reset_zoom(self):
|
|
120
|
+
"""Reset zoom to 1:1."""
|
|
121
|
+
self.resetTransform()
|
|
122
|
+
self._zoom = 1.0
|