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

refactor: moved colormap related static methods to qt_utils colors.py

This commit is contained in:
wyzula-jan
2023-08-31 21:52:45 +02:00
parent 7bcf88d5eb
commit 1a06dd7534
4 changed files with 127 additions and 60 deletions

View File

@ -1,6 +1,32 @@
plot_settings:
background_color: "white"
num_columns: 2
xy_pairs: [["samx", ["gauss_bpm", "gauss_adc1"]], xy_pairs: [["samx", ["gauss_bpm", "gauss_adc1"]],
["samx", ["gauss_adc1", "gauss_adc2"]], ["samx", ["gauss_adc1", "gauss_adc2"]]]
["samx", ["gauss_adc2"]],
["samx", ["gauss_adc1"]], plot_data:
["samx", ["gauss_bpm", "gauss_adc2"]], - BPM plot:
["samx", ["gauss_bpm", "gauss_adc1", "gauss_adc2"]]] - x:
- signal:
- name: "samx"
- entry: "samx"
- label: 'Motor X' # will serve as x label
- y:
- signal:
- name: "gauss_bpm"
- entry: "gauss_bpm"
- label: 'BPM' # will serve as y label
- ADC plot:
- name: "gauss_adc1"
- x:
- signal:
- name: "samy"
- entry: "samy"
- label: 'Motor Y'
- y:
- signal:
- name: "gauss_adc"
- entry: ["gauss_adc1", "gauss_adc2"]
- label: 'ADC'

View File

@ -8,12 +8,15 @@ from pyqtgraph import mkBrush, mkColor, mkPen
from pyqtgraph.Qt import QtCore, uic from pyqtgraph.Qt import QtCore, uic
from bec_lib.core import MessageEndpoints from bec_lib.core import MessageEndpoints
from bec_widgets.qt_utils import Crosshair from bec_widgets.qt_utils import Crosshair, Colors
# TODO implement: # TODO implement:
# - implement scanID database for visualizing previous scans # - implement scanID database for visualizing previous scans
# - change how dap is handled in bec_dispatcher to handle more workers # - change how dap is handled in bec_dispatcher to handle more workers
# - YAML config -> plot settings
# - YAML config -> xy pairs -> multiple subsignals for different devices
# - Internal logic -> if user specify
class PlotApp(QWidget): class PlotApp(QWidget):
@ -37,14 +40,21 @@ class PlotApp(QWidget):
update_signal = pyqtSignal() update_signal = pyqtSignal()
update_dap_signal = pyqtSignal() update_dap_signal = pyqtSignal()
def __init__(self, xy_pairs, parent=None): def __init__(self, plot_settings: dict, xy_pairs: list, plot_data: dict, parent=None):
super(PlotApp, self).__init__(parent) super(PlotApp, self).__init__(parent)
# YAML config
self.plot_settings = plot_settings
self.xy_pairs = xy_pairs
self.plot_data = plot_data
# Setting global plot settings
self.init_plot_background(self.plot_settings["background_color"])
# Loading UI
current_path = os.path.dirname(__file__) current_path = os.path.dirname(__file__)
uic.loadUi(os.path.join(current_path, "extreme.ui"), self) uic.loadUi(os.path.join(current_path, "extreme.ui"), self)
# xy pairs for setting number of windows
self.xy_pairs = xy_pairs
# Nested dictionary to hold x and y data for multiple plots # Nested dictionary to hold x and y data for multiple plots
self.data = {} self.data = {}
@ -55,7 +65,8 @@ class PlotApp(QWidget):
self.scanID = None self.scanID = None
# Initialize the UI # Initialize the UI
self.init_ui(self.spinBox_N_columns.value()) self.init_ui(self.plot_settings["num_columns"])
self.spinBox_N_columns.setValue(self.plot_settings["num_columns"])
self.splitter.setSizes([400, 100]) self.splitter.setSizes([400, 100])
# Connect the update signal to the update plot method # Connect the update signal to the update plot method
@ -66,6 +77,26 @@ class PlotApp(QWidget):
# Change layout of plots when the number of columns is changed in GUI # Change layout of plots when the number of columns is changed in GUI
self.spinBox_N_columns.valueChanged.connect(lambda x: self.init_ui(x)) self.spinBox_N_columns.valueChanged.connect(lambda x: self.init_ui(x))
def init_plot_background(self, background_color: str) -> None:
"""
Initialize plot settings based on the background color.
Args:
background_color (str): The background color ('white' or 'black').
This method sets the background and foreground colors for pyqtgraph.
If the background is dark ('black'), the foreground will be set to 'white',
and vice versa.
"""
if background_color.lower() == "black":
pg.setConfigOption("background", "k")
pg.setConfigOption("foreground", "w")
elif background_color.lower() == "white":
pg.setConfigOption("background", "w")
pg.setConfigOption("foreground", "k")
else:
print(f"Warning: Unknown background color {background_color}. Using default settings.")
def init_ui(self, num_columns: int = 3) -> None: def init_ui(self, num_columns: int = 3) -> None:
""" """
Initialize the UI components, create plots and store their grid positions. Initialize the UI components, create plots and store their grid positions.
@ -79,7 +110,6 @@ class PlotApp(QWidget):
stretches the last plots to fit the remaining space. stretches the last plots to fit the remaining space.
""" """
self.glw.clear() self.glw.clear()
self.plots = {} self.plots = {}
self.grid_coordinates = [] # List to keep track of grid positions for each plot self.grid_coordinates = [] # List to keep track of grid positions for each plot
@ -102,7 +132,9 @@ class PlotApp(QWidget):
remaining_space -= colspan - 1 # Update remaining space remaining_space -= colspan - 1 # Update remaining space
last_row_cols -= 1 # Update remaining plots last_row_cols -= 1 # Update remaining plots
plot = self.glw.addPlot(row=row, col=col, colspan=colspan) plot = self.glw.addPlot(
row=row, col=col, colspan=colspan, title=list(self.plot_data[i].keys())[0]
)
plot.setLabel("bottom", x) plot.setLabel("bottom", x)
plot.setLabel("left", ", ".join(ys)) plot.setLabel("left", ", ".join(ys))
plot.addLegend() plot.addLegend()
@ -126,7 +158,7 @@ class PlotApp(QWidget):
for idx, ((x, ys), plot) in enumerate(self.plots.items()): for idx, ((x, ys), plot) in enumerate(self.plots.items()):
plot.clear() plot.clear()
self.curves_data[(x, tuple(ys))] = [] self.curves_data[(x, tuple(ys))] = []
colors_ys = PlotApp.golden_angle_color(colormap="CET-R2", num=len(ys)) colors_ys = Colors.golden_angle_color(colormap="plasma", num=len(ys))
row, col = self.grid_coordinates[idx] # Retrieve the grid position for this plot row, col = self.grid_coordinates[idx] # Retrieve the grid position for this plot
@ -235,51 +267,6 @@ class PlotApp(QWidget):
self.update_signal.emit() self.update_signal.emit()
@staticmethod
def golden_ratio(num: int) -> list:
"""Calculate the golden ratio for a given number of angles.
Args:
num (int): Number of angles
"""
phi = 2 * np.pi * ((1 + np.sqrt(5)) / 2)
angles = []
for ii in range(num):
x = np.cos(ii * phi)
y = np.sin(ii * phi)
angle = np.arctan2(y, x)
angles.append(angle)
return angles
@staticmethod
def golden_angle_color(colormap: str, num: int) -> list:
"""
Extract num colors for from the specified colormap following golden angle distribution.
Args:
colormap (str): Name of the colormap
num (int): Number of requested colors
Returns:
list: List of colors with length <num>
Raises:
ValueError: If the number of requested colors is greater than the number of colors in the colormap.
"""
cmap = pg.colormap.get(colormap)
cmap_colors = cmap.color
if num > len(cmap_colors):
raise ValueError(
f"Number of colors requested ({num}) is greater than the number of colors in the colormap ({len(cmap_colors)})"
)
angles = PlotApp.golden_ratio(len(cmap_colors))
color_selection = np.round(np.interp(angles, (-np.pi, np.pi), (0, len(cmap_colors))))
colors = [
mkColor(tuple((cmap_colors[int(ii)] * 255).astype(int))) for ii in color_selection[:num]
]
return colors
if __name__ == "__main__": if __name__ == "__main__":
import yaml import yaml
@ -297,7 +284,10 @@ if __name__ == "__main__":
try: try:
with open(args.config, "r") as file: with open(args.config, "r") as file:
config = yaml.safe_load(file) config = yaml.safe_load(file)
plot_settings = config.get("plot_settings", {})
xy_pairs = config.get("xy_pairs", []) xy_pairs = config.get("xy_pairs", [])
plot_data = config.get("plot_data", {})
except FileNotFoundError: except FileNotFoundError:
print(f"The file {args.config} was not found.") print(f"The file {args.config} was not found.")
exit(1) exit(1)
@ -316,7 +306,7 @@ if __name__ == "__main__":
queue = client.queue queue = client.queue
app = QApplication([]) app = QApplication([])
plotApp = PlotApp(xy_pairs=xy_pairs) plotApp = PlotApp(xy_pairs=xy_pairs, plot_settings=plot_settings, plot_data=plot_data)
# Connecting signals from bec_dispatcher # Connecting signals from bec_dispatcher
bec_dispatcher.connect_slot(plotApp.on_scan_segment, MessageEndpoints.scan_segment()) bec_dispatcher.connect_slot(plotApp.on_scan_segment, MessageEndpoints.scan_segment())

View File

@ -1 +1,2 @@
from .crosshair import Crosshair from .crosshair import Crosshair
from .colors import Colors

View File

@ -0,0 +1,50 @@
import numpy as np
import pyqtgraph as pg
from pyqtgraph import mkColor
class Colors:
@staticmethod
def golden_ratio(num: int) -> list:
"""Calculate the golden ratio for a given number of angles.
Args:
num (int): Number of angles
"""
phi = 2 * np.pi * ((1 + np.sqrt(5)) / 2)
angles = []
for ii in range(num):
x = np.cos(ii * phi)
y = np.sin(ii * phi)
angle = np.arctan2(y, x)
angles.append(angle)
return angles
@staticmethod
def golden_angle_color(colormap: str, num: int) -> list:
"""
Extract num colors for from the specified colormap following golden angle distribution.
Args:
colormap (str): Name of the colormap
num (int): Number of requested colors
Returns:
list: List of colors with length <num>
Raises:
ValueError: If the number of requested colors is greater than the number of colors in the colormap.
"""
cmap = pg.colormap.get(colormap)
cmap_colors = cmap.color
if num > len(cmap_colors):
raise ValueError(
f"Number of colors requested ({num}) is greater than the number of colors in the colormap ({len(cmap_colors)})"
)
angles = Colors.golden_ratio(len(cmap_colors))
color_selection = np.round(np.interp(angles, (-np.pi, np.pi), (0, len(cmap_colors))))
colors = [
mkColor(tuple((cmap_colors[int(ii)] * 255).astype(int))) for ii in color_selection[:num]
]
return colors