diff --git a/bec_widgets/widgets/figure/plots/axis_settings.py b/bec_widgets/widgets/figure/plots/axis_settings.py
index 511a2a90..10572883 100644
--- a/bec_widgets/widgets/figure/plots/axis_settings.py
+++ b/bec_widgets/widgets/figure/plots/axis_settings.py
@@ -1,21 +1,19 @@
import os
-from PySide6.QtWidgets import QDialog, QDialogButtonBox
from qtpy.QtCore import Slot
-from qtpy.QtWidgets import QVBoxLayout, QWidget
+from qtpy.QtWidgets import QVBoxLayout
+from bec_widgets.qt_utils.settings_dialog import SettingWidget
from bec_widgets.utils import UILoader
-from bec_widgets.utils.colors import apply_theme
from bec_widgets.utils.widget_io import WidgetIO
-class AxisSettings(QWidget):
- def __init__(self, parent=None, target_widget: QWidget = None, *args, **kwargs):
+class AxisSettings(SettingWidget):
+ def __init__(self, parent=None, *args, **kwargs):
super().__init__(parent=parent, *args, **kwargs)
current_path = os.path.dirname(__file__)
self.ui = UILoader().load_ui(os.path.join(current_path, "axis_settings.ui"), self)
- self.target_widget = target_widget
self.layout = QVBoxLayout(self)
self.layout.addWidget(self.ui)
@@ -25,12 +23,10 @@ class AxisSettings(QWidget):
self.setMaximumHeight(280)
self.resize(380, 280)
- self.display_current_settings(self.target_widget._config_dict.get("axis", {}))
-
@Slot(dict)
def display_current_settings(self, axis_config: dict):
- if dict == {}:
+ if axis_config == {}:
return
# Top Box
@@ -45,6 +41,10 @@ class AxisSettings(QWidget):
WidgetIO.check_and_adjust_limits(self.ui.x_max, axis_config["x_lim"][1])
WidgetIO.set_value(self.ui.x_min, axis_config["x_lim"][0])
WidgetIO.set_value(self.ui.x_max, axis_config["x_lim"][1])
+ if axis_config["x_lim"] is None:
+ x_range = self.target_widget.fig.widget_list[0].plot_item.viewRange()[0]
+ WidgetIO.set_value(self.ui.x_min, x_range[0])
+ WidgetIO.set_value(self.ui.x_max, x_range[1])
# Y Axis Box
WidgetIO.set_value(self.ui.y_label, axis_config["y_label"])
@@ -55,6 +55,10 @@ class AxisSettings(QWidget):
WidgetIO.check_and_adjust_limits(self.ui.y_max, axis_config["y_lim"][1])
WidgetIO.set_value(self.ui.y_min, axis_config["y_lim"][0])
WidgetIO.set_value(self.ui.y_max, axis_config["y_lim"][1])
+ if axis_config["y_lim"] is None:
+ y_range = self.target_widget.fig.widget_list[0].plot_item.viewRange()[1]
+ WidgetIO.set_value(self.ui.y_min, y_range[0])
+ WidgetIO.set_value(self.ui.y_max, y_range[1])
@Slot()
def accept_changes(self):
@@ -82,28 +86,3 @@ class AxisSettings(QWidget):
y_lim=y_lim,
)
self.target_widget.set_grid(x_grid, y_grid)
-
-
-class AxisSettingsDialog(QDialog):
- def __init__(self, parent=None, target_widget: QWidget = None, *args, **kwargs):
- super().__init__(parent, *args, **kwargs)
-
- self.setModal(False)
-
- self.setWindowTitle("Axis Settings")
- self.target_widget = target_widget
- self.widget = AxisSettings(target_widget=self.target_widget)
- # self.widget.display_current_settings(self.target_widget._config_dict)
- self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
-
- self.button_box.accepted.connect(self.accept)
- self.button_box.rejected.connect(self.reject)
-
- self.layout = QVBoxLayout(self)
- self.layout.addWidget(self.widget)
- self.layout.addWidget(self.button_box)
-
- @Slot()
- def accept(self):
- self.widget.accept_changes()
- super().accept()
diff --git a/bec_widgets/widgets/figure/plots/plot_base.py b/bec_widgets/widgets/figure/plots/plot_base.py
index 58430603..3260712c 100644
--- a/bec_widgets/widgets/figure/plots/plot_base.py
+++ b/bec_widgets/widgets/figure/plots/plot_base.py
@@ -2,11 +2,8 @@ from __future__ import annotations
from typing import Literal, Optional
-import numpy as np
import pyqtgraph as pg
from pydantic import BaseModel, Field
-from qtpy import QT_VERSION
-from qtpy.QtGui import QFont, QFontDatabase, QFontInfo
from qtpy.QtWidgets import QWidget
from bec_widgets.utils import BECConnector, ConnectionConfig
diff --git a/bec_widgets/widgets/waveform/assets/line_axis.svg b/bec_widgets/widgets/waveform/assets/line_axis.svg
new file mode 100644
index 00000000..e0e713e0
--- /dev/null
+++ b/bec_widgets/widgets/waveform/assets/line_axis.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/bec_widgets/widgets/waveform/waveform_dialog/__init__.py b/bec_widgets/widgets/waveform/waveform_toolbar/__init__.py
similarity index 100%
rename from bec_widgets/widgets/waveform/waveform_dialog/__init__.py
rename to bec_widgets/widgets/waveform/waveform_toolbar/__init__.py
diff --git a/bec_widgets/widgets/waveform/waveform_dialog/curve_dialog.py b/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/__init__.py
similarity index 100%
rename from bec_widgets/widgets/waveform/waveform_dialog/curve_dialog.py
rename to bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/__init__.py
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
new file mode 100644
index 00000000..d8b98a51
--- /dev/null
+++ b/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/curve_dialog.py
@@ -0,0 +1,155 @@
+from __future__ import annotations
+
+import os
+
+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
+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
+
+
+class CurveSettings(SettingWidget):
+ def __init__(self, parent=None, *args, **kwargs):
+ super().__init__(parent, *args, **kwargs)
+ current_path = os.path.dirname(__file__)
+
+ self.ui = UILoader(self).loader(os.path.join(current_path, "curve_dialog.ui"))
+
+ self.layout = QVBoxLayout(self)
+ self.layout.addWidget(self.ui)
+
+ self.ui.add_curve.clicked.connect(self.add_curve)
+
+ @Slot(dict)
+ def display_current_settings(self, config: dict | BaseModel):
+ curves = config["scan_segment"]
+ first_label, first_curve = next(iter(curves.items()))
+ self.ui.x_name.setText(first_curve.config.signals.x.name)
+ self.ui.x_entry.setText(first_curve.config.signals.x.entry)
+ for label, curve in curves.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)
+
+ @Slot()
+ def accept_changes(self):
+ self.accept_scan_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:
+ curve.remove()
+ self.get_curve_params()
+
+ def get_curve_params(self):
+ x_name = self.ui.x_name.text()
+ x_entry = self.ui.x_entry.text()
+ for row in range(self.ui.scan_table.rowCount()):
+ y_name = self.ui.scan_table.cellWidget(row, 0).text()
+ y_entry = self.ui.scan_table.cellWidget(row, 1).text()
+ color = self.ui.scan_table.cellWidget(row, 2).get_color()
+ style = self.ui.scan_table.cellWidget(row, 3).currentText()
+ width = self.ui.scan_table.cellWidget(row, 4).value()
+ symbol_size = self.ui.scan_table.cellWidget(row, 5).value()
+ self.target_widget.plot(
+ x_name=x_name,
+ x_entry=x_entry,
+ y_name=y_name,
+ y_entry=y_entry,
+ 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)
+
+
+class ScanRow(QObject):
+ def __init__(
+ self,
+ parent=None,
+ table_widget: QTableWidget = None,
+ row=None,
+ config: dict | CurveConfig = None,
+ ):
+ super().__init__(parent=parent)
+
+ current_path = os.path.dirname(__file__)
+ # Remove Button
+ icon_path = os.path.join(current_path, "remove.svg")
+ self.remove_button = QPushButton()
+ self.remove_button.setIcon(QIcon(icon_path))
+
+ # Name and Entry
+ self.device_line_edit = DeviceLineEdit()
+ self.entry_line_edit = QLineEdit()
+
+ # Styling
+ self.color_button = ColorButton()
+ self.style_combo = StyleComboBox()
+ self.width = QSpinBox()
+ self.width.setMinimum(1)
+ self.width.setMaximum(20)
+
+ self.symbol_size = QSpinBox()
+ self.symbol_size.setMinimum(1)
+ self.symbol_size.setMaximum(20)
+
+ 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)
+
+ self.add_row_to_table()
+
+ 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)
+ self.table_widget.setCellWidget(self.row, 3, self.style_combo)
+ self.table_widget.setCellWidget(self.row, 4, self.width)
+ self.table_widget.setCellWidget(self.row, 5, self.symbol_size)
+ self.table_widget.setCellWidget(self.row, 6, self.remove_button)
+
+ @Slot()
+ def remove_row(self):
+ row = self.table_widget.indexAt(self.remove_button.pos()).row()
+ self.cleanup()
+ self.table_widget.removeRow(row)
+
+ def cleanup(self):
+ self.device_line_edit.cleanup()
+
+
+class StyleComboBox(QComboBox):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.addItems(["solid", "dash", "dot", "dashdot"])
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
new file mode 100644
index 00000000..32df4fa1
--- /dev/null
+++ b/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/curve_dialog.ui
@@ -0,0 +1,279 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 720
+ 806
+
+
+
+ Form
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+ -
+
+
+ X Axis
+
+
+
-
+
+
+ Name
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Orientation::Vertical
+
+
+
+ -
+
+
+ Entry
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ Y Axis
+
+
+
-
+
+
+ 0
+
+
+
+ Scan
+
+
+
+ 5
+
+
+ 5
+
+
+ 5
+
+
+ 5
+
+
-
+
+
+ Add Curve
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ 0
+
+
+ false
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+
+ Name
+
+
+
+
+ Entry
+
+
+
+
+ Color
+
+
+
+
+ Style
+
+
+
+
+ Width
+
+
+
+
+ Symbol Size
+
+
+
+
+ Delete
+
+
+
+
+
+
+
+
+ DAP
+
+
+
+ 5
+
+
+ 5
+
+
+ 5
+
+
+ 5
+
+ -
+
+
+ Add DAP
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+
+ 585
+ 20
+
+
+
+
+ -
+
+
+ true
+
+
+ true
+
+
+
+ Name
+
+
+
+
+ Entry
+
+
+
+
+ Model
+
+
+
+
+ Color
+
+
+
+
+ Style
+
+
+
+
+ Width
+
+
+
+
+ Symbol Size
+
+
+
+
+ Delete
+
+
+
+
+
+
+
+
+ Custom
+
+
+
+
+
+
+
+
+
+
+
+ DeviceLineEdit
+ QLineEdit
+
+
+
+
+
+
diff --git a/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/remove.svg b/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/remove.svg
new file mode 100644
index 00000000..5bf6de39
--- /dev/null
+++ b/bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/remove.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/bec_widgets/widgets/waveform/waveform_dialog/waveform_toolbar.py b/bec_widgets/widgets/waveform/waveform_toolbar/waveform_toolbar.py
similarity index 74%
rename from bec_widgets/widgets/waveform/waveform_dialog/waveform_toolbar.py
rename to bec_widgets/widgets/waveform/waveform_toolbar/waveform_toolbar.py
index d39f6be4..b1bf3419 100644
--- a/bec_widgets/widgets/waveform/waveform_dialog/waveform_toolbar.py
+++ b/bec_widgets/widgets/waveform/waveform_toolbar/waveform_toolbar.py
@@ -3,7 +3,17 @@ import os
from qtpy.QtCore import QSize
from qtpy.QtGui import QAction, QIcon
-from bec_widgets.widgets.toolbar.toolbar import ToolBarAction
+from bec_widgets.qt_utils.toolbar import ToolBarAction
+
+
+class CurveAction(ToolBarAction):
+ def add_to_toolbar(self, toolbar, target):
+ current_path = os.path.dirname(__file__)
+ parent_path = os.path.dirname(current_path)
+ icon = QIcon()
+ icon.addFile(os.path.join(parent_path, "assets", "line_axis.svg"), size=QSize(20, 20))
+ self.action = QAction(icon, "Open Curves Configuration", target)
+ toolbar.addAction(self.action)
class SettingsAction(ToolBarAction):
diff --git a/bec_widgets/widgets/waveform/waveform_widget.py b/bec_widgets/widgets/waveform/waveform_widget.py
index 0574f44c..f59f639d 100644
--- a/bec_widgets/widgets/waveform/waveform_widget.py
+++ b/bec_widgets/widgets/waveform/waveform_widget.py
@@ -7,13 +7,15 @@ import numpy as np
from qtpy import PYSIDE6
from qtpy.QtWidgets import QVBoxLayout, QWidget
+from bec_widgets.qt_utils.settings_dialog import SettingsDialog
+from bec_widgets.qt_utils.toolbar import ModularToolBar
from bec_widgets.utils import BECConnector
from bec_widgets.widgets.figure import BECFigure
-from bec_widgets.widgets.figure.plots.axis_settings import AxisSettingsDialog
+from bec_widgets.widgets.figure.plots.axis_settings import AxisSettings
from bec_widgets.widgets.figure.plots.waveform.waveform import Waveform1DConfig
from bec_widgets.widgets.figure.plots.waveform.waveform_curve import BECCurve
-from bec_widgets.widgets.toolbar import ModularToolBar
-from bec_widgets.widgets.waveform.waveform_dialog.waveform_toolbar import *
+from bec_widgets.widgets.waveform.waveform_toolbar.curve_dialog.curve_dialog import CurveSettings
+from bec_widgets.widgets.waveform.waveform_toolbar.waveform_toolbar import *
try:
import pandas as pd
@@ -70,7 +72,7 @@ class BECWaveformWidget(BECConnector, QWidget):
self.toolbar = ModularToolBar(
actions={
# "connect": ConnectAction(),
- # "history": ResetHistoryAction(),
+ "curves": CurveAction(),
"axis_settings": SettingsAction(),
"import": ImportAction(),
"export": ExportAction(),
@@ -88,7 +90,13 @@ class BECWaveformWidget(BECConnector, QWidget):
self._hook_actions()
+ # TEst actions
+ self.plot(x_name="samx", y_name="bpm4i")
+ self.plot(x_name="samx", y_name="bpm3a")
+ self.plot(x_name="samx", y_name="bpm6i")
+
def _hook_actions(self):
+ self.toolbar.widgets["curves"].action.triggered.connect(self.show_curve_settings)
self.toolbar.widgets["axis_settings"].action.triggered.connect(self.show_axis_settings)
self.toolbar.widgets["import"].action.triggered.connect(
lambda: self.load_config(path=None, gui=True)
@@ -98,9 +106,45 @@ class BECWaveformWidget(BECConnector, QWidget):
)
def show_axis_settings(self):
- dialog = AxisSettingsDialog(self, target_widget=self)
+ dialog = SettingsDialog(
+ self,
+ settings_widget=AxisSettings(),
+ window_title="Motor Map Settings",
+ config=self._config_dict["axis"],
+ )
dialog.exec()
+ def show_curve_settings(self):
+ dialog = SettingsDialog(
+ self,
+ settings_widget=CurveSettings(),
+ window_title="Curve Settings",
+ config=self.waveform._curves_data,
+ )
+ dialog.resize(800, 600)
+ dialog.exec()
+
+ def _check_if_scans_have_same_x(self, enabled=True, x_name_to_check: str = None) -> bool:
+ """
+ Check if all scans have the same x-axis.
+
+ Args:
+ enabled(bool): If True, check if all scans have the same x-axis.
+ x_name_to_check(str): The x-axis name to check.
+
+ Returns:
+ bool: True if all scans have the same x-axis, False otherwise.
+ """
+ if enabled and x_name_to_check is not None:
+ curves = self.waveform._curves_data["scan_segment"]
+
+ for label, curve in curves.items():
+ x_name = curve.config.signals.x.name
+ if x_name != x_name_to_check:
+ raise ValueError(
+ f"All scans must have the same x-axis. New curve provided with x-axis: {x_name}"
+ )
+
###################################
# User Access Methods from Waveform
###################################
@@ -144,6 +188,7 @@ class BECWaveformWidget(BECConnector, QWidget):
label: str | None = None,
validate: bool = True,
dap: str | None = None, # TODO add dap custom curve wrapper
+ **kwargs,
) -> BECCurve:
"""
Plot a curve to the plot widget.
@@ -165,6 +210,7 @@ class BECWaveformWidget(BECConnector, QWidget):
Returns:
BECCurve: The curve object.
"""
+ self._check_if_scans_have_same_x(enabled=True, x_name_to_check=x_name)
return self.waveform.plot(
x=x,
y=y,
@@ -179,6 +225,7 @@ class BECWaveformWidget(BECConnector, QWidget):
label=label,
validate=validate,
dap=dap,
+ **kwargs,
)
def add_dap(
@@ -410,6 +457,7 @@ class BECWaveformWidget(BECConnector, QWidget):
def cleanup(self):
self.fig.cleanup()
+ self.client.shutdown()
return super().cleanup()
def closeEvent(self, event):