setiastrosuitepro 1.6.0__py3-none-any.whl → 1.6.4.post1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of setiastrosuitepro might be problematic. Click here for more details.

Files changed (293) hide show
  1. setiastro/data/SASP_data.fits +0 -0
  2. setiastro/data/catalogs/List_of_Galaxies_with_Distances_Gly.csv +488 -0
  3. setiastro/data/catalogs/astrobin_filters.csv +890 -0
  4. setiastro/data/catalogs/astrobin_filters_page1_local.csv +51 -0
  5. setiastro/data/catalogs/cali2.csv +63 -0
  6. setiastro/data/catalogs/cali2color.csv +65 -0
  7. setiastro/data/catalogs/celestial_catalog - original.csv +16471 -0
  8. setiastro/data/catalogs/celestial_catalog.csv +24031 -0
  9. setiastro/data/catalogs/detected_stars.csv +24784 -0
  10. setiastro/data/catalogs/fits_header_data.csv +46 -0
  11. setiastro/data/catalogs/test.csv +8 -0
  12. setiastro/data/catalogs/updated_celestial_catalog.csv +16471 -0
  13. setiastro/images/Astro_Spikes.png +0 -0
  14. setiastro/images/Background_startup.jpg +0 -0
  15. setiastro/images/HRDiagram.png +0 -0
  16. setiastro/images/LExtract.png +0 -0
  17. setiastro/images/LInsert.png +0 -0
  18. setiastro/images/Oxygenation-atm-2.svg.png +0 -0
  19. setiastro/images/RGB080604.png +0 -0
  20. setiastro/images/abeicon.png +0 -0
  21. setiastro/images/aberration.png +0 -0
  22. setiastro/images/andromedatry.png +0 -0
  23. setiastro/images/andromedatry_satellited.png +0 -0
  24. setiastro/images/annotated.png +0 -0
  25. setiastro/images/aperture.png +0 -0
  26. setiastro/images/astrosuite.ico +0 -0
  27. setiastro/images/astrosuite.png +0 -0
  28. setiastro/images/astrosuitepro.icns +0 -0
  29. setiastro/images/astrosuitepro.ico +0 -0
  30. setiastro/images/astrosuitepro.png +0 -0
  31. setiastro/images/background.png +0 -0
  32. setiastro/images/background2.png +0 -0
  33. setiastro/images/benchmark.png +0 -0
  34. setiastro/images/big_moon_stabilizer_timeline.png +0 -0
  35. setiastro/images/big_moon_stabilizer_timeline_clean.png +0 -0
  36. setiastro/images/blaster.png +0 -0
  37. setiastro/images/blink.png +0 -0
  38. setiastro/images/clahe.png +0 -0
  39. setiastro/images/collage.png +0 -0
  40. setiastro/images/colorwheel.png +0 -0
  41. setiastro/images/contsub.png +0 -0
  42. setiastro/images/convo.png +0 -0
  43. setiastro/images/copyslot.png +0 -0
  44. setiastro/images/cosmic.png +0 -0
  45. setiastro/images/cosmicsat.png +0 -0
  46. setiastro/images/crop1.png +0 -0
  47. setiastro/images/cropicon.png +0 -0
  48. setiastro/images/curves.png +0 -0
  49. setiastro/images/cvs.png +0 -0
  50. setiastro/images/debayer.png +0 -0
  51. setiastro/images/denoise_cnn_custom.png +0 -0
  52. setiastro/images/denoise_cnn_graph.png +0 -0
  53. setiastro/images/disk.png +0 -0
  54. setiastro/images/dse.png +0 -0
  55. setiastro/images/exoicon.png +0 -0
  56. setiastro/images/eye.png +0 -0
  57. setiastro/images/fliphorizontal.png +0 -0
  58. setiastro/images/flipvertical.png +0 -0
  59. setiastro/images/font.png +0 -0
  60. setiastro/images/freqsep.png +0 -0
  61. setiastro/images/functionbundle.png +0 -0
  62. setiastro/images/graxpert.png +0 -0
  63. setiastro/images/green.png +0 -0
  64. setiastro/images/gridicon.png +0 -0
  65. setiastro/images/halo.png +0 -0
  66. setiastro/images/hdr.png +0 -0
  67. setiastro/images/histogram.png +0 -0
  68. setiastro/images/hubble.png +0 -0
  69. setiastro/images/imagecombine.png +0 -0
  70. setiastro/images/invert.png +0 -0
  71. setiastro/images/isophote.png +0 -0
  72. setiastro/images/isophote_demo_figure.png +0 -0
  73. setiastro/images/isophote_demo_image.png +0 -0
  74. setiastro/images/isophote_demo_model.png +0 -0
  75. setiastro/images/isophote_demo_residual.png +0 -0
  76. setiastro/images/jwstpupil.png +0 -0
  77. setiastro/images/linearfit.png +0 -0
  78. setiastro/images/livestacking.png +0 -0
  79. setiastro/images/mask.png +0 -0
  80. setiastro/images/maskapply.png +0 -0
  81. setiastro/images/maskcreate.png +0 -0
  82. setiastro/images/maskremove.png +0 -0
  83. setiastro/images/morpho.png +0 -0
  84. setiastro/images/mosaic.png +0 -0
  85. setiastro/images/multiscale_decomp.png +0 -0
  86. setiastro/images/nbtorgb.png +0 -0
  87. setiastro/images/neutral.png +0 -0
  88. setiastro/images/nuke.png +0 -0
  89. setiastro/images/openfile.png +0 -0
  90. setiastro/images/pedestal.png +0 -0
  91. setiastro/images/pen.png +0 -0
  92. setiastro/images/pixelmath.png +0 -0
  93. setiastro/images/platesolve.png +0 -0
  94. setiastro/images/ppp.png +0 -0
  95. setiastro/images/pro.png +0 -0
  96. setiastro/images/project.png +0 -0
  97. setiastro/images/psf.png +0 -0
  98. setiastro/images/redo.png +0 -0
  99. setiastro/images/redoicon.png +0 -0
  100. setiastro/images/rescale.png +0 -0
  101. setiastro/images/rgbalign.png +0 -0
  102. setiastro/images/rgbcombo.png +0 -0
  103. setiastro/images/rgbextract.png +0 -0
  104. setiastro/images/rotate180.png +0 -0
  105. setiastro/images/rotatearbitrary.png +0 -0
  106. setiastro/images/rotateclockwise.png +0 -0
  107. setiastro/images/rotatecounterclockwise.png +0 -0
  108. setiastro/images/satellite.png +0 -0
  109. setiastro/images/script.png +0 -0
  110. setiastro/images/selectivecolor.png +0 -0
  111. setiastro/images/simbad.png +0 -0
  112. setiastro/images/slot0.png +0 -0
  113. setiastro/images/slot1.png +0 -0
  114. setiastro/images/slot2.png +0 -0
  115. setiastro/images/slot3.png +0 -0
  116. setiastro/images/slot4.png +0 -0
  117. setiastro/images/slot5.png +0 -0
  118. setiastro/images/slot6.png +0 -0
  119. setiastro/images/slot7.png +0 -0
  120. setiastro/images/slot8.png +0 -0
  121. setiastro/images/slot9.png +0 -0
  122. setiastro/images/spcc.png +0 -0
  123. setiastro/images/spin_precession_vs_lunar_distance.png +0 -0
  124. setiastro/images/spinner.gif +0 -0
  125. setiastro/images/stacking.png +0 -0
  126. setiastro/images/staradd.png +0 -0
  127. setiastro/images/staralign.png +0 -0
  128. setiastro/images/starnet.png +0 -0
  129. setiastro/images/starregistration.png +0 -0
  130. setiastro/images/starspike.png +0 -0
  131. setiastro/images/starstretch.png +0 -0
  132. setiastro/images/statstretch.png +0 -0
  133. setiastro/images/supernova.png +0 -0
  134. setiastro/images/uhs.png +0 -0
  135. setiastro/images/undoicon.png +0 -0
  136. setiastro/images/upscale.png +0 -0
  137. setiastro/images/viewbundle.png +0 -0
  138. setiastro/images/whitebalance.png +0 -0
  139. setiastro/images/wimi_icon_256x256.png +0 -0
  140. setiastro/images/wimilogo.png +0 -0
  141. setiastro/images/wims.png +0 -0
  142. setiastro/images/wrench_icon.png +0 -0
  143. setiastro/images/xisfliberator.png +0 -0
  144. setiastro/qml/ResourceMonitor.qml +126 -0
  145. setiastro/saspro/__main__.py +228 -67
  146. setiastro/saspro/_generated/build_info.py +2 -1
  147. setiastro/saspro/abe.py +76 -25
  148. setiastro/saspro/aberration_ai.py +14 -14
  149. setiastro/saspro/add_stars.py +15 -12
  150. setiastro/saspro/astrobin_exporter.py +61 -58
  151. setiastro/saspro/astrospike_python.py +3 -1
  152. setiastro/saspro/autostretch.py +4 -2
  153. setiastro/saspro/backgroundneutral.py +65 -14
  154. setiastro/saspro/batch_convert.py +8 -5
  155. setiastro/saspro/batch_renamer.py +39 -36
  156. setiastro/saspro/blemish_blaster.py +15 -12
  157. setiastro/saspro/blink_comparator_pro.py +605 -379
  158. setiastro/saspro/cheat_sheet.py +62 -17
  159. setiastro/saspro/clahe.py +34 -8
  160. setiastro/saspro/comet_stacking.py +103 -38
  161. setiastro/saspro/common_tr.py +107 -0
  162. setiastro/saspro/continuum_subtract.py +7 -7
  163. setiastro/saspro/convo.py +12 -9
  164. setiastro/saspro/copyastro.py +3 -0
  165. setiastro/saspro/cosmicclarity.py +77 -52
  166. setiastro/saspro/crop_dialog_pro.py +80 -45
  167. setiastro/saspro/curve_editor_pro.py +51 -33
  168. setiastro/saspro/debayer.py +6 -3
  169. setiastro/saspro/doc_manager.py +49 -19
  170. setiastro/saspro/exoplanet_detector.py +11 -11
  171. setiastro/saspro/fitsmodifier.py +48 -44
  172. setiastro/saspro/fix_bom.py +32 -0
  173. setiastro/saspro/frequency_separation.py +18 -12
  174. setiastro/saspro/function_bundle.py +18 -16
  175. setiastro/saspro/generate_translations.py +3092 -0
  176. setiastro/saspro/ghs_dialog_pro.py +19 -16
  177. setiastro/saspro/graxpert.py +3 -0
  178. setiastro/saspro/gui/main_window.py +471 -126
  179. setiastro/saspro/gui/mixins/dock_mixin.py +123 -11
  180. setiastro/saspro/gui/mixins/file_mixin.py +25 -20
  181. setiastro/saspro/gui/mixins/geometry_mixin.py +115 -15
  182. setiastro/saspro/gui/mixins/header_mixin.py +6 -6
  183. setiastro/saspro/gui/mixins/mask_mixin.py +8 -8
  184. setiastro/saspro/gui/mixins/menu_mixin.py +62 -33
  185. setiastro/saspro/gui/mixins/toolbar_mixin.py +382 -226
  186. setiastro/saspro/gui/mixins/update_mixin.py +26 -26
  187. setiastro/saspro/gui/statistics_dialog.py +47 -0
  188. setiastro/saspro/halobgon.py +29 -3
  189. setiastro/saspro/header_viewer.py +21 -18
  190. setiastro/saspro/histogram.py +29 -26
  191. setiastro/saspro/history_explorer.py +2 -0
  192. setiastro/saspro/i18n.py +168 -0
  193. setiastro/saspro/image_combine.py +3 -0
  194. setiastro/saspro/image_peeker_pro.py +52 -44
  195. setiastro/saspro/imageops/stretch.py +5 -13
  196. setiastro/saspro/isophote.py +3 -0
  197. setiastro/saspro/legacy/numba_utils.py +64 -47
  198. setiastro/saspro/linear_fit.py +3 -0
  199. setiastro/saspro/live_stacking.py +13 -2
  200. setiastro/saspro/mask_creation.py +180 -22
  201. setiastro/saspro/mfdeconv.py +5 -0
  202. setiastro/saspro/morphology.py +38 -13
  203. setiastro/saspro/multiscale_decomp.py +713 -256
  204. setiastro/saspro/nbtorgb_stars.py +12 -2
  205. setiastro/saspro/numba_utils.py +149 -48
  206. setiastro/saspro/ops/scripts.py +77 -17
  207. setiastro/saspro/ops/settings.py +177 -100
  208. setiastro/saspro/perfect_palette_picker.py +25 -7
  209. setiastro/saspro/pixelmath.py +114 -110
  210. setiastro/saspro/plate_solver.py +118 -108
  211. setiastro/saspro/remove_green.py +24 -7
  212. setiastro/saspro/remove_stars.py +136 -162
  213. setiastro/saspro/remove_stars_preset.py +55 -13
  214. setiastro/saspro/resources.py +46 -15
  215. setiastro/saspro/rgb_combination.py +19 -18
  216. setiastro/saspro/rgbalign.py +11 -11
  217. setiastro/saspro/save_options.py +5 -4
  218. setiastro/saspro/selective_color.py +84 -25
  219. setiastro/saspro/sfcc.py +119 -72
  220. setiastro/saspro/shortcuts.py +345 -36
  221. setiastro/saspro/signature_insert.py +4 -1
  222. setiastro/saspro/stacking_suite.py +2066 -1119
  223. setiastro/saspro/star_alignment.py +291 -331
  224. setiastro/saspro/star_spikes.py +137 -53
  225. setiastro/saspro/star_stretch.py +47 -10
  226. setiastro/saspro/stat_stretch.py +52 -16
  227. setiastro/saspro/status_log_dock.py +1 -1
  228. setiastro/saspro/subwindow.py +97 -36
  229. setiastro/saspro/supernovaasteroidhunter.py +68 -61
  230. setiastro/saspro/swap_manager.py +77 -42
  231. setiastro/saspro/translations/all_source_strings.json +4726 -0
  232. setiastro/saspro/translations/ar_translations.py +4096 -0
  233. setiastro/saspro/translations/de_translations.py +3728 -0
  234. setiastro/saspro/translations/es_translations.py +4169 -0
  235. setiastro/saspro/translations/fr_translations.py +4090 -0
  236. setiastro/saspro/translations/hi_translations.py +3803 -0
  237. setiastro/saspro/translations/integrate_translations.py +271 -0
  238. setiastro/saspro/translations/it_translations.py +4728 -0
  239. setiastro/saspro/translations/ja_translations.py +3834 -0
  240. setiastro/saspro/translations/pt_translations.py +3847 -0
  241. setiastro/saspro/translations/ru_translations.py +3082 -0
  242. setiastro/saspro/translations/saspro_ar.qm +0 -0
  243. setiastro/saspro/translations/saspro_ar.ts +16019 -0
  244. setiastro/saspro/translations/saspro_de.qm +0 -0
  245. setiastro/saspro/translations/saspro_de.ts +14548 -0
  246. setiastro/saspro/translations/saspro_es.qm +0 -0
  247. setiastro/saspro/translations/saspro_es.ts +16202 -0
  248. setiastro/saspro/translations/saspro_fr.qm +0 -0
  249. setiastro/saspro/translations/saspro_fr.ts +15870 -0
  250. setiastro/saspro/translations/saspro_hi.qm +0 -0
  251. setiastro/saspro/translations/saspro_hi.ts +14855 -0
  252. setiastro/saspro/translations/saspro_it.qm +0 -0
  253. setiastro/saspro/translations/saspro_it.ts +19046 -0
  254. setiastro/saspro/translations/saspro_ja.qm +0 -0
  255. setiastro/saspro/translations/saspro_ja.ts +14980 -0
  256. setiastro/saspro/translations/saspro_pt.qm +0 -0
  257. setiastro/saspro/translations/saspro_pt.ts +15024 -0
  258. setiastro/saspro/translations/saspro_ru.qm +0 -0
  259. setiastro/saspro/translations/saspro_ru.ts +11835 -0
  260. setiastro/saspro/translations/saspro_sw.qm +0 -0
  261. setiastro/saspro/translations/saspro_sw.ts +15237 -0
  262. setiastro/saspro/translations/saspro_uk.qm +0 -0
  263. setiastro/saspro/translations/saspro_uk.ts +15248 -0
  264. setiastro/saspro/translations/saspro_zh.qm +0 -0
  265. setiastro/saspro/translations/saspro_zh.ts +15289 -0
  266. setiastro/saspro/translations/sw_translations.py +3897 -0
  267. setiastro/saspro/translations/uk_translations.py +3929 -0
  268. setiastro/saspro/translations/zh_translations.py +3910 -0
  269. setiastro/saspro/versioning.py +77 -0
  270. setiastro/saspro/view_bundle.py +20 -17
  271. setiastro/saspro/wavescale_hdr.py +54 -33
  272. setiastro/saspro/wavescale_hdr_preset.py +6 -5
  273. setiastro/saspro/wavescalede.py +54 -31
  274. setiastro/saspro/wavescalede_preset.py +9 -7
  275. setiastro/saspro/whitebalance.py +58 -22
  276. setiastro/saspro/widgets/common_utilities.py +12 -11
  277. setiastro/saspro/widgets/minigame/game.js +991 -0
  278. setiastro/saspro/widgets/minigame/index.html +53 -0
  279. setiastro/saspro/widgets/minigame/style.css +241 -0
  280. setiastro/saspro/widgets/preview_dialogs.py +8 -8
  281. setiastro/saspro/widgets/resource_monitor.py +263 -0
  282. setiastro/saspro/widgets/spinboxes.py +18 -0
  283. setiastro/saspro/widgets/wavelet_utils.py +52 -20
  284. setiastro/saspro/wimi.py +7996 -0
  285. setiastro/saspro/wims.py +578 -0
  286. setiastro/saspro/window_shelf.py +2 -2
  287. {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/METADATA +15 -3
  288. setiastrosuitepro-1.6.4.post1.dist-info/RECORD +368 -0
  289. setiastrosuitepro-1.6.0.dist-info/RECORD +0 -174
  290. {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/WHEEL +0 -0
  291. {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/entry_points.txt +0 -0
  292. {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/licenses/LICENSE +0 -0
  293. {setiastrosuitepro-1.6.0.dist-info → setiastrosuitepro-1.6.4.post1.dist-info}/licenses/license.txt +0 -0
@@ -24,7 +24,7 @@ from setiastro.saspro.legacy.image_manager import load_image, save_image
24
24
  from setiastro.saspro.legacy.numba_utils import bulk_cosmetic_correction_numba
25
25
  from setiastro.saspro.imageops.stretch import stretch_mono_image, stretch_color_image
26
26
  from setiastro.saspro.star_alignment import PolyGradientRemoval
27
- from pro import minorbodycatalog as mbc
27
+ from setiastro.saspro import minorbodycatalog as mbc
28
28
  from setiastro.saspro.plate_solver import PlateSolverDialog as PlateSolver
29
29
  from setiastro.saspro.widgets.themed_buttons import themed_toolbtn
30
30
 
@@ -159,7 +159,7 @@ class MinorBodyWorker(QObject):
159
159
  def run(self):
160
160
  try:
161
161
  # Kick off with a low percentage
162
- self.progress.emit(0, "Minor-body search: preparing catalog query...")
162
+ self.progress.emit(0, self.tr("Minor-body search: preparing catalog query..."))
163
163
  bodies = self._owner._get_predicted_minor_bodies_for_field(
164
164
  H_ast_max=self._owner.minor_H_ast_max,
165
165
  H_com_max=self._owner.minor_H_com_max,
@@ -292,11 +292,11 @@ class ImagePreviewWindow(QDialog):
292
292
 
293
293
  # toolbar
294
294
  tb = QToolBar(self)
295
- self.act_fit = QAction("Fit", self)
296
- self.act_1to1 = QAction("1:1", self)
297
- self.act_zoom_in = QAction("Zoom In", self)
298
- self.act_zoom_out = QAction("Zoom Out", self)
299
- self.act_push = QAction("Push to New View", self)
295
+ self.act_fit = QAction(self.tr("Fit"), self)
296
+ self.act_1to1 = QAction(self.tr("1:1"), self)
297
+ self.act_zoom_in = QAction(self.tr("Zoom In"), self)
298
+ self.act_zoom_out = QAction(self.tr("Zoom Out"), self)
299
+ self.act_push = QAction(self.tr("Push to New View"), self)
300
300
  # self.act_minor = QAction("Check Catalogued Minor Bodies in Field", self)
301
301
 
302
302
  self.act_zoom_in.setShortcut("Ctrl++")
@@ -343,7 +343,7 @@ class ImagePreviewWindow(QDialog):
343
343
  def _on_push(self):
344
344
  # Emit the anomaly-marked image
345
345
  self.pushed.emit(self._original, self.windowTitle())
346
- QMessageBox.information(self, "Pushed", "New View Created.")
346
+ QMessageBox.information(self, self.tr("Pushed"), self.tr("New View Created."))
347
347
 
348
348
 
349
349
  def _on_minor_body_search(self):
@@ -361,7 +361,10 @@ class SupernovaAsteroidHunterDialog(QDialog):
361
361
  image_manager=None, doc_manager=None,
362
362
  supernova_path=None, wrench_path=None, spinner_path=None):
363
363
  super().__init__(parent)
364
- self.setWindowTitle("Supernova / Asteroid Hunter")
364
+ self.setWindowTitle(self.tr("Supernova / Asteroid Hunter"))
365
+ self.setWindowFlag(Qt.WindowType.Window, True)
366
+ self.setWindowModality(Qt.WindowModality.NonModal)
367
+ self.setModal(False)
365
368
  if supernova_path:
366
369
  self.setWindowIcon(QIcon(supernova_path))
367
370
  # keep icon path for previews
@@ -404,17 +407,17 @@ class SupernovaAsteroidHunterDialog(QDialog):
404
407
  layout = self.layout()
405
408
 
406
409
  # Instruction Label
407
- instructions = QLabel(
410
+ instructions = QLabel(self.tr(
408
411
  "Select the reference image and search images. "
409
412
  "Then click Process to hunt for anomalies."
410
- )
413
+ ))
411
414
  layout.addWidget(instructions)
412
415
 
413
416
  # --- Reference Image Selection ---
414
417
  ref_layout = QHBoxLayout()
415
418
  self.ref_line_edit = QLineEdit(self)
416
- self.ref_line_edit.setPlaceholderText("No reference image selected")
417
- self.ref_button = QPushButton("Select Reference Image", self)
419
+ self.ref_line_edit.setPlaceholderText(self.tr("No reference image selected"))
420
+ self.ref_button = QPushButton(self.tr("Select Reference Image"), self)
418
421
  self.ref_button.clicked.connect(self.selectReferenceImage)
419
422
  ref_layout.addWidget(self.ref_line_edit)
420
423
  ref_layout.addWidget(self.ref_button)
@@ -423,7 +426,7 @@ class SupernovaAsteroidHunterDialog(QDialog):
423
426
  # --- Search Images Selection ---
424
427
  search_layout = QHBoxLayout()
425
428
  self.search_list = QListWidget(self)
426
- self.search_button = QPushButton("Select Search Images", self)
429
+ self.search_button = QPushButton(self.tr("Select Search Images"), self)
427
430
  self.search_button.clicked.connect(self.selectSearchImages)
428
431
  search_layout.addWidget(self.search_list)
429
432
  search_layout.addWidget(self.search_button)
@@ -431,13 +434,13 @@ class SupernovaAsteroidHunterDialog(QDialog):
431
434
 
432
435
  # --- Cosmetic Correction Checkbox ---
433
436
  self.cosmetic_checkbox = QCheckBox(
434
- "Apply Cosmetic Correction before Preprocessing", self
437
+ self.tr("Apply Cosmetic Correction before Preprocessing"), self
435
438
  )
436
439
  layout.addWidget(self.cosmetic_checkbox)
437
440
 
438
441
  # --- Threshold Slider ---
439
442
  thresh_layout = QHBoxLayout()
440
- self.thresh_label = QLabel("Anomaly Detection Threshold: 0.10", self)
443
+ self.thresh_label = QLabel(self.tr("Anomaly Detection Threshold: 0.10"), self)
441
444
  self.thresh_slider = QSlider(Qt.Orientation.Horizontal, self)
442
445
  self.thresh_slider.setMinimum(1)
443
446
  self.thresh_slider.setMaximum(50) # Represents 0.01 to 0.50
@@ -449,19 +452,19 @@ class SupernovaAsteroidHunterDialog(QDialog):
449
452
 
450
453
  # --- Process Button ---
451
454
  self.process_button = QPushButton(
452
- "Process (Cosmetic Correction, Preprocess, and Search)", self
455
+ self.tr("Process (Cosmetic Correction, Preprocess, and Search)"), self
453
456
  )
454
457
  self.process_button.clicked.connect(self.process)
455
458
  layout.addWidget(self.process_button)
456
459
 
457
460
  # --- Progress Labels ---
458
- self.preprocess_progress_label = QLabel("Preprocessing progress: 0 / 0", self)
459
- self.search_progress_label = QLabel("Processing progress: 0 / 0", self)
461
+ self.preprocess_progress_label = QLabel(self.tr("Preprocessing progress: 0 / 0"), self)
462
+ self.search_progress_label = QLabel(self.tr("Processing progress: 0 / 0"), self)
460
463
  layout.addWidget(self.preprocess_progress_label)
461
464
  layout.addWidget(self.search_progress_label)
462
465
 
463
466
  # -- Status label --
464
- self.status_label = QLabel("Status: Idle", self)
467
+ self.status_label = QLabel(self.tr("Status: Idle"), self)
465
468
  layout.addWidget(self.status_label)
466
469
 
467
470
  # Minor-body progress bar (hidden by default)
@@ -472,7 +475,7 @@ class SupernovaAsteroidHunterDialog(QDialog):
472
475
  layout.addWidget(self.minor_progress)
473
476
 
474
477
  # --- New Instance Button ---
475
- self.new_instance_button = QPushButton("New Instance", self)
478
+ self.new_instance_button = QPushButton(self.tr("New Instance"), self)
476
479
  self.new_instance_button.clicked.connect(self.newInstance)
477
480
  layout.addWidget(self.new_instance_button)
478
481
 
@@ -484,18 +487,18 @@ class SupernovaAsteroidHunterDialog(QDialog):
484
487
  def updateThreshold(self, value):
485
488
  threshold = value / 100.0 # e.g. slider value 10 becomes 0.10
486
489
  self.parameters["threshold"] = threshold
487
- self.thresh_label.setText(f"Anomaly Detection Threshold: {threshold:.2f}")
490
+ self.thresh_label.setText(self.tr("Anomaly Detection Threshold: {0:.2f}").format(threshold))
488
491
 
489
492
  def selectReferenceImage(self):
490
- file_path, _ = QFileDialog.getOpenFileName(self, "Select Reference Image", "",
491
- "Images (*.png *.tif *.tiff *.fits *.fit *.xisf)")
493
+ file_path, _ = QFileDialog.getOpenFileName(self, self.tr("Select Reference Image"), "",
494
+ self.tr("Images (*.png *.tif *.tiff *.fits *.fit *.xisf)"))
492
495
  if file_path:
493
496
  self.parameters["referenceImagePath"] = file_path
494
497
  self.ref_line_edit.setText(os.path.basename(file_path))
495
498
 
496
499
  def selectSearchImages(self):
497
- file_paths, _ = QFileDialog.getOpenFileNames(self, "Select Search Images", "",
498
- "Images (*.png *.tif *.tiff *.fits *.fit *.xisf)")
500
+ file_paths, _ = QFileDialog.getOpenFileNames(self, self.tr("Select Search Images"), "",
501
+ self.tr("Images (*.png *.tif *.tiff *.fits *.fit *.xisf)"))
499
502
  if file_paths:
500
503
  self.parameters["searchImagePaths"] = file_paths
501
504
  self.search_list.clear()
@@ -503,24 +506,24 @@ class SupernovaAsteroidHunterDialog(QDialog):
503
506
  self.search_list.addItem(os.path.basename(path))
504
507
 
505
508
  def process(self):
506
- self.status_label.setText("Process started...")
509
+ self.status_label.setText(self.tr("Process started..."))
507
510
  QApplication.processEvents()
508
511
 
509
512
  # If cosmetic correction is enabled, run it first
510
513
  if self.cosmetic_checkbox.isChecked():
511
- self.status_label.setText("Running Cosmetic Correction...")
514
+ self.status_label.setText(self.tr("Running Cosmetic Correction..."))
512
515
  QApplication.processEvents()
513
516
  self.runCosmeticCorrectionIfNeeded()
514
517
 
515
- self.status_label.setText("Preprocessing images...")
518
+ self.status_label.setText(self.tr("Preprocessing images..."))
516
519
  QApplication.processEvents()
517
520
  self.preprocessImages()
518
521
 
519
- self.status_label.setText("Analyzing anomalies...")
522
+ self.status_label.setText(self.tr("Analyzing anomalies..."))
520
523
  QApplication.processEvents()
521
524
  self.runSearch()
522
525
 
523
- self.status_label.setText("Process complete.")
526
+ self.status_label.setText(self.tr("Process complete."))
524
527
  QApplication.processEvents()
525
528
 
526
529
 
@@ -534,7 +537,9 @@ class SupernovaAsteroidHunterDialog(QDialog):
534
537
  for idx, image_path in enumerate(self.parameters["searchImagePaths"]):
535
538
  try:
536
539
  # Update status label to show which image is being handled
537
- self.status_label.setText(f"Cosmetic Correction: {idx+1}/{len(self.parameters['searchImagePaths'])} => {os.path.basename(image_path)}")
540
+ self.status_label.setText(self.tr("Cosmetic Correction: {0}/{1} => {2}").format(
541
+ idx+1, len(self.parameters['searchImagePaths']), os.path.basename(image_path)
542
+ ))
538
543
  QApplication.processEvents()
539
544
 
540
545
  img, header, bit_depth, is_mono = load_image(image_path)
@@ -558,13 +563,13 @@ class SupernovaAsteroidHunterDialog(QDialog):
558
563
 
559
564
  def preprocessImages(self):
560
565
  # Update status label for reference image
561
- self.status_label.setText("Preprocessing reference image...")
566
+ self.status_label.setText(self.tr("Preprocessing reference image..."))
562
567
  print("[Preprocessing] Preprocessing reference image...")
563
568
  QApplication.processEvents()
564
569
 
565
570
  ref_path = self.parameters["referenceImagePath"]
566
571
  if not ref_path:
567
- QMessageBox.warning(self, "Error", "No reference image selected.")
572
+ QMessageBox.warning(self, self.tr("Error"), self.tr("No reference image selected."))
568
573
  return
569
574
 
570
575
  try:
@@ -623,11 +628,11 @@ class SupernovaAsteroidHunterDialog(QDialog):
623
628
  ref_processed = self.preprocessImage(ref_img, debug_prefix=debug_prefix_ref)
624
629
  self.preprocessed_reference = ref_processed
625
630
  self.preprocess_progress_label.setText(
626
- "Preprocessing reference image... Done."
631
+ self.tr("Preprocessing reference image... Done.")
627
632
  )
628
633
 
629
634
  except Exception as e:
630
- QMessageBox.critical(self, "Error", f"Failed to preprocess reference image: {e}")
635
+ QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to preprocess reference image: {0}").format(e))
631
636
  return
632
637
 
633
638
  # --- Preprocess search images ---
@@ -638,7 +643,9 @@ class SupernovaAsteroidHunterDialog(QDialog):
638
643
  for i, path in enumerate(search_paths):
639
644
  try:
640
645
  self.status_label.setText(
641
- f"Preprocessing search image {i+1}/{total} => {os.path.basename(path)}"
646
+ self.tr("Preprocessing search image {0}/{1} => {2}").format(
647
+ i+1, total, os.path.basename(path)
648
+ )
642
649
  )
643
650
  QApplication.processEvents()
644
651
 
@@ -653,14 +660,14 @@ class SupernovaAsteroidHunterDialog(QDialog):
653
660
  self.preprocessed_search.append({"path": path, "image": processed})
654
661
 
655
662
  self.preprocess_progress_label.setText(
656
- f"Preprocessing image {i+1} of {total}... Done."
663
+ self.tr("Preprocessing image {0} of {1}... Done.").format(i+1, total)
657
664
  )
658
665
  QApplication.processEvents()
659
666
 
660
667
  except Exception as e:
661
668
  print(f"Failed to preprocess {path}: {e}")
662
669
 
663
- self.status_label.setText("All search images preprocessed.")
670
+ self.status_label.setText(self.tr("All search images preprocessed."))
664
671
  QApplication.processEvents()
665
672
 
666
673
  def _ensure_wcs(self, ref_path: str):
@@ -736,7 +743,7 @@ class SupernovaAsteroidHunterDialog(QDialog):
736
743
  False if they cancelled.
737
744
  """
738
745
  dlg = QDialog(self)
739
- dlg.setWindowTitle("Minor-body Search Limits")
746
+ dlg.setWindowTitle(self.tr("Minor-body Search Limits"))
740
747
  layout = QVBoxLayout(dlg)
741
748
 
742
749
  row_layout = QGridLayout()
@@ -837,29 +844,29 @@ class SupernovaAsteroidHunterDialog(QDialog):
837
844
  print("[MinorBodies] prediction failed:", error)
838
845
  QMessageBox.critical(
839
846
  self,
840
- "Minor-body Search",
841
- f"Minor-body prediction failed:\n{error}"
847
+ self.tr("Minor-body Search"),
848
+ self.tr("Minor-body prediction failed:\n{0}").format(error)
842
849
  )
843
- self.status_label.setText("Minor-body search failed.")
850
+ self.status_label.setText(self.tr("Minor-body search failed."))
844
851
  return
845
852
 
846
853
  self.predicted_minor_bodies = bodies or []
847
854
 
848
855
  if not self.predicted_minor_bodies:
849
856
  self.status_label.setText(
850
- "Minor-body search complete: no catalogued objects in this field "
851
- "for the current magnitude limits."
857
+ self.tr("Minor-body search complete: no catalogued objects in this field "
858
+ "for the current magnitude limits.")
852
859
  )
853
860
  QMessageBox.information(
854
861
  self,
855
- "Minor-body Search",
856
- "No catalogued minor bodies (within the configured magnitude limits) "
857
- "were found in this field."
862
+ self.tr("Minor-body Search"),
863
+ self.tr("No catalogued minor bodies (within the configured magnitude limits) "
864
+ "were found in this field.")
858
865
  )
859
866
  return
860
867
 
861
868
  self.status_label.setText(
862
- f"Minor-body search complete: {len(self.predicted_minor_bodies)} objects in field."
869
+ self.tr("Minor-body search complete: {0} objects in field.").format(len(self.predicted_minor_bodies))
863
870
  )
864
871
  QApplication.processEvents()
865
872
 
@@ -876,10 +883,10 @@ class SupernovaAsteroidHunterDialog(QDialog):
876
883
  else:
877
884
  QMessageBox.information(
878
885
  self,
879
- "Minor-body Search",
880
- "Minor bodies in field have been computed.\n\n"
881
- "Run the anomaly search (Process) to cross-match detections "
882
- "against the predicted objects."
886
+ self.tr("Minor-body Search"),
887
+ self.tr("Minor bodies in field have been computed.\n\n"
888
+ "Run the anomaly search (Process) to cross-match detections "
889
+ "against the predicted objects.")
883
890
  )
884
891
  except Exception as e:
885
892
  print("[MinorBodies] cross-match failed:", e)
@@ -897,26 +904,26 @@ class SupernovaAsteroidHunterDialog(QDialog):
897
904
  if not ref_path:
898
905
  QMessageBox.warning(
899
906
  self,
900
- "Minor-body Search",
901
- "No reference image selected.\n\n"
902
- "Please select a reference image and run Process first."
907
+ self.tr("Minor-body Search"),
908
+ self.tr("No reference image selected.\n\n"
909
+ "Please select a reference image and run Process first.")
903
910
  )
904
911
  return
905
912
 
906
913
  if self.preprocessed_reference is None:
907
914
  QMessageBox.warning(
908
915
  self,
909
- "Minor-body Search",
910
- "Reference image has not been preprocessed yet.\n\n"
911
- "Please click 'Process' before running the minor-body search."
916
+ self.tr("Minor-body Search"),
917
+ self.tr("Reference image has not been preprocessed yet.\n\n"
918
+ "Please click 'Process' before running the minor-body search.")
912
919
  )
913
920
  return
914
921
 
915
922
  if self.settings is None:
916
923
  QMessageBox.warning(
917
924
  self,
918
- "Minor-body Search",
919
- "Settings object is not available; cannot locate the minor-body database path."
925
+ self.tr("Minor-body Search"),
926
+ self.tr("Settings object is not available; cannot locate the minor-body database path.")
920
927
  )
921
928
  return
922
929
 
@@ -1,10 +1,5 @@
1
- import os
2
- import shutil
3
- import tempfile
4
- import uuid
5
- import pickle
6
- import atexit
7
- import threading
1
+ import os, shutil, tempfile, uuid, atexit, threading
2
+ from collections import OrderedDict
8
3
  import numpy as np
9
4
 
10
5
  class SwapManager:
@@ -14,66 +9,110 @@ class SwapManager:
14
9
  def __new__(cls, *args, **kwargs):
15
10
  with cls._lock:
16
11
  if cls._instance is None:
17
- cls._instance = super(SwapManager, cls).__new__(cls)
12
+ cls._instance = super().__new__(cls)
18
13
  cls._instance._initialized = False
19
14
  return cls._instance
20
15
 
21
- def __init__(self):
16
+ def __init__(self, *, cache_bytes: int = 1_000_000_000):
22
17
  if self._initialized:
23
18
  return
24
19
  self._initialized = True
25
-
26
- # Create a unique temp directory for this session
27
- self.temp_dir = os.path.join(tempfile.gettempdir(), "SetiAstroSuitePro_Swap", str(uuid.uuid4()))
20
+
21
+ self.temp_dir = os.path.join(
22
+ tempfile.gettempdir(), "SetiAstroSuitePro_Swap", str(uuid.uuid4())
23
+ )
28
24
  os.makedirs(self.temp_dir, exist_ok=True)
29
-
30
- # Register cleanup on exit
31
25
  atexit.register(self.cleanup_all)
32
26
 
27
+ # LRU of in-RAM states: swap_id -> ndarray
28
+ self._cache = OrderedDict()
29
+ self._cache_bytes = int(cache_bytes)
30
+ self._cache_used = 0
31
+ self._cache_lock = threading.Lock()
32
+
33
33
  def get_swap_path(self, swap_id: str) -> str:
34
- return os.path.join(self.temp_dir, f"{swap_id}.swap")
34
+ # store as .npy (fast + supports mmap)
35
+ return os.path.join(self.temp_dir, f"{swap_id}.npy")
36
+
37
+ def _arr_nbytes(self, a: np.ndarray) -> int:
38
+ try:
39
+ return int(a.nbytes)
40
+ except Exception:
41
+ return 0
42
+
43
+ def _cache_put(self, swap_id: str, arr: np.ndarray):
44
+ if arr is None:
45
+ return
46
+ n = self._arr_nbytes(arr)
47
+ if n <= 0:
48
+ return
35
49
 
36
- def save_state(self, image: np.ndarray) -> str:
37
- """
38
- Save the image array to a swap file.
39
- Returns the unique swap_id.
40
- """
50
+ with self._cache_lock:
51
+ # If already present, refresh
52
+ old = self._cache.pop(swap_id, None)
53
+ if old is not None:
54
+ self._cache_used -= self._arr_nbytes(old)
55
+
56
+ self._cache[swap_id] = arr
57
+ self._cache_used += n
58
+
59
+ # Evict LRU until under budget
60
+ while self._cache_used > self._cache_bytes and self._cache:
61
+ k, v = self._cache.popitem(last=False)
62
+ self._cache_used -= self._arr_nbytes(v)
63
+
64
+ def _cache_get(self, swap_id: str):
65
+ with self._cache_lock:
66
+ arr = self._cache.pop(swap_id, None)
67
+ if arr is None:
68
+ return None
69
+ # move to MRU
70
+ self._cache[swap_id] = arr
71
+ return arr
72
+
73
+ def save_state(self, image: np.ndarray) -> str | None:
41
74
  swap_id = uuid.uuid4().hex
42
75
  path = self.get_swap_path(swap_id)
43
-
44
- # We only save the image data to disk. Metadata is kept in RAM by the caller.
45
- # Using pickle for simplicity and robustness with numpy arrays.
46
- # For pure numpy arrays, np.save might be slightly faster, but pickle is more flexible if we change what we store.
47
- # Let's stick to pickle for now as per plan.
48
76
  try:
49
- with open(path, "wb") as f:
50
- pickle.dump(image, f, protocol=pickle.HIGHEST_PROTOCOL)
77
+ # Write fast .npy
78
+ np.save(path, image, allow_pickle=False)
79
+ # Optionally keep it hot in RAM too (depends how you use it)
80
+ self._cache_put(swap_id, image)
81
+ return swap_id
51
82
  except Exception as e:
52
83
  print(f"[SwapManager] Failed to save state {swap_id}: {e}")
53
84
  return None
54
-
55
- return swap_id
56
85
 
57
86
  def load_state(self, swap_id: str) -> np.ndarray | None:
58
- """
59
- Load the image array from the swap file.
60
- """
87
+ #print("[SwapManager] LOAD", swap_id)
88
+ # First: try RAM
89
+ hot = self._cache_get(swap_id)
90
+ if hot is not None:
91
+ return hot
92
+
61
93
  path = self.get_swap_path(swap_id)
62
94
  if not os.path.exists(path):
63
95
  print(f"[SwapManager] Swap file not found: {path}")
64
96
  return None
65
-
97
+
66
98
  try:
67
- with open(path, "rb") as f:
68
- return pickle.load(f)
99
+ # mmap_mode="r" is extremely fast; convert to real ndarray only if needed
100
+ arr = np.load(path, mmap_mode="r", allow_pickle=False)
101
+ # If your pipeline needs a writable array, materialize:
102
+ # arr = np.array(arr, copy=True)
103
+ # Cache the loaded array (mmap object still OK to cache; you can decide)
104
+ self._cache_put(swap_id, np.array(arr, copy=False))
105
+ return np.array(arr, copy=False)
69
106
  except Exception as e:
70
107
  print(f"[SwapManager] Failed to load state {swap_id}: {e}")
71
108
  return None
72
109
 
73
110
  def delete_state(self, swap_id: str):
74
- """
75
- Delete a specific swap file.
76
- """
111
+ with self._cache_lock:
112
+ old = self._cache.pop(swap_id, None)
113
+ if old is not None:
114
+ self._cache_used -= self._arr_nbytes(old)
115
+
77
116
  path = self.get_swap_path(swap_id)
78
117
  try:
79
118
  if os.path.exists(path):
@@ -82,13 +121,9 @@ class SwapManager:
82
121
  print(f"[SwapManager] Failed to delete state {swap_id}: {e}")
83
122
 
84
123
  def cleanup_all(self):
85
- """
86
- Delete the entire temporary directory for this session.
87
- """
88
124
  try:
89
125
  if os.path.exists(self.temp_dir):
90
126
  shutil.rmtree(self.temp_dir, ignore_errors=True)
91
- # print(f"[SwapManager] Cleaned up {self.temp_dir}")
92
127
  except Exception as e:
93
128
  print(f"[SwapManager] Cleanup failed: {e}")
94
129