setiastrosuitepro 1.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of setiastrosuitepro might be problematic. Click here for more details.
- setiastro/__init__.py +2 -0
- setiastro/saspro/__init__.py +20 -0
- setiastro/saspro/__main__.py +784 -0
- setiastro/saspro/_generated/__init__.py +7 -0
- setiastro/saspro/_generated/build_info.py +2 -0
- setiastro/saspro/abe.py +1295 -0
- setiastro/saspro/abe_preset.py +196 -0
- setiastro/saspro/aberration_ai.py +694 -0
- setiastro/saspro/aberration_ai_preset.py +224 -0
- setiastro/saspro/accel_installer.py +218 -0
- setiastro/saspro/accel_workers.py +30 -0
- setiastro/saspro/add_stars.py +621 -0
- setiastro/saspro/astrobin_exporter.py +1007 -0
- setiastro/saspro/astrospike.py +153 -0
- setiastro/saspro/astrospike_python.py +1839 -0
- setiastro/saspro/autostretch.py +196 -0
- setiastro/saspro/backgroundneutral.py +560 -0
- setiastro/saspro/batch_convert.py +325 -0
- setiastro/saspro/batch_renamer.py +519 -0
- setiastro/saspro/blemish_blaster.py +488 -0
- setiastro/saspro/blink_comparator_pro.py +2923 -0
- setiastro/saspro/bundles.py +61 -0
- setiastro/saspro/bundles_dock.py +114 -0
- setiastro/saspro/cheat_sheet.py +168 -0
- setiastro/saspro/clahe.py +342 -0
- setiastro/saspro/comet_stacking.py +1377 -0
- setiastro/saspro/config.py +38 -0
- setiastro/saspro/config_bootstrap.py +40 -0
- setiastro/saspro/config_manager.py +316 -0
- setiastro/saspro/continuum_subtract.py +1617 -0
- setiastro/saspro/convo.py +1397 -0
- setiastro/saspro/convo_preset.py +414 -0
- setiastro/saspro/copyastro.py +187 -0
- setiastro/saspro/cosmicclarity.py +1564 -0
- setiastro/saspro/cosmicclarity_preset.py +407 -0
- setiastro/saspro/crop_dialog_pro.py +948 -0
- setiastro/saspro/crop_preset.py +189 -0
- setiastro/saspro/curve_editor_pro.py +2544 -0
- setiastro/saspro/curves_preset.py +375 -0
- setiastro/saspro/debayer.py +670 -0
- setiastro/saspro/debug_utils.py +29 -0
- setiastro/saspro/dnd_mime.py +35 -0
- setiastro/saspro/doc_manager.py +2634 -0
- setiastro/saspro/exoplanet_detector.py +2166 -0
- setiastro/saspro/file_utils.py +284 -0
- setiastro/saspro/fitsmodifier.py +744 -0
- setiastro/saspro/free_torch_memory.py +48 -0
- setiastro/saspro/frequency_separation.py +1343 -0
- setiastro/saspro/function_bundle.py +1594 -0
- setiastro/saspro/ghs_dialog_pro.py +660 -0
- setiastro/saspro/ghs_preset.py +284 -0
- setiastro/saspro/graxpert.py +634 -0
- setiastro/saspro/graxpert_preset.py +287 -0
- setiastro/saspro/gui/__init__.py +0 -0
- setiastro/saspro/gui/main_window.py +8494 -0
- setiastro/saspro/gui/mixins/__init__.py +33 -0
- setiastro/saspro/gui/mixins/dock_mixin.py +263 -0
- setiastro/saspro/gui/mixins/file_mixin.py +445 -0
- setiastro/saspro/gui/mixins/geometry_mixin.py +403 -0
- setiastro/saspro/gui/mixins/header_mixin.py +441 -0
- setiastro/saspro/gui/mixins/mask_mixin.py +421 -0
- setiastro/saspro/gui/mixins/menu_mixin.py +361 -0
- setiastro/saspro/gui/mixins/theme_mixin.py +367 -0
- setiastro/saspro/gui/mixins/toolbar_mixin.py +1324 -0
- setiastro/saspro/gui/mixins/update_mixin.py +309 -0
- setiastro/saspro/gui/mixins/view_mixin.py +435 -0
- setiastro/saspro/halobgon.py +462 -0
- setiastro/saspro/header_viewer.py +445 -0
- setiastro/saspro/headless_utils.py +88 -0
- setiastro/saspro/histogram.py +753 -0
- setiastro/saspro/history_explorer.py +939 -0
- setiastro/saspro/image_combine.py +414 -0
- setiastro/saspro/image_peeker_pro.py +1596 -0
- setiastro/saspro/imageops/__init__.py +37 -0
- setiastro/saspro/imageops/mdi_snap.py +292 -0
- setiastro/saspro/imageops/scnr.py +36 -0
- setiastro/saspro/imageops/starbasedwhitebalance.py +210 -0
- setiastro/saspro/imageops/stretch.py +244 -0
- setiastro/saspro/isophote.py +1179 -0
- setiastro/saspro/layers.py +208 -0
- setiastro/saspro/layers_dock.py +714 -0
- setiastro/saspro/lazy_imports.py +193 -0
- setiastro/saspro/legacy/__init__.py +2 -0
- setiastro/saspro/legacy/image_manager.py +2226 -0
- setiastro/saspro/legacy/numba_utils.py +3659 -0
- setiastro/saspro/legacy/xisf.py +1071 -0
- setiastro/saspro/linear_fit.py +534 -0
- setiastro/saspro/live_stacking.py +1830 -0
- setiastro/saspro/log_bus.py +5 -0
- setiastro/saspro/logging_config.py +460 -0
- setiastro/saspro/luminancerecombine.py +309 -0
- setiastro/saspro/main_helpers.py +201 -0
- setiastro/saspro/mask_creation.py +928 -0
- setiastro/saspro/masks_core.py +56 -0
- setiastro/saspro/mdi_widgets.py +353 -0
- setiastro/saspro/memory_utils.py +666 -0
- setiastro/saspro/metadata_patcher.py +75 -0
- setiastro/saspro/mfdeconv.py +3826 -0
- setiastro/saspro/mfdeconv_earlystop.py +71 -0
- setiastro/saspro/mfdeconvcudnn.py +3263 -0
- setiastro/saspro/mfdeconvsport.py +2382 -0
- setiastro/saspro/minorbodycatalog.py +567 -0
- setiastro/saspro/morphology.py +382 -0
- setiastro/saspro/multiscale_decomp.py +1290 -0
- setiastro/saspro/nbtorgb_stars.py +531 -0
- setiastro/saspro/numba_utils.py +3044 -0
- setiastro/saspro/numba_warmup.py +141 -0
- setiastro/saspro/ops/__init__.py +9 -0
- setiastro/saspro/ops/command_help_dialog.py +623 -0
- setiastro/saspro/ops/command_runner.py +217 -0
- setiastro/saspro/ops/commands.py +1594 -0
- setiastro/saspro/ops/script_editor.py +1102 -0
- setiastro/saspro/ops/scripts.py +1413 -0
- setiastro/saspro/ops/settings.py +560 -0
- setiastro/saspro/parallel_utils.py +554 -0
- setiastro/saspro/pedestal.py +121 -0
- setiastro/saspro/perfect_palette_picker.py +1053 -0
- setiastro/saspro/pipeline.py +110 -0
- setiastro/saspro/pixelmath.py +1600 -0
- setiastro/saspro/plate_solver.py +2435 -0
- setiastro/saspro/project_io.py +797 -0
- setiastro/saspro/psf_utils.py +136 -0
- setiastro/saspro/psf_viewer.py +549 -0
- setiastro/saspro/pyi_rthook_astroquery.py +95 -0
- setiastro/saspro/remove_green.py +314 -0
- setiastro/saspro/remove_stars.py +1625 -0
- setiastro/saspro/remove_stars_preset.py +404 -0
- setiastro/saspro/resources.py +472 -0
- setiastro/saspro/rgb_combination.py +207 -0
- setiastro/saspro/rgb_extract.py +19 -0
- setiastro/saspro/rgbalign.py +723 -0
- setiastro/saspro/runtime_imports.py +7 -0
- setiastro/saspro/runtime_torch.py +754 -0
- setiastro/saspro/save_options.py +72 -0
- setiastro/saspro/selective_color.py +1552 -0
- setiastro/saspro/sfcc.py +1425 -0
- setiastro/saspro/shortcuts.py +2807 -0
- setiastro/saspro/signature_insert.py +1099 -0
- setiastro/saspro/stacking_suite.py +17712 -0
- setiastro/saspro/star_alignment.py +7420 -0
- setiastro/saspro/star_alignment_preset.py +329 -0
- setiastro/saspro/star_metrics.py +49 -0
- setiastro/saspro/star_spikes.py +681 -0
- setiastro/saspro/star_stretch.py +470 -0
- setiastro/saspro/stat_stretch.py +502 -0
- setiastro/saspro/status_log_dock.py +78 -0
- setiastro/saspro/subwindow.py +3267 -0
- setiastro/saspro/supernovaasteroidhunter.py +1712 -0
- setiastro/saspro/swap_manager.py +99 -0
- setiastro/saspro/torch_backend.py +89 -0
- setiastro/saspro/torch_rejection.py +434 -0
- setiastro/saspro/view_bundle.py +1555 -0
- setiastro/saspro/wavescale_hdr.py +624 -0
- setiastro/saspro/wavescale_hdr_preset.py +100 -0
- setiastro/saspro/wavescalede.py +657 -0
- setiastro/saspro/wavescalede_preset.py +228 -0
- setiastro/saspro/wcs_update.py +374 -0
- setiastro/saspro/whitebalance.py +456 -0
- setiastro/saspro/widgets/__init__.py +48 -0
- setiastro/saspro/widgets/common_utilities.py +305 -0
- setiastro/saspro/widgets/graphics_views.py +122 -0
- setiastro/saspro/widgets/image_utils.py +518 -0
- setiastro/saspro/widgets/preview_dialogs.py +280 -0
- setiastro/saspro/widgets/spinboxes.py +275 -0
- setiastro/saspro/widgets/themed_buttons.py +13 -0
- setiastro/saspro/widgets/wavelet_utils.py +299 -0
- setiastro/saspro/window_shelf.py +185 -0
- setiastro/saspro/xisf.py +1123 -0
- setiastrosuitepro-1.6.0.dist-info/METADATA +266 -0
- setiastrosuitepro-1.6.0.dist-info/RECORD +174 -0
- setiastrosuitepro-1.6.0.dist-info/WHEEL +4 -0
- setiastrosuitepro-1.6.0.dist-info/entry_points.txt +6 -0
- setiastrosuitepro-1.6.0.dist-info/licenses/LICENSE +674 -0
- setiastrosuitepro-1.6.0.dist-info/licenses/license.txt +2580 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# pro/gui/mixins/theme_mixin.py
|
|
2
|
+
"""
|
|
3
|
+
Theme management mixin for AstroSuiteProMainWindow.
|
|
4
|
+
|
|
5
|
+
This mixin contains all theme-related functionality: palette definitions,
|
|
6
|
+
theme application, and system theme detection.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from PyQt6.QtCore import Qt, QTimer
|
|
12
|
+
from PyQt6.QtGui import QBrush, QColor, QFont, QPalette
|
|
13
|
+
from PyQt6.QtWidgets import QApplication
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ThemeMixin:
|
|
20
|
+
"""
|
|
21
|
+
Mixin for theme management.
|
|
22
|
+
|
|
23
|
+
Provides methods for creating palettes, applying themes, and
|
|
24
|
+
responding to system theme changes.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def _apply_workspace_theme(self):
|
|
28
|
+
"""Retint the QMdiArea background + viewport to current theme colors."""
|
|
29
|
+
pal = QApplication.palette()
|
|
30
|
+
# Use Base for light, Window for dark (looks better with your palettes)
|
|
31
|
+
role = QPalette.ColorRole.Base if self._theme_mode() == "light" else QPalette.ColorRole.Window
|
|
32
|
+
col = pal.color(role)
|
|
33
|
+
|
|
34
|
+
# 1) Tell QMdiArea to use a flat color background
|
|
35
|
+
try:
|
|
36
|
+
self.mdi.setBackground(QBrush(col))
|
|
37
|
+
except Exception:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
# 2) Also set the viewport palette (some styles ignore setBackground)
|
|
41
|
+
try:
|
|
42
|
+
vp = self.mdi.viewport()
|
|
43
|
+
vp.setAutoFillBackground(True)
|
|
44
|
+
p = vp.palette()
|
|
45
|
+
p.setColor(QPalette.ColorRole.Window, col)
|
|
46
|
+
vp.setPalette(p)
|
|
47
|
+
vp.update()
|
|
48
|
+
except Exception:
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
# 3) Ensure the overlay canvas stays transparent and refreshes
|
|
52
|
+
try:
|
|
53
|
+
if hasattr(self, "shortcuts") and self.shortcuts and getattr(self.shortcuts, "canvas", None):
|
|
54
|
+
c = self.shortcuts.canvas
|
|
55
|
+
c.setStyleSheet("background: transparent;")
|
|
56
|
+
c.update()
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
def apply_theme_from_settings(self):
|
|
61
|
+
"""Apply the theme based on current settings."""
|
|
62
|
+
mode = self._theme_mode()
|
|
63
|
+
app = QApplication.instance()
|
|
64
|
+
color_scheme = app.styleHints().colorScheme()
|
|
65
|
+
|
|
66
|
+
# Resolve "system" to dark/light
|
|
67
|
+
if mode == "system":
|
|
68
|
+
if color_scheme == Qt.ColorScheme.Dark:
|
|
69
|
+
print("System is in Dark Mode")
|
|
70
|
+
mode = "dark"
|
|
71
|
+
else:
|
|
72
|
+
print("System is in Light Mode")
|
|
73
|
+
mode = "light"
|
|
74
|
+
|
|
75
|
+
# Base style
|
|
76
|
+
if mode in ("dark", "gray", "light", "custom"):
|
|
77
|
+
app.setStyle("Fusion")
|
|
78
|
+
else:
|
|
79
|
+
app.setStyle(None)
|
|
80
|
+
|
|
81
|
+
# Palettes
|
|
82
|
+
if mode == "dark":
|
|
83
|
+
app.setPalette(self._dark_palette())
|
|
84
|
+
app.setStyleSheet(
|
|
85
|
+
"QToolTip { color: #ffffff; background-color: #2a2a2a; border: 1px solid #5a5a5a; }"
|
|
86
|
+
)
|
|
87
|
+
elif mode == "gray":
|
|
88
|
+
app.setPalette(self._gray_palette())
|
|
89
|
+
app.setStyleSheet(
|
|
90
|
+
"QToolTip { color: #f0f0f0; background-color: #3a3a3a; border: 1px solid #5a5a5a; }"
|
|
91
|
+
)
|
|
92
|
+
elif mode == "light":
|
|
93
|
+
app.setPalette(self._light_palette())
|
|
94
|
+
app.setStyleSheet(
|
|
95
|
+
"QToolTip { color: #141414; background-color: #ffffee; border: 1px solid #c8c8c8; }"
|
|
96
|
+
)
|
|
97
|
+
elif mode == "custom":
|
|
98
|
+
app.setPalette(self._custom_palette())
|
|
99
|
+
# Tooltips roughly matching the custom dark-ish style
|
|
100
|
+
app.setStyleSheet(
|
|
101
|
+
"QToolTip { color: #f0f0f0; background-color: #303030; border: 1px solid #5a5a5a; }"
|
|
102
|
+
)
|
|
103
|
+
else: # system/native fallback
|
|
104
|
+
app.setPalette(QApplication.style().standardPalette())
|
|
105
|
+
app.setStyleSheet("")
|
|
106
|
+
|
|
107
|
+
# Optional: apply custom font
|
|
108
|
+
if mode == "custom":
|
|
109
|
+
font_str = self.settings.value("ui/custom/font", "", type=str) or ""
|
|
110
|
+
if font_str:
|
|
111
|
+
try:
|
|
112
|
+
f = QFont()
|
|
113
|
+
if f.fromString(font_str):
|
|
114
|
+
app.setFont(f)
|
|
115
|
+
except Exception:
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
# Nudge widgets to pick up role changes
|
|
119
|
+
self._repolish_top_levels()
|
|
120
|
+
self._apply_workspace_theme()
|
|
121
|
+
self._style_mdi_titlebars()
|
|
122
|
+
self._menu_view_panels = None
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
vp = self.mdi.viewport()
|
|
126
|
+
vp.setAutoFillBackground(True)
|
|
127
|
+
vp.setPalette(QApplication.palette())
|
|
128
|
+
vp.update()
|
|
129
|
+
except Exception:
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
def _repolish_top_levels(self):
|
|
133
|
+
"""Force all top-level widgets to repolish their styles."""
|
|
134
|
+
app = QApplication.instance()
|
|
135
|
+
for w in app.topLevelWidgets():
|
|
136
|
+
w.setUpdatesEnabled(False)
|
|
137
|
+
w.style().unpolish(w)
|
|
138
|
+
w.style().polish(w)
|
|
139
|
+
w.setUpdatesEnabled(True)
|
|
140
|
+
|
|
141
|
+
def _style_mdi_titlebars(self):
|
|
142
|
+
"""Apply theme-specific styles to MDI subwindow titlebars."""
|
|
143
|
+
mode = self._theme_mode()
|
|
144
|
+
if mode == "dark":
|
|
145
|
+
base = "#1b1b1b" # inactive titlebar
|
|
146
|
+
active = "#242424" # active titlebar
|
|
147
|
+
fg = "#dcdcdc"
|
|
148
|
+
elif mode in ("gray", "custom"):
|
|
149
|
+
base = "#3a3a3a"
|
|
150
|
+
active = "#454545"
|
|
151
|
+
fg = "#f0f0f0"
|
|
152
|
+
else:
|
|
153
|
+
# No override in light / system modes
|
|
154
|
+
self.mdi.setStyleSheet("")
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
self.mdi.setStyleSheet(f"""
|
|
158
|
+
QMdiSubWindow::titlebar {{ background: {base}; color: {fg}; }}
|
|
159
|
+
QMdiSubWindow::titlebar:active {{ background: {active}; color: {fg}; }}
|
|
160
|
+
""")
|
|
161
|
+
|
|
162
|
+
def _dark_palette(self) -> QPalette:
|
|
163
|
+
"""Create a dark theme palette."""
|
|
164
|
+
p = QPalette()
|
|
165
|
+
|
|
166
|
+
# Bases
|
|
167
|
+
bg = QColor(18, 18, 18) # editor / view backgrounds (Base)
|
|
168
|
+
panel = QColor(27, 27, 27) # window / panels (Window, Button)
|
|
169
|
+
altbase = QColor(33, 33, 33)
|
|
170
|
+
text = QColor(220, 220, 220)
|
|
171
|
+
dis = QColor(140, 140, 140)
|
|
172
|
+
hi = QColor(30, 144, 255) # highlight (dodger blue)
|
|
173
|
+
|
|
174
|
+
p.setColor(QPalette.ColorRole.Window, panel)
|
|
175
|
+
p.setColor(QPalette.ColorRole.WindowText, text)
|
|
176
|
+
p.setColor(QPalette.ColorRole.Base, bg)
|
|
177
|
+
p.setColor(QPalette.ColorRole.AlternateBase, altbase)
|
|
178
|
+
p.setColor(QPalette.ColorRole.ToolTipBase, panel)
|
|
179
|
+
p.setColor(QPalette.ColorRole.ToolTipText, text)
|
|
180
|
+
p.setColor(QPalette.ColorRole.Text, text)
|
|
181
|
+
p.setColor(QPalette.ColorRole.Button, panel)
|
|
182
|
+
p.setColor(QPalette.ColorRole.ButtonText, text)
|
|
183
|
+
p.setColor(QPalette.ColorRole.BrightText, QColor(255, 0, 0))
|
|
184
|
+
p.setColor(QPalette.ColorRole.Highlight, hi)
|
|
185
|
+
p.setColor(QPalette.ColorRole.HighlightedText, QColor(255, 255, 255))
|
|
186
|
+
p.setColor(QPalette.ColorRole.Link, QColor(90, 160, 255))
|
|
187
|
+
p.setColor(QPalette.ColorRole.LinkVisited, QColor(160, 140, 255))
|
|
188
|
+
|
|
189
|
+
# Qt6: explicit placeholder color helps avoid faint-on-faint
|
|
190
|
+
try:
|
|
191
|
+
p.setColor(QPalette.ColorRole.PlaceholderText, QColor(160, 160, 160))
|
|
192
|
+
except Exception:
|
|
193
|
+
pass
|
|
194
|
+
|
|
195
|
+
# Disabled
|
|
196
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, dis)
|
|
197
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, dis)
|
|
198
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText, dis)
|
|
199
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Base, QColor(24, 24, 24))
|
|
200
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Highlight, QColor(60, 60, 60))
|
|
201
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.HighlightedText, QColor(210, 210, 210))
|
|
202
|
+
|
|
203
|
+
return p
|
|
204
|
+
|
|
205
|
+
def _custom_palette(self) -> QPalette:
|
|
206
|
+
"""
|
|
207
|
+
Build a QPalette from user-defined colors in QSettings.
|
|
208
|
+
Falls back to a gray-ish baseline if any key is missing.
|
|
209
|
+
"""
|
|
210
|
+
s = self.settings
|
|
211
|
+
|
|
212
|
+
def col(key: str, default: QColor) -> QColor:
|
|
213
|
+
val = s.value(key, default.name(), type=str) or default.name()
|
|
214
|
+
return QColor(val)
|
|
215
|
+
|
|
216
|
+
window = col("ui/custom/window", QColor(54, 54, 54))
|
|
217
|
+
base = col("ui/custom/base", QColor(40, 40, 40))
|
|
218
|
+
altbase = col("ui/custom/altbase", QColor(64, 64, 64))
|
|
219
|
+
text = col("ui/custom/text", QColor(230, 230, 230))
|
|
220
|
+
button = col("ui/custom/button", window)
|
|
221
|
+
hi = col("ui/custom/highlight", QColor(95, 145, 230))
|
|
222
|
+
link = col("ui/custom/link", QColor(120, 170, 255))
|
|
223
|
+
linkv = col("ui/custom/link_visited", QColor(180, 150, 255))
|
|
224
|
+
|
|
225
|
+
p = QPalette()
|
|
226
|
+
|
|
227
|
+
# Core roles
|
|
228
|
+
p.setColor(QPalette.ColorRole.Window, window)
|
|
229
|
+
p.setColor(QPalette.ColorRole.WindowText, text)
|
|
230
|
+
p.setColor(QPalette.ColorRole.Base, base)
|
|
231
|
+
p.setColor(QPalette.ColorRole.AlternateBase, altbase)
|
|
232
|
+
p.setColor(QPalette.ColorRole.ToolTipBase, window)
|
|
233
|
+
p.setColor(QPalette.ColorRole.ToolTipText, text)
|
|
234
|
+
p.setColor(QPalette.ColorRole.Text, text)
|
|
235
|
+
p.setColor(QPalette.ColorRole.Button, button)
|
|
236
|
+
p.setColor(QPalette.ColorRole.ButtonText, text)
|
|
237
|
+
p.setColor(QPalette.ColorRole.BrightText, QColor(255, 0, 0))
|
|
238
|
+
p.setColor(QPalette.ColorRole.Highlight, hi)
|
|
239
|
+
p.setColor(QPalette.ColorRole.HighlightedText, QColor(255, 255, 255))
|
|
240
|
+
p.setColor(QPalette.ColorRole.Link, link)
|
|
241
|
+
p.setColor(QPalette.ColorRole.LinkVisited, linkv)
|
|
242
|
+
|
|
243
|
+
# Placeholder / disabled
|
|
244
|
+
try:
|
|
245
|
+
p.setColor(QPalette.ColorRole.PlaceholderText, QColor(170, 170, 170))
|
|
246
|
+
except Exception:
|
|
247
|
+
pass
|
|
248
|
+
|
|
249
|
+
dis = QColor(150, 150, 150)
|
|
250
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, dis)
|
|
251
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, dis)
|
|
252
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText, dis)
|
|
253
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Base, base.darker(115))
|
|
254
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Highlight, hi.darker(140))
|
|
255
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.HighlightedText, QColor(210, 210, 210))
|
|
256
|
+
|
|
257
|
+
return p
|
|
258
|
+
|
|
259
|
+
def _gray_palette(self) -> QPalette:
|
|
260
|
+
"""Create a mid-gray theme palette."""
|
|
261
|
+
p = QPalette()
|
|
262
|
+
|
|
263
|
+
# Mid-gray neutrals
|
|
264
|
+
window = QColor(54, 54, 54) # panels/docks
|
|
265
|
+
base = QColor(64, 64, 64) # editors / text fields
|
|
266
|
+
altbase = QColor(72, 72, 72) # alternating rows
|
|
267
|
+
text = QColor(230, 230, 230)
|
|
268
|
+
btn = window
|
|
269
|
+
dis = QColor(150, 150, 150)
|
|
270
|
+
link = QColor(120, 170, 255)
|
|
271
|
+
linkv = QColor(180, 150, 255)
|
|
272
|
+
hi = QColor(95, 145, 230)
|
|
273
|
+
hitxt = QColor(255, 255, 255)
|
|
274
|
+
|
|
275
|
+
# Core roles
|
|
276
|
+
p.setColor(QPalette.ColorRole.Window, window)
|
|
277
|
+
p.setColor(QPalette.ColorRole.WindowText, text)
|
|
278
|
+
p.setColor(QPalette.ColorRole.Base, base)
|
|
279
|
+
p.setColor(QPalette.ColorRole.AlternateBase, altbase)
|
|
280
|
+
p.setColor(QPalette.ColorRole.ToolTipBase, QColor(60, 60, 60))
|
|
281
|
+
p.setColor(QPalette.ColorRole.ToolTipText, text)
|
|
282
|
+
p.setColor(QPalette.ColorRole.Text, text)
|
|
283
|
+
p.setColor(QPalette.ColorRole.Button, btn)
|
|
284
|
+
p.setColor(QPalette.ColorRole.ButtonText, text)
|
|
285
|
+
p.setColor(QPalette.ColorRole.BrightText, QColor(255, 0, 0))
|
|
286
|
+
p.setColor(QPalette.ColorRole.Highlight, hi)
|
|
287
|
+
p.setColor(QPalette.ColorRole.HighlightedText, hitxt)
|
|
288
|
+
p.setColor(QPalette.ColorRole.Link, link)
|
|
289
|
+
p.setColor(QPalette.ColorRole.LinkVisited, linkv)
|
|
290
|
+
|
|
291
|
+
# Placeholder
|
|
292
|
+
try:
|
|
293
|
+
p.setColor(QPalette.ColorRole.PlaceholderText, QColor(170, 170, 170))
|
|
294
|
+
except Exception:
|
|
295
|
+
pass
|
|
296
|
+
|
|
297
|
+
# Disabled group
|
|
298
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, dis)
|
|
299
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, dis)
|
|
300
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText, dis)
|
|
301
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Base, QColor(58, 58, 58))
|
|
302
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Highlight, QColor(80, 80, 80))
|
|
303
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.HighlightedText, QColor(210, 210, 210))
|
|
304
|
+
|
|
305
|
+
return p
|
|
306
|
+
|
|
307
|
+
def _light_palette(self) -> QPalette:
|
|
308
|
+
"""Create a light theme palette."""
|
|
309
|
+
p = QPalette()
|
|
310
|
+
|
|
311
|
+
# Light neutrals
|
|
312
|
+
window = QColor(246, 246, 246) # panels/docks
|
|
313
|
+
base = QColor(255, 255, 255) # text fields, editors
|
|
314
|
+
altbase = QColor(242, 242, 242) # alternating rows
|
|
315
|
+
text = QColor(20, 20, 20) # primary text
|
|
316
|
+
btn = QColor(246, 246, 246) # buttons same as window
|
|
317
|
+
dis = QColor(140, 140, 140) # disabled text
|
|
318
|
+
link = QColor(25, 100, 210) # link blue
|
|
319
|
+
linkv = QColor(120, 70, 200) # visited
|
|
320
|
+
hi = QColor(43, 120, 228) # selection blue (Windows-like)
|
|
321
|
+
hitxt = QColor(255, 255, 255) # text over selection
|
|
322
|
+
|
|
323
|
+
# Core roles
|
|
324
|
+
p.setColor(QPalette.ColorRole.Window, window)
|
|
325
|
+
p.setColor(QPalette.ColorRole.WindowText, text)
|
|
326
|
+
p.setColor(QPalette.ColorRole.Base, base)
|
|
327
|
+
p.setColor(QPalette.ColorRole.AlternateBase, altbase)
|
|
328
|
+
p.setColor(QPalette.ColorRole.ToolTipBase, QColor(255, 255, 238)) # soft yellow tooltip
|
|
329
|
+
p.setColor(QPalette.ColorRole.ToolTipText, text)
|
|
330
|
+
p.setColor(QPalette.ColorRole.Text, text)
|
|
331
|
+
p.setColor(QPalette.ColorRole.Button, btn)
|
|
332
|
+
p.setColor(QPalette.ColorRole.ButtonText, text)
|
|
333
|
+
p.setColor(QPalette.ColorRole.BrightText, QColor(180, 0, 0))
|
|
334
|
+
p.setColor(QPalette.ColorRole.Highlight, hi)
|
|
335
|
+
p.setColor(QPalette.ColorRole.HighlightedText, hitxt)
|
|
336
|
+
p.setColor(QPalette.ColorRole.Link, link)
|
|
337
|
+
p.setColor(QPalette.ColorRole.LinkVisited, linkv)
|
|
338
|
+
|
|
339
|
+
# Helps line edits/placeholders avoid too-faint gray
|
|
340
|
+
try:
|
|
341
|
+
p.setColor(QPalette.ColorRole.PlaceholderText, QColor(110, 110, 110))
|
|
342
|
+
except Exception:
|
|
343
|
+
pass
|
|
344
|
+
|
|
345
|
+
# Disabled group (keep contrasts sane)
|
|
346
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, dis)
|
|
347
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, dis)
|
|
348
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText, dis)
|
|
349
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Highlight, QColor(200, 200, 200))
|
|
350
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.HighlightedText, QColor(120, 120, 120))
|
|
351
|
+
p.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Base, QColor(248, 248, 248))
|
|
352
|
+
|
|
353
|
+
return p
|
|
354
|
+
|
|
355
|
+
def _apply_theme_safely(self):
|
|
356
|
+
"""Apply theme with re-entrancy guard."""
|
|
357
|
+
if self._theme_guard:
|
|
358
|
+
return
|
|
359
|
+
self._theme_guard = True
|
|
360
|
+
try:
|
|
361
|
+
self.apply_theme_from_settings()
|
|
362
|
+
finally:
|
|
363
|
+
QTimer.singleShot(0, lambda: setattr(self, "_theme_guard", False))
|
|
364
|
+
|
|
365
|
+
def _theme_mode(self) -> str:
|
|
366
|
+
"""Get the current theme mode from settings."""
|
|
367
|
+
return (self.settings.value("ui/theme", "system", type=str) or "system").lower()
|