0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-13 19:21:50 +02:00

fix(colors): pyqtgraph styling updated on the app level

This commit is contained in:
2025-02-12 21:45:09 +01:00
committed by wyzula_j
parent 97c0ed53df
commit ae18279685
5 changed files with 125 additions and 18 deletions

View File

@ -66,7 +66,7 @@ class BECWidget(BECConnector):
if hasattr(qapp, "theme_signal"):
qapp.theme_signal.theme_updated.connect(self._update_theme)
def _update_theme(self, theme: str):
def _update_theme(self, theme: str | None = None):
"""Update the theme."""
if theme is None:
qapp = QApplication.instance()

View File

@ -1,6 +1,5 @@
from __future__ import annotations
import itertools
import re
from typing import TYPE_CHECKING, Literal
@ -71,15 +70,64 @@ def apply_theme(theme: Literal["dark", "light"]):
Apply the theme to all pyqtgraph widgets. Do not use this function directly. Use set_theme instead.
"""
app = QApplication.instance()
# go through all pyqtgraph widgets and set background
children = itertools.chain.from_iterable(
top.findChildren(pg.GraphicsLayoutWidget) for top in app.topLevelWidgets()
)
pg.setConfigOptions(
foreground="d" if theme == "dark" else "k", background="k" if theme == "dark" else "w"
)
for pg_widget in children:
pg_widget.setBackground("k" if theme == "dark" else "w")
graphic_layouts = [
child
for top in app.topLevelWidgets()
for child in top.findChildren(pg.GraphicsLayoutWidget)
]
plot_items = [
item
for gl in graphic_layouts
for item in gl.ci.items.keys() # ci is internal pg.GraphicsLayout that hosts all items
if isinstance(item, pg.PlotItem)
]
histograms = [
item
for gl in graphic_layouts
for item in gl.ci.items.keys() # ci is internal pg.GraphicsLayout that hosts all items
if isinstance(item, pg.HistogramLUTItem)
]
# Update background color based on the theme
if theme == "light":
background_color = "#e9ecef" # Subtle contrast for light mode
foreground_color = "#141414"
label_color = "#000000"
axis_color = "#666666"
else:
background_color = "#141414" # Dark mode
foreground_color = "#e9ecef"
label_color = "#FFFFFF"
axis_color = "#CCCCCC"
# update GraphicsLayoutWidget
pg.setConfigOptions(foreground=foreground_color, background=background_color)
for pg_widget in graphic_layouts:
pg_widget.setBackground(background_color)
# update PlotItems
for plot_item in plot_items:
for axis in ["left", "right", "top", "bottom"]:
plot_item.getAxis(axis).setPen(pg.mkPen(color=axis_color))
plot_item.getAxis(axis).setTextPen(pg.mkPen(color=label_color))
# Change title color
plot_item.titleLabel.setText(plot_item.titleLabel.text, color=label_color)
# Change legend color
if hasattr(plot_item, "legend") and plot_item.legend is not None:
plot_item.legend.setLabelTextColor(label_color)
# if legend is in plot item and theme is changed, has to be like that because of pg opt logic
for sample, label in plot_item.legend.items:
label_text = label.text
label.setText(label_text, color=label_color)
# update HistogramLUTItem
for histogram in histograms:
histogram.axis.setPen(pg.mkPen(color=axis_color))
histogram.axis.setTextPen(pg.mkPen(color=label_color))
# now define stylesheet according to theme and apply it
style = bec_qthemes.load_stylesheet(theme)

View File

@ -1,9 +1,15 @@
import pyqtgraph as pg
import pytest
from pydantic import ValidationError
from qtpy.QtGui import QColor
from qtpy.QtWidgets import QVBoxLayout, QWidget
from bec_widgets.utils import Colors
from bec_widgets.utils import Colors, ConnectionConfig
from bec_widgets.utils.bec_widget import BECWidget
from bec_widgets.utils.colors import apply_theme
from bec_widgets.widgets.containers.figure.plots.waveform.waveform_curve import CurveConfig
from tests.unit_tests.client_mocks import mocked_client
from tests.unit_tests.conftest import create_widget
def test_color_validation_CSS():
@ -110,3 +116,55 @@ def test_golder_angle_colors(num):
assert all(color.isValid() for color in colors_qcolor)
assert all(color.startswith("#") for color in colors_hex)
##################################################
# Testing of the ExamplePlotWidget theme change
##################################################
class ExamplePlotWidget(BECWidget, QWidget):
def __init__(
self,
parent: QWidget | None = None,
config: ConnectionConfig | None = None,
client=None,
gui_id: str | None = None,
) -> None:
if config is None:
config = ConnectionConfig(widget_class=self.__class__.__name__)
super().__init__(client=client, gui_id=gui_id, config=config)
QWidget.__init__(self, parent=parent)
self.layout = QVBoxLayout(self)
self.glw = pg.GraphicsLayoutWidget()
self.pi = pg.PlotItem()
self.layout.addWidget(self.glw)
self.glw.addItem(self.pi)
self.pi.plot([1, 2, 3, 4, 5], pen="r")
def test_apply_theme(qtbot, mocked_client):
widget = create_widget(qtbot, ExamplePlotWidget, client=mocked_client)
apply_theme("dark")
# Get the default state of dark theme
dark_bg = widget.glw.backgroundBrush().color().name()
dark_axis_color = widget.pi.getAxis("left").pen().color().name()
dark_label_color = widget.pi.getAxis("left").textPen().color().name()
assert dark_bg == "#141414"
assert dark_axis_color == "#cccccc"
assert dark_label_color == "#ffffff"
apply_theme("light")
# Get the default state of light theme
light_bg = widget.glw.backgroundBrush().color().name()
light_axis_color = widget.pi.getAxis("left").pen().color().name()
light_label_color = widget.pi.getAxis("left").textPen().color().name()
assert light_bg == "#e9ecef"
assert light_axis_color == "#666666"
assert light_label_color == "#000000"

View File

@ -271,7 +271,8 @@ def test_multi_waveform_widget_theme_update(qtbot, multi_waveform_widget):
palette = get_theme_palette()
waveform_color_dark = multi_waveform_widget.waveform.plot_item.getAxis("left").pen().color()
bg_color = multi_waveform_widget.fig.backgroundBrush().color()
assert bg_color == QColor("black")
assert bg_color == QColor(20, 20, 20)
assert waveform_color_dark == palette.text().color()
# Set the theme to light
@ -279,7 +280,7 @@ def test_multi_waveform_widget_theme_update(qtbot, multi_waveform_widget):
palette = get_theme_palette()
waveform_color_light = multi_waveform_widget.waveform.plot_item.getAxis("left").pen().color()
bg_color = multi_waveform_widget.fig.backgroundBrush().color()
assert bg_color == QColor("white")
assert bg_color == QColor(233, 236, 239)
assert waveform_color_light == palette.text().color()
assert waveform_color_dark != waveform_color_light
@ -291,5 +292,5 @@ def test_multi_waveform_widget_theme_update(qtbot, multi_waveform_widget):
waveform_color = multi_waveform_widget.waveform.plot_item.getAxis("left").pen().color()
bg_color = multi_waveform_widget.fig.backgroundBrush().color()
assert bg_color == QColor("black")
assert bg_color == QColor(20, 20, 20)
assert waveform_color == waveform_color_dark

View File

@ -484,7 +484,7 @@ def test_waveform_widget_theme_update(qtbot, waveform_widget):
palette = get_theme_palette()
waveform_color_dark = waveform_widget.waveform.plot_item.getAxis("left").pen().color()
bg_color = waveform_widget.fig.backgroundBrush().color()
assert bg_color == QColor("black")
assert bg_color == QColor(20, 20, 20)
assert waveform_color_dark == palette.text().color()
# Set the theme to light; equivalent to clicking the light mode button
@ -493,7 +493,7 @@ def test_waveform_widget_theme_update(qtbot, waveform_widget):
palette = get_theme_palette()
waveform_color_light = waveform_widget.waveform.plot_item.getAxis("left").pen().color()
bg_color = waveform_widget.fig.backgroundBrush().color()
assert bg_color == QColor("white")
assert bg_color == QColor(233, 236, 239)
assert waveform_color_light == palette.text().color()
assert waveform_color_dark != waveform_color_light
@ -509,7 +509,7 @@ def test_waveform_widget_theme_update(qtbot, waveform_widget):
# we compare the waveform color to the dark theme color
waveform_color = waveform_widget.waveform.plot_item.getAxis("left").pen().color()
bg_color = waveform_widget.fig.backgroundBrush().color()
assert bg_color == QColor("black")
assert bg_color == QColor(20, 20, 20)
assert waveform_color == waveform_color_dark