From e8305652fde384da037242cf8f7e3606f22bcfb6 Mon Sep 17 00:00:00 2001 From: wyzula-jan Date: Sat, 6 Jul 2024 11:18:16 +0200 Subject: [PATCH] feat(curve_dialog): add DAP functionality --- .../curve_dialog/curve_dialog.py | 210 ++++++++++++++---- .../curve_dialog/curve_dialog.ui | 92 ++++++-- .../widgets/waveform/waveform_widget.py | 7 +- 3 files changed, 236 insertions(+), 73 deletions(-) diff --git a/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/curve_dialog.py b/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/curve_dialog.py index aa85dc25..4d752238 100644 --- a/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/curve_dialog.py +++ b/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/curve_dialog.py @@ -1,20 +1,20 @@ from __future__ import annotations import os +from typing import Literal +from pydantic import BaseModel from PySide6.QtCore import QObject from PySide6.QtGui import QIcon from PySide6.QtWidgets import QComboBox, QLineEdit, QPushButton, QSpinBox, QTableWidget -from pydantic import BaseModel from qtpy.QtCore import Slot from qtpy.QtWidgets import QVBoxLayout from bec_widgets.qt_utils.settings_dialog import SettingWidget -from bec_widgets.utils import UILoader, Colors +from bec_widgets.utils import BECConnector, Colors, UILoader from bec_widgets.widgets.color_button.color_button import ColorButton from bec_widgets.widgets.device_line_edit.device_line_edit import DeviceLineEdit from bec_widgets.widgets.figure.plots.plot_base import AxisConfig - from bec_widgets.widgets.figure.plots.waveform.waveform_curve import CurveConfig @@ -29,26 +29,46 @@ class CurveSettings(SettingWidget): self.layout.addWidget(self.ui) self.ui.add_curve.clicked.connect(self.add_curve) + self.ui.add_dap.clicked.connect(self.add_dap) self.ui.x_mode.currentIndexChanged.connect(self.set_x_mode) - self.ui.normalize_colors.clicked.connect(self.change_colormap) + self.ui.normalize_colors_scan.clicked.connect(lambda: self.change_colormap("scan")) + self.ui.normalize_colors_dap.clicked.connect(lambda: self.change_colormap("dap")) @Slot(dict) def display_current_settings(self, config: dict | BaseModel): - curves = config["scan_segment"] - # set mode of x axis box + # What elements should be enabled x_name = self.target_widget.waveform._x_axis_mode["name"] x_entry = self.target_widget.waveform._x_axis_mode["entry"] - self._setup_x_box(x_name, x_entry) + self._enable_ui_elements(x_name, x_entry) cm = self.target_widget.config.color_palette - self.ui.color_map_selector.combo.setCurrentText(cm) + self.ui.color_map_selector_scan.combo.setCurrentText(cm) - for label, curve in curves.items(): + # Scan Curve Table + for label, curve in config["scan_segment"].items(): row_count = self.ui.scan_table.rowCount() self.ui.scan_table.insertRow(row_count) - ScanRow(table_widget=self.ui.scan_table, row=row_count, config=curve.config) + DialogRow( + parent=self, + table_widget=self.ui.scan_table, + client=self.target_widget.client, + row=row_count, + config=curve.config, + ).add_scan_row() - def _setup_x_box(self, name, entry): + # Add DAP Curves + for label, curve in config["DAP"].items(): + row_count = self.ui.dap_table.rowCount() + self.ui.dap_table.insertRow(row_count) + DialogRow( + parent=self, + table_widget=self.ui.dap_table, + client=self.target_widget.client, + row=row_count, + config=curve.config, + ).add_dap_row() + + def _enable_ui_elements(self, name, entry): if name in ["index", "timestamp", "best_effort"]: self.ui.x_mode.setCurrentText(name) self.set_x_mode() @@ -64,26 +84,38 @@ class CurveSettings(SettingWidget): if x_mode in ["index", "timestamp", "best_effort"]: self.ui.x_name.setEnabled(False) self.ui.x_entry.setEnabled(False) + self.ui.dap_table.setEnabled(False) + self.ui.add_dap.setEnabled(False) else: self.ui.x_name.setEnabled(True) self.ui.x_entry.setEnabled(True) + self.ui.dap_table.setEnabled(True) + self.ui.add_dap.setEnabled(True) @Slot() - def change_colormap(self): - cm = self.ui.color_map_selector.combo.currentText() - rows = self.ui.scan_table.rowCount() + def change_colormap(self, target: Literal["scan", "dap"]): + if target == "scan": + cm = self.ui.color_map_selector_scan.combo.currentText() + table = self.ui.scan_table + if target == "dap": + cm = self.ui.color_map_selector_dap.combo.currentText() + table = self.ui.dap_table + rows = table.rowCount() colors = Colors.golden_angle_color(colormap=cm, num=rows + 1, format="HEX") + color_button_col = 2 if target == "scan" else 3 for row, color in zip(range(rows), colors): - self.ui.scan_table.cellWidget(row, 2).setColor(color) - self.target_widget.set_colormap(cm) + table.cellWidget(row, color_button_col).setColor(color) @Slot() def accept_changes(self): - self.accept_scan_curve_changes() + self.accept_curve_changes() - def accept_scan_curve_changes(self): - old_curves = list(self.target_widget.waveform._curves_data["scan_segment"].values()) - for curve in old_curves: + def accept_curve_changes(self): + old_curves_scans = list(self.target_widget.waveform._curves_data["scan_segment"].values()) + old_curves_dap = list(self.target_widget.waveform._curves_data["DAP"].values()) + for curve in old_curves_scans: + curve.remove() + for curve in old_curves_dap: curve.remove() self.get_curve_params() @@ -114,38 +146,85 @@ class CurveSettings(SettingWidget): pen_width=width, symbol_size=symbol_size, ) + + if x_mode not in ["index", "timestamp", "best_effort"]: + for row in range(self.ui.dap_table.rowCount()): + y_name = self.ui.dap_table.cellWidget(row, 0).text() + y_entry = self.ui.dap_table.cellWidget(row, 1).text() + dap = self.ui.dap_table.cellWidget(row, 2).currentText() + color = self.ui.dap_table.cellWidget(row, 3).get_color() + style = self.ui.dap_table.cellWidget(row, 4).currentText() + width = self.ui.dap_table.cellWidget(row, 5).value() + symbol_size = self.ui.dap_table.cellWidget(row, 6).value() + + self.target_widget.add_dap( + x_name=x_name, + x_entry=x_entry, + y_name=y_name, + y_entry=y_entry, + dap=dap, + color=color, + pen_style=style, + pen_width=width, + symbol_size=symbol_size, + ) self.target_widget.scan_history(-1) def add_curve(self): row_count = self.ui.scan_table.rowCount() self.ui.scan_table.insertRow(row_count) - ScanRow(table_widget=self.ui.scan_table, row=row_count, config=None) + DialogRow( + parent=self, + table_widget=self.ui.scan_table, + client=self.target_widget.client, + row=row_count, + config=None, + ).add_scan_row() + + def add_dap(self): + row_count = self.ui.dap_table.rowCount() + self.ui.dap_table.insertRow(row_count) + DialogRow( + parent=self, + table_widget=self.ui.dap_table, + client=self.target_widget.client, + row=row_count, + config=None, + ).add_dap_row() -class ScanRow(QObject): +class DialogRow(QObject): def __init__( self, parent=None, table_widget: QTableWidget = None, - row=None, - config: dict | CurveConfig = None, + row: int = None, + config: dict = None, + client=None, ): - super().__init__(parent=parent) - current_path = os.path.dirname(__file__) + super().__init__(parent=parent) + self.client = client + + self.table_widget = table_widget + self.row = row + self.config = config + self.init_default_widgets() + + def init_default_widgets(self): + # Remove Button - icon_path = os.path.join(current_path, "remove.svg") - self.remove_button = QPushButton() - self.remove_button.setIcon(QIcon(icon_path)) + self.remove_button = RemoveButton() # Name and Entry self.device_line_edit = DeviceLineEdit() self.entry_line_edit = QLineEdit() + self.dap_combo = QComboBox() + self.populate_dap_combobox() + # Styling - default_color = Colors.golden_angle_color(colormap="magma", num=row + 1, format="HEX")[-1] self.color_button = ColorButton() - self.color_button.setColor(default_color) self.style_combo = StyleComboBox() self.width = QSpinBox() self.width.setMinimum(1) @@ -157,27 +236,34 @@ class ScanRow(QObject): self.symbol_size.setMaximum(20) self.symbol_size.setValue(5) - self.table_widget = table_widget - self.row = row - self.remove_button.clicked.connect( lambda: self.remove_row() ) # From some reason do not work without lambda - if config is not None: - self.fill_row_from_config(config) + def populate_dap_combobox(self): + available_models = [ + attr + for attr in dir(self.client.dap) + if not attr.startswith("__") + and not callable(getattr(self.client.dap, attr)) + and not attr.startswith("_") + ] + self.dap_combo.addItems(available_models) - self.add_row_to_table() + def add_scan_row(self): + if self.config is not None: + self.device_line_edit.setText(self.config.signals.y.name) + self.entry_line_edit.setText(self.config.signals.y.entry) + self.color_button.setColor(self.config.color) + self.style_combo.setCurrentText(self.config.pen_style) + self.width.setValue(self.config.pen_width) + self.symbol_size.setValue(self.config.symbol_size) + else: + default_color = Colors.golden_angle_color( + colormap="magma", num=self.row + 1, format="HEX" + )[-1] + self.color_button.setColor(default_color) - def fill_row_from_config(self, config): - self.device_line_edit.setText(config.signals.y.name) - self.entry_line_edit.setText(config.signals.y.entry) - self.color_button.setColor(config.color) - self.style_combo.setCurrentText(config.pen_style) - self.width.setValue(config.pen_width) - self.symbol_size.setValue(config.symbol_size) - - def add_row_to_table(self): self.table_widget.setCellWidget(self.row, 0, self.device_line_edit) self.table_widget.setCellWidget(self.row, 1, self.entry_line_edit) self.table_widget.setCellWidget(self.row, 2, self.color_button) @@ -186,6 +272,30 @@ class ScanRow(QObject): self.table_widget.setCellWidget(self.row, 5, self.symbol_size) self.table_widget.setCellWidget(self.row, 6, self.remove_button) + def add_dap_row(self): + if self.config is not None: + self.device_line_edit.setText(self.config.signals.y.name) + self.entry_line_edit.setText(self.config.signals.y.entry) + self.dap_combo.setCurrentText(self.config.signals.dap) + self.color_button.setColor(self.config.color) + self.style_combo.setCurrentText(self.config.pen_style) + self.width.setValue(self.config.pen_width) + self.symbol_size.setValue(self.config.symbol_size) + else: + default_color = Colors.golden_angle_color( + colormap="magma", num=self.row + 1, format="HEX" + )[-1] + self.color_button.setColor(default_color) + + self.table_widget.setCellWidget(self.row, 0, self.device_line_edit) + self.table_widget.setCellWidget(self.row, 1, self.entry_line_edit) + self.table_widget.setCellWidget(self.row, 2, self.dap_combo) + self.table_widget.setCellWidget(self.row, 3, self.color_button) + self.table_widget.setCellWidget(self.row, 4, self.style_combo) + self.table_widget.setCellWidget(self.row, 5, self.width) + self.table_widget.setCellWidget(self.row, 6, self.symbol_size) + self.table_widget.setCellWidget(self.row, 7, self.remove_button) + @Slot() def remove_row(self): row = self.table_widget.indexAt(self.remove_button.pos()).row() @@ -200,3 +310,11 @@ class StyleComboBox(QComboBox): def __init__(self, parent=None): super().__init__(parent) self.addItems(["solid", "dash", "dot", "dashdot"]) + + +class RemoveButton(QPushButton): + def __init__(self, parent=None): + super().__init__(parent) + current_path = os.path.dirname(__file__) + icon_path = os.path.join(current_path, "remove.svg") + self.setIcon(QIcon(icon_path)) diff --git a/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/curve_dialog.ui b/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/curve_dialog.ui index 094595e1..21402569 100644 --- a/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/curve_dialog.ui +++ b/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/curve_dialog.ui @@ -31,7 +31,7 @@ X Axis - + @@ -41,6 +41,12 @@ + + + 150 + 26 + + best_effort @@ -92,6 +98,19 @@ + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + @@ -158,7 +177,7 @@ - + 150 @@ -243,7 +262,7 @@ - + Normalize Colors @@ -268,27 +287,7 @@ 5 - - - - Add DAP - - - - - - - Qt::Orientation::Horizontal - - - - 585 - 20 - - - - - + true @@ -338,6 +337,51 @@ + + + + Normalize Colors + + + + + + + Qt::Orientation::Horizontal + + + + 585 + 20 + + + + + + + + Add DAP + + + + + + + + 150 + 0 + + + + + inferno + viridis + plasma + magma + + + + diff --git a/bec_widgets/widgets/waveform/waveform_widget.py b/bec_widgets/widgets/waveform/waveform_widget.py index 2f412d14..d9b585de 100644 --- a/bec_widgets/widgets/waveform/waveform_widget.py +++ b/bec_widgets/widgets/waveform/waveform_widget.py @@ -91,7 +91,7 @@ class BECWaveformWidget(BECConnector, QWidget): self._hook_actions() # TEst actions - self.plot(x_name="samx", y_name="bpm4i") + self.plot(x_name="samx", y_name="bpm4i", dap="GaussianModel") self.plot(x_name="samx", y_name="bpm3a") self.plot(x_name="samx", y_name="bpm6i") @@ -261,6 +261,7 @@ class BECWaveformWidget(BECConnector, QWidget): y_entry: str | None = None, color: str | None = None, dap: str = "GaussianModel", + validate_bec: bool = True, **kwargs, ) -> BECCurve: """ @@ -272,9 +273,8 @@ class BECWaveformWidget(BECConnector, QWidget): y_name(str): Name of the y signal. y_entry(str): Entry of the y signal. color(str, optional): Color of the curve. Defaults to None. - color_map_z(str): The color map to use for the z-axis. - label(str, optional): Label of the curve. Defaults to None. dap(str): The dap model to use for the curve. + validate_bec(bool, optional): If True, validate the signal with BEC. Defaults to True. **kwargs: Additional keyword arguments for the curve configuration. Returns: @@ -287,6 +287,7 @@ class BECWaveformWidget(BECConnector, QWidget): y_entry=y_entry, color=color, dap=dap, + validate_bec=validate_bec, **kwargs, )