setiastrosuitepro 1.6.2__py3-none-any.whl → 1.6.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. setiastro/images/rotatearbitrary.png +0 -0
  2. setiastro/saspro/_generated/build_info.py +2 -2
  3. setiastro/saspro/backgroundneutral.py +10 -1
  4. setiastro/saspro/blink_comparator_pro.py +474 -251
  5. setiastro/saspro/crop_dialog_pro.py +11 -1
  6. setiastro/saspro/doc_manager.py +1 -1
  7. setiastro/saspro/function_bundle.py +16 -16
  8. setiastro/saspro/gui/main_window.py +93 -64
  9. setiastro/saspro/gui/mixins/dock_mixin.py +31 -18
  10. setiastro/saspro/gui/mixins/geometry_mixin.py +105 -5
  11. setiastro/saspro/gui/mixins/menu_mixin.py +1 -0
  12. setiastro/saspro/gui/mixins/toolbar_mixin.py +33 -10
  13. setiastro/saspro/multiscale_decomp.py +710 -256
  14. setiastro/saspro/remove_stars_preset.py +55 -13
  15. setiastro/saspro/resources.py +30 -11
  16. setiastro/saspro/selective_color.py +79 -20
  17. setiastro/saspro/shortcuts.py +94 -21
  18. setiastro/saspro/stacking_suite.py +296 -107
  19. setiastro/saspro/star_alignment.py +275 -330
  20. setiastro/saspro/status_log_dock.py +1 -1
  21. setiastro/saspro/swap_manager.py +77 -42
  22. setiastro/saspro/translations/all_source_strings.json +1588 -516
  23. setiastro/saspro/translations/ar_translations.py +915 -684
  24. setiastro/saspro/translations/de_translations.py +442 -463
  25. setiastro/saspro/translations/es_translations.py +277 -47
  26. setiastro/saspro/translations/fr_translations.py +279 -47
  27. setiastro/saspro/translations/hi_translations.py +253 -21
  28. setiastro/saspro/translations/integrate_translations.py +3 -2
  29. setiastro/saspro/translations/it_translations.py +1211 -161
  30. setiastro/saspro/translations/ja_translations.py +3340 -3107
  31. setiastro/saspro/translations/pt_translations.py +3315 -3337
  32. setiastro/saspro/translations/ru_translations.py +351 -117
  33. setiastro/saspro/translations/saspro_ar.qm +0 -0
  34. setiastro/saspro/translations/saspro_ar.ts +15902 -138
  35. setiastro/saspro/translations/saspro_de.qm +0 -0
  36. setiastro/saspro/translations/saspro_de.ts +14428 -133
  37. setiastro/saspro/translations/saspro_es.qm +0 -0
  38. setiastro/saspro/translations/saspro_es.ts +11503 -7821
  39. setiastro/saspro/translations/saspro_fr.qm +0 -0
  40. setiastro/saspro/translations/saspro_fr.ts +11168 -7812
  41. setiastro/saspro/translations/saspro_hi.qm +0 -0
  42. setiastro/saspro/translations/saspro_hi.ts +14733 -135
  43. setiastro/saspro/translations/saspro_it.qm +0 -0
  44. setiastro/saspro/translations/saspro_it.ts +14347 -7821
  45. setiastro/saspro/translations/saspro_ja.qm +0 -0
  46. setiastro/saspro/translations/saspro_ja.ts +14860 -137
  47. setiastro/saspro/translations/saspro_pt.qm +0 -0
  48. setiastro/saspro/translations/saspro_pt.ts +14904 -137
  49. setiastro/saspro/translations/saspro_ru.qm +0 -0
  50. setiastro/saspro/translations/saspro_ru.ts +11766 -168
  51. setiastro/saspro/translations/saspro_sw.qm +0 -0
  52. setiastro/saspro/translations/saspro_sw.ts +15115 -135
  53. setiastro/saspro/translations/saspro_uk.qm +0 -0
  54. setiastro/saspro/translations/saspro_uk.ts +11206 -6729
  55. setiastro/saspro/translations/saspro_zh.qm +0 -0
  56. setiastro/saspro/translations/saspro_zh.ts +10581 -7812
  57. setiastro/saspro/translations/sw_translations.py +282 -56
  58. setiastro/saspro/translations/uk_translations.py +264 -35
  59. setiastro/saspro/translations/zh_translations.py +282 -47
  60. setiastro/saspro/view_bundle.py +17 -17
  61. setiastro/saspro/widgets/minigame/game.js +11 -6
  62. setiastro/saspro/widgets/resource_monitor.py +26 -0
  63. setiastro/saspro/widgets/spinboxes.py +18 -0
  64. setiastro/saspro/wimi.py +65 -65
  65. setiastro/saspro/wims.py +33 -33
  66. setiastro/saspro/window_shelf.py +2 -2
  67. {setiastrosuitepro-1.6.2.dist-info → setiastrosuitepro-1.6.4.dist-info}/METADATA +7 -7
  68. {setiastrosuitepro-1.6.2.dist-info → setiastrosuitepro-1.6.4.dist-info}/RECORD +72 -71
  69. {setiastrosuitepro-1.6.2.dist-info → setiastrosuitepro-1.6.4.dist-info}/WHEEL +0 -0
  70. {setiastrosuitepro-1.6.2.dist-info → setiastrosuitepro-1.6.4.dist-info}/entry_points.txt +0 -0
  71. {setiastrosuitepro-1.6.2.dist-info → setiastrosuitepro-1.6.4.dist-info}/licenses/LICENSE +0 -0
  72. {setiastrosuitepro-1.6.2.dist-info → setiastrosuitepro-1.6.4.dist-info}/licenses/license.txt +0 -0
@@ -9,7 +9,7 @@ class StatusLogDock(QDockWidget):
9
9
  MAX_BLOCKS = 2000
10
10
 
11
11
  def __init__(self, parent=None):
12
- super().__init__("Stacking Log", parent)
12
+ super().__init__(self.tr("Stacking Log"), parent)
13
13
  self.setObjectName("StackingLogDock")
14
14
  self.setAllowedAreas(
15
15
  Qt.DockWidgetArea.BottomDockWidgetArea
@@ -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