diff --git a/bec_widgets/assets/toolbar_icons/fitting_parameters.svg b/bec_widgets/assets/toolbar_icons/fitting_parameters.svg
new file mode 100644
index 00000000..42b3bb15
--- /dev/null
+++ b/bec_widgets/assets/toolbar_icons/fitting_parameters.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/bec_widgets/widgets/figure/plots/waveform/waveform.py b/bec_widgets/widgets/figure/plots/waveform/waveform.py
index 5c7d9eca..be900ae9 100644
--- a/bec_widgets/widgets/figure/plots/waveform/waveform.py
+++ b/bec_widgets/widgets/figure/plots/waveform/waveform.py
@@ -75,6 +75,7 @@ class BECWaveform(BECPlotBase):
scan_signal_update = pyqtSignal()
async_signal_update = pyqtSignal()
dap_params_update = pyqtSignal(dict)
+ dap_summary_update = pyqtSignal(dict)
autorange_signal = pyqtSignal()
def __init__(
@@ -655,6 +656,19 @@ class BECWaveform(BECPlotBase):
params[curve_id] = curve.dap_params
return params
+ @pyqtSlot()
+ def get_dap_summary(self) -> dict:
+ """
+ Get the DAP summary of all DAP curves.
+
+ Returns:
+ dict: DAP summary of all DAP curves.
+ """
+ summary = {}
+ for curve_id, curve in self._curves_data["DAP"].items():
+ summary[curve_id] = curve.dap_summary
+ return summary
+
def _add_curve_object(
self,
name: str,
@@ -1071,7 +1085,9 @@ class BECWaveform(BECPlotBase):
y = msg["data"][0]["y"]
curve.setData(x, y)
curve.dap_params = msg["data"][1]["fit_parameters"]
+ curve.dap_summary = msg["data"][1]["fit_summary"]
self.dap_params_update.emit(curve.dap_params)
+ self.dap_summary_update.emit(curve.dap_summary)
break
@pyqtSlot(dict, dict)
diff --git a/bec_widgets/widgets/figure/plots/waveform/waveform_curve.py b/bec_widgets/widgets/figure/plots/waveform/waveform_curve.py
index cec0b4c4..2d5d910b 100644
--- a/bec_widgets/widgets/figure/plots/waveform/waveform_curve.py
+++ b/bec_widgets/widgets/figure/plots/waveform/waveform_curve.py
@@ -101,6 +101,7 @@ class BECCurve(BECConnector, pg.PlotDataItem):
self.parent_item = parent_item
self.apply_config()
self.dap_params = None
+ self.dap_summary = None
if kwargs:
self.set(**kwargs)
@@ -132,6 +133,14 @@ class BECCurve(BECConnector, pg.PlotDataItem):
def dap_params(self, value):
self._dap_params = value
+ @property
+ def dap_summary(self):
+ return self._dap_report
+
+ @dap_summary.setter
+ def dap_summary(self, value):
+ self._dap_report = value
+
def set_data(self, x, y):
if self.config.source == "custom":
self.setData(x, y)
diff --git a/bec_widgets/widgets/waveform/waveform_toolbar/dap_summary_dialog/__init__.py b/bec_widgets/widgets/waveform/waveform_toolbar/dap_summary_dialog/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/bec_widgets/widgets/waveform/waveform_toolbar/dap_summary_dialog/dap_summary.ui b/bec_widgets/widgets/waveform/waveform_toolbar/dap_summary_dialog/dap_summary.ui
new file mode 100644
index 00000000..49160460
--- /dev/null
+++ b/bec_widgets/widgets/waveform/waveform_toolbar/dap_summary_dialog/dap_summary.ui
@@ -0,0 +1,127 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 800
+ 600
+
+
+
+ Form
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ QFrame::Shape::VLine
+
+
+ QFrame::Shadow::Plain
+
+
+ 1
+
+
+ Qt::Orientation::Horizontal
+
+
+ true
+
+
+ true
+
+
+
+ Select Curve
+
+
+
-
+
+
+ Refresh DAP Summary
+
+
+
+ -
+
+
+
+
+
+
+
+ 2
+ 0
+
+
+
+ Qt::Orientation::Vertical
+
+
+
+ Fit Summary
+
+
+ -
+
+
+ false
+
+
+
+ Property
+
+
+
+
+ Value
+
+
+
+
+
+
+
+
+ Parameter Details
+
+
+ -
+
+
+
+ Parameter
+
+
+
+
+ Value
+
+
+
+
+ Std
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bec_widgets/widgets/waveform/waveform_toolbar/dap_summary_dialog/dap_summary_dialog.py b/bec_widgets/widgets/waveform/waveform_toolbar/dap_summary_dialog/dap_summary_dialog.py
new file mode 100644
index 00000000..6b4aeb1f
--- /dev/null
+++ b/bec_widgets/widgets/waveform/waveform_toolbar/dap_summary_dialog/dap_summary_dialog.py
@@ -0,0 +1,69 @@
+import os
+
+from qtpy.QtCore import Slot
+from qtpy.QtWidgets import QDialog, QTreeWidgetItem, QVBoxLayout
+
+from bec_widgets.utils import UILoader
+
+
+class FitSummaryWidget(QDialog):
+ def __init__(self, parent=None, target_widget=None):
+ super().__init__(parent=parent)
+
+ self.target_widget = target_widget
+ self.summary_data = self.target_widget.get_dap_summary()
+
+ self.setModal(True)
+
+ current_path = os.path.dirname(__file__)
+ self.ui = UILoader(self).loader(os.path.join(current_path, "dap_summary.ui"))
+ self.layout = QVBoxLayout(self)
+ self.layout.addWidget(self.ui)
+
+ self.ui.curve_list.currentItemChanged.connect(self.display_fit_details)
+ self.ui.refresh_button.clicked.connect(self.refresh_dap)
+
+ self.populate_curve_list()
+
+ def populate_curve_list(self):
+ for curve_name in self.summary_data.keys():
+ self.ui.curve_list.addItem(curve_name)
+
+ def display_fit_details(self, current):
+ if current:
+ curve_name = current.text()
+ data = self.summary_data[curve_name]
+ if data is None:
+ return
+ self.refresh_trees(data)
+
+ @Slot()
+ def refresh_dap(self):
+ self.ui.curve_list.clear()
+ self.summary_data = self.target_widget.get_dap_summary()
+ self.populate_curve_list()
+
+ def refresh_trees(self, data):
+ self.update_summary_tree(data)
+ self.update_param_tree(data["params"])
+
+ def update_summary_tree(self, data):
+ self.ui.summary_tree.clear()
+ properties = [
+ ("Model", data.get("model", "")),
+ ("Method", data.get("method", "")),
+ ("Chi-Squared", str(data.get("chisqr", ""))),
+ ("Reduced Chi-Squared", str(data.get("redchi", ""))),
+ ("AIC", str(data.get("aic", ""))),
+ ("BIC", str(data.get("bic", ""))),
+ ("R-Squared", str(data.get("rsquared", ""))),
+ ("Message", data.get("message", "")),
+ ]
+ for prop, val in properties:
+ QTreeWidgetItem(self.ui.summary_tree, [prop, val])
+
+ def update_param_tree(self, params):
+ self.ui.param_tree.clear()
+ for param in params:
+ param_name, param_value, param_std = param[0], str(param[1]), str(param[7])
+ QTreeWidgetItem(self.ui.param_tree, [param_name, param_value, param_std])
diff --git a/bec_widgets/widgets/waveform/waveform_toolbar/waveform_toolbar.py b/bec_widgets/widgets/waveform/waveform_toolbar/waveform_toolbar.py
index bb887a86..e369cb3a 100644
--- a/bec_widgets/widgets/waveform/waveform_toolbar/waveform_toolbar.py
+++ b/bec_widgets/widgets/waveform/waveform_toolbar/waveform_toolbar.py
@@ -41,6 +41,17 @@ class CurveAction(ToolBarAction):
toolbar.addAction(self.action)
+class FitParamsAction(ToolBarAction):
+ def add_to_toolbar(self, toolbar, target):
+ icon = QIcon()
+ icon.addFile(
+ os.path.join(MODULE_PATH, "assets", "toolbar_icons", "fitting_parameters.svg"),
+ size=QSize(20, 20),
+ )
+ self.action = QAction(icon, "Open Fitting Parameters", target)
+ toolbar.addAction(self.action)
+
+
class SettingsAction(ToolBarAction):
def add_to_toolbar(self, toolbar, target):
icon = QIcon()
diff --git a/bec_widgets/widgets/waveform/waveform_widget.py b/bec_widgets/widgets/waveform/waveform_widget.py
index 5efaf5a2..d429f42e 100644
--- a/bec_widgets/widgets/waveform/waveform_widget.py
+++ b/bec_widgets/widgets/waveform/waveform_widget.py
@@ -6,7 +6,7 @@ from typing import Literal
import numpy as np
from qtpy.QtWidgets import QVBoxLayout, QWidget
-from bec_widgets.qt_utils.error_popups import WarningPopupUtility, SafeSlot
+from bec_widgets.qt_utils.error_popups import SafeSlot, WarningPopupUtility
from bec_widgets.qt_utils.settings_dialog import SettingsDialog
from bec_widgets.qt_utils.toolbar import ModularToolBar, SeparatorAction
from bec_widgets.utils import BECConnector
@@ -15,6 +15,9 @@ 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.waveform.waveform_toolbar.curve_dialog.curve_dialog import CurveSettings
+from bec_widgets.widgets.waveform.waveform_toolbar.dap_summary_dialog.dap_summary_dialog import (
+ FitSummaryWidget,
+)
from bec_widgets.widgets.waveform.waveform_toolbar.waveform_toolbar import *
try:
@@ -73,6 +76,7 @@ class BECWaveformWidget(BECConnector, QWidget):
"matplotlib": MatplotlibAction(),
"separator_1": SeparatorAction(),
"curves": CurveAction(),
+ "fit_params": FitParamsAction(),
"axis_settings": SettingsAction(),
"separator_2": SeparatorAction(),
"import": ImportAction(),
@@ -95,13 +99,14 @@ class BECWaveformWidget(BECConnector, QWidget):
# TEst actions
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="bpm3a", dap="GaussianModel")
self.plot(x_name="samx", y_name="bpm6i")
def _hook_actions(self):
self.toolbar.widgets["save"].action.triggered.connect(self.export)
self.toolbar.widgets["matplotlib"].action.triggered.connect(self.export_to_matplotlib)
self.toolbar.widgets["curves"].action.triggered.connect(self.show_curve_settings)
+ self.toolbar.widgets["fit_params"].action.triggered.connect(self.show_fit_summary_dialog)
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)
@@ -129,6 +134,11 @@ class BECWaveformWidget(BECConnector, QWidget):
dialog.resize(800, 600)
dialog.exec()
+ def show_fit_summary_dialog(self):
+ dialog = FitSummaryWidget(target_widget=self)
+ dialog.resize(800, 600)
+ dialog.show()
+
def _check_if_scans_have_same_x(
self, enabled=True, x_name_to_check: str = None
) -> bool: # TODO probably not needed anymore
@@ -307,7 +317,16 @@ class BECWaveformWidget(BECConnector, QWidget):
dict: DAP parameters of all DAP curves.
"""
- self.waveform.get_dap_params()
+ return self.waveform.get_dap_params()
+
+ def get_dap_summary(self) -> dict:
+ """
+ Get the DAP summary of all DAP curves.
+
+ Returns:
+ dict: DAP summary of all DAP curves.
+ """
+ return self.waveform.get_dap_summary()
def remove_curve(self, *identifiers):
"""