setiastrosuitepro 1.6.2__py3-none-any.whl → 1.6.12__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/images/abeicon.svg +16 -0
- setiastro/images/acv_icon.png +0 -0
- setiastro/images/colorwheel.svg +97 -0
- setiastro/images/cosmic.svg +40 -0
- setiastro/images/cosmicsat.svg +24 -0
- setiastro/images/first_quarter.png +0 -0
- setiastro/images/full_moon.png +0 -0
- setiastro/images/graxpert.svg +19 -0
- setiastro/images/last_quarter.png +0 -0
- setiastro/images/linearfit.svg +32 -0
- setiastro/images/new_moon.png +0 -0
- setiastro/images/pixelmath.svg +42 -0
- setiastro/images/rotatearbitrary.png +0 -0
- setiastro/images/waning_crescent_1.png +0 -0
- setiastro/images/waning_crescent_2.png +0 -0
- setiastro/images/waning_crescent_3.png +0 -0
- setiastro/images/waning_crescent_4.png +0 -0
- setiastro/images/waning_crescent_5.png +0 -0
- setiastro/images/waning_gibbous_1.png +0 -0
- setiastro/images/waning_gibbous_2.png +0 -0
- setiastro/images/waning_gibbous_3.png +0 -0
- setiastro/images/waning_gibbous_4.png +0 -0
- setiastro/images/waning_gibbous_5.png +0 -0
- setiastro/images/waxing_crescent_1.png +0 -0
- setiastro/images/waxing_crescent_2.png +0 -0
- setiastro/images/waxing_crescent_3.png +0 -0
- setiastro/images/waxing_crescent_4.png +0 -0
- setiastro/images/waxing_crescent_5.png +0 -0
- setiastro/images/waxing_gibbous_1.png +0 -0
- setiastro/images/waxing_gibbous_2.png +0 -0
- setiastro/images/waxing_gibbous_3.png +0 -0
- setiastro/images/waxing_gibbous_4.png +0 -0
- setiastro/images/waxing_gibbous_5.png +0 -0
- setiastro/qml/ResourceMonitor.qml +84 -82
- setiastro/saspro/__main__.py +20 -1
- setiastro/saspro/_generated/build_info.py +2 -2
- setiastro/saspro/abe.py +37 -4
- setiastro/saspro/aberration_ai.py +237 -21
- setiastro/saspro/acv_exporter.py +379 -0
- setiastro/saspro/add_stars.py +33 -6
- setiastro/saspro/backgroundneutral.py +114 -37
- setiastro/saspro/blemish_blaster.py +4 -1
- setiastro/saspro/blink_comparator_pro.py +548 -275
- setiastro/saspro/clahe.py +4 -1
- setiastro/saspro/continuum_subtract.py +4 -1
- setiastro/saspro/convo.py +13 -7
- setiastro/saspro/cosmicclarity.py +129 -18
- setiastro/saspro/crop_dialog_pro.py +134 -8
- setiastro/saspro/curve_editor_pro.py +109 -42
- setiastro/saspro/doc_manager.py +246 -16
- setiastro/saspro/exoplanet_detector.py +120 -28
- setiastro/saspro/frequency_separation.py +1158 -204
- setiastro/saspro/function_bundle.py +16 -16
- setiastro/saspro/ghs_dialog_pro.py +81 -16
- setiastro/saspro/graxpert.py +1 -0
- setiastro/saspro/gui/main_window.py +519 -289
- setiastro/saspro/gui/mixins/dock_mixin.py +276 -42
- setiastro/saspro/gui/mixins/geometry_mixin.py +105 -5
- setiastro/saspro/gui/mixins/menu_mixin.py +28 -1
- setiastro/saspro/gui/mixins/theme_mixin.py +160 -14
- setiastro/saspro/gui/mixins/toolbar_mixin.py +416 -27
- setiastro/saspro/gui/mixins/update_mixin.py +138 -36
- setiastro/saspro/gui/mixins/view_mixin.py +42 -0
- setiastro/saspro/halobgon.py +4 -0
- setiastro/saspro/histogram.py +5 -1
- setiastro/saspro/image_combine.py +4 -0
- setiastro/saspro/image_peeker_pro.py +4 -0
- setiastro/saspro/imageops/starbasedwhitebalance.py +23 -52
- setiastro/saspro/imageops/stretch.py +582 -62
- setiastro/saspro/isophote.py +4 -0
- setiastro/saspro/layers.py +13 -9
- setiastro/saspro/layers_dock.py +183 -3
- setiastro/saspro/legacy/image_manager.py +154 -20
- setiastro/saspro/legacy/numba_utils.py +67 -47
- setiastro/saspro/legacy/xisf.py +240 -98
- setiastro/saspro/live_stacking.py +180 -79
- setiastro/saspro/luminancerecombine.py +228 -27
- setiastro/saspro/mask_creation.py +174 -15
- setiastro/saspro/mfdeconv.py +113 -35
- setiastro/saspro/mfdeconvcudnn.py +119 -70
- setiastro/saspro/mfdeconvsport.py +112 -35
- setiastro/saspro/morphology.py +4 -0
- setiastro/saspro/multiscale_decomp.py +748 -255
- setiastro/saspro/numba_utils.py +72 -57
- setiastro/saspro/ops/commands.py +18 -18
- setiastro/saspro/ops/script_editor.py +10 -2
- setiastro/saspro/ops/scripts.py +122 -0
- setiastro/saspro/perfect_palette_picker.py +37 -3
- setiastro/saspro/plate_solver.py +84 -49
- setiastro/saspro/psf_viewer.py +119 -37
- setiastro/saspro/remove_stars_preset.py +55 -13
- setiastro/saspro/resources.py +97 -11
- setiastro/saspro/rgbalign.py +4 -0
- setiastro/saspro/selective_color.py +83 -21
- setiastro/saspro/sfcc.py +364 -152
- setiastro/saspro/shortcuts.py +253 -49
- setiastro/saspro/signature_insert.py +692 -33
- setiastro/saspro/stacking_suite.py +1610 -574
- setiastro/saspro/star_alignment.py +522 -453
- setiastro/saspro/star_spikes.py +4 -0
- setiastro/saspro/star_stretch.py +38 -3
- setiastro/saspro/stat_stretch.py +743 -128
- setiastro/saspro/status_log_dock.py +1 -1
- setiastro/saspro/subwindow.py +786 -360
- setiastro/saspro/supernovaasteroidhunter.py +1 -1
- setiastro/saspro/swap_manager.py +77 -42
- setiastro/saspro/translations/all_source_strings.json +1588 -516
- setiastro/saspro/translations/ar_translations.py +915 -684
- setiastro/saspro/translations/de_translations.py +442 -463
- setiastro/saspro/translations/es_translations.py +277 -47
- setiastro/saspro/translations/fr_translations.py +279 -47
- setiastro/saspro/translations/hi_translations.py +253 -21
- setiastro/saspro/translations/integrate_translations.py +3 -2
- setiastro/saspro/translations/it_translations.py +1211 -161
- setiastro/saspro/translations/ja_translations.py +3340 -3107
- setiastro/saspro/translations/pt_translations.py +3315 -3337
- setiastro/saspro/translations/ru_translations.py +351 -117
- setiastro/saspro/translations/saspro_ar.qm +0 -0
- setiastro/saspro/translations/saspro_ar.ts +15902 -138
- setiastro/saspro/translations/saspro_de.qm +0 -0
- setiastro/saspro/translations/saspro_de.ts +14428 -133
- setiastro/saspro/translations/saspro_es.qm +0 -0
- setiastro/saspro/translations/saspro_es.ts +11503 -7821
- setiastro/saspro/translations/saspro_fr.qm +0 -0
- setiastro/saspro/translations/saspro_fr.ts +11168 -7812
- setiastro/saspro/translations/saspro_hi.qm +0 -0
- setiastro/saspro/translations/saspro_hi.ts +14733 -135
- setiastro/saspro/translations/saspro_it.qm +0 -0
- setiastro/saspro/translations/saspro_it.ts +14347 -7821
- setiastro/saspro/translations/saspro_ja.qm +0 -0
- setiastro/saspro/translations/saspro_ja.ts +14860 -137
- setiastro/saspro/translations/saspro_pt.qm +0 -0
- setiastro/saspro/translations/saspro_pt.ts +14904 -137
- setiastro/saspro/translations/saspro_ru.qm +0 -0
- setiastro/saspro/translations/saspro_ru.ts +11766 -168
- setiastro/saspro/translations/saspro_sw.qm +0 -0
- setiastro/saspro/translations/saspro_sw.ts +15115 -135
- setiastro/saspro/translations/saspro_uk.qm +0 -0
- setiastro/saspro/translations/saspro_uk.ts +11206 -6729
- setiastro/saspro/translations/saspro_zh.qm +0 -0
- setiastro/saspro/translations/saspro_zh.ts +10581 -7812
- setiastro/saspro/translations/sw_translations.py +282 -56
- setiastro/saspro/translations/uk_translations.py +264 -35
- setiastro/saspro/translations/zh_translations.py +282 -47
- setiastro/saspro/view_bundle.py +17 -17
- setiastro/saspro/wavescale_hdr.py +4 -1
- setiastro/saspro/wavescalede.py +4 -1
- setiastro/saspro/whitebalance.py +84 -12
- setiastro/saspro/widgets/common_utilities.py +28 -21
- setiastro/saspro/widgets/minigame/game.js +11 -6
- setiastro/saspro/widgets/resource_monitor.py +133 -57
- setiastro/saspro/widgets/spinboxes.py +28 -13
- setiastro/saspro/wimi.py +92 -721
- setiastro/saspro/wims.py +46 -36
- setiastro/saspro/window_shelf.py +2 -2
- setiastro/saspro/xisf.py +101 -11
- {setiastrosuitepro-1.6.2.dist-info → setiastrosuitepro-1.6.12.dist-info}/METADATA +8 -7
- {setiastrosuitepro-1.6.2.dist-info → setiastrosuitepro-1.6.12.dist-info}/RECORD +162 -128
- {setiastrosuitepro-1.6.2.dist-info → setiastrosuitepro-1.6.12.dist-info}/WHEEL +0 -0
- {setiastrosuitepro-1.6.2.dist-info → setiastrosuitepro-1.6.12.dist-info}/entry_points.txt +0 -0
- {setiastrosuitepro-1.6.2.dist-info → setiastrosuitepro-1.6.12.dist-info}/licenses/LICENSE +0 -0
- {setiastrosuitepro-1.6.2.dist-info → setiastrosuitepro-1.6.12.dist-info}/licenses/license.txt +0 -0
setiastro/saspro/wims.py
CHANGED
|
@@ -38,6 +38,8 @@ def _app_root() -> str:
|
|
|
38
38
|
def imgs_path(*parts) -> str:
|
|
39
39
|
return os.path.join(_app_root(), "imgs", *parts)
|
|
40
40
|
|
|
41
|
+
from setiastro.saspro.resources import get_icon_path
|
|
42
|
+
|
|
41
43
|
getcontext().prec = 24
|
|
42
44
|
warnings.filterwarnings("ignore")
|
|
43
45
|
|
|
@@ -289,7 +291,7 @@ def _tz_vs_longitude_hint(tz_name: str, date_str: str, time_str: str, lon_deg: f
|
|
|
289
291
|
class WhatsInMySkyDialog(QDialog):
|
|
290
292
|
def __init__(self, parent=None, wims_path: Optional[str] = None, wrench_path: Optional[str] = None):
|
|
291
293
|
super().__init__(parent)
|
|
292
|
-
self.setWindowTitle("What's In My Sky")
|
|
294
|
+
self.setWindowTitle(self.tr("What's In My Sky"))
|
|
293
295
|
if wims_path:
|
|
294
296
|
self.setWindowIcon(QIcon(wims_path))
|
|
295
297
|
|
|
@@ -316,14 +318,14 @@ class WhatsInMySkyDialog(QDialog):
|
|
|
316
318
|
self.timezone_combo.setFixedWidth(fixed_w)
|
|
317
319
|
|
|
318
320
|
r = 0
|
|
319
|
-
layout.addWidget(QLabel("Latitude:"), r, 0); layout.addWidget(self.latitude_entry, r, 1); r += 1
|
|
320
|
-
layout.addWidget(QLabel("Longitude (E+, W−):"), r, 0); layout.addWidget(self.longitude_entry, r, 1); r += 1
|
|
321
|
-
layout.addWidget(QLabel("Date (YYYY-MM-DD):"), r, 0); layout.addWidget(self.date_entry, r, 1); r += 1
|
|
322
|
-
layout.addWidget(QLabel("Time (HH:MM):"), r, 0); layout.addWidget(self.time_entry, r, 1); r += 1
|
|
323
|
-
layout.addWidget(QLabel("Time Zone:"), r, 0); layout.addWidget(self.timezone_combo, r, 1); r += 1
|
|
321
|
+
layout.addWidget(QLabel(self.tr("Latitude:")), r, 0); layout.addWidget(self.latitude_entry, r, 1); r += 1
|
|
322
|
+
layout.addWidget(QLabel(self.tr("Longitude (E+, W−):")), r, 0); layout.addWidget(self.longitude_entry, r, 1); r += 1
|
|
323
|
+
layout.addWidget(QLabel(self.tr("Date (YYYY-MM-DD):")), r, 0); layout.addWidget(self.date_entry, r, 1); r += 1
|
|
324
|
+
layout.addWidget(QLabel(self.tr("Time (HH:MM):")), r, 0); layout.addWidget(self.time_entry, r, 1); r += 1
|
|
325
|
+
layout.addWidget(QLabel(self.tr("Time Zone:")), r, 0); layout.addWidget(self.timezone_combo, r, 1); r += 1
|
|
324
326
|
|
|
325
327
|
self.min_altitude_entry = QLineEdit(); self.min_altitude_entry.setFixedWidth(fixed_w)
|
|
326
|
-
layout.addWidget(QLabel("Min Altitude (0–90°):"), r, 0); layout.addWidget(self.min_altitude_entry, r, 1); r += 1
|
|
328
|
+
layout.addWidget(QLabel(self.tr("Min Altitude (0–90°):")), r, 0); layout.addWidget(self.min_altitude_entry, r, 1); r += 1
|
|
327
329
|
|
|
328
330
|
# catalogs
|
|
329
331
|
catalog_frame = QScrollArea()
|
|
@@ -334,36 +336,36 @@ class WhatsInMySkyDialog(QDialog):
|
|
|
334
336
|
cat_layout.addWidget(cb, i // 5, i % 5)
|
|
335
337
|
self.catalog_vars[name] = cb
|
|
336
338
|
catalog_frame.setWidget(cat_widget); catalog_frame.setFixedWidth(fixed_w + 250)
|
|
337
|
-
layout.addWidget(QLabel("Catalog Filters:"), r, 0); layout.addWidget(catalog_frame, r, 1); r += 1
|
|
339
|
+
layout.addWidget(QLabel(self.tr("Catalog Filters:")), r, 0); layout.addWidget(catalog_frame, r, 1); r += 1
|
|
338
340
|
|
|
339
341
|
# RA/Dec format
|
|
340
|
-
self.ra_dec_degrees = QRadioButton("Degrees")
|
|
341
|
-
self.ra_dec_hms = QRadioButton("H:M:S / D:M:S")
|
|
342
|
+
self.ra_dec_degrees = QRadioButton(self.tr("Degrees"))
|
|
343
|
+
self.ra_dec_hms = QRadioButton(self.tr("H:M:S / D:M:S"))
|
|
342
344
|
self.ra_dec_degrees.setChecked(True)
|
|
343
345
|
g = QButtonGroup(self); g.addButton(self.ra_dec_degrees); g.addButton(self.ra_dec_hms)
|
|
344
346
|
ra_row = QHBoxLayout(); ra_row.addWidget(self.ra_dec_degrees); ra_row.addWidget(self.ra_dec_hms)
|
|
345
|
-
layout.addWidget(QLabel("RA/Dec Format:"), r, 0); layout.addLayout(ra_row, r, 1); r += 1
|
|
347
|
+
layout.addWidget(QLabel(self.tr("RA/Dec Format:")), r, 0); layout.addLayout(ra_row, r, 1); r += 1
|
|
346
348
|
self.ra_dec_degrees.toggled.connect(self.update_ra_dec_format)
|
|
347
349
|
self.ra_dec_hms.toggled.connect(self.update_ra_dec_format)
|
|
348
350
|
|
|
349
351
|
# action buttons / status
|
|
350
|
-
calc_btn = QPushButton("Calculate"); calc_btn.setFixedWidth(fixed_w); calc_btn.clicked.connect(self.start_calculation)
|
|
352
|
+
calc_btn = QPushButton(self.tr("Calculate")); calc_btn.setFixedWidth(fixed_w); calc_btn.clicked.connect(self.start_calculation)
|
|
351
353
|
layout.addWidget(calc_btn, r, 0); r += 1
|
|
352
354
|
|
|
353
|
-
self.status_label = QLabel("Status: Idle"); layout.addWidget(self.status_label, r, 0, 1, 2); r += 1
|
|
354
|
-
self.lst_label = QLabel("Local Sidereal Time: 0.000"); layout.addWidget(self.lst_label, r, 0, 1, 2); r += 1
|
|
355
|
+
self.status_label = QLabel(self.tr("Status: Idle")); layout.addWidget(self.status_label, r, 0, 1, 2); r += 1
|
|
356
|
+
self.lst_label = QLabel(self.tr("Local Sidereal Time: 0.000")); layout.addWidget(self.lst_label, r, 0, 1, 2); r += 1
|
|
355
357
|
|
|
356
358
|
# moon phase preview
|
|
357
359
|
self.lunar_phase_image_label = QLabel()
|
|
358
360
|
layout.addWidget(self.lunar_phase_image_label, 0, 2, 4, 1)
|
|
359
|
-
self.lunar_phase_label = QLabel("Lunar Phase: N/A")
|
|
361
|
+
self.lunar_phase_label = QLabel(self.tr("Lunar Phase: N/A"))
|
|
360
362
|
layout.addWidget(self.lunar_phase_label, 4, 2)
|
|
361
363
|
|
|
362
364
|
# results tree
|
|
363
365
|
self.tree = QTreeWidget()
|
|
364
366
|
self.tree.setHeaderLabels([
|
|
365
|
-
"Name","RA","Dec","Altitude","Azimuth","Minutes to Transit","Before/After Transit",
|
|
366
|
-
"Degrees from Moon","Alt Name","Type","Magnitude","Size (arcmin)"
|
|
367
|
+
self.tr("Name"),self.tr("RA"),self.tr("Dec"),self.tr("Altitude"),self.tr("Azimuth"),self.tr("Minutes to Transit"),self.tr("Before/After Transit"),
|
|
368
|
+
self.tr("Degrees from Moon"),self.tr("Alt Name"),self.tr("Type"),self.tr("Magnitude"),self.tr("Size (arcmin)")
|
|
367
369
|
])
|
|
368
370
|
self.tree.setSortingEnabled(True)
|
|
369
371
|
hdr = self.tree.header()
|
|
@@ -374,10 +376,10 @@ class WhatsInMySkyDialog(QDialog):
|
|
|
374
376
|
layout.addWidget(self.tree, r, 0, 1, 3); r += 1
|
|
375
377
|
|
|
376
378
|
# bottom row
|
|
377
|
-
add_btn = QPushButton("Add Custom Object"); add_btn.setFixedWidth(fixed_w); add_btn.clicked.connect(self.add_custom_object)
|
|
379
|
+
add_btn = QPushButton(self.tr("Add Custom Object")); add_btn.setFixedWidth(fixed_w); add_btn.clicked.connect(self.add_custom_object)
|
|
378
380
|
layout.addWidget(add_btn, r, 0)
|
|
379
381
|
|
|
380
|
-
save_btn = QPushButton("Save to CSV"); save_btn.setFixedWidth(fixed_w); save_btn.clicked.connect(self.save_to_csv)
|
|
382
|
+
save_btn = QPushButton(self.tr("Save to CSV")); save_btn.setFixedWidth(fixed_w); save_btn.clicked.connect(self.save_to_csv)
|
|
381
383
|
layout.addWidget(save_btn, r, 1)
|
|
382
384
|
|
|
383
385
|
settings_btn = QPushButton(); settings_btn.setFixedWidth(fixed_w)
|
|
@@ -434,7 +436,7 @@ class WhatsInMySkyDialog(QDialog):
|
|
|
434
436
|
tz_str = self.timezone_combo.currentText()
|
|
435
437
|
min_alt = float(self.min_altitude_entry.text())
|
|
436
438
|
except ValueError as e:
|
|
437
|
-
self.update_status(
|
|
439
|
+
self.update_status(self.tr("Invalid input: {}").format(e))
|
|
438
440
|
return
|
|
439
441
|
|
|
440
442
|
# Heuristic warning (and gentle auto-fix if user probably forgot the suffix)
|
|
@@ -450,9 +452,9 @@ class WhatsInMySkyDialog(QDialog):
|
|
|
450
452
|
self.longitude_entry.setText(_format_with_suffix(longitude, "lon"))
|
|
451
453
|
self.update_status(f"{msg} → Assuming you meant {_format_with_suffix(longitude, 'lon')} (auto-corrected).")
|
|
452
454
|
else:
|
|
453
|
-
self.update_status(msg + " Please verify your longitude/timezone.")
|
|
455
|
+
self.update_status(msg + self.tr(" Please verify your longitude/timezone."))
|
|
454
456
|
else:
|
|
455
|
-
self.update_status("Inputs look consistent.")
|
|
457
|
+
self.update_status(self.tr("Inputs look consistent."))
|
|
456
458
|
|
|
457
459
|
# Persist settings (numeric)
|
|
458
460
|
self._save_settings(latitude, longitude, date_str, time_str, tz_str, min_alt)
|
|
@@ -467,16 +469,24 @@ class WhatsInMySkyDialog(QDialog):
|
|
|
467
469
|
self.calc_thread.lst_calculated.connect(self.update_lst)
|
|
468
470
|
self.calc_thread.status_update.connect(self.update_status)
|
|
469
471
|
|
|
470
|
-
self.update_status("Calculating…")
|
|
472
|
+
self.update_status(self.tr("Calculating…"))
|
|
471
473
|
self.calc_thread.start()
|
|
472
474
|
|
|
473
475
|
def update_lunar_phase(self, phase_percentage: int, phase_image_name: str):
|
|
474
|
-
self.lunar_phase_label.setText(
|
|
475
|
-
|
|
476
|
+
self.lunar_phase_label.setText(self.tr("Lunar Phase: {}% illuminated").format(phase_percentage))
|
|
477
|
+
|
|
478
|
+
pth = get_icon_path(phase_image_name) # phase_image_name already includes .png
|
|
476
479
|
if os.path.exists(pth):
|
|
477
|
-
pm = QPixmap(pth).scaled(
|
|
478
|
-
|
|
480
|
+
pm = QPixmap(pth).scaled(
|
|
481
|
+
100, 100,
|
|
482
|
+
Qt.AspectRatioMode.KeepAspectRatio,
|
|
483
|
+
Qt.TransformationMode.SmoothTransformation
|
|
484
|
+
)
|
|
479
485
|
self.lunar_phase_image_label.setPixmap(pm)
|
|
486
|
+
else:
|
|
487
|
+
# super helpful while debugging
|
|
488
|
+
self.lunar_phase_image_label.clear()
|
|
489
|
+
self.update_status(self.tr("Moon icon missing: {}").format(pth))
|
|
480
490
|
|
|
481
491
|
def on_calculation_complete(self, df: pd.DataFrame, message: str):
|
|
482
492
|
self.update_status(message)
|
|
@@ -509,13 +519,13 @@ class WhatsInMySkyDialog(QDialog):
|
|
|
509
519
|
self.tree.addTopLevelItem(SortableTreeWidgetItem(vals))
|
|
510
520
|
|
|
511
521
|
def update_status(self, msg: str):
|
|
512
|
-
self.status_label.setText(
|
|
522
|
+
self.status_label.setText(self.tr("Status: {}").format(msg))
|
|
513
523
|
|
|
514
524
|
def update_lst(self, msg: str):
|
|
515
525
|
self.lst_label.setText(msg)
|
|
516
526
|
|
|
517
527
|
def open_settings(self):
|
|
518
|
-
n, ok = QInputDialog.getInt(self, "Settings", "Enter number of objects to display:",
|
|
528
|
+
n, ok = QInputDialog.getInt(self, self.tr("Settings"), self.tr("Enter number of objects to display:"),
|
|
519
529
|
value=int(self.object_limit), min=1, max=1000)
|
|
520
530
|
if ok:
|
|
521
531
|
self.object_limit = int(n)
|
|
@@ -526,12 +536,12 @@ class WhatsInMySkyDialog(QDialog):
|
|
|
526
536
|
webbrowser.open(f"https://www.astrobin.com/search/?q={name}")
|
|
527
537
|
|
|
528
538
|
def add_custom_object(self):
|
|
529
|
-
name, ok = QInputDialog.getText(self, "Add Custom Object", "Enter object name:")
|
|
539
|
+
name, ok = QInputDialog.getText(self, self.tr("Add Custom Object"), self.tr("Enter object name:"))
|
|
530
540
|
if not ok or not name:
|
|
531
541
|
return
|
|
532
|
-
ra, ok = QInputDialog.getDouble(self, "Add Custom Object", "Enter RA (deg):", decimals=3)
|
|
542
|
+
ra, ok = QInputDialog.getDouble(self, self.tr("Add Custom Object"), self.tr("Enter RA (deg):"), decimals=3)
|
|
533
543
|
if not ok: return
|
|
534
|
-
dec, ok = QInputDialog.getDouble(self, "Add Custom Object", "Enter Dec (deg):", decimals=3)
|
|
544
|
+
dec, ok = QInputDialog.getDouble(self, self.tr("Add Custom Object"), self.tr("Enter Dec (deg):"), decimals=3)
|
|
535
545
|
if not ok: return
|
|
536
546
|
|
|
537
547
|
entry = {"Name": name, "RA": ra, "Dec": dec, "Catalog": "User",
|
|
@@ -542,9 +552,9 @@ class WhatsInMySkyDialog(QDialog):
|
|
|
542
552
|
df = pd.read_csv(catalog_csv, encoding="ISO-8859-1") if os.path.exists(catalog_csv) else pd.DataFrame()
|
|
543
553
|
df = pd.concat([df, pd.DataFrame([entry])], ignore_index=True)
|
|
544
554
|
df.to_csv(catalog_csv, index=False, encoding="ISO-8859-1")
|
|
545
|
-
self.update_status(
|
|
555
|
+
self.update_status(self.tr("Added custom object: {}").format(name))
|
|
546
556
|
except Exception as e:
|
|
547
|
-
QMessageBox.warning(self, "Add Custom Object",
|
|
557
|
+
QMessageBox.warning(self, self.tr("Add Custom Object"), self.tr("Could not update catalog:\n{}").format(e))
|
|
548
558
|
|
|
549
559
|
def update_ra_dec_format(self):
|
|
550
560
|
use_deg = self.ra_dec_degrees.isChecked()
|
|
@@ -566,7 +576,7 @@ class WhatsInMySkyDialog(QDialog):
|
|
|
566
576
|
pass
|
|
567
577
|
|
|
568
578
|
def save_to_csv(self):
|
|
569
|
-
path, _ = QFileDialog.getSaveFileName(self, "Save CSV File", "", "CSV files (*.csv);;All Files (*)")
|
|
579
|
+
path, _ = QFileDialog.getSaveFileName(self, self.tr("Save CSV File"), "", self.tr("CSV files (*.csv);;All Files (*)"))
|
|
570
580
|
if not path:
|
|
571
581
|
return
|
|
572
582
|
cols = [self.tree.headerItem().text(i) for i in range(self.tree.columnCount())]
|
|
@@ -575,4 +585,4 @@ class WhatsInMySkyDialog(QDialog):
|
|
|
575
585
|
it = self.tree.topLevelItem(i)
|
|
576
586
|
rows.append([it.text(j) for j in range(self.tree.columnCount())])
|
|
577
587
|
pd.DataFrame(rows, columns=cols).to_csv(path, index=False)
|
|
578
|
-
self.update_status(
|
|
588
|
+
self.update_status(self.tr("Data saved to {}").format(path))
|
setiastro/saspro/window_shelf.py
CHANGED
|
@@ -17,7 +17,7 @@ def _dbg(owner, msg: str):
|
|
|
17
17
|
|
|
18
18
|
class WindowShelf(QDockWidget):
|
|
19
19
|
def __init__(self, parent=None):
|
|
20
|
-
super().__init__("Minimized Views", parent)
|
|
20
|
+
super().__init__(self.tr("Minimized Views"), parent)
|
|
21
21
|
|
|
22
22
|
# PyQt6 dock area enum
|
|
23
23
|
self.setAllowedAreas(Qt.DockWidgetArea.AllDockWidgetAreas)
|
|
@@ -53,7 +53,7 @@ class WindowShelf(QDockWidget):
|
|
|
53
53
|
if sub is None or sub.widget() is None:
|
|
54
54
|
return
|
|
55
55
|
|
|
56
|
-
title = sub.windowTitle() or "Untitled"
|
|
56
|
+
title = sub.windowTitle() or self.tr("Untitled")
|
|
57
57
|
# strip leading dot and Active prefix for the shelf display text only
|
|
58
58
|
|
|
59
59
|
# Remove any number of leading glyphs like ■ ● ◆ ▲ etc.
|
setiastro/saspro/xisf.py
CHANGED
|
@@ -34,7 +34,14 @@ import sys
|
|
|
34
34
|
from datetime import datetime
|
|
35
35
|
import ast
|
|
36
36
|
|
|
37
|
-
__version__ = "1.0.
|
|
37
|
+
__version__ = "1.0.1"
|
|
38
|
+
|
|
39
|
+
def _is_attached_or_inline_property(p_dict):
|
|
40
|
+
return "location" in p_dict # location implies inline/embedded/attachment
|
|
41
|
+
|
|
42
|
+
def _make_lazy(p_dict):
|
|
43
|
+
p_dict["_lazy"] = True
|
|
44
|
+
return p_dict
|
|
38
45
|
|
|
39
46
|
class XISF:
|
|
40
47
|
"""Implements an baseline XISF Decoder and a simple baseline Encoder.
|
|
@@ -340,7 +347,7 @@ class XISF:
|
|
|
340
347
|
def _read_embedded_data_block(elem):
|
|
341
348
|
assert elem["location"][0] == "embedded"
|
|
342
349
|
data_elem = ET.fromstring(elem["value"])
|
|
343
|
-
encoding, data =
|
|
350
|
+
encoding, data = data_elem.attrib["encoding"], data_elem.text
|
|
344
351
|
return XISF._decode_inline_or_embedded_data(encoding, data, elem)
|
|
345
352
|
|
|
346
353
|
@staticmethod
|
|
@@ -741,11 +748,13 @@ class XISF:
|
|
|
741
748
|
# Keep original string value if parsing fails
|
|
742
749
|
p_dict["datetime"] = None
|
|
743
750
|
elif p_dict["type"] == "String":
|
|
744
|
-
p_dict["value"] = p_et.text
|
|
751
|
+
# NOTE: currently does: p_dict["value"] = p_et.text; then if location -> read block now
|
|
752
|
+
p_dict["value"] = p_et.text # may be None
|
|
745
753
|
if "location" in p_dict:
|
|
746
|
-
# Process location and compression attributes to find data block
|
|
747
754
|
self._process_location_compression(p_dict)
|
|
748
|
-
|
|
755
|
+
# LAZY: do NOT read block here
|
|
756
|
+
return _make_lazy(p_dict)
|
|
757
|
+
return p_dict
|
|
749
758
|
elif p_dict["type"] == "Boolean":
|
|
750
759
|
# Boolean valid values are "true" and "false"
|
|
751
760
|
p_dict["value"] = p_dict["value"] == "true"
|
|
@@ -757,24 +766,105 @@ class XISF:
|
|
|
757
766
|
p_dict["length"] = int(p_dict["length"])
|
|
758
767
|
p_dict["dtype"] = self._parse_vector_dtype(p_dict["type"])
|
|
759
768
|
self._process_location_compression(p_dict)
|
|
760
|
-
|
|
761
|
-
|
|
769
|
+
# LAZY: do NOT read block here
|
|
770
|
+
return _make_lazy(p_dict)
|
|
771
|
+
|
|
762
772
|
elif "Matrix" in p_dict["type"]:
|
|
763
773
|
p_dict["value"] = p_et.text
|
|
764
774
|
p_dict["rows"] = int(p_dict["rows"])
|
|
765
775
|
p_dict["columns"] = int(p_dict["columns"])
|
|
766
|
-
length = p_dict["rows"] * p_dict["columns"]
|
|
767
776
|
p_dict["dtype"] = self._parse_vector_dtype(p_dict["type"])
|
|
768
777
|
self._process_location_compression(p_dict)
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
p_dict["value"] = p_dict["value"].reshape((p_dict["rows"], p_dict["columns"]))
|
|
778
|
+
# LAZY: do NOT read block here
|
|
779
|
+
return _make_lazy(p_dict)
|
|
772
780
|
else:
|
|
773
781
|
print(f"Unsupported Property type {p_dict['type']}: {p_et}")
|
|
774
782
|
p_dict = False
|
|
775
783
|
|
|
776
784
|
return p_dict
|
|
777
785
|
|
|
786
|
+
def resolve_property(self, p_dict):
|
|
787
|
+
"""
|
|
788
|
+
Resolve a lazy property (String/Vector/Matrix with a data block).
|
|
789
|
+
Mutates p_dict in place and returns decoded 'value'.
|
|
790
|
+
"""
|
|
791
|
+
if not p_dict.get("_lazy"):
|
|
792
|
+
return p_dict.get("value")
|
|
793
|
+
|
|
794
|
+
raw = self._read_data_block(p_dict)
|
|
795
|
+
|
|
796
|
+
t = p_dict["type"]
|
|
797
|
+
if t == "String":
|
|
798
|
+
val = raw.decode("utf-8")
|
|
799
|
+
elif "Vector" in t:
|
|
800
|
+
val = np.frombuffer(raw, dtype=p_dict["dtype"], count=p_dict["length"])
|
|
801
|
+
elif "Matrix" in t:
|
|
802
|
+
length = p_dict["rows"] * p_dict["columns"]
|
|
803
|
+
val = np.frombuffer(raw, dtype=p_dict["dtype"], count=length).reshape((p_dict["rows"], p_dict["columns"]))
|
|
804
|
+
else:
|
|
805
|
+
# if something else ever gets marked lazy
|
|
806
|
+
val = raw
|
|
807
|
+
|
|
808
|
+
p_dict["value"] = val
|
|
809
|
+
p_dict["_lazy"] = False
|
|
810
|
+
return val
|
|
811
|
+
|
|
812
|
+
def can_partial_read_image(self, n=0):
|
|
813
|
+
meta = self._images_meta[n]
|
|
814
|
+
if meta["location"][0] != "attachment":
|
|
815
|
+
return False
|
|
816
|
+
if "compression" in meta:
|
|
817
|
+
return False
|
|
818
|
+
return True
|
|
819
|
+
|
|
820
|
+
def read_image_roi(self, n=0, x0=0, y0=0, x1=None, y1=None, channels=None, data_format="channels_last"):
|
|
821
|
+
meta = self._images_meta[n]
|
|
822
|
+
if meta["location"][0] != "attachment":
|
|
823
|
+
raise NotImplementedError("ROI read only supported for attachment blocks")
|
|
824
|
+
if "compression" in meta:
|
|
825
|
+
raise NotImplementedError("ROI read not supported for compressed image blocks")
|
|
826
|
+
|
|
827
|
+
w, h, chc = meta["geometry"]
|
|
828
|
+
dtype = meta["dtype"]
|
|
829
|
+
itemsize = dtype.itemsize
|
|
830
|
+
|
|
831
|
+
if x1 is None: x1 = w
|
|
832
|
+
if y1 is None: y1 = h
|
|
833
|
+
x0 = max(0, min(w, x0)); x1 = max(0, min(w, x1))
|
|
834
|
+
y0 = max(0, min(h, y0)); y1 = max(0, min(h, y1))
|
|
835
|
+
if x1 <= x0 or y1 <= y0:
|
|
836
|
+
raise ValueError("Empty ROI")
|
|
837
|
+
|
|
838
|
+
if channels is None:
|
|
839
|
+
channels = list(range(chc))
|
|
840
|
+
else:
|
|
841
|
+
channels = list(channels)
|
|
842
|
+
|
|
843
|
+
_, pos, _size = meta["location"]
|
|
844
|
+
roi_w = x1 - x0
|
|
845
|
+
roi_h = y1 - y0
|
|
846
|
+
|
|
847
|
+
out = np.empty((len(channels), roi_h, roi_w), dtype=dtype)
|
|
848
|
+
|
|
849
|
+
row_bytes = w * itemsize
|
|
850
|
+
roi_bytes = roi_w * itemsize
|
|
851
|
+
plane_bytes = h * row_bytes
|
|
852
|
+
|
|
853
|
+
with open(self._fname, "rb") as f:
|
|
854
|
+
for ci, c in enumerate(channels):
|
|
855
|
+
if c < 0 or c >= chc:
|
|
856
|
+
raise IndexError(f"channel {c} out of range")
|
|
857
|
+
plane_base = pos + c * plane_bytes
|
|
858
|
+
for r, y in enumerate(range(y0, y1)):
|
|
859
|
+
offset = plane_base + y * row_bytes + x0 * itemsize
|
|
860
|
+
f.seek(offset)
|
|
861
|
+
out[ci, r, :] = np.frombuffer(f.read(roi_bytes), dtype=dtype, count=roi_w)
|
|
862
|
+
|
|
863
|
+
if data_format == "channels_last":
|
|
864
|
+
return np.transpose(out, (1, 2, 0))
|
|
865
|
+
return out
|
|
866
|
+
|
|
867
|
+
|
|
778
868
|
@staticmethod
|
|
779
869
|
def _process_location_compression(p_dict):
|
|
780
870
|
p_dict["location"] = XISF._parse_location(p_dict["location"])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: setiastrosuitepro
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.12
|
|
4
4
|
Summary: Seti Astro Suite Pro - Advanced astrophotography toolkit for image calibration, stacking, registration, photometry, and visualization
|
|
5
5
|
License: GPL-3.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -25,6 +25,7 @@ Requires-Dist: astroalign
|
|
|
25
25
|
Requires-Dist: astropy
|
|
26
26
|
Requires-Dist: astroquery
|
|
27
27
|
Requires-Dist: exifread
|
|
28
|
+
Requires-Dist: imagecodecs (>=2025.11.11,<2026.0.0) ; python_version >= "3.11" and sys_platform == "darwin"
|
|
28
29
|
Requires-Dist: imageio
|
|
29
30
|
Requires-Dist: jplephem
|
|
30
31
|
Requires-Dist: lightkurve
|
|
@@ -65,18 +66,18 @@ Description-Content-Type: text/markdown
|
|
|
65
66
|
|
|
66
67
|
### Other contributors:
|
|
67
68
|
- [Fabio Tempera](https://github.com/Ft2801) 🥇
|
|
68
|
-
- Complete code refactoring of `setiastrosuitepro.py` (20,000+ lines)
|
|
69
|
-
- Addition of AstroSpikes tool and 10+ language translations
|
|
70
|
-
- Implementation of UI elements, startup window, caching methods, lazy imports, utils functions,
|
|
69
|
+
- Complete code refactoring of `setiastrosuitepro.py` (20,000+ lines), and duplicated code removal across the project
|
|
70
|
+
- Addition of AstroSpikes tool, secret minigame, system resources monitor, app statistics, and 10+ language translations
|
|
71
|
+
- Implementation of UI elements, startup window, caching methods, lazy imports, utils functions, better memory management, and other important code optimizations across the entire project
|
|
71
72
|
- [Joaquin Rodriguez](https://github.com/jrhuerta)
|
|
72
|
-
- Project migration to Poetry
|
|
73
|
+
- Project migration to Poetry
|
|
73
74
|
- [Tim Dicke](https://github.com/dickett)
|
|
74
75
|
- Windows and MacOS installer development
|
|
75
76
|
- MacOS Wiki instructions maintenance
|
|
76
|
-
- App testing
|
|
77
|
+
- App testing and small bugfixes
|
|
77
78
|
- [Michael Lev](https://github.com/MichaelLevAstro)
|
|
78
79
|
- Addition of hebrew language
|
|
79
|
-
- [
|
|
80
|
+
- [Andrew Witwicki](https://github.com/awitwicki)
|
|
80
81
|
- Addition of ukrainian language
|
|
81
82
|
---
|
|
82
83
|
|