mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 11:41:49 +02:00
feat(palette_viewer): added widget to display the current palette and accent colors
This commit is contained in:
183
bec_widgets/qt_utils/palette_viewer.py
Normal file
183
bec_widgets/qt_utils/palette_viewer.py
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
from qtpy.QtCore import Qt
|
||||||
|
from qtpy.QtWidgets import (
|
||||||
|
QApplication,
|
||||||
|
QFrame,
|
||||||
|
QGridLayout,
|
||||||
|
QHBoxLayout,
|
||||||
|
QLabel,
|
||||||
|
QScrollArea,
|
||||||
|
QSizePolicy,
|
||||||
|
QVBoxLayout,
|
||||||
|
QWidget,
|
||||||
|
)
|
||||||
|
|
||||||
|
from bec_widgets.utils.bec_widget import BECWidget
|
||||||
|
from bec_widgets.utils.colors import get_accent_colors, get_theme_palette
|
||||||
|
from bec_widgets.widgets.dark_mode_button.dark_mode_button import DarkModeButton
|
||||||
|
|
||||||
|
|
||||||
|
class PaletteViewer(BECWidget, QWidget):
|
||||||
|
"""
|
||||||
|
This class is a widget that displays current palette colors.
|
||||||
|
"""
|
||||||
|
|
||||||
|
ICON_NAME = "palette"
|
||||||
|
|
||||||
|
def __init__(self, *args, parent=None, **kwargs):
|
||||||
|
super().__init__(*args, theme_update=True, **kwargs)
|
||||||
|
QWidget.__init__(self, parent=parent)
|
||||||
|
self.setFixedSize(400, 600)
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
dark_mode_button = DarkModeButton(self)
|
||||||
|
layout.addWidget(dark_mode_button)
|
||||||
|
|
||||||
|
# Create a scroll area to hold the color boxes
|
||||||
|
scroll_area = QScrollArea(self)
|
||||||
|
scroll_area.setWidgetResizable(True)
|
||||||
|
scroll_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
|
|
||||||
|
# Create a frame to hold the color boxes
|
||||||
|
self.frame = QFrame(self)
|
||||||
|
self.frame_layout = QGridLayout(self.frame)
|
||||||
|
self.frame_layout.setSpacing(0)
|
||||||
|
self.frame_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
scroll_area.setWidget(self.frame)
|
||||||
|
layout.addWidget(scroll_area)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
self.update_palette()
|
||||||
|
|
||||||
|
def apply_theme(self, theme) -> None:
|
||||||
|
"""
|
||||||
|
Apply the theme to the widget.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
theme (str): The theme to apply.
|
||||||
|
"""
|
||||||
|
self.update_palette()
|
||||||
|
|
||||||
|
def clear_palette(self) -> None:
|
||||||
|
"""
|
||||||
|
Clear the palette colors from the frame.
|
||||||
|
Recursively removes all widgets and layouts in the frame layout.
|
||||||
|
"""
|
||||||
|
# Iterate over all items in the layout in reverse to safely remove them
|
||||||
|
for i in reversed(range(self.frame_layout.count())):
|
||||||
|
item = self.frame_layout.itemAt(i)
|
||||||
|
|
||||||
|
# If the item is a layout, clear its contents
|
||||||
|
if isinstance(item, QHBoxLayout):
|
||||||
|
# Recursively remove all widgets from the layout
|
||||||
|
for j in reversed(range(item.count())):
|
||||||
|
widget = item.itemAt(j).widget()
|
||||||
|
if widget:
|
||||||
|
item.removeWidget(widget)
|
||||||
|
widget.deleteLater()
|
||||||
|
self.frame_layout.removeItem(item)
|
||||||
|
|
||||||
|
# If the item is a widget, remove and delete it
|
||||||
|
elif item.widget():
|
||||||
|
widget = item.widget()
|
||||||
|
self.frame_layout.removeWidget(widget)
|
||||||
|
widget.deleteLater()
|
||||||
|
|
||||||
|
def update_palette(self) -> None:
|
||||||
|
"""
|
||||||
|
Update the palette colors in the frame.
|
||||||
|
"""
|
||||||
|
self.clear_palette()
|
||||||
|
palette_label = QLabel("Palette Colors (e.g. palette.windowText().color())")
|
||||||
|
palette_label.setStyleSheet("font-weight: bold;")
|
||||||
|
self.frame_layout.addWidget(palette_label, 0, 0)
|
||||||
|
|
||||||
|
palette = get_theme_palette()
|
||||||
|
# Add the palette colors (roles) to the frame
|
||||||
|
palette_roles = [
|
||||||
|
palette.windowText,
|
||||||
|
palette.toolTipText,
|
||||||
|
palette.placeholderText,
|
||||||
|
palette.text,
|
||||||
|
palette.buttonText,
|
||||||
|
palette.highlight,
|
||||||
|
palette.link,
|
||||||
|
palette.light,
|
||||||
|
palette.midlight,
|
||||||
|
palette.mid,
|
||||||
|
palette.shadow,
|
||||||
|
palette.button,
|
||||||
|
palette.brightText,
|
||||||
|
palette.toolTipBase,
|
||||||
|
palette.alternateBase,
|
||||||
|
palette.dark,
|
||||||
|
palette.base,
|
||||||
|
palette.window,
|
||||||
|
palette.highlightedText,
|
||||||
|
palette.linkVisited,
|
||||||
|
]
|
||||||
|
|
||||||
|
offset = 1
|
||||||
|
for i, pal in enumerate(palette_roles):
|
||||||
|
i += offset
|
||||||
|
color = pal().color()
|
||||||
|
label_layout = QHBoxLayout()
|
||||||
|
color_label = QLabel(f"{pal().color().name()} ({pal.__name__})")
|
||||||
|
background_label = self.background_label_with_clipboard(color)
|
||||||
|
label_layout.addWidget(color_label)
|
||||||
|
label_layout.addWidget(background_label)
|
||||||
|
self.frame_layout.addLayout(label_layout, i, 0)
|
||||||
|
|
||||||
|
# add a horizontal spacer
|
||||||
|
spacer = QLabel()
|
||||||
|
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||||
|
self.frame_layout.addWidget(spacer, i + 1, 0)
|
||||||
|
|
||||||
|
accent_colors_label = QLabel("Accent Colors (e.g. accent_colors.default)")
|
||||||
|
accent_colors_label.setStyleSheet("font-weight: bold;")
|
||||||
|
self.frame_layout.addWidget(accent_colors_label, i + 2, 0)
|
||||||
|
|
||||||
|
accent_colors = get_accent_colors()
|
||||||
|
items = [
|
||||||
|
(accent_colors.default, "default"),
|
||||||
|
(accent_colors.success, "success"),
|
||||||
|
(accent_colors.warning, "warning"),
|
||||||
|
(accent_colors.emergency, "emergency"),
|
||||||
|
(accent_colors.highlight, "highlight"),
|
||||||
|
]
|
||||||
|
|
||||||
|
offset = len(palette_roles) + 2
|
||||||
|
for i, (color, name) in enumerate(items):
|
||||||
|
i += offset
|
||||||
|
label_layout = QHBoxLayout()
|
||||||
|
color_label = QLabel(f"{color.name()} ({name})")
|
||||||
|
background_label = self.background_label_with_clipboard(color)
|
||||||
|
label_layout.addWidget(color_label)
|
||||||
|
label_layout.addWidget(background_label)
|
||||||
|
self.frame_layout.addLayout(label_layout, i + 2, 0)
|
||||||
|
|
||||||
|
def background_label_with_clipboard(self, color) -> QLabel:
|
||||||
|
"""
|
||||||
|
Create a label with a background color that copies the color to the clipboard when clicked.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
color (QColor): The color to display in the background.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
QLabel: The label with the background color.
|
||||||
|
"""
|
||||||
|
button = QLabel()
|
||||||
|
button.setStyleSheet(f"background-color: {color.name()};")
|
||||||
|
button.setToolTip("Click to copy color to clipboard")
|
||||||
|
button.setCursor(Qt.PointingHandCursor)
|
||||||
|
button.mousePressEvent = lambda event: QApplication.clipboard().setText(color.name())
|
||||||
|
return button
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": # pragma: no cover
|
||||||
|
import sys
|
||||||
|
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
viewer = PaletteViewer()
|
||||||
|
viewer.show()
|
||||||
|
sys.exit(app.exec_())
|
37
tests/unit_tests/test_palette_viewer.py
Normal file
37
tests/unit_tests/test_palette_viewer.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import pytest
|
||||||
|
from qtpy.QtCore import Qt
|
||||||
|
|
||||||
|
from bec_widgets.qt_utils.palette_viewer import PaletteViewer
|
||||||
|
from bec_widgets.widgets.dark_mode_button.dark_mode_button import DarkModeButton
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def palette_viewer(qtbot):
|
||||||
|
widget = PaletteViewer()
|
||||||
|
qtbot.addWidget(widget)
|
||||||
|
qtbot.waitExposed(widget)
|
||||||
|
yield widget
|
||||||
|
|
||||||
|
|
||||||
|
def test_palette_viewer_renders_palette_and_accents(qtbot, palette_viewer):
|
||||||
|
assert palette_viewer.frame_layout.count() == 28
|
||||||
|
palette_viewer.clear_palette()
|
||||||
|
assert palette_viewer.frame_layout.count() == 0
|
||||||
|
palette_viewer.update_palette()
|
||||||
|
assert palette_viewer.frame_layout.count() == 28
|
||||||
|
|
||||||
|
|
||||||
|
def test_palette_viewer_updates_on_theme_change(qtbot, palette_viewer):
|
||||||
|
light_window_text_color = palette_viewer.frame_layout.itemAt(1).itemAt(0).widget().text()
|
||||||
|
assert "(windowText)" in light_window_text_color
|
||||||
|
light_hex_color = light_window_text_color.split(" ")[0]
|
||||||
|
|
||||||
|
button = palette_viewer.findChild(DarkModeButton)
|
||||||
|
qtbot.mouseClick(button.mode_button, Qt.MouseButton.LeftButton)
|
||||||
|
qtbot.wait(100)
|
||||||
|
|
||||||
|
dark_window_text_color = palette_viewer.frame_layout.itemAt(1).itemAt(0).widget().text()
|
||||||
|
assert "(windowText)" in dark_window_text_color
|
||||||
|
dark_hex_color = dark_window_text_color.split(" ")[0]
|
||||||
|
|
||||||
|
assert light_hex_color != dark_hex_color
|
Reference in New Issue
Block a user