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
|
@@ -10,6 +10,10 @@ from PyQt6.QtCore import Qt, QTimer, QUrl
|
|
|
10
10
|
from PyQt6.QtGui import QAction, QActionGroup, QIcon, QKeySequence, QDesktopServices
|
|
11
11
|
from PyQt6.QtWidgets import QMenu, QToolButton
|
|
12
12
|
|
|
13
|
+
from PyQt6.QtCore import QElapsedTimer
|
|
14
|
+
|
|
15
|
+
import sys
|
|
16
|
+
|
|
13
17
|
if TYPE_CHECKING:
|
|
14
18
|
pass
|
|
15
19
|
|
|
@@ -20,7 +24,7 @@ from setiastro.saspro.resources import (
|
|
|
20
24
|
LInsert_path, rgbcombo_path, rgbextract_path, graxperticon_path,
|
|
21
25
|
cropicon_path, openfile_path, abeicon_path, undoicon_path, redoicon_path,
|
|
22
26
|
blastericon_path, hdr_path, invert_path, fliphorizontal_path,
|
|
23
|
-
flipvertical_path, rotateclockwise_path, rotatecounterclockwise_path,
|
|
27
|
+
flipvertical_path, rotateclockwise_path, rotatecounterclockwise_path,rotatearbitrary_path,
|
|
24
28
|
rotate180_path, maskcreate_path, maskapply_path, maskremove_path,
|
|
25
29
|
pixelmath_path, histogram_path, mosaic_path, rescale_path, staralign_path,
|
|
26
30
|
platesolve_path, psf_path, supernova_path, starregistration_path,
|
|
@@ -73,7 +77,8 @@ class ToolbarMixin:
|
|
|
73
77
|
|
|
74
78
|
def _init_toolbar(self):
|
|
75
79
|
# View toolbar (Undo / Redo / Display-Stretch)
|
|
76
|
-
tb = DraggableToolBar("View", self)
|
|
80
|
+
tb = DraggableToolBar(self.tr("View"), self)
|
|
81
|
+
tb.setObjectName("View")
|
|
77
82
|
tb.setSettingsKey("Toolbar/View")
|
|
78
83
|
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, tb)
|
|
79
84
|
|
|
@@ -175,7 +180,8 @@ class ToolbarMixin:
|
|
|
175
180
|
pass
|
|
176
181
|
|
|
177
182
|
# Functions toolbar
|
|
178
|
-
tb_fn = DraggableToolBar("Functions", self)
|
|
183
|
+
tb_fn = DraggableToolBar(self.tr("Functions"), self)
|
|
184
|
+
tb_fn.setObjectName("Functions")
|
|
179
185
|
tb_fn.setSettingsKey("Toolbar/Functions")
|
|
180
186
|
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, tb_fn)
|
|
181
187
|
|
|
@@ -198,16 +204,16 @@ class ToolbarMixin:
|
|
|
198
204
|
tb_fn.addAction(self.act_convo)
|
|
199
205
|
tb_fn.addAction(self.act_extract_luma)
|
|
200
206
|
|
|
201
|
-
btn_luma = tb_fn.widgetForAction(self.act_extract_luma)
|
|
202
|
-
if isinstance(btn_luma, QToolButton):
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
207
|
+
#btn_luma = tb_fn.widgetForAction(self.act_extract_luma)
|
|
208
|
+
#if isinstance(btn_luma, QToolButton):
|
|
209
|
+
# luma_menu = QMenu(btn_luma)
|
|
210
|
+
# luma_menu.addActions(self._luma_group.actions())
|
|
211
|
+
# btn_luma.setMenu(luma_menu)
|
|
212
|
+
# btn_luma.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
|
|
213
|
+
# btn_luma.setStyleSheet("""
|
|
214
|
+
# QToolButton { color: #dcdcdc; }
|
|
215
|
+
# QToolButton:pressed, QToolButton:checked { color: #DAA520; font-weight: 600; }
|
|
216
|
+
# """)
|
|
211
217
|
|
|
212
218
|
tb_fn.addAction(self.act_recombine_luma)
|
|
213
219
|
tb_fn.addAction(self.act_rgb_extract)
|
|
@@ -227,7 +233,8 @@ class ToolbarMixin:
|
|
|
227
233
|
except Exception:
|
|
228
234
|
pass
|
|
229
235
|
|
|
230
|
-
tbCosmic = DraggableToolBar("Cosmic Clarity", self)
|
|
236
|
+
tbCosmic = DraggableToolBar(self.tr("Cosmic Clarity"), self)
|
|
237
|
+
tbCosmic.setObjectName("Cosmic Clarity")
|
|
231
238
|
tbCosmic.setSettingsKey("Toolbar/Cosmic")
|
|
232
239
|
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, tbCosmic)
|
|
233
240
|
|
|
@@ -241,7 +248,8 @@ class ToolbarMixin:
|
|
|
241
248
|
except Exception:
|
|
242
249
|
pass
|
|
243
250
|
|
|
244
|
-
tb_tl = DraggableToolBar("Tools", self)
|
|
251
|
+
tb_tl = DraggableToolBar(self.tr("Tools"), self)
|
|
252
|
+
tb_tl.setObjectName("Tools")
|
|
245
253
|
tb_tl.setSettingsKey("Toolbar/Tools")
|
|
246
254
|
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, tb_tl)
|
|
247
255
|
|
|
@@ -260,7 +268,8 @@ class ToolbarMixin:
|
|
|
260
268
|
except Exception:
|
|
261
269
|
pass
|
|
262
270
|
|
|
263
|
-
tb_geom = DraggableToolBar("Geometry", self)
|
|
271
|
+
tb_geom = DraggableToolBar(self.tr("Geometry"), self)
|
|
272
|
+
tb_geom.setObjectName("Geometry")
|
|
264
273
|
tb_geom.setSettingsKey("Toolbar/Geometry")
|
|
265
274
|
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, tb_geom)
|
|
266
275
|
|
|
@@ -272,6 +281,7 @@ class ToolbarMixin:
|
|
|
272
281
|
tb_geom.addAction(self.act_geom_rot_cw)
|
|
273
282
|
tb_geom.addAction(self.act_geom_rot_ccw)
|
|
274
283
|
tb_geom.addAction(self.act_geom_rot_180)
|
|
284
|
+
tb_geom.addAction(self.act_geom_rot_any)
|
|
275
285
|
tb_geom.addSeparator()
|
|
276
286
|
tb_geom.addAction(self.act_geom_rescale)
|
|
277
287
|
tb_geom.addSeparator()
|
|
@@ -283,7 +293,8 @@ class ToolbarMixin:
|
|
|
283
293
|
except Exception:
|
|
284
294
|
pass
|
|
285
295
|
|
|
286
|
-
tb_star = DraggableToolBar("Star Stuff", self)
|
|
296
|
+
tb_star = DraggableToolBar(self.tr("Star Stuff"), self)
|
|
297
|
+
tb_star.setObjectName("Star Stuff")
|
|
287
298
|
tb_star.setSettingsKey("Toolbar/StarStuff")
|
|
288
299
|
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, tb_star)
|
|
289
300
|
|
|
@@ -308,7 +319,8 @@ class ToolbarMixin:
|
|
|
308
319
|
except Exception:
|
|
309
320
|
pass
|
|
310
321
|
|
|
311
|
-
tb_msk = DraggableToolBar("Masks", self)
|
|
322
|
+
tb_msk = DraggableToolBar(self.tr("Masks"), self)
|
|
323
|
+
tb_msk.setObjectName("Masks")
|
|
312
324
|
tb_msk.setSettingsKey("Toolbar/Masks")
|
|
313
325
|
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, tb_msk)
|
|
314
326
|
|
|
@@ -322,7 +334,8 @@ class ToolbarMixin:
|
|
|
322
334
|
except Exception:
|
|
323
335
|
pass
|
|
324
336
|
|
|
325
|
-
tb_wim = DraggableToolBar("What's In My...", self)
|
|
337
|
+
tb_wim = DraggableToolBar(self.tr("What's In My..."), self)
|
|
338
|
+
tb_wim.setObjectName("What's In My...")
|
|
326
339
|
tb_wim.setSettingsKey("Toolbar/WhatsInMy")
|
|
327
340
|
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, tb_wim)
|
|
328
341
|
|
|
@@ -335,7 +348,8 @@ class ToolbarMixin:
|
|
|
335
348
|
except Exception:
|
|
336
349
|
pass
|
|
337
350
|
|
|
338
|
-
tb_bundle = DraggableToolBar("Bundles", self)
|
|
351
|
+
tb_bundle = DraggableToolBar(self.tr("Bundles"), self)
|
|
352
|
+
tb_bundle.setObjectName("Bundles")
|
|
339
353
|
tb_bundle.setSettingsKey("Toolbar/Bundles")
|
|
340
354
|
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, tb_bundle)
|
|
341
355
|
|
|
@@ -348,9 +362,25 @@ class ToolbarMixin:
|
|
|
348
362
|
except Exception:
|
|
349
363
|
pass
|
|
350
364
|
|
|
365
|
+
|
|
366
|
+
tb_hidden = DraggableToolBar(self.tr("Hidden"), self)
|
|
367
|
+
tb_hidden.setObjectName("Hidden")
|
|
368
|
+
tb_hidden.setSettingsKey("Toolbar/Hidden")
|
|
369
|
+
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, tb_hidden)
|
|
370
|
+
tb_hidden.setVisible(False) # <- always hidden
|
|
371
|
+
|
|
351
372
|
# This can move actions between toolbars, so do it after each toolbar has its base order restored.
|
|
352
373
|
self._restore_toolbar_memberships()
|
|
353
374
|
|
|
375
|
+
# Migrate legacy per-toolbar Hidden lists into the new Hidden toolbar
|
|
376
|
+
self._migrate_old_hidden_sets_to_hidden_toolbar()
|
|
377
|
+
|
|
378
|
+
# (Optional) Re-apply per-toolbar order after migration (since we removed actions)
|
|
379
|
+
for _tb in self.findChildren(DraggableToolBar):
|
|
380
|
+
key = getattr(_tb, "_settings_key", None)
|
|
381
|
+
if key:
|
|
382
|
+
self._restore_toolbar_order(_tb, str(key))
|
|
383
|
+
|
|
354
384
|
# Re-apply hidden state AFTER memberships (actions may have moved toolbars).
|
|
355
385
|
# This also guarantees correctness even if any toolbar was rebuilt/adjusted internally.
|
|
356
386
|
for _tb in self.findChildren(DraggableToolBar):
|
|
@@ -359,7 +389,9 @@ class ToolbarMixin:
|
|
|
359
389
|
except Exception:
|
|
360
390
|
pass
|
|
361
391
|
|
|
392
|
+
# Rebind ALL dropdowns after reorder/membership moves
|
|
362
393
|
self._rebind_view_dropdowns()
|
|
394
|
+
self._rebind_extract_luma_dropdown()
|
|
363
395
|
|
|
364
396
|
def _toolbar_containing_action(self, action: QAction):
|
|
365
397
|
from setiastro.saspro.shortcuts import DraggableToolBar
|
|
@@ -368,6 +400,140 @@ class ToolbarMixin:
|
|
|
368
400
|
return tb
|
|
369
401
|
return None
|
|
370
402
|
|
|
403
|
+
def _rebind_extract_luma_dropdown(self):
|
|
404
|
+
from PyQt6.QtWidgets import QMenu, QToolButton
|
|
405
|
+
from setiastro.saspro.luminancerecombine import LUMA_PROFILES
|
|
406
|
+
|
|
407
|
+
tb = self._toolbar_containing_action(self.act_extract_luma)
|
|
408
|
+
if not tb:
|
|
409
|
+
return
|
|
410
|
+
|
|
411
|
+
btn = tb.widgetForAction(self.act_extract_luma)
|
|
412
|
+
if not isinstance(btn, QToolButton):
|
|
413
|
+
return
|
|
414
|
+
|
|
415
|
+
menu = QMenu(btn)
|
|
416
|
+
|
|
417
|
+
# Ensure cache exists (created in _create_actions(), but be defensive)
|
|
418
|
+
if not hasattr(self, "_luma_sensor_actions"):
|
|
419
|
+
self._luma_sensor_actions = {} # key -> QAction
|
|
420
|
+
|
|
421
|
+
cur_method = str(getattr(self, "luma_method", "rec709"))
|
|
422
|
+
|
|
423
|
+
# ============================================================
|
|
424
|
+
# PATCH SITE #1 (Standard menu) <-- THIS IS WHERE "C" GOES
|
|
425
|
+
# Find your old block:
|
|
426
|
+
#
|
|
427
|
+
# # ---- Standard (use your QActionGroup, keep it simple) ----
|
|
428
|
+
# if getattr(self, "_luma_group", None) is not None:
|
|
429
|
+
# std_menu = QMenu(self.tr("Standard"), menu)
|
|
430
|
+
# std_menu.addActions(self._luma_group.actions())
|
|
431
|
+
# menu.addMenu(std_menu)
|
|
432
|
+
#
|
|
433
|
+
# Replace it with the block below.
|
|
434
|
+
# ============================================================
|
|
435
|
+
if getattr(self, "_luma_group", None) is not None:
|
|
436
|
+
# Sync standard checked states from self.luma_method
|
|
437
|
+
for a in self._luma_group.actions():
|
|
438
|
+
data = str(a.data() or "")
|
|
439
|
+
if data.startswith("sensor:"):
|
|
440
|
+
continue # standards only in Standard menu
|
|
441
|
+
a.blockSignals(True)
|
|
442
|
+
try:
|
|
443
|
+
a.setChecked(data == cur_method)
|
|
444
|
+
finally:
|
|
445
|
+
a.blockSignals(False)
|
|
446
|
+
|
|
447
|
+
# Add ONLY standard actions to the Standard menu (exclude sensors)
|
|
448
|
+
std_menu = QMenu(self.tr("Standard"), menu)
|
|
449
|
+
for a in self._luma_group.actions():
|
|
450
|
+
data = str(a.data() or "")
|
|
451
|
+
if data.startswith("sensor:"):
|
|
452
|
+
continue
|
|
453
|
+
std_menu.addAction(a)
|
|
454
|
+
menu.addMenu(std_menu)
|
|
455
|
+
|
|
456
|
+
# ---- Sensors (nested by category path) ----
|
|
457
|
+
sensors_root = QMenu(self.tr("Sensors"), menu)
|
|
458
|
+
|
|
459
|
+
# Build tree of submenus keyed by "Sensors/<path>"
|
|
460
|
+
submenu_cache: dict[str, QMenu] = {}
|
|
461
|
+
|
|
462
|
+
def get_or_make_path(root_menu: QMenu, path: str) -> QMenu:
|
|
463
|
+
parts = [p for p in path.split("/") if p.strip()]
|
|
464
|
+
cur = root_menu
|
|
465
|
+
acc = ""
|
|
466
|
+
for part in parts:
|
|
467
|
+
acc = f"{acc}/{part}" if acc else part
|
|
468
|
+
if acc not in submenu_cache:
|
|
469
|
+
sm = QMenu(self.tr(part), cur)
|
|
470
|
+
cur.addMenu(sm)
|
|
471
|
+
submenu_cache[acc] = sm
|
|
472
|
+
cur = submenu_cache[acc]
|
|
473
|
+
return cur
|
|
474
|
+
|
|
475
|
+
any_sensor = False
|
|
476
|
+
for key, prof in LUMA_PROFILES.items():
|
|
477
|
+
if not str(key).startswith("sensor:"):
|
|
478
|
+
continue
|
|
479
|
+
|
|
480
|
+
cat = str(prof.get("category", "Sensors/Other"))
|
|
481
|
+
group_path = cat.split("Sensors/", 1)[1] if "Sensors/" in cat else cat
|
|
482
|
+
parent_menu = get_or_make_path(sensors_root, group_path)
|
|
483
|
+
|
|
484
|
+
display_name = key.split("sensor:", 1)[1].strip()
|
|
485
|
+
desc = str(prof.get("description", display_name))
|
|
486
|
+
info = str(prof.get("info", "")).strip()
|
|
487
|
+
|
|
488
|
+
# ============================================================
|
|
489
|
+
# PATCH SITE #2 (Sensors become mutually exclusive with standards)
|
|
490
|
+
# - Cache the QAction so we don't pile up connections/actions
|
|
491
|
+
# - Add it to the SAME QActionGroup (exclusive) as standards
|
|
492
|
+
# - Do NOT use _pick_sensor; rely on QActionGroup.triggered
|
|
493
|
+
# ============================================================
|
|
494
|
+
act = self._luma_sensor_actions.get(key)
|
|
495
|
+
if act is None:
|
|
496
|
+
act = parent_menu.addAction(self.tr(display_name))
|
|
497
|
+
act.setCheckable(True)
|
|
498
|
+
act.setData(key) # IMPORTANT: enables group.triggered to set luma_method
|
|
499
|
+
|
|
500
|
+
# Put sensors into the SAME exclusive group so selecting one
|
|
501
|
+
# deselects everything else (standards and other sensors).
|
|
502
|
+
if getattr(self, "_luma_group", None) is not None:
|
|
503
|
+
self._luma_group.addAction(act)
|
|
504
|
+
|
|
505
|
+
self._luma_sensor_actions[key] = act
|
|
506
|
+
else:
|
|
507
|
+
# Reuse action, but re-add it to the correct submenu location
|
|
508
|
+
parent_menu.addAction(act)
|
|
509
|
+
act.setText(self.tr(display_name))
|
|
510
|
+
|
|
511
|
+
# Update UI info each bind (safe if translations change)
|
|
512
|
+
if info:
|
|
513
|
+
act.setStatusTip(info)
|
|
514
|
+
act.setToolTip(f"{desc}\n{info}")
|
|
515
|
+
else:
|
|
516
|
+
act.setToolTip(desc)
|
|
517
|
+
|
|
518
|
+
# Checked state reflects current selection
|
|
519
|
+
act.blockSignals(True)
|
|
520
|
+
try:
|
|
521
|
+
act.setChecked(cur_method == str(key))
|
|
522
|
+
finally:
|
|
523
|
+
act.blockSignals(False)
|
|
524
|
+
|
|
525
|
+
any_sensor = True
|
|
526
|
+
|
|
527
|
+
if any_sensor:
|
|
528
|
+
menu.addMenu(sensors_root)
|
|
529
|
+
|
|
530
|
+
btn.setMenu(menu)
|
|
531
|
+
btn.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
|
|
532
|
+
btn.setStyleSheet("""
|
|
533
|
+
QToolButton { color: #dcdcdc; }
|
|
534
|
+
QToolButton:pressed, QToolButton:checked { color: #DAA520; font-weight: 600; }
|
|
535
|
+
""")
|
|
536
|
+
|
|
371
537
|
|
|
372
538
|
def _rebind_view_dropdowns(self):
|
|
373
539
|
"""
|
|
@@ -429,7 +595,16 @@ class ToolbarMixin:
|
|
|
429
595
|
fit_menu.addAction(self.act_auto_fit_resize) # use the real action
|
|
430
596
|
btn_fit.setMenu(fit_menu)
|
|
431
597
|
btn_fit.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
|
|
598
|
+
tb = self._toolbar_containing_action(self.act_autostretch)
|
|
599
|
+
if tb:
|
|
600
|
+
btn = tb.widgetForAction(self.act_autostretch)
|
|
601
|
+
if isinstance(btn, QToolButton):
|
|
602
|
+
# ... build menu ...
|
|
603
|
+
btn.setMenu(menu)
|
|
604
|
+
btn.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
|
|
432
605
|
|
|
606
|
+
# IMPORTANT: re-apply style after action moves / rebind
|
|
607
|
+
self._style_toggle_toolbutton(btn)
|
|
433
608
|
|
|
434
609
|
def _bind_view_toolbar_menus(self, tb: DraggableToolBar):
|
|
435
610
|
# --- Display-Stretch menu ---
|
|
@@ -485,6 +660,12 @@ class ToolbarMixin:
|
|
|
485
660
|
btn_fit.setMenu(fit_menu)
|
|
486
661
|
btn_fit.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
|
|
487
662
|
|
|
663
|
+
def _linux_force_text_action(self, act: QAction, text: str) -> None:
|
|
664
|
+
"""On Linux, show text-only for this action (no theme icon)."""
|
|
665
|
+
if not sys.platform.startswith("linux"):
|
|
666
|
+
return
|
|
667
|
+
act.setIcon(QIcon()) # remove whatever theme icon got assigned
|
|
668
|
+
act.setText(text) # show the glyph/text
|
|
488
669
|
|
|
489
670
|
def _create_actions(self):
|
|
490
671
|
# File actions
|
|
@@ -606,30 +787,30 @@ class ToolbarMixin:
|
|
|
606
787
|
self.act_bake_display_stretch.triggered.connect(self._bake_display_stretch)
|
|
607
788
|
|
|
608
789
|
# --- Zoom controls ---
|
|
609
|
-
# --- Zoom controls (themed icons) ---
|
|
610
790
|
self.act_zoom_out = QAction(QIcon.fromTheme("zoom-out"), self.tr("Zoom Out"), self)
|
|
611
791
|
self.act_zoom_out.setStatusTip(self.tr("Zoom out"))
|
|
612
792
|
self.act_zoom_out.setShortcuts([QKeySequence("Ctrl+-")])
|
|
613
793
|
self.act_zoom_out.triggered.connect(lambda: self._zoom_step_active(-1))
|
|
794
|
+
self._linux_force_text_action(self.act_zoom_out, "−") # true minus
|
|
614
795
|
|
|
615
796
|
self.act_zoom_in = QAction(QIcon.fromTheme("zoom-in"), self.tr("Zoom In"), self)
|
|
616
797
|
self.act_zoom_in.setStatusTip(self.tr("Zoom in"))
|
|
617
|
-
self.act_zoom_in.setShortcuts([
|
|
618
|
-
QKeySequence("Ctrl++"), # Ctrl + (Shift + = on many keyboards)
|
|
619
|
-
QKeySequence("Ctrl+="), # fallback
|
|
620
|
-
])
|
|
798
|
+
self.act_zoom_in.setShortcuts([QKeySequence("Ctrl++"), QKeySequence("Ctrl+=")])
|
|
621
799
|
self.act_zoom_in.triggered.connect(lambda: self._zoom_step_active(+1))
|
|
800
|
+
self._linux_force_text_action(self.act_zoom_in, "+")
|
|
622
801
|
|
|
623
802
|
self.act_zoom_1_1 = QAction(QIcon.fromTheme("zoom-original"), self.tr("1:1"), self)
|
|
624
803
|
self.act_zoom_1_1.setStatusTip(self.tr("Zoom to 100% (pixel-for-pixel)"))
|
|
625
804
|
self.act_zoom_1_1.setShortcut(QKeySequence("Ctrl+1"))
|
|
626
805
|
self.act_zoom_1_1.triggered.connect(self._zoom_active_1_1)
|
|
806
|
+
self._linux_force_text_action(self.act_zoom_1_1, "1:1")
|
|
627
807
|
|
|
628
808
|
self.act_zoom_fit = QAction(QIcon.fromTheme("zoom-fit-best"), self.tr("Fit"), self)
|
|
629
809
|
self.act_zoom_fit.setStatusTip(self.tr("Fit image to current window"))
|
|
630
810
|
self.act_zoom_fit.setShortcut(QKeySequence("Ctrl+0"))
|
|
631
811
|
self.act_zoom_fit.triggered.connect(self._zoom_active_fit)
|
|
632
812
|
self.act_zoom_fit.setCheckable(True)
|
|
813
|
+
self._linux_force_text_action(self.act_zoom_fit, "Fit")
|
|
633
814
|
|
|
634
815
|
self.act_auto_fit_resize = QAction(self.tr("Auto-fit on Resize"), self)
|
|
635
816
|
self.act_auto_fit_resize.setCheckable(True)
|
|
@@ -754,6 +935,7 @@ class ToolbarMixin:
|
|
|
754
935
|
self.luma_method = getattr(self, "luma_method", "rec709") # default
|
|
755
936
|
self._luma_group = QActionGroup(self)
|
|
756
937
|
self._luma_group.setExclusive(True)
|
|
938
|
+
self._luma_sensor_actions = {} # key -> QAction
|
|
757
939
|
|
|
758
940
|
def _mk(method_key, text):
|
|
759
941
|
act = QAction(text, self, checkable=True)
|
|
@@ -773,8 +955,10 @@ class ToolbarMixin:
|
|
|
773
955
|
|
|
774
956
|
# update method when user picks from the menu
|
|
775
957
|
def _on_luma_pick(act):
|
|
776
|
-
|
|
777
|
-
|
|
958
|
+
key = act.data()
|
|
959
|
+
if key is None:
|
|
960
|
+
return
|
|
961
|
+
self.luma_method = str(key)
|
|
778
962
|
try:
|
|
779
963
|
self.settings.setValue("ui/luminance_method", self.luma_method)
|
|
780
964
|
except Exception:
|
|
@@ -873,6 +1057,12 @@ class ToolbarMixin:
|
|
|
873
1057
|
self.act_geom_rot_180.setStatusTip(self.tr("Rotate image 180°"))
|
|
874
1058
|
self.act_geom_rot_180.triggered.connect(self._exec_geom_rot_180)
|
|
875
1059
|
|
|
1060
|
+
self.act_geom_rot_any = QAction(QIcon(rotatearbitrary_path), self.tr("Rotate..."), self)
|
|
1061
|
+
self.act_geom_rot_any.setIconVisibleInMenu(True)
|
|
1062
|
+
self.act_geom_rot_any.setStatusTip(self.tr("Rotate image by an arbitrary angle (degrees)"))
|
|
1063
|
+
self.act_geom_rot_any.triggered.connect(self._exec_geom_rot_any)
|
|
1064
|
+
|
|
1065
|
+
|
|
876
1066
|
self.act_geom_rescale = QAction(QIcon(rescale_path), self.tr("Rescale..."), self)
|
|
877
1067
|
self.act_geom_rescale.setIconVisibleInMenu(True)
|
|
878
1068
|
self.act_geom_rescale.setStatusTip(self.tr("Rescale image by a factor"))
|
|
@@ -1086,6 +1276,13 @@ class ToolbarMixin:
|
|
|
1086
1276
|
self.act_copy_astrometry = QAction(self.tr("Copy Astrometric Solution..."), self)
|
|
1087
1277
|
self.act_copy_astrometry.triggered.connect(self._open_copy_astrometry)
|
|
1088
1278
|
|
|
1279
|
+
self.act_acv_exporter = QAction(self.tr("Astro Catalogue Viewer Exporter..."), self)
|
|
1280
|
+
self.act_acv_exporter.setIconVisibleInMenu(True)
|
|
1281
|
+
self.act_acv_exporter.setStatusTip(self.tr("Export current view into Astro Catalogue Viewer folders"))
|
|
1282
|
+
self.act_acv_exporter.triggered.connect(self._open_acv_exporter)
|
|
1283
|
+
|
|
1284
|
+
|
|
1285
|
+
|
|
1089
1286
|
# Create Mask
|
|
1090
1287
|
self.act_create_mask = QAction(QIcon(maskcreate_path), self.tr("Create Mask..."), self)
|
|
1091
1288
|
self.act_create_mask.setIconVisibleInMenu(True)
|
|
@@ -1196,6 +1393,7 @@ class ToolbarMixin:
|
|
|
1196
1393
|
reg("geom_rotate_clockwise", self.act_geom_rot_cw)
|
|
1197
1394
|
reg("geom_rotate_counterclockwise",self.act_geom_rot_ccw)
|
|
1198
1395
|
reg("geom_rotate_180", self.act_geom_rot_180)
|
|
1396
|
+
reg("geom_rotate_any", self.act_geom_rot_any)
|
|
1199
1397
|
reg("geom_rescale", self.act_geom_rescale)
|
|
1200
1398
|
reg("project_new", self.act_project_new)
|
|
1201
1399
|
reg("project_save", self.act_project_save)
|
|
@@ -1336,6 +1534,188 @@ class ToolbarMixin:
|
|
|
1336
1534
|
if key:
|
|
1337
1535
|
self._restore_toolbar_order(tb, str(key))
|
|
1338
1536
|
|
|
1537
|
+
def _hidden_toolbar(self):
|
|
1538
|
+
from setiastro.saspro.shortcuts import DraggableToolBar
|
|
1539
|
+
for tb in self.findChildren(DraggableToolBar):
|
|
1540
|
+
if getattr(tb, "_settings_key", None) == "Toolbar/Hidden" or tb.objectName() == "Hidden":
|
|
1541
|
+
return tb
|
|
1542
|
+
return None
|
|
1543
|
+
|
|
1544
|
+
def _toolbar_by_settings_key(self, key: str):
|
|
1545
|
+
from setiastro.saspro.shortcuts import DraggableToolBar
|
|
1546
|
+
for tb in self.findChildren(DraggableToolBar):
|
|
1547
|
+
if getattr(tb, "_settings_key", None) == key:
|
|
1548
|
+
return tb
|
|
1549
|
+
return None
|
|
1550
|
+
|
|
1551
|
+
def _action_cid(self, act):
|
|
1552
|
+
return str(act.property("command_id") or act.objectName() or "")
|
|
1553
|
+
|
|
1554
|
+
def _hide_action_to_hidden_toolbar(self, act):
|
|
1555
|
+
"""
|
|
1556
|
+
Move action to the Hidden toolbar, remembering where it came from.
|
|
1557
|
+
"""
|
|
1558
|
+
tb_hidden = self._hidden_toolbar()
|
|
1559
|
+
if not tb_hidden:
|
|
1560
|
+
return
|
|
1561
|
+
|
|
1562
|
+
cid = self._action_cid(act)
|
|
1563
|
+
if not cid:
|
|
1564
|
+
return
|
|
1565
|
+
|
|
1566
|
+
# Find current toolbar (if any) and remember it
|
|
1567
|
+
cur_tb = self._toolbar_containing_action(act)
|
|
1568
|
+
if cur_tb and getattr(cur_tb, "_settings_key", None) and cur_tb is not tb_hidden:
|
|
1569
|
+
prev_key = str(cur_tb._settings_key)
|
|
1570
|
+
|
|
1571
|
+
# Persist "previous toolbar" so unhide can restore
|
|
1572
|
+
try:
|
|
1573
|
+
raw = self.settings.value("Toolbar/HiddenPrev", "", type=str) or ""
|
|
1574
|
+
except Exception:
|
|
1575
|
+
raw = ""
|
|
1576
|
+
try:
|
|
1577
|
+
prev = json.loads(raw) if raw else {}
|
|
1578
|
+
except Exception:
|
|
1579
|
+
prev = {}
|
|
1580
|
+
prev[cid] = prev_key
|
|
1581
|
+
self.settings.setValue("Toolbar/HiddenPrev", json.dumps(prev))
|
|
1582
|
+
|
|
1583
|
+
# Remove from all toolbars, add to hidden
|
|
1584
|
+
from setiastro.saspro.shortcuts import DraggableToolBar
|
|
1585
|
+
for t in self.findChildren(DraggableToolBar):
|
|
1586
|
+
if act in t.actions():
|
|
1587
|
+
t.removeAction(act)
|
|
1588
|
+
|
|
1589
|
+
tb_hidden.addAction(act)
|
|
1590
|
+
|
|
1591
|
+
# Update assignment mapping so restore works on next launch
|
|
1592
|
+
tb_hidden._update_assignment_for_action(act)
|
|
1593
|
+
|
|
1594
|
+
# Persist ordering (optional but nice)
|
|
1595
|
+
try:
|
|
1596
|
+
tb_hidden._persist_order()
|
|
1597
|
+
except Exception:
|
|
1598
|
+
pass
|
|
1599
|
+
if cur_tb:
|
|
1600
|
+
try:
|
|
1601
|
+
cur_tb._persist_order()
|
|
1602
|
+
except Exception:
|
|
1603
|
+
pass
|
|
1604
|
+
|
|
1605
|
+
def _unhide_action_from_hidden_toolbar(self, act):
|
|
1606
|
+
"""
|
|
1607
|
+
Move action out of Hidden toolbar back to its remembered toolbar.
|
|
1608
|
+
Falls back to Toolbar/View if missing.
|
|
1609
|
+
"""
|
|
1610
|
+
tb_hidden = self._hidden_toolbar()
|
|
1611
|
+
if not tb_hidden:
|
|
1612
|
+
return
|
|
1613
|
+
|
|
1614
|
+
cid = self._action_cid(act)
|
|
1615
|
+
if not cid:
|
|
1616
|
+
return
|
|
1617
|
+
|
|
1618
|
+
# Load prev mapping
|
|
1619
|
+
try:
|
|
1620
|
+
raw = self.settings.value("Toolbar/HiddenPrev", "", type=str) or ""
|
|
1621
|
+
except Exception:
|
|
1622
|
+
raw = ""
|
|
1623
|
+
try:
|
|
1624
|
+
prev = json.loads(raw) if raw else {}
|
|
1625
|
+
except Exception:
|
|
1626
|
+
prev = {}
|
|
1627
|
+
|
|
1628
|
+
target_key = prev.get(cid) or "Toolbar/View"
|
|
1629
|
+
tb_target = self._toolbar_by_settings_key(target_key) or self._toolbar_by_settings_key("Toolbar/View")
|
|
1630
|
+
if not tb_target:
|
|
1631
|
+
return
|
|
1632
|
+
|
|
1633
|
+
tb_hidden.removeAction(act)
|
|
1634
|
+
tb_target.addAction(act)
|
|
1635
|
+
|
|
1636
|
+
# Update assignment mapping
|
|
1637
|
+
tb_target._update_assignment_for_action(act)
|
|
1638
|
+
|
|
1639
|
+
# Cleanup prev mapping (optional)
|
|
1640
|
+
if cid in prev:
|
|
1641
|
+
prev.pop(cid, None)
|
|
1642
|
+
self.settings.setValue("Toolbar/HiddenPrev", json.dumps(prev))
|
|
1643
|
+
|
|
1644
|
+
try:
|
|
1645
|
+
tb_target._persist_order()
|
|
1646
|
+
tb_hidden._persist_order()
|
|
1647
|
+
except Exception:
|
|
1648
|
+
pass
|
|
1649
|
+
|
|
1650
|
+
def _migrate_old_hidden_sets_to_hidden_toolbar(self):
|
|
1651
|
+
"""
|
|
1652
|
+
One-time migration:
|
|
1653
|
+
Old system: per-toolbar QSettings lists at "<ToolbarKey>/Hidden" storing action IDs
|
|
1654
|
+
New system: move those actions into the dedicated Hidden toolbar (Toolbar/Hidden)
|
|
1655
|
+
and persist membership via Toolbar/Assignments.
|
|
1656
|
+
"""
|
|
1657
|
+
if not hasattr(self, "settings"):
|
|
1658
|
+
return
|
|
1659
|
+
|
|
1660
|
+
# Run once
|
|
1661
|
+
if self.settings.value("Toolbar/HiddenMigrationDone", False, type=bool):
|
|
1662
|
+
return
|
|
1663
|
+
|
|
1664
|
+
tb_hidden = self._hidden_toolbar()
|
|
1665
|
+
if not tb_hidden:
|
|
1666
|
+
return
|
|
1667
|
+
|
|
1668
|
+
from setiastro.saspro.shortcuts import DraggableToolBar
|
|
1669
|
+
|
|
1670
|
+
# If Hidden toolbar already has actions, assume user is already on new system
|
|
1671
|
+
# but we still can migrate any remaining old keys.
|
|
1672
|
+
# (We won't early-return.)
|
|
1673
|
+
|
|
1674
|
+
for tb in self.findChildren(DraggableToolBar):
|
|
1675
|
+
k = getattr(tb, "_settings_key", None)
|
|
1676
|
+
if not k or str(k) == "Toolbar/Hidden":
|
|
1677
|
+
continue
|
|
1678
|
+
|
|
1679
|
+
# Old storage key
|
|
1680
|
+
old_key = f"{k}/Hidden"
|
|
1681
|
+
|
|
1682
|
+
# Read old hidden list (Qt backend may return list OR str)
|
|
1683
|
+
try:
|
|
1684
|
+
raw = self.settings.value(old_key, [], type=list)
|
|
1685
|
+
except Exception:
|
|
1686
|
+
raw = self.settings.value(old_key, []) # fallback
|
|
1687
|
+
|
|
1688
|
+
if not raw:
|
|
1689
|
+
continue
|
|
1690
|
+
|
|
1691
|
+
if isinstance(raw, str):
|
|
1692
|
+
raw_list = [raw]
|
|
1693
|
+
else:
|
|
1694
|
+
try:
|
|
1695
|
+
raw_list = list(raw)
|
|
1696
|
+
except Exception:
|
|
1697
|
+
raw_list = []
|
|
1698
|
+
|
|
1699
|
+
hidden_ids = {str(x) for x in raw_list if str(x).strip()}
|
|
1700
|
+
if not hidden_ids:
|
|
1701
|
+
# Clear junk so we don’t keep trying
|
|
1702
|
+
self.settings.setValue(old_key, [])
|
|
1703
|
+
continue
|
|
1704
|
+
|
|
1705
|
+
# Move matching actions into Hidden toolbar
|
|
1706
|
+
for act in list(tb.actions()):
|
|
1707
|
+
if act is None or act.isSeparator():
|
|
1708
|
+
continue
|
|
1709
|
+
cid = str(act.property("command_id") or act.objectName() or "")
|
|
1710
|
+
if cid and cid in hidden_ids:
|
|
1711
|
+
self._hide_action_to_hidden_toolbar(act)
|
|
1712
|
+
|
|
1713
|
+
# Clear old storage so you don't keep re-migrating
|
|
1714
|
+
self.settings.setValue(old_key, [])
|
|
1715
|
+
|
|
1716
|
+
# Mark migration complete
|
|
1717
|
+
self.settings.setValue("Toolbar/HiddenMigrationDone", True)
|
|
1718
|
+
|
|
1339
1719
|
|
|
1340
1720
|
def update_undo_redo_action_labels(self):
|
|
1341
1721
|
if not hasattr(self, "act_undo"): # not built yet
|
|
@@ -1387,6 +1767,7 @@ class ToolbarMixin:
|
|
|
1387
1767
|
a.setStatusTip(tip)
|
|
1388
1768
|
a.setEnabled(False)
|
|
1389
1769
|
|
|
1770
|
+
|
|
1390
1771
|
def _sync_link_action_state(self):
|
|
1391
1772
|
g = self._current_group_of_active()
|
|
1392
1773
|
self.act_link_group.blockSignals(True)
|
|
@@ -1432,6 +1813,7 @@ class ToolbarMixin:
|
|
|
1432
1813
|
QTimer.singleShot(0, self.update_undo_redo_action_labels)
|
|
1433
1814
|
|
|
1434
1815
|
def _refresh_mask_action_states(self):
|
|
1816
|
+
|
|
1435
1817
|
active_doc = self._active_doc()
|
|
1436
1818
|
|
|
1437
1819
|
can_apply = bool(active_doc and self._list_candidate_mask_sources(exclude_doc=active_doc))
|
|
@@ -1455,3 +1837,10 @@ class ToolbarMixin:
|
|
|
1455
1837
|
if hasattr(self, "act_hide_mask"):
|
|
1456
1838
|
self.act_hide_mask.setEnabled(has_mask and overlay_on)
|
|
1457
1839
|
|
|
1840
|
+
def _style_toggle_toolbutton(self, btn: QToolButton):
|
|
1841
|
+
# Make sure the action visually shows "on" state
|
|
1842
|
+
btn.setCheckable(True) # safe even if already
|
|
1843
|
+
btn.setStyleSheet("""
|
|
1844
|
+
QToolButton { color: #dcdcdc; }
|
|
1845
|
+
QToolButton:checked { color: #DAA520; font-weight: 600; }
|
|
1846
|
+
""")
|